/// <summary> /// Find the rotation quaternion at the specified animation time for the specified animation sequence /// </summary> /// <param name="animationTime"> The number of ticks in the animation time domain </param> /// <param name="animationNode"> The node to extract the translation vector from </param> /// <returns> The linearly interpolated rotation quaternion that represents the rotation at specified time </returns> private Quaternion CalcInterpolateRotation(double animationTime, ClientAnimationNode animationNode) { if (animationNode.Rotations.Count == 1) { return(animationNode.Rotations[0]); } if (animationNode.RotationTime[0] > animationTime) { return(animationNode.Rotations[0]); } // find the key frame before or at the current frame int rotationIndex = -1; for (int i = 0; i < animationNode.Rotations.Count; i++) { if (animationNode.RotationTime[i] > animationTime) { rotationIndex = i - 1; break; } } if (rotationIndex == -1) { return(animationNode.Rotations[animationNode.Rotations.Count - 1]); } int nextRotationIndex = rotationIndex + 1; double frameDuration = animationNode.RotationTime[nextRotationIndex] - animationNode.RotationTime[rotationIndex]; double factor = (animationTime - animationNode.RotationTime[rotationIndex]) / frameDuration; if (factor <= 0.0) { return(animationNode.Rotations[rotationIndex]); } if (factor >= 1.0) { return(animationNode.Rotations[nextRotationIndex]); } return(Quaternion.Lerp(animationNode.Rotations[rotationIndex], animationNode.Rotations[nextRotationIndex], (float)factor)); }
/// <summary> /// Find the scaling vector at the specified animation time for the specified animation sequence /// </summary> /// <param name="animationTime"> The number of ticks in the animation time domain </param> /// <param name="animationNode"> The node to extract the translation vector from </param> /// <returns> The linearly interpolated scaling vector that represents the scaling at specified time </returns> private Vector3 CalcInterpolateScaling(double animationTime, ClientAnimationNode animationNode) { if (animationNode.Scalings.Count == 1) { return(animationNode.Scalings[0]); } if (animationNode.ScalingTime[0] > animationTime) { return(animationNode.Scalings[0]); } // find the key frame before or at the current frame int scalingIndex = -1; for (int i = 0; i < animationNode.Scalings.Count; i++) { if (animationNode.ScalingTime[i] > animationTime) { scalingIndex = i - 1; break; } } if (scalingIndex == -1) { return(animationNode.Scalings[animationNode.Scalings.Count - 1]); } int nextScalingIndex = scalingIndex + 1; double frameDuration = animationNode.ScalingTime[nextScalingIndex] - animationNode.ScalingTime[scalingIndex]; double factor = (animationTime - animationNode.ScalingTime[scalingIndex]) / frameDuration; if (factor <= 0.0) { return(animationNode.Scalings[scalingIndex]); } if (factor >= 1.0) { return(animationNode.Scalings[nextScalingIndex]); } return(Vector3.Lerp(animationNode.Scalings[scalingIndex], animationNode.Scalings[nextScalingIndex], (float)factor)); }
/// <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()); }