protected override void DoRender() { // Calculate elapsed seconds var time = clock.ElapsedMilliseconds / 1000.0f; // Retrieve device context var context = this.DeviceManager.Direct3DContext; // Calculate skin matrices for each bone ConstantBuffers.PerArmature skinMatrices = new ConstantBuffers.PerArmature(); if (mesh.Bones != null) { // Retrieve each bone's local transform for (var i = 0; i < mesh.Bones.Count; i++) { skinMatrices.Bones[i] = mesh.Bones[i].BoneLocalTransform; } // Load bone transforms from animation frames if (CurrentAnimation.HasValue) { // Keep track of the last key-frame used for each bone Mesh.Keyframe?[] lastKeyForBones = new Mesh.Keyframe?[mesh.Bones.Count]; // Keep track of whether a bone has been interpolated bool[] lerpedBones = new bool[mesh.Bones.Count]; for (var i = 0; i < CurrentAnimation.Value.Keyframes.Count; i++) { // Retrieve current key-frame var frame = CurrentAnimation.Value.Keyframes[i]; // If the current frame is not in the future if (frame.Time <= time) { // Keep track of last key-frame for bone lastKeyForBones[frame.BoneIndex] = frame; // Retrieve transform from current key-frame skinMatrices.Bones[frame.BoneIndex] = frame.Transform; } // Frame is in the future, check if we should interpolate else { // Only interpolate a bone's key-frames ONCE if (!lerpedBones[frame.BoneIndex]) { // Retrieve the previous key-frame if exists Mesh.Keyframe prevFrame; if (lastKeyForBones[frame.BoneIndex] != null) prevFrame = lastKeyForBones[frame.BoneIndex].Value; else continue; // nothing to interpolate // Make sure we only interpolate with // one future frame for this bone lerpedBones[frame.BoneIndex] = true; // Calculate time difference between frames var frameLength = frame.Time - prevFrame.Time; var timeDiff = time - prevFrame.Time; var amount = timeDiff / frameLength; // Interpolation using Lerp on scale and translation, and Slerp on Rotation (Quaternion) Vector3 t1, t2; // Translation Quaternion q1, q2;// Rotation float s1, s2; // Scale // Decompose the previous key-frame's transform prevFrame.Transform.DecomposeUniformScale(out s1, out q1, out t1); // Decompose the current key-frame's transform frame.Transform.DecomposeUniformScale(out s2, out q2, out t2); // Perform interpolation and reconstitute matrix skinMatrices.Bones[frame.BoneIndex] = Matrix.Scaling(MathUtil.Lerp(s1, s2, amount)) * Matrix.RotationQuaternion(Quaternion.Slerp(q1, q2, amount)) * Matrix.Translation(Vector3.Lerp(t1, t2, amount)); } } } } // Apply parent bone transforms // We assume here that the first bone has no parent // and that each parent bone appears before children for (var i = 1; i < mesh.Bones.Count; i++) { var bone = mesh.Bones[i]; if (bone.ParentIndex > -1) { var parentTransform = skinMatrices.Bones[bone.ParentIndex]; skinMatrices.Bones[i] = (skinMatrices.Bones[i] * parentTransform); } } // Change the bone transform from rest pose space into bone space (using the inverse of the bind/rest pose) for (var i = 0; i < mesh.Bones.Count; i++) { skinMatrices.Bones[i] = Matrix.Transpose(mesh.Bones[i].InvBindPose * skinMatrices.Bones[i]); } // Check need to loop animation if (!PlayOnce && CurrentAnimation.HasValue && CurrentAnimation.Value.EndTime <= time) { this.Clock.Restart(); } } // Update the constant buffer with the skin matrices for each bone context.UpdateSubresource(skinMatrices.Bones, PerArmatureBuffer); // Draw sub-meshes grouped by material for (var mIndx = 0; mIndx < mesh.Materials.Count; mIndx++) { // Retrieve sub meshes for this material var subMeshesForMaterial = (from sm in mesh.SubMeshes where sm.MaterialIndex == mIndx select sm).ToArray(); // If the material buffer is available and there are submeshes // using the material update the PerMaterialBuffer if (PerMaterialBuffer != null && subMeshesForMaterial.Length > 0) { // update the PerMaterialBuffer constant buffer var material = new ConstantBuffers.PerMaterial() { Ambient = new Color4(mesh.Materials[mIndx].Ambient), Diffuse = new Color4(mesh.Materials[mIndx].Diffuse), Emissive = new Color4(mesh.Materials[mIndx].Emissive), Specular = new Color4(mesh.Materials[mIndx].Specular), SpecularPower = mesh.Materials[mIndx].SpecularPower, UVTransform = mesh.Materials[mIndx].UVTransform, }; // Bind textures to the pixel shader int texIndxOffset = mIndx * Common.Mesh.MaxTextures; material.HasTexture = (uint)(textureViews[texIndxOffset] != null ? 1 : 0); // 0=false context.PixelShader.SetShaderResources(0, textureViews.GetRange(texIndxOffset, Common.Mesh.MaxTextures).ToArray()); // Set texture sampler state context.PixelShader.SetSampler(0, samplerState); // Update material buffer context.UpdateSubresource(ref material, PerMaterialBuffer); } // For each sub-mesh foreach (var subMesh in subMeshesForMaterial) { // Ensure the vertex buffer and index buffers are in range if (subMesh.VertexBufferIndex < vertexBuffers.Count && subMesh.IndexBufferIndex < indexBuffers.Count) { // Retrieve and set the vertex and index buffers var vertexBuffer = vertexBuffers[(int)subMesh.VertexBufferIndex]; context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, Utilities.SizeOf<Vertex>(), 0)); context.InputAssembler.SetIndexBuffer(indexBuffers[(int)subMesh.IndexBufferIndex], Format.R16_UInt, 0); // Set topology context.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList; } // Draw the sub-mesh (includes Primitive count which we multiply by 3) // The submesh also includes a start index into the vertex buffer context.DrawIndexed((int)subMesh.PrimCount * 3, (int)subMesh.StartIndex, 0); } } }
protected override void DoRender() { // Calculate elapsed seconds var time = clock.ElapsedMilliseconds / 1000.0f; // Retrieve device context var context = this.DeviceManager.Direct3DContext; // Calculate skin matrices for each bone ConstantBuffers.PerArmature skinMatrices = new ConstantBuffers.PerArmature(); if (mesh.Bones != null) { // Retrieve each bone's local transform for (var i = 0; i < mesh.Bones.Count; i++) { skinMatrices.Bones[i] = mesh.Bones[i].BoneLocalTransform; } // Load bone transforms from animation frames if (CurrentAnimation.HasValue) { // Keep track of the last key-frame used for each bone Mesh.Keyframe?[] lastKeyForBones = new Mesh.Keyframe?[mesh.Bones.Count]; // Keep track of whether a bone has been interpolated bool[] lerpedBones = new bool[mesh.Bones.Count]; for (var i = 0; i < CurrentAnimation.Value.Keyframes.Count; i++) { // Retrieve current key-frame var frame = CurrentAnimation.Value.Keyframes[i]; // If the current frame is not in the future if (frame.Time <= time) { // Keep track of last key-frame for bone lastKeyForBones[frame.BoneIndex] = frame; // Retrieve transform from current key-frame skinMatrices.Bones[frame.BoneIndex] = frame.Transform; } // Frame is in the future, check if we should interpolate else { // Only interpolate a bone's key-frames ONCE if (!lerpedBones[frame.BoneIndex]) { // Retrieve the previous key-frame if exists Mesh.Keyframe prevFrame; if (lastKeyForBones[frame.BoneIndex] != null) { prevFrame = lastKeyForBones[frame.BoneIndex].Value; } else { continue; // nothing to interpolate } // Make sure we only interpolate with // one future frame for this bone lerpedBones[frame.BoneIndex] = true; // Calculate time difference between frames var frameLength = frame.Time - prevFrame.Time; var timeDiff = time - prevFrame.Time; var amount = timeDiff / frameLength; // Interpolation using Lerp on scale and translation, and Slerp on Rotation (Quaternion) Vector3 t1, t2; // Translation Quaternion q1, q2; // Rotation float s1, s2; // Scale // Decompose the previous key-frame's transform prevFrame.Transform.DecomposeUniformScale(out s1, out q1, out t1); // Decompose the current key-frame's transform frame.Transform.DecomposeUniformScale(out s2, out q2, out t2); // Perform interpolation and reconstitute matrix skinMatrices.Bones[frame.BoneIndex] = Matrix.Scaling(MathUtil.Lerp(s1, s2, amount)) * Matrix.RotationQuaternion(Quaternion.Slerp(q1, q2, amount)) * Matrix.Translation(Vector3.Lerp(t1, t2, amount)); } } } } // Apply parent bone transforms // We assume here that the first bone has no parent // and that each parent bone appears before children for (var i = 1; i < mesh.Bones.Count; i++) { var bone = mesh.Bones[i]; if (bone.ParentIndex > -1) { var parentTransform = skinMatrices.Bones[bone.ParentIndex]; skinMatrices.Bones[i] = (skinMatrices.Bones[i] * parentTransform); } } // Change the bone transform from rest pose space into bone space (using the inverse of the bind/rest pose) for (var i = 0; i < mesh.Bones.Count; i++) { skinMatrices.Bones[i] = Matrix.Transpose(mesh.Bones[i].InvBindPose * skinMatrices.Bones[i]); } // Check need to loop animation if (!PlayOnce && CurrentAnimation.HasValue && CurrentAnimation.Value.EndTime <= time) { this.Clock.Restart(); } } // Update the constant buffer with the skin matrices for each bone context.UpdateSubresource(skinMatrices.Bones, PerArmatureBuffer); // Draw sub-meshes grouped by material for (var mIndx = 0; mIndx < mesh.Materials.Count; mIndx++) { // Retrieve sub meshes for this material var subMeshesForMaterial = (from sm in mesh.SubMeshes where sm.MaterialIndex == mIndx select sm).ToArray(); // If the material buffer is available and there are submeshes // using the material update the PerMaterialBuffer if (PerMaterialBuffer != null && subMeshesForMaterial.Length > 0) { // update the PerMaterialBuffer constant buffer var material = new ConstantBuffers.PerMaterial() { Ambient = new Color4(mesh.Materials[mIndx].Ambient), Diffuse = new Color4(mesh.Materials[mIndx].Diffuse), Emissive = new Color4(mesh.Materials[mIndx].Emissive), Specular = new Color4(mesh.Materials[mIndx].Specular), SpecularPower = mesh.Materials[mIndx].SpecularPower, UVTransform = mesh.Materials[mIndx].UVTransform, }; // Bind textures to the pixel shader int texIndxOffset = mIndx * Common.Mesh.MaxTextures; material.HasTexture = (uint)(textureViews[texIndxOffset] != null ? 1 : 0); // 0=false context.PixelShader.SetShaderResources(0, textureViews.GetRange(texIndxOffset, Common.Mesh.MaxTextures).ToArray()); // Set texture sampler state context.PixelShader.SetSampler(0, samplerState); // Update material buffer context.UpdateSubresource(ref material, PerMaterialBuffer); } // For each sub-mesh foreach (var subMesh in subMeshesForMaterial) { // Ensure the vertex buffer and index buffers are in range if (subMesh.VertexBufferIndex < vertexBuffers.Count && subMesh.IndexBufferIndex < indexBuffers.Count) { // Retrieve and set the vertex and index buffers var vertexBuffer = vertexBuffers[(int)subMesh.VertexBufferIndex]; context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, Utilities.SizeOf <Vertex>(), 0)); context.InputAssembler.SetIndexBuffer(indexBuffers[(int)subMesh.IndexBufferIndex], Format.R16_UInt, 0); // Set topology context.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList; } // Draw the sub-mesh (includes Primitive count which we multiply by 3) // The submesh also includes a start index into the vertex buffer context.DrawIndexed((int)subMesh.PrimCount * 3, (int)subMesh.StartIndex, 0); } } }