private static float4 Sample2(sampler tex, float4 coords, float polar)
        {
            float4 tex1 = tex.SampleLevel(coords.xy);
            float4 tex2 = tex.SampleLevel(coords.zw);

            return(_lerp(tex1, tex2, polar));
        }
Beispiel #2
0
        internal List <float> GetTimesForBone(string bone, library_visual_scenes lvs)
        {
            List <float> ret = new List <float>();

            foreach (channel chan in mChannels)
            {
                //extract the node name and address
                int    sidx = chan.target.IndexOf('/');
                string sid  = chan.target.Substring(0, sidx);

                //ok this is tricky, the spec says that the <source>
                //referenced by the input with the JOINT semantic
                //should contain a <Name_array> that contains sids
                //to identify the joint nodes.  sids are used instead
                //of IDREFs to allow a skin controller to be instantiated
                //multiple times, where each instance can be animated
                //independently.
                //
                //So max's default collada exporter doesn't even give the
                //bones sids at all, and the other one whose name escapes
                //me gives the bones sids, but then the address element
                //says Name (note the case), so I guess you need to try
                //to match via sid first and if that fails, use name?
                node n = AnimForm.LookUpNode(lvs, sid);

                if (n == null)
                {
                    continue;
                }

                if (bone != n.name)
                {
                    continue;
                }

                //grab sampler key
                string sampKey = chan.source;

                //strip #
                sampKey = sampKey.Substring(1);

                sampler samp   = mSamplers[sampKey];
                string  srcInp = GetSourceForSemantic(samp, "INPUT");

                float_array srcTimes = mSources[srcInp].Item as float_array;

                foreach (float time in srcTimes.Values)
                {
                    float t = time;
                    if (ret.Contains(t))
                    {
                        continue;
                    }
                    ret.Add(t);
                }
            }

            return(ret);
        }
        private static float4 SampleSmooth(sampler tex, float2 coord, float2 size)
        {
            float2 pix = coord * size;
            float2 sub = _floor(pix - 0.5f) + 0.5f;
            float2 f   = pix - sub;

            f = _smoothstep(_float2(0.0f, 0.0f), _float2(1.0f, 1.0f), f);

            return(tex.SampleLevel((sub + f) / size));
        }
Beispiel #4
0
        string GetSourceForSemantic(sampler samp, string sem)
        {
            string srcInp = "";

            foreach (InputLocal inp in samp.input)
            {
                if (inp.semantic == sem)
                {
                    srcInp = inp.source.Substring(1);
                }
            }
            return(srcInp);
        }
        private static bool PrepareTexture(Texture texture, bool repX, bool repY, ref sampler sam)
        {
            sam.set = false;
#if UNITY_EDITOR
            SgtHelper.MakeTextureReadable(texture);
#endif
            if (texture != null && texture is Texture2D)
            {
                sam.set  = true;
                sam.tex  = (Texture2D)texture;
                sam.repX = repX;
                sam.repY = repY;
                sam.w    = 0.5f / texture.width;
                sam.h    = 0.5f / texture.height;
            }

            return(sam.set);
        }
Beispiel #6
0
 public SubAnimation(animation anim)
 {
     foreach (object anObj in anim.Items)
     {
         if (anObj is source)
         {
             source src = anObj as source;
             mSources.Add(src.id, src);
         }
         else if (anObj is sampler)
         {
             sampler samp = anObj as sampler;
             mSamplers.Add(samp.id, samp);
         }
         else if (anObj is channel)
         {
             channel chan = anObj as channel;
             mChannels.Add(chan);
         }
     }
 }
Beispiel #7
0
        private void ImportSampler()
        {
            sampler sampler = null;

            foreach (var item in Animation.Items)
            {
                if (item is sampler)
                {
                    sampler = item as sampler;
                    break;
                }
            }

            if (sampler == null)
            {
                throw new ParsingException("Animation " + Animation.id + " has no sampler!");
            }

            ColladaSource inputSource = null, outputSource = null, interpolationSource = null;

            foreach (var input in sampler.input)
            {
                if (input.source[0] != '#')
                {
                    throw new ParsingException("Only ID references are supported for animation input sources");
                }

                ColladaSource source;
                if (!Sources.TryGetValue(input.source.Substring(1), out source))
                {
                    throw new ParsingException("Animation sampler " + input.semantic + " references nonexistent source: " + input.source);
                }

                switch (input.semantic)
                {
                case "INPUT":
                    inputSource = source;
                    break;

                case "OUTPUT":
                    outputSource = source;
                    break;

                case "INTERPOLATION":
                    interpolationSource = source;
                    break;

                default:
                    break;
                }
            }

            if (inputSource == null || outputSource == null || interpolationSource == null)
            {
                throw new ParsingException("Animation " + Animation.id + " must have an INPUT, OUTPUT and INTERPOLATION sampler input!");
            }

            if (!inputSource.FloatParams.TryGetValue("TIME", out Times))
            {
                Times = inputSource.FloatParams.Values.SingleOrDefault();
            }

            if (Times == null)
            {
                throw new ParsingException("Animation " + Animation.id + " INPUT must have a TIME parameter!");
            }

            if (!outputSource.MatrixParams.TryGetValue("TRANSFORM", out Transforms))
            {
                Transforms = outputSource.MatrixParams.Values.SingleOrDefault();
            }

            if (Transforms == null)
            {
                throw new ParsingException("Animation " + Animation.id + " OUTPUT must have a TRANSFORM parameter!");
            }

            if (Transforms.Count != Times.Count)
            {
                throw new ParsingException("Animation " + Animation.id + " has different time and transform counts!");
            }

            for (var i = 0; i < Transforms.Count; i++)
            {
                var m = Transforms[i];
                m.Transpose();
                Transforms[i] = m;
            }
        }
        protected void CreateChannel(List <object> listAnimationObjects, uint caseIndex, string sData, string sDataType, string sTarget, List <double> lValues)
        {
            string sTime   = string.Format("sTime_{0}_ID", caseIndex);
            string sInterp = string.Format("sInterp_{0}_ID", caseIndex);

            // source
            source source_ = new source()
            {
                id   = string.Format("sValue_{0}_{1}_ID", caseIndex, sData),
                name = string.Format("sValue_{0}_{1}", caseIndex, sData),
                Item = new float_array()
                {
                    id = string.Format("fa_{0}_{1}_ID", caseIndex, sData), count = (ulong)lValues.Count, Values = lValues.ToArray()
                },
                technique_common = new sourceTechnique_common()
                {
                    accessor = new accessor()
                    {
                        source = string.Format("#fa_{0}_{1}_ID", caseIndex, sData),
                        count  = (ulong)lValues.Count,
                        stride = 1,
                        param  = new param[]
                        {
                            new param()
                            {
                                name = sDataType, type = "float"
                            }
                        }
                    }
                }
            };

            listAnimationObjects.Add(source_);

            // sampler
            sampler sampler_ = new sampler()
            {
                id    = string.Format("sampler_{0}_{1}_ID", caseIndex, sData),
                input = new InputLocal[]
                {
                    new InputLocal()
                    {
                        semantic = "INPUT", source = "#" + sTime
                    },
                    new InputLocal()
                    {
                        semantic = "OUTPUT", source = "#" + source_.id
                    },
                    new InputLocal()
                    {
                        semantic = "INTERPOLATION", source = "#" + sInterp
                    }
                }
            };

            listAnimationObjects.Add(sampler_);

            // channel
            channel channel_ = new channel()
            {
                source = "#" + sampler_.id,
                target = sTarget
            };

            listAnimationObjects.Add(channel_);
        }
        public List <animation> ExportKeyframeTrack(TransformTrack transformTrack, string name, string target)
        {
            var track = transformTrack.ToKeyframes();

            track.MergeAdjacentFrames();
            track.InterpolateFrames();

            var anims  = new List <animation>();
            var inputs = new List <InputLocal>();

            var outputs = new List <float>(track.Keyframes.Count * 16);

            foreach (var keyframe in track.Keyframes.Values)
            {
                var transform = keyframe.ToTransform().ToMatrix4();
                transform.Transpose();
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        outputs.Add(transform[i, j]);
                    }
                }
            }

            var interpolations = new List <string>(track.Keyframes.Count);

            for (int i = 0; i < track.Keyframes.Count; i++)
            {
                interpolations.Add("LINEAR");
            }

            var knots = new List <float>(track.Keyframes.Count);

            foreach (var keyframe in track.Keyframes)
            {
                knots.Add(keyframe.Key);
            }

            /*
             * Fix up animations that have only one keyframe by adding another keyframe at
             * the end of the animation.
             * (This mainly applies to DaIdentity and DnConstant32f)
             */
            if (track.Keyframes.Count == 1)
            {
                knots.Add(transformTrack.ParentAnimation.Duration);
                for (int i = 0; i < 16; i++)
                {
                    outputs.Add(outputs[i]);
                }
                interpolations.Add(interpolations[0]);
            }

            var knotsSource = ColladaUtils.MakeFloatSource(name, "inputs", new string[] { "TIME" }, knots.ToArray());
            var knotsInput  = new InputLocal();

            knotsInput.semantic = "INPUT";
            knotsInput.source   = "#" + knotsSource.id;
            inputs.Add(knotsInput);

            var outSource = ColladaUtils.MakeFloatSource(name, "outputs", new string[] { "TRANSFORM" }, outputs.ToArray(), 16, "float4x4");
            var outInput  = new InputLocal();

            outInput.semantic = "OUTPUT";
            outInput.source   = "#" + outSource.id;
            inputs.Add(outInput);

            var interpSource = ColladaUtils.MakeNameSource(name, "interpolations", new string[] { "INTERPOLATION" }, interpolations.ToArray());

            var interpInput = new InputLocal();

            interpInput.semantic = "INTERPOLATION";
            interpInput.source   = "#" + interpSource.id;
            inputs.Add(interpInput);

            var sampler = new sampler();

            sampler.id    = name + "_sampler";
            sampler.input = inputs.ToArray();

            var channel = new channel();

            channel.source = "#" + sampler.id;
            channel.target = target;

            var animation = new animation();

            animation.id   = name;
            animation.name = name;
            var animItems = new List <object>();

            animItems.Add(knotsSource);
            animItems.Add(outSource);
            animItems.Add(interpSource);
            animItems.Add(sampler);
            animItems.Add(channel);
            animation.Items = animItems.ToArray();
            anims.Add(animation);
            return(anims);
        }
Beispiel #10
0
        public List <animation> ExportTransform(IList <Keyframe> keyframes, string name, string target)
        {
            var anims  = new List <animation>();
            var inputs = new List <InputLocal>();

            var outputs = new List <float>(keyframes.Count * 16);

            foreach (var keyframe in keyframes)
            {
                var transform = Matrix4.Identity;
                if (keyframe.hasRotation)
                {
                    transform *= Matrix4.CreateFromQuaternion(keyframe.rotation.Inverted());
                }

                if (keyframe.hasScaleShear)
                {
                    var scaleShear = Matrix4.Identity;
                    for (int i = 0; i < 3; i++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            scaleShear[i, j] = keyframe.scaleShear[i, j];
                        }
                    }

                    transform *= scaleShear;
                }

                if (keyframe.hasTranslation)
                {
                    transform[0, 3] += keyframe.translation[0];
                    transform[1, 3] += keyframe.translation[1];
                    transform[2, 3] += keyframe.translation[2];
                }

                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        outputs.Add(transform[i, j]);
                    }
                }
            }

            var interpolations = new List <string>(keyframes.Count);

            for (int i = 0; i < keyframes.Count; i++)
            {
                // TODO: Add control point estimation code and add in/out tangents for Bezier
                //interpolations.Add("BEZIER");
                interpolations.Add("LINEAR");
            }

            var knots = new List <float>(keyframes.Count);

            foreach (var keyframe in keyframes)
            {
                knots.Add(keyframe.time);
            }

            /*
             * Fix up animations that have only one keyframe by adding another keyframe at
             * the end of the animation.
             * (This mainly applies to DaIdentity and DnConstant32f)
             */
            if (keyframes.Count == 1)
            {
                knots.Add(ParentAnimation.Duration);
                for (int i = 0; i < 16; i++)
                {
                    outputs.Add(outputs[i]);
                }
                interpolations.Add(interpolations[0]);
            }

            var knotsSource = ColladaUtils.MakeFloatSource(name, "inputs", new string[] { "TIME" }, knots.ToArray());
            var knotsInput  = new InputLocal();

            knotsInput.semantic = "INPUT";
            knotsInput.source   = "#" + knotsSource.id;
            inputs.Add(knotsInput);

            var outSource = ColladaUtils.MakeFloatSource(name, "outputs", new string[] { "TRANSFORM" }, outputs.ToArray(), 16, "float4x4");
            var outInput  = new InputLocal();

            outInput.semantic = "OUTPUT";
            outInput.source   = "#" + outSource.id;
            inputs.Add(outInput);

            var interpSource = ColladaUtils.MakeNameSource(name, "interpolations", new string[] { "INTERPOLATION" }, interpolations.ToArray());

            var interpInput = new InputLocal();

            interpInput.semantic = "INTERPOLATION";
            interpInput.source   = "#" + interpSource.id;
            inputs.Add(interpInput);

            var sampler = new sampler();

            sampler.id    = name + "_sampler";
            sampler.input = inputs.ToArray();

            var channel = new channel();

            channel.source = "#" + sampler.id;
            channel.target = target;

            var animation = new animation();

            animation.id   = name;
            animation.name = name;
            var animItems = new List <object>();

            animItems.Add(knotsSource);
            animItems.Add(outSource);
            animItems.Add(interpSource);
            animItems.Add(sampler);
            animItems.Add(channel);
            animation.Items = animItems.ToArray();
            anims.Add(animation);
            return(anims);
        }
Beispiel #11
0
        internal Animation.KeyPartsUsed SetKeys(string bone,
                                                List <float> times, List <MeshLib.KeyFrame> keys,
                                                library_visual_scenes scenes,
                                                List <MeshLib.KeyFrame> axisAngleKeys)
        {
            Animation.KeyPartsUsed ret = 0;

            foreach (channel chan in mChannels)
            {
                //extract the node name and address
                int    sidx = chan.target.IndexOf('/');
                string sid  = chan.target.Substring(0, sidx);

                node n = AnimForm.LookUpNode(scenes, sid);

                if (n == null)
                {
                    continue;
                }

                if (bone != n.name)
                {
                    continue;
                }

                //grab sampler key
                string sampKey = chan.source;

                //strip #
                sampKey = sampKey.Substring(1);

                sampler samp = mSamplers[sampKey];

                string srcInp = GetSourceForSemantic(samp, "INPUT");
                string srcOut = GetSourceForSemantic(samp, "OUTPUT");
                string srcC1  = GetSourceForSemantic(samp, "IN_TANGENT");
                string srcC2  = GetSourceForSemantic(samp, "OUT_TANGENT");

                float_array  chanTimes  = mSources[srcInp].Item as float_array;
                float_array  chanValues = mSources[srcOut].Item as float_array;
                List <float> outValues  = new List <float>();

                int numChanKeys = chanValues.Values.Length;

                numChanKeys /= (int)mSources[srcOut].technique_common.accessor.stride;

                Debug.Assert(numChanKeys == (int)chanTimes.count);

                //grab values for this channel
                //along the overall list of times
                for (int tidx = 0; tidx < times.Count; tidx++)
                {
                    outValues.AddRange(LerpValue(times[tidx], chanTimes,
                                                 chanValues,
                                                 (int)mSources[srcOut].technique_common.accessor.stride));
                }

                int    slashIndex  = chan.target.IndexOf("/");
                string nodeID      = chan.target.Substring(0, slashIndex);
                string nodeElement = chan.target.Substring(slashIndex + 1);

                //see if the element has an additional address
                string addr   = null;
                int    dotIdx = nodeElement.IndexOf('.');
                if (dotIdx != -1)
                {
                    addr        = nodeElement.Substring(dotIdx + 1);
                    nodeElement = nodeElement.Substring(0, dotIdx);
                }

                node targeted = AnimForm.LookUpNode(scenes, nodeID);
                int  idx      = AnimForm.GetNodeItemIndex(targeted, nodeElement);

                if (idx == -1)
                {
                    continue;                           //bad anim stuffs?
                }

                if (targeted.ItemsElementName[idx] == ItemsChoiceType2.lookat)
                {
                    Debug.Assert(false);                        //haven't dealt with this one yet
                }
                else if (targeted.ItemsElementName[idx] == ItemsChoiceType2.matrix)
                {
                    //this doesn't really work yet
                    List <Matrix> mats = AnimForm.GetMatrixListFromFloatList(outValues);
                    for (int v = 0; v < mats.Count; v++)
                    {
                        mats[v].Decompose(out keys[v].mScale, out keys[v].mRotation, out keys[v].mPosition);
                    }
                    ret |= Animation.KeyPartsUsed.All;
                }
                else if (targeted.ItemsElementName[idx] == ItemsChoiceType2.rotate)
                {
                    if (addr == null)
                    {
                        //I'm guessing these would be true quaternions
                        //I don't really support that, as I store the
                        //usual axis angle stuff I've seen in a quaternion
                        //and then later fix it up to be a real quaternion
                        Debug.Assert(false);
                    }
                    else if (addr == "ANGLE")
                    {
                        Debug.Assert(targeted.Items[idx] is rotate);

                        rotate rot = targeted.Items[idx] as rotate;

                        if (rot.Values[0] > 0.999f)
                        {
                            for (int v = 0; v < outValues.Count; v++)
                            {
                                float val = outValues[v];
                                keys[v].mRotation.X = val;
                                if (!axisAngleKeys.Contains(keys[v]))
                                {
                                    axisAngleKeys.Add(keys[v]);
                                }
                            }
                            ret |= Animation.KeyPartsUsed.RotateX;
                        }
                        else if (rot.Values[1] > 0.999f)
                        {
                            for (int v = 0; v < outValues.Count; v++)
                            {
                                float val = outValues[v];
                                keys[v].mRotation.Y = val;
                                if (!axisAngleKeys.Contains(keys[v]))
                                {
                                    axisAngleKeys.Add(keys[v]);
                                }
                            }
                            ret |= Animation.KeyPartsUsed.RotateY;
                        }
                        else if (rot.Values[2] > 0.999f)
                        {
                            for (int v = 0; v < outValues.Count; v++)
                            {
                                float val = outValues[v];
                                keys[v].mRotation.Z = val;
                                if (!axisAngleKeys.Contains(keys[v]))
                                {
                                    axisAngleKeys.Add(keys[v]);
                                }
                            }
                            ret |= Animation.KeyPartsUsed.RotateZ;
                        }
                        else
                        {
                            Debug.Assert(false);                                //broken!
                        }
                    }
                }
                else if (targeted.ItemsElementName[idx] == ItemsChoiceType2.scale)
                {
                    if (addr == null)
                    {
                        //I haven't seen this happen, but I'm guessing it
                        //would be vector3s
                        for (int v = 0; v < outValues.Count; v += 3)
                        {
                            keys[v / 3].mScale.X = outValues[v];
                            keys[v / 3].mScale.Y = outValues[v + 1];
                            keys[v / 3].mScale.Z = outValues[v + 2];
                        }
                        ret |= Animation.KeyPartsUsed.ScaleX;
                        ret |= Animation.KeyPartsUsed.ScaleY;
                        ret |= Animation.KeyPartsUsed.ScaleZ;
                    }
                    else if (addr == "X")
                    {
                        for (int v = 0; v < outValues.Count; v++)
                        {
                            float val = outValues[v];
                            keys[v].mScale.X = val;
                        }
                        ret |= Animation.KeyPartsUsed.ScaleX;
                    }
                    else if (addr == "Y")
                    {
                        for (int v = 0; v < outValues.Count; v++)
                        {
                            float val = outValues[v];
                            keys[v].mScale.Y = val;
                        }
                        ret |= Animation.KeyPartsUsed.ScaleY;
                    }
                    else if (addr == "Z")
                    {
                        for (int v = 0; v < outValues.Count; v++)
                        {
                            float val = outValues[v];
                            keys[v].mScale.Z = val;
                        }
                        ret |= Animation.KeyPartsUsed.ScaleZ;
                    }
                }
                else if (targeted.ItemsElementName[idx] == ItemsChoiceType2.skew)
                {
                    Debug.Assert(false);                        //haven't dealt with this one yet
                }
                else if (targeted.ItemsElementName[idx] == ItemsChoiceType2.translate)
                {
                    if (addr == null)
                    {
                        //the values are vector3s in this case
                        for (int v = 0; v < outValues.Count; v += 3)
                        {
                            keys[v / 3].mPosition.X = outValues[v];
                            keys[v / 3].mPosition.Y = outValues[v + 1];
                            keys[v / 3].mPosition.Z = outValues[v + 2];
                        }
                        ret |= Animation.KeyPartsUsed.TranslateX;
                        ret |= Animation.KeyPartsUsed.TranslateY;
                        ret |= Animation.KeyPartsUsed.TranslateZ;
                    }
                    else if (addr == "X")
                    {
                        for (int v = 0; v < outValues.Count; v++)
                        {
                            float val = outValues[v];
                            keys[v].mPosition.X = val;
                        }
                        ret |= Animation.KeyPartsUsed.TranslateX;
                    }
                    else if (addr == "Y")
                    {
                        for (int v = 0; v < outValues.Count; v++)
                        {
                            float val = outValues[v];
                            keys[v].mPosition.Y = val;
                        }
                        ret |= Animation.KeyPartsUsed.TranslateY;
                    }
                    else if (addr == "Z")
                    {
                        for (int v = 0; v < outValues.Count; v++)
                        {
                            float val = outValues[v];
                            keys[v].mPosition.Z = val;
                        }
                        ret |= Animation.KeyPartsUsed.TranslateZ;
                    }
                }
            }
            return(ret);
        }
Beispiel #12
0
        public List <animation> ExportTransform(string name, string target)
        {
            var anims = new List <animation>();

            if (NumKnots() > 0)
            {
                var inputs   = new List <InputLocal>();
                var numKnots = NumKnots();
                var knots    = GetKnots();

                var outputs = new List <float>(knots.Count * 16);
                var quats   = GetQuaternions();
                foreach (var rotation in quats)
                {
                    var transform = Matrix4.CreateFromQuaternion(rotation);
                    for (int i = 0; i < 4; i++)
                    {
                        for (int j = 0; j < 4; j++)
                        {
                            outputs.Add(transform[i, j]);
                        }
                    }
                }

                var interpolations = new List <string>(numKnots);
                for (int i = 0; i < numKnots; i++)
                {
                    // TODO: Add control point estimation code and add in/out tangents for Bezier
                    //interpolations.Add("BEZIER");
                    interpolations.Add("LINEAR");
                }

                /*
                 * Fix up animations that have only one keyframe by adding another keyframe at
                 * the end of the animation.
                 * (This mainly applies to DaIdentity and DnConstant32f)
                 */
                if (numKnots == 1)
                {
                    knots.Add(ParentAnimation.Duration);
                    for (int i = 0; i < 16; i++)
                    {
                        outputs.Add(outputs[i]);
                    }
                    interpolations.Add(interpolations[0]);
                }

                var knotsSource = ColladaUtils.MakeFloatSource(name, "inputs", new string[] { "TIME" }, knots.ToArray());
                var knotsInput  = new InputLocal();
                knotsInput.semantic = "INPUT";
                knotsInput.source   = "#" + knotsSource.id;
                inputs.Add(knotsInput);

                var outSource = ColladaUtils.MakeFloatSource(name, "outputs", new string[] { "TRANSFORM" }, outputs.ToArray(), 16, "float4x4");
                var outInput  = new InputLocal();
                outInput.semantic = "OUTPUT";
                outInput.source   = "#" + outSource.id;
                inputs.Add(outInput);

                var interpSource = ColladaUtils.MakeNameSource(name, "interpolations", new string[] { "" }, interpolations.ToArray());

                var interpInput = new InputLocal();
                interpInput.semantic = "INTERPOLATION";
                interpInput.source   = "#" + interpSource.id;
                inputs.Add(interpInput);

                var sampler = new sampler();
                sampler.id    = name + "_sampler";
                sampler.input = inputs.ToArray();

                var channel = new channel();
                channel.source = "#" + sampler.id;
                channel.target = target;

                var animation = new animation();
                animation.id   = name;
                animation.name = name;
                var animItems = new List <object>();
                animItems.Add(knotsSource);
                animItems.Add(outSource);
                animItems.Add(interpSource);
                animItems.Add(sampler);
                animItems.Add(channel);
                animation.Items = animItems.ToArray();
                anims.Add(animation);
            }
            return(anims);
        }
Beispiel #13
0
        public animation ExportChannel(string name, string target, string paramName, int coordinate, bool isRotation)
        {
            var inputs   = new List <InputLocal>();
            var numKnots = NumKnots();
            var knots    = GetKnots();
            var outputs  = ExportChannelControlData(coordinate, isRotation);

            var interpolations = new List <string>(numKnots);

            for (int i = 0; i < numKnots; i++)
            {
                // TODO: Add control point estimation code and add in/out tangents for Bezier
                //interpolations.Add("BEZIER");
                interpolations.Add("LINEAR");
            }

            /*
             * Fix up animations that have only one keyframe by adding another keyframe at
             * the end of the animation.
             * (This mainly applies to DaIdentity and DnConstant32f)
             */
            if (numKnots == 1)
            {
                knots.Add(ParentAnimation.Duration);
                outputs.Add(outputs[0]);
                interpolations.Add(interpolations[0]);
            }

            var knotsSource = ColladaUtils.MakeFloatSource(name, "inputs", new string[] { "TIME" }, knots.ToArray());
            var knotsInput  = new InputLocal();

            knotsInput.semantic = "INPUT";
            knotsInput.source   = "#" + knotsSource.id;
            inputs.Add(knotsInput);

            var outSource = ColladaUtils.MakeFloatSource(name, "outputs", new string[] { paramName }, outputs.ToArray());
            var outInput  = new InputLocal();

            outInput.semantic = "OUTPUT";
            outInput.source   = "#" + outSource.id;
            inputs.Add(outInput);

            var interpSource = ColladaUtils.MakeNameSource(name, "interpolations", new string[] { "" }, interpolations.ToArray());

            var interpInput = new InputLocal();

            interpInput.semantic = "INTERPOLATION";
            interpInput.source   = "#" + interpSource.id;
            inputs.Add(interpInput);

            var sampler = new sampler();

            sampler.id    = name + "_sampler";
            sampler.input = inputs.ToArray();

            var channel = new channel();

            channel.source = "#" + sampler.id;
            channel.target = target;

            var animation = new animation();

            animation.id   = name;
            animation.name = name;
            var animItems = new List <object>();

            animItems.Add(knotsSource);
            animItems.Add(outSource);
            animItems.Add(interpSource);
            animItems.Add(sampler);
            animItems.Add(channel);
            animation.Items = animItems.ToArray();
            return(animation);
        }