private ProcessedAnimationChannel ConvertChannel(NodeAnimationChannel nac) { string nodeName = nac.NodeName; AssetPrimitives.VectorKey[] positions = new AssetPrimitives.VectorKey[nac.PositionKeyCount]; for (int i = 0; i < nac.PositionKeyCount; i++) { Assimp.VectorKey assimpKey = nac.PositionKeys[i]; positions[i] = new AssetPrimitives.VectorKey(assimpKey.Time, assimpKey.Value.ToSystemVector3()); } AssetPrimitives.VectorKey[] scales = new AssetPrimitives.VectorKey[nac.ScalingKeyCount]; for (int i = 0; i < nac.ScalingKeyCount; i++) { Assimp.VectorKey assimpKey = nac.ScalingKeys[i]; scales[i] = new AssetPrimitives.VectorKey(assimpKey.Time, assimpKey.Value.ToSystemVector3()); } AssetPrimitives.QuaternionKey[] rotations = new AssetPrimitives.QuaternionKey[nac.RotationKeyCount]; for (int i = 0; i < nac.RotationKeyCount; i++) { Assimp.QuaternionKey assimpKey = nac.RotationKeys[i]; rotations[i] = new AssetPrimitives.QuaternionKey(assimpKey.Time, assimpKey.Value.ToSystemQuaternion()); } return(new ProcessedAnimationChannel(nodeName, positions, scales, rotations)); }
/// <summary> /// protected Constructor /// </summary> /// <param name="nodeAnimationChannel">Assimp NodeAnimationChannel</param> protected AssimpNodeAnimationNode(NodeAnimationChannel nodeAnimationChannel) { if (nodeAnimationChannel == null) { throw new ArgumentNullException(nameof(nodeAnimationChannel)); } NodeAnimationChannel = nodeAnimationChannel; if (nodeAnimationChannel.HasPositionKeys) { foreach (var positionKey in nodeAnimationChannel.PositionKeys) { this.PositionTrack.Keys.Add(new Position3DKeyFrame(positionKey.Time, positionKey.Value.ToWpfPoint3D())); } } if (nodeAnimationChannel.HasScalingKeys) { foreach (var scalingKeys in nodeAnimationChannel.ScalingKeys) { this.ScaleTrack.Keys.Add(new Vector3DKeyFrame(scalingKeys.Time, scalingKeys.Value.ToWpfVector3D())); } } if (nodeAnimationChannel.HasRotationKeys) { foreach (var quaternionKey in nodeAnimationChannel.RotationKeys) { this.RotationTrack.Keys.Add(new QuaternionRotationKeyFrame(quaternionKey.Time, new System.Windows.Media.Media3D.Quaternion(quaternionKey.Value.X, quaternionKey.Value.Y, quaternionKey.Value.Z, quaternionKey.Value.W))); } } }
private aiMatrix4x4 InterpolateRotation(double time, NodeAnimationChannel channel) { aiQuaternion rotation; if (channel.RotationKeyCount == 1) { rotation = channel.RotationKeys[0].Value; } else { uint frameIndex = 0; for (uint i = 0; i < channel.RotationKeyCount - 1; i++) { if (time < (float)channel.RotationKeys[(int)(i + 1)].Time) { frameIndex = i; break; } } QuaternionKey currentFrame = channel.RotationKeys[(int)frameIndex]; QuaternionKey nextFrame = channel.RotationKeys[(int)((frameIndex + 1) % channel.RotationKeyCount)]; double delta = (time - (float)currentFrame.Time) / (float)(nextFrame.Time - currentFrame.Time); aiQuaternion start = currentFrame.Value; aiQuaternion end = nextFrame.Value; rotation = aiQuaternion.Slerp(start, end, (float)delta); rotation.Normalize(); } return(rotation.GetMatrix()); }
Matrix4x4 FindRotation(NodeAnimationChannel n) { Matrix4x4 result = Matrix4x4.Identity; for (int c = 0; c < n.RotationKeyCount; c++) { QuaternionKey q = n.RotationKeys[c]; q.Value = new Assimp.Quaternion(q.Value.W, q.Value.X, q.Value.Y, q.Value.Z); VectorKey pk = n.PositionKeys[c]; VectorKey ps = n.ScalingKeys[c]; if (_animationController.FrameCounter <= q.Time) { result = (Matrix4x4)q.Value.GetMatrix() * (Matrix4x4.FromTranslation(pk.Value)) * (Matrix4x4.FromScaling(ps.Value)) ; break; } } return(result); }
private aiMatrix4x4 InterpolateScale(double time, NodeAnimationChannel channel) { Vector3D scale; if (channel.ScalingKeyCount == 1) { scale = channel.ScalingKeys[0].Value; } else { uint frameIndex = 0; for (uint i = 0; i < channel.ScalingKeyCount - 1; i++) { if (time < (float)channel.ScalingKeys[(int)(i + 1)].Time) { frameIndex = i; break; } } VectorKey currentFrame = channel.ScalingKeys[(int)frameIndex]; VectorKey nextFrame = channel.ScalingKeys[(int)((frameIndex + 1) % channel.ScalingKeyCount)]; double delta = (time - (float)currentFrame.Time) / (float)(nextFrame.Time - currentFrame.Time); Vector3D start = currentFrame.Value; Vector3D end = nextFrame.Value; scale = (start + (float)delta * (end - start)); } return(aiMatrix4x4.FromScaling(scale)); }
public void CalcAnimation2(Node node, Animation ani, Matrix4x4 parent_transform) { NodeAnimationChannel n = FindChannel(ani, node.Name); Matrix4x4 node_transform = node.Transform; string node_name = node.Name; if (n != null) { node_transform = FindRotation(n); } Matrix4x4 global_transform = node_transform * parent_transform; if (m_bone_mapping.ContainsKey(node_name)) { uint bone_index = m_bone_mapping[node_name]; BoneMatrix bi = m_bone_matrices[(int)bone_index]; //bi.final_world_transform = m_global_inverse_transform * global_transform * m_bone_matrices[(int)bone_index].offset_matrix; bi.final_world_transform = m_bone_matrices[(int)bone_index].offset_matrix * global_transform * m_global_inverse_transform; m_bone_matrices[(int)bone_index] = bi; //counter++; } for (int i = 0; i < node.ChildCount; i++) { Node child = node.Children[i]; CalcAnimation2(child, ani, global_transform); } }
public void ExtractAnimations(string animationName = "*") { for (int i = 0; i < ModelAnimationGraph.Animations.Count; i++) { var tagAnimation = ModelAnimationGraph.Animations[i]; var name = CacheContext.StringTable.GetString(tagAnimation.Name); if (animationName == "*" || name == animationName) { Animation animation = new Animation { Name = name }; animation.TicksPerSecond = 30; animation.DurationInTicks = tagAnimation.FrameCount; // // Convert tag animation into assimp animation. A channel affects only a single node (bone) // //Animation data should go here for (int j = 0; j < tagAnimation.NodeCount; j++) { NodeAnimationChannel channel = new NodeAnimationChannel(); channel.NodeName = Nodes[j].Name; // parse animation data for a specific node } Scene.Animations.Add(animation); } } }
public Animations.Animation CreateGenericAnimation(Assimp.Animation animation) { Animations.Animation STanim = new Animations.Animation(); STanim.Text = animation.Name; STanim.FrameCount = (int)animation.DurationInTicks; //Load node animations if (animation.HasNodeAnimations) { var _channels = new NodeAnimationChannel[animation.NodeAnimationChannelCount]; for (int i = 0; i < _channels.Length; i++) { _channels[i] = new NodeAnimationChannel(); } } //Load mesh animations if (animation.HasMeshAnimations) { var _meshChannels = new MeshAnimationChannel[animation.MeshAnimationChannelCount]; for (int i = 0; i < _meshChannels.Length; i++) { _meshChannels[i] = new MeshAnimationChannel(); } } return(STanim); }
static NumMatrix4x4 GetTransformForFrame(NodeAnimationChannel Ch, int Frame) { VectorKey PosKey = Ch.PositionKeys[Ch.PositionKeys.Count - 1]; if (Frame < Ch.PositionKeys.Count) { PosKey = Ch.PositionKeys[Frame]; } QuaternionKey RotKey = Ch.RotationKeys[Ch.RotationKeys.Count - 1]; if (Frame < Ch.RotationKeys.Count) { RotKey = Ch.RotationKeys[Frame]; } VectorKey SclKey = Ch.ScalingKeys[Ch.ScalingKeys.Count - 1]; if (Frame < Ch.ScalingKeys.Count) { SclKey = Ch.ScalingKeys[Frame]; } NumMatrix4x4 Rot = NumMatrix4x4.CreateFromQuaternion(ConvertQuat(RotKey.Value)); NumMatrix4x4 Pos = NumMatrix4x4.CreateTranslation(ConvertVec(PosKey.Value)); NumMatrix4x4 Scl = NumMatrix4x4.CreateScale(ConvertVec(SclKey.Value)); //return Pos * Rot * Scl; return(Scl * Rot * Pos); }
private void FillAnimationNode(Scene scene, Node node, int animationIndex) { var animation = scene.Animations[animationIndex]; NodeAnimationChannel channel = null; for (var i = 0; i < animation.NodeAnimationChannelCount; i++) { if (animation.NodeAnimationChannels[i].NodeName == node.Name) { channel = animation.NodeAnimationChannels[i]; } } if (channel != null && !СurrentAnimation.AnimationNodes.ContainsKey(node.Name)) { var nodeAnim = new NodeAnimation { PositionKeysCount = channel.PositionKeyCount, ScalingKeysCount = channel.ScalingKeyCount, RotationKeysCount = channel.RotationKeyCount }; nodeAnim.PositionKeys = new VectorInfo[nodeAnim.PositionKeysCount]; for (var i = 0; i < nodeAnim.PositionKeysCount; i++) { nodeAnim.PositionKeys[i] = new VectorInfo { Time = (float)channel.PositionKeys[i].Time, Value = FromVector(channel.PositionKeys[i].Value) } } ; nodeAnim.RotationKeys = new QuaternionInfo[nodeAnim.RotationKeysCount]; for (var i = 0; i < nodeAnim.RotationKeysCount; i++) { nodeAnim.RotationKeys[i] = new QuaternionInfo { Time = (float)channel.RotationKeys[i].Time, Value = channel.RotationKeys[i].Value } } ; nodeAnim.ScalingKeys = new VectorInfo[nodeAnim.ScalingKeysCount]; for (var i = 0; i < nodeAnim.ScalingKeysCount; i++) { nodeAnim.ScalingKeys[i] = new VectorInfo { Time = (float)channel.ScalingKeys[i].Time, Value = FromVector(channel.ScalingKeys[i].Value) } } ; СurrentAnimation.AnimationNodes.Add(node.Name, nodeAnim); } for (var i = 0; i < node.ChildCount; i++) { FillAnimationNode(scene, node.Children[i], animationIndex); } }
public NodeAnimationChannel ExtractChannel(AnimationData data) { NodeAnimationChannel channel = new NodeAnimationChannel(); channel.NodeName = ""; return(null); }
/// <summary> /// Constructor /// </summary> /// <param name="nodeAnimationChannel">Assimp NodeAnimationChannel</param> /// <param name="animatedModel3D">WPF Model3D</param> public AssimpNodeAnimationNode(NodeAnimationChannel nodeAnimationChannel, Model3D animatedModel3D) : this(nodeAnimationChannel) { if (animatedModel3D == null) { throw new ArgumentNullException(nameof(animatedModel3D)); } _animatedModel3D = animatedModel3D; _animatedSkeletonNode = null; }
static FoamAnimationFrame ReadFrame(List <NodeAnimationChannel> Channels, string[] BoneNames, int Frame) { NumMatrix4x4[] BoneTransforms = new NumMatrix4x4[BoneNames.Length]; for (int i = 0; i < BoneNames.Length; i++) { NodeAnimationChannel Ch = FindBoneChannel(Channels, BoneNames[i]); BoneTransforms[i] = GetTransformForFrame(Ch, Frame); } return(new FoamAnimationFrame(BoneTransforms)); }
NodeAnimationChannel FindChannel(Animation ani, string NodeName) { for (int i = 0; i < ani.NodeAnimationChannelCount; i++) { NodeAnimationChannel n = ani.NodeAnimationChannels[i]; if (n.NodeName == NodeName) { return(n); } } return(null); }
IEnumerable <Frame> GetFrames(NodeAnimationChannel nch) { var m = new[] { nch.PositionKeyCount, nch.RotationKeyCount, nch.ScalingKeyCount }.Max(); for (int i = 0; i < m; i++) { var pos = new Vector3D(); var scale = new Vector3D(1); var rot = new Assimp.Quaternion(1, 0, 0, 0); if (nch.HasPositionKeys) { if (i < nch.PositionKeyCount) { pos = nch.PositionKeys[i].Value; } else { pos = nch.PositionKeys.Last().Value; } } if (nch.HasRotationKeys) { if (i < nch.RotationKeyCount) { rot = nch.RotationKeys[i].Value; } else { rot = nch.RotationKeys.Last().Value; } } if (nch.HasScalingKeys) { if (i < nch.ScalingKeyCount) { scale = nch.ScalingKeys[i].Value; } else { scale = nch.ScalingKeys.Last().Value; } } yield return(new Frame() { pos = pos, rot = rot, scal = scale }); } yield break; }
/// <summary> /// Constructor /// </summary> /// <param name="nodeAnimationChannel">Assimp NodeAnimationChannel</param> /// <param name="animatedSkeletonNode">SkeletonNode</param> public AssimpNodeAnimationNode(NodeAnimationChannel nodeAnimationChannel, SkeletonNode animatedSkeletonNode) : this(nodeAnimationChannel) { if (animatedSkeletonNode == null) { throw new ArgumentNullException(nameof(animatedSkeletonNode)); } //if (nodeAnimationChannel.NodeName != animatedSkeletonNode.AssimpNode.Name) // throw new Exception("nodeAnimationChannel and animatedSkeletonNode that were used to create an instance of AssimpNodeAnimationNode are not connected to each other (do not have the same name)"); _animatedModel3D = null; _animatedSkeletonNode = animatedSkeletonNode; }
private int FindPosition(float animationTime, NodeAnimationChannel pNodeAnim) { // TODO add asserts for (int i = 0; i < pNodeAnim.PositionKeyCount - 1; i++) { if (animationTime < pNodeAnim.PositionKeys[i + 1].Time) { return(i); } } return(0); }
private bool GetChannel(Node node, out NodeAnimationChannel channel) { foreach (NodeAnimationChannel c in _animation.NodeAnimationChannels) { if (c.NodeName == node.Name) { channel = c; return(true); } } channel = null; return(false); }
private ErrorCode AddAnimationsToScene(Scene scene) { if (animations == null || animations.Count == 0) { return(ErrorCode.Succeed); } for (int i = 0; i < animations.Count; ++i) { var ani = new Animation { Name = string.IsNullOrEmpty(animations[i].Name) ? $"Animation_{i}" : animations[i].Name, TicksPerSecond = DefaultTicksPerSecond, DurationInTicks = (animations[i].EndTime - animations[i].StartTime) * DefaultTicksPerSecond }; foreach (var f in animations[i].NodeAnimationCollection) { if (f.Node == null || string.IsNullOrEmpty(f.Node.Name)) { logger.LogWarning("Node Animation NodeName is empty. AnimationName:{}", ani.Name); continue; } var ch = new NodeAnimationChannel { NodeName = f.Node.Name }; foreach (var kf in f.KeyFrames) { var t = kf.Time * DefaultTicksPerSecond; ch.PositionKeys.Add(new VectorKey(t, kf.Translation.ToAssimpVector3D())); ch.ScalingKeys.Add(new VectorKey(t, kf.Scale.ToAssimpVector3D())); ch.RotationKeys.Add(new QuaternionKey(t, kf.Rotation.ToAssimpQuaternion())); } ani.NodeAnimationChannels.Add(ch); } scene.Animations.Add(ani); } return(ErrorCode.Succeed); }
private Vector3 CalcInterpolatedPosition(float animationTime, NodeAnimationChannel pNodeAnim) { // TODO add asserts if (pNodeAnim.PositionKeyCount == 1) { return(pNodeAnim.PositionKeys[0].Value.ToOtk()); } var positionIndex = FindPosition(animationTime, pNodeAnim); var nextPositionIndex = (positionIndex + 1); var deltaTime = pNodeAnim.PositionKeys[nextPositionIndex].Time - pNodeAnim.PositionKeys[positionIndex].Time; var factor = (animationTime - pNodeAnim.PositionKeys[positionIndex].Time) / deltaTime; var start = pNodeAnim.PositionKeys[positionIndex].Value; var end = pNodeAnim.PositionKeys[nextPositionIndex].Value; var delta = end - start; return((start + ((float)factor * delta)).ToOtk()); }
private static AnimationChannel BuildAnimtionChannel(Node animatedNode, NodeAnimationChannel nac) { AnimationChannel newChan = new AnimationChannel(); Matrix animatedNodeTransform = Matrix.Transpose(ToXna(animatedNode.Transform)); Vector3 xnaScale; Microsoft.Xna.Framework.Quaternion xnaRotation; Vector3 xnaPositon; animatedNodeTransform.Decompose(out xnaScale, out xnaRotation, out xnaPositon); Vector3D scale = new Vector3D(xnaScale.X, xnaScale.Y, xnaScale.Z); Assimp.Quaternion rotation; rotation.W = xnaRotation.W; rotation.X = xnaRotation.X; rotation.Y = xnaRotation.Y; rotation.Z = xnaRotation.Z; Vector3D position = new Vector3D(xnaPositon.X, xnaPositon.Y, xnaPositon.Z); int sKeyIndex = 0; int qKeyIndex = 0; int tKeyIndex = 0; double firstSKeyTime = nac.ScalingKeyCount > sKeyIndex ? nac.ScalingKeys[sKeyIndex].Time : Double.MaxValue; double firstQKeyTime = nac.RotationKeyCount > qKeyIndex ? nac.RotationKeys[qKeyIndex].Time : Double.MaxValue; double firstTKeyTime = nac.PositionKeyCount > tKeyIndex ? nac.PositionKeys[tKeyIndex].Time : Double.MaxValue; double currTime = Math.Min(Math.Min(firstSKeyTime, firstQKeyTime), firstTKeyTime); if (firstSKeyTime <= currTime) { scale = nac.ScalingKeys[sKeyIndex].Value; } if (firstQKeyTime <= currTime) { rotation = nac.RotationKeys[qKeyIndex].Value; } if (firstTKeyTime <= currTime) { position = nac.PositionKeys[tKeyIndex].Value; } while (currTime < double.MaxValue) { while (nac.ScalingKeyCount > sKeyIndex + 1 && nac.ScalingKeys[sKeyIndex + 1].Time <= currTime) { sKeyIndex++; scale = nac.ScalingKeys[sKeyIndex].Value; } while (nac.RotationKeyCount > qKeyIndex + 1 && nac.RotationKeys[qKeyIndex + 1].Time <= currTime) { qKeyIndex++; rotation = nac.RotationKeys[qKeyIndex].Value; } while (nac.PositionKeyCount > tKeyIndex + 1 && nac.PositionKeys[tKeyIndex + 1].Time <= currTime) { tKeyIndex++; position = nac.PositionKeys[tKeyIndex].Value; } xnaRotation.W = rotation.W; xnaRotation.X = rotation.X; xnaRotation.Y = rotation.Y; xnaRotation.Z = rotation.Z; Matrix transform = Matrix.CreateScale(ToXna(scale)) * Matrix.CreateFromQuaternion(xnaRotation) * Matrix.CreateTranslation(ToXna(position)); AnimationKeyframe newKeyframe = new AnimationKeyframe(TimeSpan.FromSeconds(currTime), transform); newChan.Add(newKeyframe); // Increment the time: double nextSKeyTime = nac.ScalingKeyCount > sKeyIndex + 1 ? nac.ScalingKeys[sKeyIndex + 1].Time : Double.MaxValue; double nextQKeyTime = nac.RotationKeyCount > qKeyIndex + 1 ? nac.RotationKeys[qKeyIndex + 1].Time : Double.MaxValue; double nextTKeyTime = nac.PositionKeyCount > tKeyIndex + 1 ? nac.PositionKeys[tKeyIndex + 1].Time : Double.MaxValue; currTime = Math.Min(Math.Min(nextSKeyTime, nextQKeyTime), nextTKeyTime); } return(newChan); }
public unsafe override ProcessedModel ProcessT(Stream stream, string extension) { AssimpContext ac = new AssimpContext(); Scene scene = ac.ImportFileFromStream( stream, PostProcessSteps.FlipWindingOrder | PostProcessSteps.GenerateNormals | PostProcessSteps.FlipUVs, extension); aiMatrix4x4 rootNodeInverseTransform = scene.RootNode.Transform; rootNodeInverseTransform.Inverse(); List <ProcessedMeshPart> parts = new List <ProcessedMeshPart>(); List <ProcessedAnimation> animations = new List <ProcessedAnimation>(); HashSet <string> encounteredNames = new HashSet <string>(); for (int meshIndex = 0; meshIndex < scene.MeshCount; meshIndex++) { Mesh mesh = scene.Meshes[meshIndex]; string meshName = mesh.Name; if (string.IsNullOrEmpty(meshName)) { meshName = $"mesh_{meshIndex}"; } int counter = 1; while (!encounteredNames.Add(meshName)) { meshName = mesh.Name + "_" + counter.ToString(); counter += 1; } int vertexCount = mesh.VertexCount; int positionOffset = 0; int normalOffset = 12; int texCoordsOffset = -1; int boneWeightOffset = -1; int boneIndicesOffset = -1; List <VertexElementDescription> elementDescs = new List <VertexElementDescription>(); elementDescs.Add(new VertexElementDescription("Position", VertexElementSemantic.Position, VertexElementFormat.Float3)); elementDescs.Add(new VertexElementDescription("Normal", VertexElementSemantic.Normal, VertexElementFormat.Float3)); normalOffset = 12; int vertexSize = 24; bool hasTexCoords = mesh.HasTextureCoords(0); elementDescs.Add(new VertexElementDescription("TexCoords", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2)); texCoordsOffset = vertexSize; vertexSize += 8; bool hasBones = mesh.HasBones; if (hasBones) { elementDescs.Add(new VertexElementDescription("BoneWeights", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float4)); elementDescs.Add(new VertexElementDescription("BoneIndices", VertexElementSemantic.TextureCoordinate, VertexElementFormat.UInt4)); boneWeightOffset = vertexSize; vertexSize += 16; boneIndicesOffset = vertexSize; vertexSize += 16; } byte[] vertexData = new byte[vertexCount * vertexSize]; VertexDataBuilder builder = new VertexDataBuilder(vertexData, vertexSize); Vector3 min = vertexCount > 0 ? mesh.Vertices[0].ToSystemVector3() : Vector3.Zero; Vector3 max = vertexCount > 0 ? mesh.Vertices[0].ToSystemVector3() : Vector3.Zero; for (int i = 0; i < vertexCount; i++) { Vector3 position = mesh.Vertices[i].ToSystemVector3(); min = Vector3.Min(min, position); max = Vector3.Max(max, position); builder.WriteVertexElement( i, positionOffset, position); Vector3 normal = mesh.Normals[i].ToSystemVector3(); builder.WriteVertexElement(i, normalOffset, normal); if (mesh.HasTextureCoords(0)) { builder.WriteVertexElement( i, texCoordsOffset, new Vector2(mesh.TextureCoordinateChannels[0][i].X, mesh.TextureCoordinateChannels[0][i].Y)); } else { builder.WriteVertexElement( i, texCoordsOffset, new Vector2()); } } List <int> indices = new List <int>(); foreach (Face face in mesh.Faces) { if (face.IndexCount == 3) { indices.Add(face.Indices[0]); indices.Add(face.Indices[1]); indices.Add(face.Indices[2]); } } Dictionary <string, uint> boneIDsByName = new Dictionary <string, uint>(); System.Numerics.Matrix4x4[] boneOffsets = new System.Numerics.Matrix4x4[mesh.BoneCount]; if (hasBones) { Dictionary <int, int> assignedBoneWeights = new Dictionary <int, int>(); for (uint boneID = 0; boneID < mesh.BoneCount; boneID++) { Bone bone = mesh.Bones[(int)boneID]; string boneName = bone.Name; int suffix = 1; while (boneIDsByName.ContainsKey(boneName)) { boneName = bone.Name + "_" + suffix.ToString(); suffix += 1; } boneIDsByName.Add(boneName, boneID); foreach (VertexWeight weight in bone.VertexWeights) { int relativeBoneIndex = GetAndIncrementRelativeBoneIndex(assignedBoneWeights, weight.VertexID); builder.WriteVertexElement(weight.VertexID, boneIndicesOffset + (relativeBoneIndex * sizeof(uint)), boneID); builder.WriteVertexElement(weight.VertexID, boneWeightOffset + (relativeBoneIndex * sizeof(float)), weight.Weight); } System.Numerics.Matrix4x4 offsetMat = bone.OffsetMatrix.ToSystemMatrixTransposed(); System.Numerics.Matrix4x4.Decompose(offsetMat, out var scale, out var rot, out var trans); offsetMat = System.Numerics.Matrix4x4.CreateScale(scale) * System.Numerics.Matrix4x4.CreateFromQuaternion(rot) * System.Numerics.Matrix4x4.CreateTranslation(trans); boneOffsets[boneID] = offsetMat; } } builder.FreeGCHandle(); uint indexCount = (uint)indices.Count; int[] int32Indices = indices.ToArray(); byte[] indexData = new byte[indices.Count * sizeof(uint)]; fixed(byte *indexDataPtr = indexData) { fixed(int *int32Ptr = int32Indices) { Buffer.MemoryCopy(int32Ptr, indexDataPtr, indexData.Length, indexData.Length); } } ProcessedMeshPart part = new ProcessedMeshPart( vertexData, elementDescs.ToArray(), indexData, IndexFormat.UInt32, (uint)indices.Count, boneIDsByName, boneOffsets); parts.Add(part); } // Nodes Node rootNode = scene.RootNode; List <ProcessedNode> processedNodes = new List <ProcessedNode>(); ConvertNode(rootNode, -1, processedNodes); ProcessedNodeSet nodes = new ProcessedNodeSet(processedNodes.ToArray(), 0, rootNodeInverseTransform.ToSystemMatrixTransposed()); for (int animIndex = 0; animIndex < scene.AnimationCount; animIndex++) { Animation animation = scene.Animations[animIndex]; Dictionary <string, ProcessedAnimationChannel> channels = new Dictionary <string, ProcessedAnimationChannel>(); for (int channelIndex = 0; channelIndex < animation.NodeAnimationChannelCount; channelIndex++) { NodeAnimationChannel nac = animation.NodeAnimationChannels[channelIndex]; channels[nac.NodeName] = ConvertChannel(nac); } string baseAnimName = animation.Name; if (string.IsNullOrEmpty(baseAnimName)) { baseAnimName = "anim_" + animIndex; } string animationName = baseAnimName; int counter = 1; while (!encounteredNames.Add(animationName)) { animationName = baseAnimName + "_" + counter.ToString(); counter += 1; } } return(new ProcessedModel() { MeshParts = parts.ToArray(), Animations = animations.ToArray(), Nodes = nodes }); }
/// <summary> /// Create a new geometry given filename /// </summary> /// <param name="fileName"> filepath to the 3D model file </param> public Geometry(string fileName, bool enableRigging = false) { RiggingEnabled = enableRigging; sourceFileName = fileName; //Create new importer. importer = new AssimpContext(); //import the file scene = importer.ImportFile(fileName, PostProcessSteps.CalculateTangentSpace | PostProcessSteps.Triangulate | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.GenerateUVCoords | PostProcessSteps.FlipUVs | PostProcessSteps.LimitBoneWeights | PostProcessSteps.ValidateDataStructure); //make sure scene not null if (scene == null) { throw new FileNotFoundException(); } //loop through sizes and count them. allMeshes = new List <ClientMesh>(scene.MeshCount); //loop through and store sizes for (int idx = 0; idx < scene.MeshCount; idx++) { ClientMesh mesh = new ClientMesh(); allMeshes.Add(mesh); mesh.CountVertices = scene.Meshes[idx].VertexCount; mesh.vertSize = scene.Meshes[idx].VertexCount * Vector3.SizeInBytes; mesh.normSize = scene.Meshes[idx].Normals.Count * Vector3.SizeInBytes; mesh.faceSize = scene.Meshes[idx].FaceCount * scene.Meshes[idx].Faces[0].IndexCount * sizeof(int); if (scene.Meshes[idx].HasTextureCoords(0)) { mesh.texSize = scene.Meshes[idx].TextureCoordinateChannels[0].Count * Vector3.SizeInBytes; } } diffuseTextureSRV = new Dictionary <string, ShaderResourceView>(); // do all the processing that rigging is required to have if (enableRigging) { _allBones = new List <ClientBone>(); _allBoneMappings = new Dictionary <string, int>(); _allBoneLookup = new Dictionary <string, ClientBone>(); // set the animation related lookup tables AnimationIndices = new Dictionary <string, int>(); _animationNodes = new List <Dictionary <string, ClientAnimationNode> >(scene.AnimationCount); for (int i = 0; i < scene.AnimationCount; i++) { AnimationIndices[scene.Animations[i].Name] = i; _animationNodes.Add(new Dictionary <string, ClientAnimationNode>()); for (int j = 0; j < scene.Animations[i].NodeAnimationChannelCount; j++) { NodeAnimationChannel ch = scene.Animations[i].NodeAnimationChannels[j]; ClientAnimationNode myNode = new ClientAnimationNode(ch.NodeName); _animationNodes[i][ch.NodeName] = myNode; myNode.Translations = new List <Vector3>(); myNode.TranslationTime = new List <double>(); myNode.Rotations = new List <Quaternion>(); myNode.RotationTime = new List <double>(); myNode.Scalings = new List <Vector3>(); myNode.ScalingTime = new List <double>(); // copy over all the necessary information in the animation channels for (int k = 0; k < ch.PositionKeyCount; k++) { myNode.Translations.Add(ch.PositionKeys[k].Value.ToVector3()); myNode.TranslationTime.Add(ch.PositionKeys[k].Time); } for (int k = 0; k < ch.RotationKeyCount; k++) { myNode.Rotations.Add(ch.RotationKeys[k].Value.ToQuaternion()); myNode.RotationTime.Add(ch.RotationKeys[k].Time); } for (int k = 0; k < ch.ScalingKeyCount; k++) { myNode.Scalings.Add(ch.ScalingKeys[k].Value.ToVector3()); myNode.ScalingTime.Add(ch.ScalingKeys[k].Time); } } } // create and store the big scene tree _rootBone = CreateBoneTree(scene.RootNode, null); // set each bone offset foreach (var sceneMesh in scene.Meshes) { foreach (var rawBone in sceneMesh.Bones) { ClientBone found; if (!_allBoneLookup.TryGetValue(rawBone.Name, out found)) { Console.WriteLine("Cannot find bone: " + rawBone.Name); continue; } found.BoneOffset = rawBone.OffsetMatrix.ToMatrix(); _allBones.Add(found); _allBoneMappings[found.BoneName] = _allBones.IndexOf(found); } } // for bones not inside the meshes...? jasdkl;fja;lskdjkfl foreach (var boneName in _allBoneLookup.Keys.Where(b => _allBones.All(b1 => b1.BoneName != b) && b.StartsWith("Bone"))) { _allBoneLookup[boneName].BoneOffset = _allBoneLookup[boneName].Parent.BoneOffset.Clone(); _allBones.Add(_allBoneLookup[boneName]); _allBoneMappings[boneName] = _allBones.IndexOf(_allBoneLookup[boneName]); } // load the bone weights for (int idx = 0; idx < scene.MeshCount; idx++) { LoadBoneWeights(scene.Meshes[idx], allMeshes[idx]); } //_boneTransformStream = new DataStream(MAX_BONES_PER_GEO * sizeof(float) * 16, true, true); _boneTransformList = new List <Matrix>(MAX_BONES_PER_GEO); for (int i = 0; i < MAX_BONES_PER_GEO; i++) { _boneTransformList.Add(Matrix.Identity); } } // main loading loop; copy cover the scene content into the datastreams and then to the buffers for (int idx = 0; idx < scene.MeshCount; idx++) { ClientMesh mesh = allMeshes[idx]; //create new datastreams. mesh.Vertices = new DataStream(mesh.vertSize, true, true); mesh.Normals = new DataStream(mesh.normSize, true, true); mesh.Faces = new DataStream(mesh.faceSize, true, true); // create a new material mesh.Materials = new ClientMaterial(); //min and max bounds var min = new Vector3(float.MaxValue); var max = new Vector3(float.MinValue); // copy the buffers scene.Meshes[idx].Vertices.ForEach(vertex => { mesh.Vertices.Write(vertex.ToVector3()); //keep track of min and max for obj boundaries. min = Vector3.Minimize(min, vertex.ToVector3()); max = Vector3.Maximize(max, vertex.ToVector3()); }); BoundingBoxes.Add(new BoundingBox(min, max)); scene.Meshes[idx].Normals.ForEach(normal => { mesh.Normals.Write(normal.ToVector3()); }); scene.Meshes[idx].Faces.ForEach(face => { mesh.Faces.WriteRange(face.Indices.ToArray()); }); // check if the mesh has texture coordinates if (scene.Meshes[idx].HasTextureCoords(0)) { mesh.TexCoords = new DataStream(mesh.texSize, true, true); scene.Meshes[idx].TextureCoordinateChannels[0].ForEach(texture => { mesh.TexCoords.Write(texture); }); mesh.TexCoords.Position = 0; } // Parse material properties ApplyMaterial(scene.Materials[scene.Meshes[idx].MaterialIndex], mesh.Materials); // reset datastream positions mesh.Vertices.Position = 0; mesh.Normals.Position = 0; mesh.Faces.Position = 0; //create vertex vbo and faces ebo. mesh.VBOPositions = new Buffer(GraphicsRenderer.Device, mesh.Vertices, mesh.vertSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.VBONormals = new Buffer(GraphicsRenderer.Device, mesh.Normals, mesh.normSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); if (scene.Meshes[idx].HasTextureCoords(0)) { mesh.VBOTexCoords = new Buffer(GraphicsRenderer.Device, mesh.TexCoords, mesh.texSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); } // buffer creation flags var ibd = new BufferDescription( mesh.faceSize, ResourceUsage.Immutable, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.EBO = new Buffer(GraphicsRenderer.Device, mesh.Faces, ibd); } _inverseGlobalTransform = Matrix.Invert(scene.RootNode.Transform.ToMatrix()); }
public static Scene ExportToScene(HavokAnimationData anim) { var scene = new Scene(); scene.RootNode = new Node("scene_root"); scene.RootNode.Metadata["FrameRate"] = new Metadata.Entry(MetaDataType.Int32, 14); scene.RootNode.Metadata["TimeSpanStart"] = new Metadata.Entry(MetaDataType.UInt64, (ulong)0); scene.RootNode.Metadata["TimeSpanStop"] = new Metadata.Entry(MetaDataType.UInt64, (ulong)0); scene.RootNode.Metadata["CustomFrameRate"] = new Metadata.Entry(MetaDataType.Float, 1f / anim.FrameDuration); scene.RootNode.Metadata["FrontAxisSign"] = new Metadata.Entry(MetaDataType.Int32, -1); scene.RootNode.Metadata["OriginalUnitScaleFactor"] = new Metadata.Entry(MetaDataType.Int32, 100); scene.RootNode.Metadata["UnitScaleFactor"] = new Metadata.Entry(MetaDataType.Int32, 100); var a = new Assimp.Animation(); a.DurationInTicks = anim.Duration * 30; a.TicksPerSecond = 30; a.Name = anim.Name; List <Node> animTrackNodes = new List <Node>(); for (int i = 0; i < anim.TransformTrackIndexToHkxBoneMap.Length; i++) { int boneIndex = anim.TransformTrackIndexToHkxBoneMap[i]; if (boneIndex < 0 || boneIndex > anim.hkaSkeleton.Bones.Capacity) { continue; } string trackName = anim.hkaSkeleton.Bones[boneIndex].Name.GetString(); var animTrack = new NodeAnimationChannel(); animTrack.NodeName = trackName; for (int f = 0; f < anim.FrameCount; f++) { var t = anim.GetTransformOnFrame(i, f, enableLooping: false); animTrack.PositionKeys.Add(new VectorKey(1.0 * f * anim.FrameDuration, new Vector3D(t.Translation.X * -100, t.Translation.Y * 100, t.Translation.Z * 100))); animTrack.ScalingKeys.Add(new VectorKey(1.0 * f * anim.FrameDuration, new Vector3D(t.Scale.X, t.Scale.Y, t.Scale.Z))); var q = t.Rotation; //q.X *= -1; //q.Y *= -1; //q.Z *= -1; //q.W *= -1; q = SapMath.MirrorQuat(q); //q.X *= -1; //q.Y *= -1; //q.Z *= -1; //q.W *= -1; animTrack.RotationKeys.Add(new QuaternionKey(1.0 * f * anim.FrameDuration, new Quaternion(q.W, q.X, q.Y, q.Z))); } a.NodeAnimationChannels.Add(animTrack); var fakeNode = new Node(trackName); animTrackNodes.Add(fakeNode); } List <Node> topLevelTrackNodes = new List <Node>(); for (int i = 0; i < animTrackNodes.Count; i++) { var hkxBoneIndex = anim.TransformTrackIndexToHkxBoneMap[i]; var parentBoneIndex = anim.hkaSkeleton.ParentIndices[hkxBoneIndex].data; if (parentBoneIndex >= 0) { var parentTrackIndex = anim.HkxBoneIndexToTransformTrackMap[parentBoneIndex]; if (parentTrackIndex >= 0) { animTrackNodes[parentTrackIndex].Children.Add(animTrackNodes[i]); } } else { topLevelTrackNodes.Add(animTrackNodes[i]); } } var actualRootNode = new Node("root"); if (anim.RootMotion != null) { var animTrack = new NodeAnimationChannel(); animTrack.NodeName = actualRootNode.Name; for (int f = 0; f < anim.FrameCount; f++) { var rootMotionOnFrame = anim.RootMotion.GetSampleClamped(f * anim.FrameDuration); animTrack.PositionKeys.Add(new VectorKey(1.0 * f * anim.FrameDuration, new Vector3D(rootMotionOnFrame.X * -100, rootMotionOnFrame.Y * 100, rootMotionOnFrame.Z * 100))); var q = NQuaternion.CreateFromRotationMatrix(NMatrix.CreateRotationY(rootMotionOnFrame.W)); animTrack.RotationKeys.Add(new QuaternionKey(1.0 * f * anim.FrameDuration, new Quaternion(q.W, q.X, q.Y, q.Z))); } a.NodeAnimationChannels.Add(animTrack); } foreach (var t in topLevelTrackNodes) { actualRootNode.Children.Add(t); } scene.RootNode.Children.Add(actualRootNode); scene.Animations.Add(a); return(scene); }
private OpenToolkit.Mathematics.Quaternion CalcInterpolatedRotation(float animationTime, NodeAnimationChannel pNodeAnim) { // TODO add asserts if (pNodeAnim.RotationKeyCount == 1) { return(pNodeAnim.RotationKeys[0].Value.ToOtk()); } var rotationIndex = FindRotation(animationTime, pNodeAnim); var nextRotationIndex = rotationIndex + 1; var deltaTime = pNodeAnim.RotationKeys[nextRotationIndex].Time - pNodeAnim.RotationKeys[rotationIndex].Time; var factor = (animationTime - pNodeAnim.RotationKeys[rotationIndex].Time) / deltaTime; var startRotationQ = pNodeAnim.RotationKeys[rotationIndex].Value; var endRotationQ = pNodeAnim.RotationKeys[nextRotationIndex].Value; var interpolated = Assimp.Quaternion.Slerp(startRotationQ, endRotationQ, (float)factor); interpolated.Normalize(); return(interpolated.ToOtk()); }
/// <summary> /// Processes the node animation. /// </summary> /// <param name="channel">The channel.</param> /// <param name="ticksPerSecond">The ticks per second.</param> /// <param name="list">The list.</param> /// <returns></returns> protected virtual ErrorCode ProcessNodeAnimation(NodeAnimationChannel channel, double ticksPerSecond, out FastList <HxAnimations.Keyframe> list) { var posCount = channel.HasPositionKeys ? channel.PositionKeyCount : 0; var rotCount = channel.HasRotationKeys ? channel.RotationKeyCount : 0; var scaleCount = channel.HasScalingKeys ? channel.ScalingKeyCount : 0; int maxCount = Math.Max(posCount, Math.Max(rotCount, scaleCount)); var ret = new FastList <HxAnimations.Keyframe>(maxCount); if (posCount != rotCount || rotCount != scaleCount) { Log(HelixToolkit.Logger.LogLevel.Trace, "Animation Channel is non-uniform lengths." + $" Position={posCount}; Rotation={rotCount}; Scale={scaleCount};" + " Trying to automatically create uniform animation keys"); // Adds dummy key if it is empty if (posCount == 0) { channel.PositionKeys.Add(new VectorKey(0, new Vector3D())); } if (rotCount == 0) { channel.RotationKeys.Add(new QuaternionKey(0, new Quaternion())); } if (scaleCount == 0) { channel.ScalingKeys.Add(new VectorKey(0, new Vector3D(1, 1, 1))); } int i = 0, j = 0, k = 0; double nextT1 = channel.PositionKeys[i].Time, nextT2 = channel.RotationKeys[j].Time, nextT3 = channel.ScalingKeys[k].Time; var minT = Math.Min(nextT1, Math.Min(nextT2, nextT3)); for (int x = 0; x < maxCount && i < posCount && j < rotCount && k < scaleCount; ++x) { ret.Add(new HxAnimations.Keyframe() { Time = (float)(minT / ticksPerSecond), Translation = channel.PositionKeys[i].Value.ToSharpDXVector3(), Rotation = channel.RotationKeys[j].Value.ToSharpDXQuaternion(), Scale = channel.ScalingKeys[k].Value.ToSharpDXVector3(), }); nextT1 = (i + 1) < posCount ? channel.PositionKeys[i + 1].Time : double.MaxValue; // Set to max so index will not increase nextT2 = (j + 1) < rotCount ? channel.RotationKeys[j + 1].Time : double.MaxValue; nextT3 = (k + 1) < scaleCount ? channel.ScalingKeys[k + 1].Time : double.MaxValue; minT = Math.Min(nextT1, Math.Min(nextT2, nextT3)); if (minT == nextT1) { ++i; } if (minT == nextT2) { ++j; } if (minT == nextT3) { ++k; } } } else { for (int i = 0; i < posCount; ++i) { ret.Add(new HxAnimations.Keyframe() { Time = (float)(channel.PositionKeys[i].Time / ticksPerSecond), Translation = channel.PositionKeys[i].Value.ToSharpDXVector3(), Rotation = channel.RotationKeys[i].Value.ToSharpDXQuaternion(), Scale = channel.ScalingKeys[i].Value.ToSharpDXVector3() }); } } list = ret; return(ErrorCode.Succeed); }
static void Main(string[] args) { string[] strs = Directory.GetFiles(Environment.CurrentDirectory, "*.dae", SearchOption.TopDirectoryOnly); foreach (string path in strs) { try { using (AssimpContext ACont = new AssimpContext()) { ACont.SetConfig(new NormalSmoothingAngleConfig(66f)); Scene scene = ACont.ImportFile(path, PostProcessSteps.Triangulate); if (!scene.HasAnimations) { Console.WriteLine("Skipping " + path); continue; } Animation anim = scene.Animations[0]; if (!anim.HasNodeAnimations) { Console.WriteLine("Invalid animation in " + path); continue; } StringBuilder sb = new StringBuilder(); sb.Append("// mcmonkey's animation details file format v0.2\n"); sb.Append("general\n"); sb.Append("{\n"); sb.Append("\tlength: ").Append((anim.DurationInTicks / anim.TicksPerSecond).ToString()).Append(";\n"); sb.Append("}\n"); for (int i = 0; i < anim.NodeAnimationChannelCount; i++) { NodeAnimationChannel node = anim.NodeAnimationChannels[i]; sb.Append(node.NodeName).Append('\n'); sb.Append("{\n"); Node found = LookForMatch(scene.RootNode, node.NodeName); string pNode = "<none>"; if (found != null) { pNode = found.Parent.Name; } sb.Append("\tparent: " + pNode + ";\n"); /*Bone bfound = LookForBone(scene, node.NodeName); * Matrix4x4 mat = Matrix4x4.Identity; * if (bfound != null) * { * mat = bfound.OffsetMatrix; * //bpos = bfound.OffsetMatrix.C1 + "=" + bfound.OffsetMatrix.C2 + "=" + bfound.OffsetMatrix.C3; * //bpos = bfound.OffsetMatrix.A4 + "=" + bfound.OffsetMatrix.B4 + "=" + bfound.OffsetMatrix.C4; * }*/ /*sb.Append("\toffset: " + mat.A1 + "=" + mat.A2 + "=" + mat.A3 + "=" + mat.A4 + "=" + * mat.B1 + "=" + mat.B2 + "=" + mat.B3 + "=" + mat.B4 + "=" + * mat.C1 + "=" + mat.C2 + "=" + mat.C3 + "=" + mat.C4 + "=" + * mat.D1 + "=" + mat.D2 + "=" + mat.D3 + "=" + mat.D4 +";\n");*/ if (node.HasPositionKeys) { sb.Append("\tpositions: "); for (int x = 0; x < node.PositionKeyCount; x++) { VectorKey key = node.PositionKeys[x]; sb.Append(key.Time.ToString() + "=" + key.Value.X.ToString() + "=" + key.Value.Y.ToString() + "=" + key.Value.Z.ToString() + " "); } sb.Append(";\n"); } if (node.HasRotationKeys) { sb.Append("\trotations: "); for (int x = 0; x < node.RotationKeyCount; x++) { QuaternionKey key = node.RotationKeys[x]; sb.Append(key.Time.ToString() + "=" + key.Value.X.ToString() + "=" + key.Value.Y.ToString() + "=" + key.Value.Z.ToString() + "=" + key.Value.W + " "); } sb.Append(";\n"); } // TODO: Should scaling matter? Probably not. sb.Append("}\n"); } File.WriteAllText(path + ".anim", sb.ToString()); } } catch (Exception ex) { Console.WriteLine("Processing " + path + ": " + ex.ToString()); } } }
public static Animations.Animation CreateGenericAnimation(Assimp.Animation animation) { Animations.Animation STanim = new Animations.Animation(); STanim.Text = animation.Name; float TicksPerSecond = animation.TicksPerSecond != 0 ? (float)animation.TicksPerSecond : 25.0f; float Duriation = (float)animation.DurationInTicks; STanim.FrameCount = (int)(Duriation * 30); //Load node animations if (animation.HasNodeAnimations) { var _channels = new NodeAnimationChannel[animation.NodeAnimationChannelCount]; for (int i = 0; i < _channels.Length; i++) { _channels[i] = new NodeAnimationChannel(); var boneAnim = new Animations.Animation.KeyNode(_channels[i].NodeName); boneAnim.RotType = Animations.Animation.RotationType.EULER; STanim.Bones.Add(boneAnim); STConsole.WriteLine($"Creating Bone Anims {boneAnim.Text} "); for (int frame = 0; frame < STanim.FrameCount; i++) { if (_channels[i].HasPositionKeys) { for (int key = 0; key < _channels[i].PositionKeyCount; key++) { if (frame == _channels[i].PositionKeys[key].Time) { boneAnim.XPOS.Keys.Add(new Animations.Animation.KeyFrame() { Value = _channels[i].PositionKeys[key].Value.X, Frame = frame, }); boneAnim.YPOS.Keys.Add(new Animations.Animation.KeyFrame() { Value = _channels[i].PositionKeys[key].Value.Y, Frame = frame, }); boneAnim.ZPOS.Keys.Add(new Animations.Animation.KeyFrame() { Value = _channels[i].PositionKeys[key].Value.Z, Frame = frame, }); } } } if (_channels[i].HasRotationKeys) { for (int key = 0; key < _channels[i].RotationKeyCount; key++) { if (frame == _channels[i].RotationKeys[key].Time) { var quat = _channels[i].RotationKeys[key].Value; var euler = STMath.ToEulerAngles(quat.X, quat.Y, quat.Z, quat.W); boneAnim.XROT.Keys.Add(new Animations.Animation.KeyFrame() { Value = euler.X, Frame = frame, }); boneAnim.YROT.Keys.Add(new Animations.Animation.KeyFrame() { Value = euler.Y, Frame = frame, }); boneAnim.ZROT.Keys.Add(new Animations.Animation.KeyFrame() { Value = euler.Z, Frame = frame, }); boneAnim.WROT.Keys.Add(new Animations.Animation.KeyFrame() { Value = 1, Frame = frame, }); } } } if (_channels[i].HasScalingKeys) { for (int key = 0; key < _channels[i].ScalingKeyCount; key++) { if (frame == _channels[i].ScalingKeys[key].Time) { boneAnim.XSCA.Keys.Add(new Animations.Animation.KeyFrame() { Value = _channels[i].ScalingKeys[key].Value.X, Frame = frame, }); boneAnim.YSCA.Keys.Add(new Animations.Animation.KeyFrame() { Value = _channels[i].ScalingKeys[key].Value.Y, Frame = frame, }); boneAnim.ZSCA.Keys.Add(new Animations.Animation.KeyFrame() { Value = _channels[i].ScalingKeys[key].Value.Z, Frame = frame, }); } } } } } } //Load mesh animations if (animation.HasMeshAnimations) { var _meshChannels = new MeshAnimationChannel[animation.MeshAnimationChannelCount]; for (int i = 0; i < _meshChannels.Length; i++) { _meshChannels[i] = new MeshAnimationChannel(); } } return(STanim); }