// Construct an animation class from the animation description private void ConstructFromDesc(NTROStruct animDesc, NTROStruct decodeKey, AnimDecoderType[] decoderArray, NTROArray segmentArray) { // Get animation properties Name = animDesc.Get<string>("m_name"); Fps = animDesc.Get<float>("fps"); // Only consider first frame block for now var pData = animDesc.Get<NTROArray>("m_pData").Get<NTROStruct>(0); var frameBlockArray = pData.Get<NTROArray>("m_frameblockArray").ToArray<NTROStruct>(); FrameCount = pData.Get<int>("m_nFrames"); Frames = new Frame[FrameCount]; // Figure out each frame for (var frame = 0; frame < FrameCount; frame++) { // Create new frame object Frames[frame] = new Frame(); // Read all frame blocks foreach (var frameBlock in frameBlockArray) { var startFrame = frameBlock.Get<int>("m_nStartFrame"); var endFrame = frameBlock.Get<int>("m_nEndFrame"); // Only consider blocks that actual contain info for this frame if (frame >= startFrame && frame <= endFrame) { var segmentIndexArray = frameBlock.Get<NTROArray>("m_segmentIndexArray").ToArray<int>(); foreach (var segmentIndex in segmentIndexArray) { var segment = segmentArray.Get<NTROStruct>(segmentIndex); ReadSegment(frame - startFrame, segment, decodeKey, decoderArray, ref Frames[frame]); } } } } }
// Get the transformation matrices at a time private Frame GetTransformsAtTime(float time) { // Calculate the index of the current frame var frameIndex = (int)(time * Fps) % FrameCount; var t = ((time * Fps) - frameIndex) % 1; // Get current and next frame var frame1 = Frames[frameIndex]; var frame2 = Frames[(frameIndex + 1) % FrameCount]; // Create output frame var frame = new Frame(); var length = FrameCount / Fps; // Interpolate bone positions and angles foreach (var bonePair in frame1.Bones) { var position = Vector3.Lerp(frame1.Bones[bonePair.Key].Position, frame2.Bones[bonePair.Key].Position, t); var angle = Quaternion.Slerp(frame1.Bones[bonePair.Key].Angle, frame2.Bones[bonePair.Key].Angle, t); frame.Bones[bonePair.Key] = new FrameBone(position, angle); } return frame; }
private void ReadSegment(int frame, NTROStruct segment, NTROStruct decodeKey, AnimDecoderType[] decoderArray, ref Frame outFrame) { //Clamp the frame number to be between 0 and the maximum frame frame = frame < 0 ? 0 : frame; frame = frame >= FrameCount ? FrameCount - 1 : frame; var localChannel = segment.Get<int>("m_nLocalChannel"); var dataChannel = decodeKey.Get<NTROArray>("m_dataChannelArray").Get<NTROStruct>(localChannel); var boneNames = dataChannel.Get<NTROArray>("m_szElementNameArray").ToArray<string>(); var channelAttribute = dataChannel.Get<string>("m_szVariableName"); // Read container var container = segment.Get<NTROArray>("m_container").ToArray<byte>(); using (var containerReader = new BinaryReader(new MemoryStream(container))) { var elementIndexArray = dataChannel.Get<NTROArray>("m_nElementIndexArray").ToArray<int>(); var elementBones = new int[decodeKey.Get<int>("m_nChannelElements")]; for (var i = 0; i < elementIndexArray.Length; i++) { elementBones[elementIndexArray[i]] = i; } // Read header var decoder = decoderArray[containerReader.ReadInt16()]; var cardinality = containerReader.ReadInt16(); var numBones = containerReader.ReadInt16(); var totalLength = containerReader.ReadInt16(); // Read bone list var elements = new List<int>(); for (var i = 0; i < numBones; i++) { elements.Add(containerReader.ReadInt16()); } // Skip data to find the data for the current frame. // Structure is just | Bone 0 - Frame 0 | Bone 1 - Frame 0 | Bone 0 - Frame 1 | Bone 1 - Frame 1| containerReader.BaseStream.Position += decoder.Size() * frame * numBones; // Read animation data for all bones for (var element = 0; element < numBones; element++) { //Get the bone we are reading for var bone = elementBones[elements[element]]; // Look at the decoder to see what to read switch (decoder) { case AnimDecoderType.CCompressedStaticFullVector3: case AnimDecoderType.CCompressedFullVector3: case AnimDecoderType.CCompressedDeltaVector3: outFrame.SetAttribute(boneNames[bone], channelAttribute, new Vector3( containerReader.ReadSingle(), containerReader.ReadSingle(), containerReader.ReadSingle())); break; case AnimDecoderType.CCompressedStaticVector: outFrame.SetAttribute(boneNames[bone], channelAttribute, new Vector3( ReadHalfFloat(containerReader), ReadHalfFloat(containerReader), ReadHalfFloat(containerReader))); break; case AnimDecoderType.CCompressedAnimQuaternion: outFrame.SetAttribute(boneNames[bone], channelAttribute, ReadQuaternion(containerReader)); break; } } } }
private void GetAnimationMatrixRecursive(Bone bone, Matrix4 parentBindPose, Matrix4 parentInvBindPose, Frame transforms, ref Matrix4[] matrices) { // Calculate world space bind and inverse bind pose var bindPose = parentBindPose; var invBindPose = parentInvBindPose * bone.InverseBindPose; // Calculate transformation matrix var transformMatrix = Matrix4.Identity; if (transforms.Bones.ContainsKey(bone.Name)) { var transform = transforms.Bones[bone.Name]; transformMatrix = Matrix4.CreateFromQuaternion(transform.Angle) * Matrix4.CreateTranslation(transform.Position); } // Apply tranformation var transformed = transformMatrix * bindPose; // Store result if (bone.Index != -1) { matrices[bone.Index] = invBindPose * transformed; } // Propagate to childen foreach (var child in bone.Children) { GetAnimationMatrixRecursive(child, transformed, invBindPose, transforms, ref matrices); } }