CreateNodeTrack() public method

Creates an NodeAnimationTrack.
public CreateNodeTrack ( ushort handle ) : NodeAnimationTrack
handle ushort Handle to give the track, used for accessing the track later.
return NodeAnimationTrack
        protected void TransformAnimation(Matrix4 exportTransform, 
										  Animation newAnim, Animation anim,
										  Skeleton newSkeleton)
        {
            foreach (NodeAnimationTrack track in anim.NodeTracks.Values) {
                NodeAnimationTrack newTrack = newAnim.CreateNodeTrack(track.Handle);
                Bone targetBone = (Bone)track.TargetNode;
                newTrack.TargetNode = newSkeleton.GetBone(targetBone.Handle);
                TransformTrack(exportTransform, newTrack, track, targetBone);
            }
        }
Example #2
0
		/// <summary>
		///    Reads an animation track.
		/// </summary>
		protected void ReadAnimationTrack( BinaryReader reader, Animation anim )
		{
			// read the bone handle to apply this track to
			ushort boneHandle = ReadUShort( reader );

			// get a reference to the target bone
			Bone targetBone = skeleton.GetBone( boneHandle );

			// create an animation track for this bone
			NodeAnimationTrack track = anim.CreateNodeTrack( boneHandle, targetBone );

			// keep reading all keyframes for this track
			if ( !IsEOF( reader ) )
			{
				SkeletonChunkID chunkID = ReadChunk( reader );
				while ( !IsEOF( reader ) && ( chunkID == SkeletonChunkID.KeyFrame ) )
				{
					// read the key frame
					ReadKeyFrame( reader, track );
					// read the next chunk id
					// If we're not end of file get the next chunk ID
					if ( !IsEOF( reader ) )
					{
						chunkID = ReadChunk( reader );
					}
				}
				// backpedal to the start of the chunk
				if ( !IsEOF( reader ) )
				{
					Seek( reader, -ChunkOverheadSize );
				}
			}
		}
        public static void CleanupAnimation(Skeleton skel, Animation anim)
        {
            Animation newAnim = skel.CreateAnimation("_replacement", anim.Length);
            Animation tmpAnim = skel.CreateAnimation("_temporary", anim.Length);
            foreach (NodeAnimationTrack track in anim.NodeTracks.Values) {
                Bone bone = skel.GetBone((ushort)track.Handle);
                NodeAnimationTrack newTrack = newAnim.CreateNodeTrack(track.Handle, bone);
                NodeAnimationTrack tmpTrack = tmpAnim.CreateNodeTrack(track.Handle, bone);
                int maxFrame = track.KeyFrames.Count;
                int lastKeyFrame = -1;
                for (int keyFrameIndex = 0; keyFrameIndex < track.KeyFrames.Count; ++keyFrameIndex) {
                    if (anim.InterpolationMode == InterpolationMode.Linear) {
                        // Linear is based on one point before and one after.
                        TransformKeyFrame cur = track.GetTransformKeyFrame(keyFrameIndex);
                        if (keyFrameIndex == 0 ||
                            keyFrameIndex == (track.KeyFrames.Count - 1)) {
                            // Add the key frame if it is the first or last keyframe.
                            lastKeyFrame = keyFrameIndex;
                            DuplicateKeyFrame(newTrack, cur);
                        } else {
                            // Make sure tmpTrack is clean.. we just use it for interpolation
                            tmpTrack.RemoveAllKeyFrames();
                            TransformKeyFrame prior = track.GetTransformKeyFrame(lastKeyFrame);
                            TransformKeyFrame next = track.GetTransformKeyFrame(keyFrameIndex + 1);
                            DuplicateKeyFrame(tmpTrack, prior);
                            DuplicateKeyFrame(tmpTrack, next);
                            // Check to see if removing this last keyframe will throw off
                            // any of the other keyframes that were considered redundant.
                            bool needKeyFrame = false;
                            for (int i = lastKeyFrame + 1; i <= keyFrameIndex; ++i) {
                                TransformKeyFrame orig = track.GetTransformKeyFrame(i);
                                TransformKeyFrame interp = new TransformKeyFrame(tmpTrack, orig.Time);
                                tmpTrack.GetInterpolatedKeyFrame(orig.Time, interp);
                                // Is this interpolated frame useful or redundant?
                                if (!CompareKeyFrames(interp, cur)) {
                                    needKeyFrame = true;
                                    break;
                                }
                            }
                            if (needKeyFrame) {
                                lastKeyFrame = keyFrameIndex;
                                DuplicateKeyFrame(newTrack, cur);
                            }
                        }
                    } else if (anim.InterpolationMode == InterpolationMode.Spline) {
                        // Spline is based on two points before and two after.
                        TransformKeyFrame cur = track.GetTransformKeyFrame(keyFrameIndex);
            #if DISABLED_CODE
                        if (keyFrameIndex == 0 ||
                            keyFrameIndex == 1 ||
                            keyFrameIndex == (track.KeyFrames.Count - 1) ||
                            keyFrameIndex == (track.KeyFrames.Count - 2)) {
                            // Add the key frame if it is the first, second, last or second to last keyframe.
                            DuplicateKeyFrame(newTrack, cur);
                        } else {
                            // Make sure tmpTrack is clean.. we just use it for interpolation
                            tmpTrack.RemoveAllKeyFrames();
                            TransformKeyFrame prior1 = track.GetTransformKeyFrame(keyFrameIndex - 2);
                            TransformKeyFrame prior2 = track.GetTransformKeyFrame(keyFrameIndex - 1);
                            TransformKeyFrame next1 = track.GetTransformKeyFrame(keyFrameIndex + 1);
                            TransformKeyFrame next2 = track.GetTransformKeyFrame(keyFrameIndex + 2);
                            DuplicateKeyFrame(tmpTrack, prior1);
                            DuplicateKeyFrame(tmpTrack, prior2);
                            DuplicateKeyFrame(tmpTrack, next1);
                            DuplicateKeyFrame(tmpTrack, next2);
                            TransformKeyFrame interp = new TransformKeyFrame(tmpTrack, cur.Time);
                            tmpTrack.GetInterpolatedKeyFrame(cur.Time, interp);
                            // Is this interpolated frame useful or redundant?
                            if (!CompareKeyFrames(interp, cur))
                                DuplicateKeyFrame(newTrack, cur);
                        }
            #else
                        DuplicateKeyFrame(newTrack, cur);
            #endif
                    } else {
                        System.Diagnostics.Debug.Assert(false, "Invalid InterpolationMode: " + anim.InterpolationMode);
                    }
                }
            }
            skel.RemoveAnimation(tmpAnim.Name);
            skel.RemoveAnimation(newAnim.Name);
            skel.RemoveAnimation(anim.Name);

            // Recreate the animation with the proper name (awkward)
            anim = skel.CreateAnimation(anim.Name, anim.Length);
            foreach (NodeAnimationTrack track in newAnim.NodeTracks.Values) {
                Bone bone = skel.GetBone((ushort)track.Handle);
                NodeAnimationTrack newTrack = anim.CreateNodeTrack(track.Handle, bone);
                foreach (KeyFrame keyFrame in track.KeyFrames)
                    DuplicateKeyFrame(newTrack, (TransformKeyFrame)keyFrame);
            }
        }
        protected void ReadTrack(XmlNode node, Animation anim)
        {
            string boneName = node.Attributes["bone"].Value;
            // get a reference to the target bone
            Bone targetBone = skeleton.GetBone(boneName);
            // create an animation track for this bone
            NodeAnimationTrack track = anim.CreateNodeTrack(targetBone.Handle, targetBone);

            foreach (XmlNode childNode in node.ChildNodes) {
                switch (childNode.Name) {
                    case "keyframes":
                        ReadKeyFrames(childNode, track);
                        break;
                    default:
                        DebugMessage(childNode);
                        break;
                }
            }
        }
        /// <summary>
        ///   Use the data in the channels structures to create animations on
        ///   our skeleton.  This variant ignores the morph/pose animations,
        ///   and only considers the skeletal node animations.
        /// </summary>
        /// <param name="worldTransform"></param>
        /// <param name="anim">animation which will be populated with the data from channels</param>
        /// <param name="skeleton">the skeleton that contians the bone data</param>
        protected void ProcessChannels( Matrix4 worldTransform,
                                       Animation anim, Skeleton skeleton )
        {
            // We will get the rotation and translation transforms
            // from our parent, but we need the scale transform
            // as well.
            Quaternion worldRotate;
            Vector3 worldTranslate, worldScale;
            Matrix4.DecomposeMatrix( ref worldTransform, out worldTranslate, out worldRotate, out worldScale );

            // Run through the bones, find the channels associated with that bone.
            // create a track for that bone, and set up keyframes within the track.
            for( ushort i = 0; i < m_AxiomMesh.Skeleton.BoneCount; i++ )
            {
                Bone bone = m_AxiomMesh.Skeleton.GetBone( i );
                string boneName = bone.Name;
                // Build a list of the channels that apply to this bone.
                List<Channel> boneChannels = new List<Channel>();
                foreach( Channel channel in Channels )
                {
                    if( channel.TargetNode != bone.Name )
                        // this channel isn't for this bone.. just continue;
                        continue;
                    boneChannels.Add( channel );
                }

                // Get the bind pose data out of the skeleton
                Quaternion bindRotation = bone.Orientation;
                // bind translation -- the bone.Position has has already been
                // converted, and we are going to compare this to the
                // converted data in the joint transform chains
                Vector3 bindTranslation = bone.Position;
                // Get the scene data that shows the current bone position
                if( !JointTransformChains.ContainsKey( bone.Name ) )
                {
                    m_Log.WarnFormat( "Missing joint transform chain for possible tag point {0}.", bone.Name );
                    continue;
                }
                List<NamedTransform> transforms = JointTransformChains[ bone.Name ];
                Matrix4 posedBoneTransform = MathHelpers.Normalize( worldTransform * GetBonePoseTransform( bone.Name ) );
                Matrix4 posedParentTransform = Matrix4.Identity;
                if( bone.Parent != null )
                    posedParentTransform = MathHelpers.Normalize( worldTransform * GetBonePoseTransform( bone.Parent.Name ) );
                Matrix4 initialPoseTransform = posedParentTransform.Inverse() * posedBoneTransform;
                m_Log.DebugFormat( "Matrices for {0}: posedBoneTransform:\n{1}\nposedParentTransform:\n{2}", bone.Name, posedBoneTransform, posedParentTransform );

                if( boneChannels.Count == 0 )
                {
                    Matrix4 bindTransform = Multiverse.MathLib.MathUtil.GetTransform( bindRotation, bindTranslation );
                    // Check to see if the bind pose and the initial pose for this
                    // scene are the same.  If they are not the same, generate
                    // a track that has one keyframe, just to put the bone in
                    // the right position.
                    m_Log.InfoFormat( "No bone channels for {0}", bone.Name );
                    if( initialPoseTransform == bindTransform )
                    {
                        // No need to create an animation track to move the
                        // bone to the initial pose, since this is the bind
                        // pose already.
                        continue;
                    }
                    // I used to skip bones for which I lacked bind pose data (from a skin cluster)
                    // but now that I have moved the channel processing code after the skeleton
                    // generation code, I am certain to have bind pose data for the bones in the
                    // skeleton.
                }
                if( m_Log.IsDebugEnabled )
                    m_Log.DebugFormat( "Matrices for {0}: Bind:\n{1}\nInitial:\n{2}", bone.Name, Multiverse.MathLib.MathUtil.GetTransform( bindRotation, bindTranslation ), initialPoseTransform );

                NodeAnimationTrack track = anim.CreateNodeTrack( bone.Handle, bone );

                if( boneChannels.Count == 0 )
                {
                    // Generate a track that has one keyframe, just to put
                    // the bone in the right position.
                    m_Log.InfoFormat( "Generating initial keyframe based on bind pose for {0}", bone.Name );
                    float time = 0;
                    TransformKeyFrame keyFrame = (TransformKeyFrame) track.CreateKeyFrame( time );
                    Matrix4 frameTransform = initialPoseTransform;
                    Quaternion frameRotate;
                    Vector3 frameTranslate, frameScale;
                    Matrix4.DecomposeMatrix( ref frameTransform, out frameTranslate, out frameRotate, out frameScale );
                    m_Log.DebugFormat( "Initial pose for {0}:\n{1}", bone.Name, frameTransform );
                    keyFrame.Rotation = CleanupQuaternion( bindRotation.Inverse() * frameRotate );
                    keyFrame.Translate = frameTranslate - bindTranslation;
                    continue;
                }

                // Set the interpolation mode based on the bone channel
                bool interpModeSet = false;
                foreach( Channel channel in boneChannels )
                {
                    InterpolationMode interpMode = InterpolationMode.Linear;
                    for( int j = 0; j < channel.Sampler.Interpolation.Count; ++j )
                    {
                        if( GetInterpolationMode( channel, j, ref interpMode ) )
                        {
                            if( interpModeSet && anim.InterpolationMode != interpMode )
                                m_Log.WarnFormat( "Mismatched interpolation modes for different channels: {0} != {1}", anim.InterpolationMode, interpMode );
                            anim.InterpolationMode = interpMode;
                            interpModeSet = true;
                        }
                    }
                }

                // Assert that all the time accessors for the controlling channels are the same.
                // This will break if we don't bake transforms, since in that case, we will get
                // independent timeChannels for the various boneChannels.
                if( boneChannels.Count != 1 )
                    m_Log.Warn( "Multiple bone channels for a single bone.  Check that transforms have been baked." );
                // Build a list of all the times that we access any bone channel
                List<float> times = new List<float>();
                foreach( Channel channel in boneChannels )
                {
                    Accessor timeAccessor = channel.Sampler.Input.Accessor;
                    for( int j = 0; j < timeAccessor.Count; ++j )
                    {
                        // TODO: Hardcoded parameter names
                        float time = 0.0f;
                        if( timeAccessor.ContainsParam( "TIME" ) )
                            time = (float) timeAccessor.GetParam( "TIME", j );
                        else if( timeAccessor.ContainsParam( "time" ) )
                            time = (float) timeAccessor.GetParam( "time", j );
                        else
                            Debug.Assert( false, "No time parameter for sampler" );
                        if( !times.Contains( time ) )
                            times.Add( time );
                    }
                }
                foreach( float time in times )
                {
                    TransformKeyFrame keyFrame = (TransformKeyFrame) track.CreateKeyFrame( time );
                    Dictionary<Channel, Triple<int, int, float>> channelIndices =
                        GetKeyframeIndicesByTime( boneChannels, time );
                    foreach( Channel channel in boneChannels )
                    {
                        Triple<int, int, float> interpInfo = channelIndices[ channel ];
                        SetTransformForChannel( transforms, channel, interpInfo.first, interpInfo.second, interpInfo.third );
                    }
                    // Matrix4 fullTransform = worldTransform * GetBakedTransform(transforms) * bone.FullTransform;
                    Matrix4 frameTransform = GetBakedTransform( transforms );
                    Quaternion frameRotate;
                    Vector3 frameTranslate, frameScale;
                    Matrix4.DecomposeMatrix( ref frameTransform, out frameTranslate, out frameRotate, out frameScale );
                    //log.InfoFormat("frameRotate = {0}", GetEulerString(frameRotate));
                    //{
                    //foreach (NamedTransform nmt in transforms) {
                    //    if (nmt is NamedRotateTransform) {
                    //        Matrix4 tmp = nmt.Transform;
                    //        Quaternion foo = Quaternion.Identity;
                    //        foo.FromRotationMatrix(tmp.GetMatrix3());
                    //        log.InfoFormat("NamedRotateTransform for {0} = {1}", nmt.Name, GetEulerString(foo));
                    //    }
                    //}
                    //}
                    // FIXME
                    if( bone.Parent != null )
                    {
                        // we have a parent, so apply scale factor to our
                        // translation, but any orientation or translation
                        // from the world will already be included in our
                        // parent.
                        frameTranslate = MathHelpers.ScaleVector( worldScale, frameTranslate );
                    }
                    else
                    {
                        // no parent, so we need to incorporate the world
                        // transform's scale, orientation and translation.
                        frameTranslate = worldTransform * frameTranslate;
                        frameRotate = worldRotate * frameRotate;
                    }

                    // Frame transform is our transform relative to the
                    // world-transformed parent bone. If there is no parent,
                    // we are using the identity matrix.
                    // transform from the dae file is based on the position of
                    // the bone relative to the parent bone.  however, in
                    // axiom, the keyframes are based on the position of the
                    // bone relative to the bind position of the bone.
                    // log.DebugFormat("bindRotation = {0}", GetEulerString(bindRotation));
                    // log.DebugFormat("bindRotation.Inverse = {0}", GetEulerString(bindRotation.Inverse()));
                    // log.DebugFormat("frameRotate = {0}", GetEulerString(frameRotate));
                    keyFrame.Rotation = CleanupQuaternion( bindRotation.Inverse() * frameRotate );
                    keyFrame.Translate = frameTranslate - bindTranslation;
                }
            }
        }