// template FrameTransformMatrix // { // Matrix4x4 frameMatrix; // } /// <summary> /// Imports a transform matrix attached to a ContentNode /// </summary> /// <returns>The transform matrix attached to the current ContentNode</returns> private Matrix ImportFrameTransformMatrix() { Matrix m = tokens.SkipName().NextMatrix(); // Reflect the matrix across the Z axis to swap from left hand to right hand // coordinate system ContentUtil.ReflectMatrix(ref m); // skip the "}" at the end of the node tokens.SkipToken(); return(m); }
// "This template is instantiated on a per-mesh basis. Within a mesh, a sequence of n // instances of this template will appear, where n is the number of bones (X file frames) // that influence the vertices in the mesh. Each instance of the template basically defines // the influence of a particular bone on the mesh. There is a list of vertex indices, and a // corresponding list of weights. // template SkinWeights // { // STRING transformNodeName; // DWORD nWeights; // array DWORD vertexIndices[nWeights]; // array float weights[nWeights]; // Matrix4x4 matrixOffset; // } // - The name of the bone whose influence is being defined is transformNodeName, // and nWeights is the number of vertices affected by this bone. // - The vertices influenced by this bone are contained in vertexIndices, and the weights for // each of the vertices influenced by this bone are contained in weights. // - The matrix matrixOffset transforms the mesh vertices to the space of the bone. When concatenated // to the bone's transform, this provides the world space coordinates of the mesh as affected by the bone." // (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ // directx9_c/dx9_graphics_reference_x_file_format_templates.asp) // // Reads in a bone that contains skin weights. It then adds one bone weight // to every vertex that is influenced by ths bone, which contains the name of the bone and the // weight. public void ImportSkinWeights() { isSkinned = true; string boneName = tokens.SkipName().NextString(); // an influence is an index to a vertex that is affected by the current bone int numInfluences = tokens.NextInt(); List <int> influences = new List <int>(); List <float> weights = new List <float>(); for (int i = 0; i < numInfluences; i++) { influences.Add(tokens.NextInt()); } for (int i = 0; i < numInfluences; i++) { weights.Add(tokens.NextFloat()); if (weights[i] == 0) { influences[i] = -1; } } influences.RemoveAll(delegate(int i) { return(i == -1); }); weights.RemoveAll(delegate(float f) { return(f == 0); }); // Add the matrix that transforms the vertices to the space of the bone. // we will need this for skinned animation. Matrix blendOffset = tokens.NextMatrix(); ContentUtil.ReflectMatrix(ref blendOffset); skinTransformDictionary.Add(boneName, blendOffset); SkinTransformContent trans = new SkinTransformContent(); trans.BoneName = boneName; trans.Transform = blendOffset; skinTransforms.Add(trans); // end of skin weights tokens.SkipToken(); // add a new name/weight pair to every vertex influenced by the bone for (int i = 0; i < influences.Count; i++) { skinInfo[influences[i]].Add(new BoneWeight(boneName, weights[i])); } }
/// <summary> /// Interpolates an AnimationContent object to 60 fps. /// </summary> /// <param name="input">The AnimationContent to interpolate.</param> /// <returns>The interpolated AnimationContent.</returns> public virtual AnimationContent Interpolate(AnimationContent input) { AnimationContent output = new AnimationContent(); long time = 0; long animationDuration = input.Duration.Ticks; // default XNA importers, due to floating point errors or TimeSpan // estimation, sometimes have channels with a duration slightly longer than // the animation duration. So, set the animation duration to its true // value foreach (KeyValuePair <string, AnimationChannel> c in input.Channels) { if (c.Value[c.Value.Count - 1].Time.Ticks > animationDuration) { animationDuration = c.Value[c.Value.Count - 1].Time.Ticks; } } foreach (KeyValuePair <string, AnimationChannel> c in input.Channels) { time = 0; string channelName = c.Key; AnimationChannel channel = c.Value; AnimationChannel outChannel = new AnimationChannel(); int currentFrame = 0; // Step through time until the time passes the animation duration while (time <= animationDuration) { AnimationKeyframe keyframe; // Clamp the time to the duration of the animation and make this // keyframe equal to the last animation frame. if (time >= animationDuration) { time = animationDuration; keyframe = new AnimationKeyframe(new TimeSpan(time), channel[channel.Count - 1].Transform); } else { // If the channel only has one keyframe, set the transform for the current time // to that keyframes transform if (channel.Count == 1 || time < channel[0].Time.Ticks) { keyframe = new AnimationKeyframe(new TimeSpan(time), channel[0].Transform); } // If the current track duration is less than the animation duration, // use the last transform in the track once the time surpasses the duration else if (channel[channel.Count - 1].Time.Ticks <= time) { keyframe = new AnimationKeyframe(new TimeSpan(time), channel[channel.Count - 1].Transform); } else // proceed as normal { // Go to the next frame that is less than the current time while (channel[currentFrame + 1].Time.Ticks < time) { currentFrame++; } // Numerator of the interpolation factor double interpNumerator = (double)(time - channel[currentFrame].Time.Ticks); // Denominator of the interpolation factor double interpDenom = (double)(channel[currentFrame + 1].Time.Ticks - channel[currentFrame].Time.Ticks); // The interpolation factor, or amount to interpolate between the current // and next frame double interpAmount = interpNumerator / interpDenom; // If the frames are roughly 60 frames per second apart, use linear interpolation if (channel[currentFrame + 1].Time.Ticks - channel[currentFrame].Time.Ticks <= ContentUtil.TICKS_PER_60FPS * 1.05) { keyframe = new AnimationKeyframe(new TimeSpan(time), Matrix.Lerp( channel[currentFrame].Transform, channel[currentFrame + 1].Transform, (float)interpAmount)); } else // else if the transforms between the current frame and the next aren't identical // decompose the matrix and interpolate the rotation separately if (channel[currentFrame].Transform != channel[currentFrame + 1].Transform) { keyframe = new AnimationKeyframe(new TimeSpan(time), ContentUtil.SlerpMatrix( channel[currentFrame].Transform, channel[currentFrame + 1].Transform, (float)interpAmount)); } else // Else the adjacent frames have identical transforms and we can use // the current frames transform for the current keyframe. { keyframe = new AnimationKeyframe(new TimeSpan(time), channel[currentFrame].Transform); } } } // Add the interpolated keyframe to the new channel. outChannel.Add(keyframe); // Step the time forward by 1/60th of a second time += ContentUtil.TICKS_PER_60FPS; } // Compensate for the time error,(animation duration % TICKS_PER_60FPS), // caused by the interpolation by setting the last keyframe in the // channel to the animation duration. if (outChannel[outChannel.Count - 1].Time.Ticks < animationDuration) { outChannel.Add(new AnimationKeyframe( TimeSpan.FromTicks(animationDuration), channel[channel.Count - 1].Transform)); } outChannel.Add(new AnimationKeyframe(input.Duration, channel[channel.Count - 1].Transform)); // Add the interpolated channel to the animation output.Channels.Add(channelName, outChannel); } // Set the interpolated duration to equal the inputs duration for consistency output.Duration = TimeSpan.FromTicks(animationDuration); return(output); }
/* * template Animation * { * [...] * } */ /// <summary> /// Fills in all the channels of an animation. Each channel refers to /// a single bone's role in the animation. /// </summary> /// <param name="boneName">The name of the bone associated with the channel</param> /// <returns>The imported animation channel</returns> private AnimationChannel ImportAnimationChannel(out string boneName) { AnimationChannel anim = new AnimationChannel(); // Store the frames in an array, which acts as an intermediate data set // This will allow us to more easily provide support for non Matrix // animation keys at a later time AnimationKeyframe[] rotFrames = null; AnimationKeyframe[] transFrames = null; AnimationKeyframe[] scaleFrames = null; List <AnimationKeyframe> matrixFrames = null; boneName = null; tokens.SkipName(); for (string next = tokens.NextToken(); next != "}"; next = tokens.NextToken()) { // A set of animation keys if (next == "AnimationKey") { // These keys can be rotation (0),scale(1),translation(2), or matrix(3 or 4) keys. int keyType; AnimationKeyframe[] frames = ImportAnimationKey(out keyType); if (keyType == 0) { rotFrames = frames; } else if (keyType == 1) { scaleFrames = frames; } else if (keyType == 2) { transFrames = frames; } else { matrixFrames = new List <AnimationKeyframe>(frames); } } // A possible bone name else if (next == "{") { string token = tokens.NextToken(); if (tokens.NextToken() != "}") { tokens.SkipNode(); } else { boneName = token; } } } // Fill in the channel with the frames if (matrixFrames != null) { matrixFrames.Sort(new Comparison <AnimationKeyframe>(delegate(AnimationKeyframe one, AnimationKeyframe two) { return(one.Time.CompareTo(two.Time)); })); if (matrixFrames[0].Time != TimeSpan.Zero) { matrixFrames.Insert(0, new AnimationKeyframe(new TimeSpan(), matrixFrames[0].Transform)); } for (int i = 0; i < matrixFrames.Count; i++) { Matrix m = matrixFrames[i].Transform; ContentUtil.ReflectMatrix(ref m); matrixFrames[i].Transform = m; anim.Add(matrixFrames[i]); } } else { List <AnimationKeyframe> combinedFrames = ContentUtil.MergeKeyFrames( scaleFrames, transFrames, rotFrames); for (int i = 0; i < combinedFrames.Count; i++) { Matrix m = combinedFrames[i].Transform; ContentUtil.ReflectMatrix(ref m); combinedFrames[i].Transform = m; //* Matrix.CreateRotationX(MathHelper.PiOver2); anim.Add(combinedFrames[i]); } } return(anim); }