/// <summary> /// Calculate mesh only once ... no worry about animation, no accurate bounding box /// </summary> /// <param name="bones"></param> /// <param name="mesh"></param> /// <returns></returns> /// <remarks> /// https://gamedev.stackexchange.com/questions/46332/mesh-manipulation-on-gpu-vs-cpu /// https://gamedev.stackexchange.com/questions/43986/calculate-an-aabb-for-bone-animated-model /// </remarks> AnimationHittableGeometryComponent ConstructMesh(Matrix4x4[] bones, CMOAnimateMeshComponent mesh) { var pos = new List <Vector3>(); var norms = new List <Vector3>(); var indeces = new List <int>(); for (int indx = 0; indx < mesh.VertexBuffers.Count; indx++) { var vb = mesh.VertexBuffers[indx]; var vertices = new Vertex[vb.Length]; for (var i = 0; i < vb.Length; i++) { // Retrieve skinning information for vertex var skin = new SkinningVertex(); if (mesh.SkinningVertexBuffers.Count > 0) { skin = mesh.SkinningVertexBuffers[indx][i]; } // Create vertex vertices[i] = new Vertex(vb[i].Position, vb[i].Normal, (Vector4)vb[i].Color, vb[i].UV, skin); //the same matrix as in animation.hlsl var skinTransform = Matrix4x4.Transpose( bones[skin.BoneIndex0] * skin.BoneWeight0 + bones[skin.BoneIndex1] * skin.BoneWeight1 + bones[skin.BoneIndex2] * skin.BoneWeight2 + bones[skin.BoneIndex3] * skin.BoneWeight3); pos.Add(Vector3.Transform(vb[i].Position, skinTransform)); norms.Add(Vector3.TransformNormal(vb[i].Normal, skinTransform)); } } var offset = 0; // Initialize index buffers for (var i = 0; i < mesh.IndexBuffers.Count; i++) { var ib = mesh.IndexBuffers[i]; foreach (var ii in ib) { indeces.Add(offset + (int)ii); } offset += mesh.VertexBuffers[i].Length; } var geo = new AnimationHittableGeometryComponent(); geo.Positions = pos.ToImmutableArray(); geo.Indices = indeces.ToImmutableArray(); geo.Normals = norms.ToImmutableArray(); geo.IsModified = true; return(geo); }
void RenderMaterial(DeviceContext context, D3DAnimRenderComponent render, CMOAnimateMeshComponent mesh) { var materialCount = mesh.Materials.Count; // If there are no materials if (materialCount == 0) { RenderMeshes(context, render, mesh.SubMeshes); return; } // Draw sub-meshes grouped by material for (var mIndx = 0; mIndx < materialCount; mIndx++) { // Retrieve sub meshes for this material var subMeshesForMaterial = mesh.SubMeshes.Where(x => x.MaterialIndex == mIndx).ToList(); try { // If the material buffer is available and there are submeshes // using the material update the PerMaterialBuffer if (subMeshesForMaterial.Count > 0) { // update the PerMaterialBuffer constant buffer var material = new ConstantBuffers.PerMaterial() { Ambient = mesh.Materials[mIndx].Ambient, Diffuse = mesh.Materials[mIndx].Diffuse, Emissive = mesh.Materials[mIndx].Emissive, Specular = mesh.Materials[mIndx].Specular, SpecularPower = mesh.Materials[mIndx].SpecularPower, UVTransform = mesh.Materials[mIndx].UVTransform, }; // Bind textures to the pixel shader int texIndxOffset = mIndx * CMOAnimateMeshComponent.MaxTextures; material.HasTexture = (uint)(render.TextureViews.Get()[texIndxOffset] != null ? 1 : 0); // 0=false context.PixelShader.SetShaderResources(0, render.TextureViews.Get().GetRange(texIndxOffset, CMOAnimateMeshComponent.MaxTextures).ToArray()); context.PixelShader.SetSampler(0, render.SamplerState.Get()); context.UpdateSubresource(ref material, render.PerMaterialBuffer.Get()); } } catch (Exception ex) { ex.ToString(); } // For each sub-mesh RenderMeshes(context, render, subMeshesForMaterial); } }
/// <summary> /// calculate bones matrix each render frame /// </summary> /// <param name="AnimatedMesh"></param> /// <param name="currentAnimation"></param> /// <returns></returns> Matrix4x4[] CalculateBonesMatrix(CMOAnimateMeshComponent AnimatedMesh, CMOAnimateMeshComponent.Animation currentAnimation) { Matrix4x4[] bones = new Matrix4x4[MaxBones]; // Retrieve each bone's local transform for (var i = 0; i < AnimatedMesh.Bones.Count; i++) { bones[i] = AnimatedMesh.Bones[i].BoneLocalTransform; } // Keep track of the last key-frame used for each bone CMOAnimateMeshComponent.Keyframe?[] lastKeyForBones = new CMOAnimateMeshComponent.Keyframe?[AnimatedMesh.Bones.Count]; // Keep track of whether a bone has been interpolated bool[] lerpedBones = new bool[AnimatedMesh.Bones.Count]; for (var i = 0; i < currentAnimation.Keyframes.Count; i++) { // Retrieve current key-frame var frame = currentAnimation.Keyframes[i]; // If the current frame is not in the future if (frame.Time <= CurrentAnimationTime) { // Keep track of last key-frame for bone lastKeyForBones[frame.BoneIndex] = frame; // Retrieve transform from current key-frame bones[frame.BoneIndex] = frame.Transform; } else // Frame is in the future, check if we should interpolate // Only interpolate a bone's key-frames ONCE { if (!lerpedBones[frame.BoneIndex]) { // Retrieve the previous key-frame if exists CMOAnimateMeshComponent.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 = CurrentAnimationTime - prevFrame.Time; float amount = (float)timeDiff / frameLength; // Interpolation using Lerp on scale and translation, and Slerp on Rotation (Quaternion) Vector3 t1, t2; // Translation Quaternion q1, q2; // Rotation Vector3 s1, s2; // Scale // Decompose the previous key-frame's transform //SharpDX.Matrix.DecomposeUniformScale(out s1, out q1, out t1); Matrix4x4.Decompose(prevFrame.Transform, out s1, out q1, out t1); // Decompose the current key-frame's transform //frame.Transform.DecomposeUniformScale(out s2, out q2, out t2); Matrix4x4.Decompose(frame.Transform, out s2, out q2, out t2); // Perform interpolation and reconstitute matrix bones[frame.BoneIndex] = Matrix4x4.CreateScale((float)MathUtil.Lerp(s1.X, s2.X, amount)) * Matrix4x4.CreateFromQuaternion(Quaternion.Slerp(q1, q2, amount)) * Matrix4x4.CreateTranslation(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 < AnimatedMesh.Bones.Count; i++) { var bone = AnimatedMesh.Bones[i]; if (bone.ParentIndex > -1) { var parentTransform = bones[bone.ParentIndex]; bones[i] = (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 < AnimatedMesh.Bones.Count; i++) { bones[i] = Matrix4x4.Transpose(AnimatedMesh.Bones[i].InvBindPose * bones[i]); } // Check need to loop animation if (currentAnimation.EndTime <= CurrentAnimationTime) { CurrentAnimationTime = 0; } return(bones); }
void UpdateRenderComponent(Device device, D3DAnimRenderComponent render, CMOAnimateMeshComponent mesh) { render.ClearIndexBuffer(); render.ClearVertexBuffer(); render.TextureViews.Set(new List <ShaderResourceView>()); try { // Initialize vertex buffers for (int indx = 0; indx < mesh.VertexBuffers.Count; indx++) { var vb = mesh.VertexBuffers[indx]; Vertex[] vertices = new Vertex[vb.Length]; for (var i = 0; i < vb.Length; i++) { // Retrieve skinning information for vertex var skin = new SkinningVertex(); if (mesh.SkinningVertexBuffers.Count > 0) { skin = mesh.SkinningVertexBuffers[indx][i]; } // Create vertex vertices[i] = new Vertex(vb[i].Position, vb[i].Normal, (Vector4)vb[i].Color, vb[i].UV, skin); } render .AddVertexBuffer(Buffer.Create(device, BindFlags.VertexBuffer, vertices)) .DebugName = "VertexBuffer_" + indx.ToString(); } // Initialize index buffers for (var i = 0; i < mesh.IndexBuffers.Count; i++) { var ib = mesh.IndexBuffers[i]; render .AddIndexBuffer(Buffer.Create(device, BindFlags.IndexBuffer, ib)) .DebugName = "IndexBuffer_" + i.ToString(); } var tloader = new TextureLoader(device); //Load textures if a material has any. foreach (var mat in mesh.Materials) { for (var i = 0; i < mat.Textures.Length; i++) { if (System.IO.File.Exists(mat.Textures[i])) { render.TextureViews.Get().Add(tloader.LoadShaderResource(new FileInfo(mat.Textures[i]))); } else { render.TextureViews.Get().Add(null); } } } } catch (Exception ex) { ex.ToString(); } render.PerMaterialBuffer.Set(new Buffer(device, Unsafe.SizeOf <ConstantBuffers.PerMaterial>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0)); render.PerObjectBuffer.Set(new Buffer(device, Unsafe.SizeOf <ConstantBuffers.PerObject>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0)); render.PerArmatureBuffer.Set(new Buffer(device, ConstantBuffers.PerArmature.Size(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0)); }