public static Model ConvertFromAssimpScene(string filePath, ModelConverterOptions options) { // For importing textures string baseDirectoryPath = Path.GetDirectoryName(filePath); // Import scene var aiScene = AssimpSceneImporter.ImportFile(filePath); // Build textures & Materials var model = new Model(options.Version); model.Textures = new TextureDictionary(options.Version); model.Materials = new MaterialDictionary(options.Version); foreach (var aiSceneMaterial in aiScene.Materials) { var material = ConvertMaterialAndTextures(aiSceneMaterial, options, baseDirectoryPath, model.Textures); model.Materials.Add(material); } // Create scene var sceneConverterOptions = new SceneConverterOptions() { Version = options.Version, ConvertSkinToZUp = options.ConvertSkinToZUp, GenerateVertexColors = options.GenerateVertexColors, }; model.Scene = SceneConverter.ConvertFromAssimpScene(aiScene, sceneConverterOptions); return(model); }
public static Scene ConvertFromAssimpScene(Ai.Scene aiScene, SceneConverterOptions options) { var scene = new Scene(options.Version); // Convert assimp nodes to our nodes var nodeLookup = new Dictionary <string, NodeInfo>(); int nextNodeIndex = 0; scene.RootNode = ConvertAssimpNodeRecursively(aiScene.RootNode, nodeLookup, ref nextNodeIndex); // Process the meshes attached to the assimp nodes var nodeToBoneIndices = new Dictionary <int, List <int> >(); int nextBoneIndex = 0; var boneInverseBindMatrices = new List <Matrix4x4>(); var transformedVertices = new List <Vector3>(); ProcessAssimpNodeMeshesRecursively(aiScene.RootNode, aiScene, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, transformedVertices, options); // Don't build a bone palette if there are no skinned meshes if (boneInverseBindMatrices.Count > 0) { // Build bone palette for skinning scene.BonePalette = BuildBonePalette(boneInverseBindMatrices, nodeToBoneIndices); } // Build bounding box & sphere scene.BoundingBox = BoundingBox.Calculate(transformedVertices); scene.BoundingSphere = BoundingSphere.Calculate(scene.BoundingBox.Value, transformedVertices); return(scene); }
private static Geometry ConvertAssimpMeshToGeometry(Ai.Mesh aiMesh, Ai.Material material, Dictionary <string, NodeInfo> nodeLookup, ref int nextBoneIndex, Dictionary <int, List <int> > nodeToBoneIndices, List <Matrix4x4> boneInverseBindMatrices, ref Matrix4x4 nodeWorldTransform, ref Matrix4x4 nodeInverseWorldTransform, List <Vector3> transformedVertices, SceneConverterOptions options) { if (!aiMesh.HasVertices) { throw new Exception("Assimp mesh has no vertices"); } var geometry = new Geometry(); var geometryTransformedVertices = new Vector3[aiMesh.VertexCount]; geometry.Vertices = aiMesh.Vertices .Select(x => new Vector3(x.X, x.Y, x.Z)) .ToArray(); for (int i = 0; i < geometry.Vertices.Length; i++) { geometryTransformedVertices[i] = Vector3.Transform(geometry.Vertices[i], nodeWorldTransform); } transformedVertices.AddRange(geometryTransformedVertices); if (aiMesh.HasNormals) { geometry.Normals = aiMesh.Normals .Select(x => new Vector3(x.X, x.Y, x.Z)) .ToArray(); } if (aiMesh.HasTextureCoords(0)) { geometry.TexCoordsChannel0 = aiMesh.TextureCoordinateChannels[0] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasTextureCoords(1)) { geometry.TexCoordsChannel1 = aiMesh.TextureCoordinateChannels[1] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasTextureCoords(2)) { geometry.TexCoordsChannel2 = aiMesh.TextureCoordinateChannels[2] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasVertexColors(0)) { geometry.ColorChannel0 = aiMesh.VertexColorChannels[0] .Select(x => ( uint )(( byte )(x.B * 255f) | ( byte )(x.G * 255f) << 8 | ( byte )(x.R * 255f) << 16 | ( byte )(x.A * 255f) << 24)) .ToArray(); } else if (options.GenerateVertexColors) { geometry.ColorChannel0 = new uint[geometry.VertexCount]; for (int i = 0; i < geometry.ColorChannel0.Length; i++) { geometry.ColorChannel0[i] = 0xFFFFFFFF; } } if (aiMesh.HasVertexColors(1)) { geometry.ColorChannel1 = aiMesh.VertexColorChannels[1] .Select(x => ( uint )(( byte )(x.B * 255f) | ( byte )(x.G * 255f) << 8 | ( byte )(x.R * 255f) << 16 | ( byte )(x.A * 255f) << 24)) .ToArray(); } if (aiMesh.HasFaces) { geometry.TriangleIndexType = aiMesh.VertexCount <= ushort.MaxValue ? TriangleIndexType.UInt16 : TriangleIndexType.UInt32; geometry.Triangles = aiMesh.Faces .Select(x => new Triangle(( uint )x.Indices[0], ( uint )x.Indices[1], ( uint )x.Indices[2])) .ToArray(); } if (aiMesh.HasBones) { geometry.VertexWeights = new VertexWeight[geometry.VertexCount]; for (int i = 0; i < geometry.VertexWeights.Length; i++) { geometry.VertexWeights[i].Indices = new byte[4]; geometry.VertexWeights[i].Weights = new float[4]; } var vertexWeightCounts = new int[geometry.VertexCount]; for (var i = 0; i < aiMesh.Bones.Count; i++) { var aiMeshBone = aiMesh.Bones[i]; // Find node index for the bone var boneLookupData = nodeLookup[AssimpConverterCommon.UnescapeName(aiMeshBone.Name)]; int nodeIndex = boneLookupData.Index; // Calculate inverse bind matrix var boneNode = boneLookupData.Node; var bindMatrix = boneNode.WorldTransform * nodeInverseWorldTransform; if (options.ConvertSkinToZUp) { bindMatrix *= YToZUpMatrix; } Matrix4x4.Invert(bindMatrix, out var inverseBindMatrix); // Get bone index int boneIndex; if (!nodeToBoneIndices.TryGetValue(nodeIndex, out var boneIndices)) { // No entry for the node was found, so we add a new one boneIndex = nextBoneIndex++; nodeToBoneIndices.Add(nodeIndex, new List <int>() { boneIndex }); boneInverseBindMatrices.Add(inverseBindMatrix); } else { // Entry for the node was found // Try to find the bone index based on whether the inverse bind matrix matches boneIndex = -1; foreach (int index in boneIndices) { if (boneInverseBindMatrices[index].Equals(inverseBindMatrix)) { boneIndex = index; } } if (boneIndex == -1) { // None matching inverse bind matrix was found, so we add a new entry boneIndex = nextBoneIndex++; nodeToBoneIndices[nodeIndex].Add(boneIndex); boneInverseBindMatrices.Add(inverseBindMatrix); } } foreach (var aiVertexWeight in aiMeshBone.VertexWeights) { int vertexWeightCount = vertexWeightCounts[aiVertexWeight.VertexID]++; geometry.VertexWeights[aiVertexWeight.VertexID].Indices[vertexWeightCount] = ( byte )boneIndex; geometry.VertexWeights[aiVertexWeight.VertexID].Weights[vertexWeightCount] = aiVertexWeight.Weight; } } } geometry.MaterialName = AssimpConverterCommon.UnescapeName(material.Name); geometry.BoundingBox = BoundingBox.Calculate(geometry.Vertices); geometry.BoundingSphere = BoundingSphere.Calculate(geometry.BoundingBox.Value, geometry.Vertices); geometry.Flags |= GeometryFlags.Flag80000000; return(geometry); }
private static void ProcessAssimpNodeMeshesRecursively(Ai.Node aiNode, Ai.Scene aiScene, Dictionary <string, NodeInfo> nodeLookup, ref int nextBoneIndex, Dictionary <int, List <int> > nodeToBoneIndices, List <Matrix4x4> boneInverseBindMatrices, List <Vector3> transformedVertices, SceneConverterOptions options) { if (aiNode.HasMeshes) { var nodeInfo = nodeLookup[AssimpConverterCommon.UnescapeName(aiNode.Name)]; var node = nodeInfo.Node; var nodeWorldTransform = node.WorldTransform; Matrix4x4.Invert(nodeWorldTransform, out var nodeInverseWorldTransform); foreach (var aiMeshIndex in aiNode.MeshIndices) { var aiMesh = aiScene.Meshes[aiMeshIndex]; var aiMaterial = aiScene.Materials[aiMesh.MaterialIndex]; var geometry = ConvertAssimpMeshToGeometry(aiMesh, aiMaterial, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, ref nodeWorldTransform, ref nodeInverseWorldTransform, transformedVertices, options); if (!nodeInfo.IsMeshAttachment) { node.Attachments.Add(new NodeGeometryAttachment(geometry)); } else { node.Parent.Attachments.Add(new NodeGeometryAttachment(geometry)); node.Parent.RemoveChildNode(node); } } } foreach (var aiNodeChild in aiNode.Children) { ProcessAssimpNodeMeshesRecursively(aiNodeChild, aiScene, nodeLookup, ref nextBoneIndex, nodeToBoneIndices, boneInverseBindMatrices, transformedVertices, options); } }
public static Scene ConvertFromAssimpScene(string filePath, SceneConverterOptions options) { var aiScene = AssimpSceneImporter.ImportFile(filePath); return(ConvertFromAssimpScene(aiScene, options)); }