private void ApplyBoneOrientationToNode_(Node assNode, IBone finBone) { var finTransform = MatrixTransformUtil.FromTrs(finBone.LocalPosition, finBone.LocalRotation, finBone.LocalScale); var assNodeTransform = assNode.Transform; MatrixConversionUtil.CopyFinIntoAssimp( finTransform, ref assNodeTransform); assNode.Transform = assNodeTransform; }
public void BuildAndBindMesh( Scene assScene, IModel finModel) { var meshNode = new Node("meshes"); assScene.RootNode.Children.Add(meshNode); var primitivesByMaterial = new ReadOnlyListDictionary <IMaterial, IPrimitive>( finModel.Skin.Primitives, primitive => primitive.Material); var allFinVertices = finModel.Skin.Vertices; var allAssVertexLocationsByFinIndex = new Vector3D[allFinVertices.Count]; var allAssVertexNormalsByFinIndex = new Vector3D[allFinVertices.Count]; for (var i = 0; i < allFinVertices.Count; ++i) { var finVertex = allFinVertices[i]; var location = finVertex.LocalPosition; allAssVertexLocationsByFinIndex[i] = new Vector3D(location.X, location.Y, location.Z); var normal = finVertex.LocalNormal; if (normal != null) { allAssVertexNormalsByFinIndex[i] = new Vector3D(normal.X, normal.Y, normal.Z); } } var finToAssVertexIndices = new int[allFinVertices.Count]; var assMeshes = assScene.Meshes; foreach (var primitivesAndMaterial in primitivesByMaterial) { var meshIndex = meshNode.MeshIndices.Count; var meshName = $"mesh {meshIndex}"; var assMesh = new Mesh(AssPrimitiveType.Triangle); meshNode.MeshIndices.Add(meshIndex); assMesh.Name = meshName; for (var i = 0; i < finToAssVertexIndices.Length; ++i) { finToAssVertexIndices[i] = -1; } // Adds all primitives var finVertexIndicesInAssMesh = new List <int>(); var assVertexCounter = 0; foreach (var finPrimitive in primitivesAndMaterial.Value) { var finVertices = finPrimitive.Vertices; // TODO: Add more support for primitives. foreach (var finVertex in finVertices) { var finVertexIndex = finVertex.Index; if (finToAssVertexIndices[finVertexIndex] == -1) { finToAssVertexIndices[finVertexIndex] = assVertexCounter++; finVertexIndicesInAssMesh.Add(finVertexIndex); } } switch (finPrimitive.Type) { case FinPrimitiveType.TRIANGLES: { for (var v = 0; v < finVertices.Count; v += 3) { var assFaceVertexIndices = new int[3]; for (var i = 0; i < 3; ++i) { assFaceVertexIndices[i] = finToAssVertexIndices[finVertices[v + i].Index]; } //assMesh.Faces.Add(new Face(assFaceVertexIndices)); } break; } case FinPrimitiveType.TRIANGLE_STRIP: { for (var v = 0; v < finVertices.Count - 2; ++v) { var assVertexIndices = new int[3]; if (v % 2 == 0) { for (var i = 0; i < 3; ++i) { assVertexIndices[i] = finToAssVertexIndices[finVertices[v + i].Index]; } } else { // Switches drawing order to maintain proper winding: // https://www.khronos.org/opengl/wiki/Primitive for (var i = 0; i < 3; ++i) { var swizzle = i < 2 ? 1 - i : i; assVertexIndices[i] = finToAssVertexIndices[finVertices[v + swizzle].Index]; } } //assMesh.Faces.Add(new Face(assVertexIndices)); } break; } case FinPrimitiveType.QUADS: { for (var v = 0; v < finVertices.Count; v += 4) { // TODO: Factor in distance like this approach: // http://james-ramsden.com/triangulate-a-quad-mesh-in-c/ var a = finToAssVertexIndices[finVertices[v + 0].Index]; var b = finToAssVertexIndices[finVertices[v + 1].Index]; var c = finToAssVertexIndices[finVertices[v + 2].Index]; var d = finToAssVertexIndices[finVertices[v + 3].Index]; //assMesh.Faces.Add(new Face(new[] {a, b, d})); //assMesh.Faces.Add(new Face(new[] {b, c, d})); } break; } default: throw new NotSupportedException(); } } // Adds all vertices to the mesh foreach (var finVertexIndex in finVertexIndicesInAssMesh) { //assMesh.Vertices.Add( // allAssVertexLocationsByFinIndex[finVertexIndex]); // TODO: Are these allowed to be null? var normal = allAssVertexNormalsByFinIndex[finVertexIndex]; //assMesh.Normals.Add(normal); } // NOTES: // - All bones are added for each mesh // - All bones must have at least one vertex weight in the list. If none, // this should be vertex 0 with weight 0. var assBones = new Dictionary <string, Bone>(); // Adds all bones to the mesh. var finBoneQueue = new Queue <IBone>(); finBoneQueue.Enqueue(finModel.Skeleton.Root); while (finBoneQueue.Count > 0) { var finBone = finBoneQueue.Dequeue(); var boneName = finBone.Name; if (boneName != null) { var assBone = new Bone { Name = boneName }; //assMesh.Bones.Add(assBone); assBones[boneName] = assBone; } foreach (var childBone in finBone.Children) { finBoneQueue.Enqueue(childBone); } } // Adds weights for each bone. for (var i = 0; i < finVertexIndicesInAssMesh.Count; ++i) { var finVertexIndex = finVertexIndicesInAssMesh[i]; var finVertex = allFinVertices[finVertexIndex]; var finWeights = finVertex.Weights; if (finWeights != null) { foreach (var finWeight in finWeights) { var boneName = finWeight.Bone.Name; var assBone = assBones[boneName]; if (assBone.VertexWeights.Count == 0) { var assOffsetMatrix = new Matrix4x4(); MatrixConversionUtil.CopyFinIntoAssimp( finWeight.SkinToBone, ref assOffsetMatrix); assBone.OffsetMatrix = assOffsetMatrix; } assBone.VertexWeights.Add( new VertexWeight(i, finWeight.Weight)); } } } // Verifies that all bones have at least one weight. // (Is this needed?) foreach (var assBone in assBones.Values) { if (assBone.VertexWeights.Count == 0) { assBone.VertexWeights.Add(new VertexWeight(0, 0)); } } } }