private static Node ConvertNode(Assimp.Node aiNode, List <Assimp.Mesh> aiMeshes, List <MaterialBuildInfo> materialBuildInfos) { Node ConvertHierarchyNodeRecursively(Assimp.Node curAiNode, ref Node previousSibling, Node parent, ref Assimp.Matrix4x4 parentNodeWorldTransform) { var nodeWorldTransform = curAiNode.Transform * parentNodeWorldTransform; var nodeInverseWorldTransform = nodeWorldTransform; nodeInverseWorldTransform.Inverse(); curAiNode.Transform.Decompose(out var scale, out var rotation, out var translation); // Create node var node = new Node(AssimpHelper.FromAssimp(translation), AngleVector.FromQuaternion(AssimpHelper.FromAssimp(rotation)), AssimpHelper.FromAssimp(scale), parent); if (curAiNode.HasMeshes) { var geometry = new Geometry(); // Convert meshes var vertexPositions = new List <Assimp.Vector3D>(); var vertexNormals = new List <Assimp.Vector3D>(); var vertexUVs = new List <Assimp.Vector3D>(); var vertexColors = new List <Assimp.Color4D>(); var lastRenderState = new MeshRenderState(); foreach (var aiMeshIndex in curAiNode.MeshIndices) { var aiMesh = aiMeshes[aiMeshIndex]; var material = materialBuildInfos[aiMesh.MaterialIndex]; var mesh = new Mesh(); var renderState = new MeshRenderState(); renderState.IndexFlags = IndexAttributeFlags.HasPosition | IndexAttributeFlags.Position16BitIndex; var useColors = false; var hasColors = aiMesh.HasVertexColors(0); var hasUVs = aiMesh.HasTextureCoords(0); var hasNormals = aiMesh.HasNormals; if (hasColors || !hasNormals) { renderState.IndexFlags |= IndexAttributeFlags.HasColor | IndexAttributeFlags.Color16BitIndex; useColors = true; } else { renderState.IndexFlags |= IndexAttributeFlags.HasNormal | IndexAttributeFlags.Normal16BitIndex; } if (hasUVs) { renderState.IndexFlags |= IndexAttributeFlags.HasUV | IndexAttributeFlags.UV16BitIndex; } // Convert faces var triangleIndices = new Index[aiMesh.FaceCount * 3]; for (var i = 0; i < aiMesh.Faces.Count; i++) { var aiFace = aiMesh.Faces[i]; Debug.Assert(aiFace.IndexCount == 3); for (var j = 0; j < aiFace.Indices.Count; j++) { int aiFaceIndex = aiFace.Indices[j]; var position = aiMesh.Vertices[aiFaceIndex]; var positionIndex = vertexPositions.IndexOf(position); if (positionIndex == -1) { positionIndex = vertexPositions.Count; vertexPositions.Add(position); } var normalIndex = 0; var colorIndex = 0; var uvIndex = 0; if (useColors) { var color = hasColors ? aiMesh.VertexColorChannels[0][aiFaceIndex] : new Assimp.Color4D(); colorIndex = vertexColors.IndexOf(color); if (colorIndex == -1) { colorIndex = vertexColors.Count; vertexColors.Add(color); } } else { var normal = aiMesh.Normals[aiFaceIndex]; normalIndex = vertexNormals.IndexOf(normal); if (normalIndex == -1) { normalIndex = vertexNormals.Count; vertexNormals.Add(normal); } } if (hasUVs) { var uv = aiMesh.TextureCoordinateChannels[0][aiFaceIndex]; uvIndex = vertexUVs.IndexOf(uv); if (uvIndex == -1) { uvIndex = vertexUVs.Count; vertexUVs.Add(uv); } } triangleIndices[(i * 3) + j] = new Index { PositionIndex = ( ushort )positionIndex, NormalIndex = ( ushort )normalIndex, ColorIndex = ( ushort )colorIndex, UVIndex = ( ushort )uvIndex }; } } // Build display list var displayList = new GXDisplayList(GXPrimitive.Triangles, triangleIndices); mesh.DisplayLists.Add(displayList); // Set up render params var indexFlagsParam = new IndexAttributeFlagsParam(renderState.IndexFlags); mesh.Parameters.Add(indexFlagsParam); if (useColors) { mesh.Parameters.Add(new LightingParams(LightingParams.Preset.Colors)); } else { mesh.Parameters.Add(new LightingParams(LightingParams.Preset.Normals)); } mesh.Parameters.Add(new TextureParams(( ushort )(material.TextureId))); mesh.Parameters.Add(new MipMapParams()); geometry.OpaqueMeshes.Add(mesh); } // Build vertex buffers if (vertexPositions.Count > 0) { geometry.VertexBuffers.Add(new VertexPositionBuffer(vertexPositions.Select(x => { Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeInverseWorldTransform); return(AssimpHelper.FromAssimp(x)); }).ToArray())); } if (vertexNormals.Count > 0) { nodeInverseWorldTransform.Transpose(); geometry.VertexBuffers.Add(new VertexNormalBuffer(vertexNormals.Select(x => { Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeInverseWorldTransform); return(AssimpHelper.FromAssimp(x)); }).ToArray())); } if (vertexColors.Count > 0) { geometry.VertexBuffers.Add(new VertexColorBuffer(vertexColors.Select(AssimpHelper.FromAssimp).ToArray())); } if (vertexUVs.Count > 0) { geometry.VertexBuffers.Add(new VertexUVBuffer(vertexUVs.Select(x => UVCodec.Encode1023(AssimpHelper .FromAssimpAsVector2(x))) .ToArray())); } } // Set sibling (next) reference of previous if (previousSibling != null) { previousSibling.Sibling = node; } previousSibling = node; if (curAiNode.HasChildren) { Node childPreviousSibling = null; foreach (var aiChildNode in curAiNode.Children) { var childNode = ConvertHierarchyNodeRecursively(aiChildNode, ref childPreviousSibling, node, ref nodeWorldTransform); // Make sure to set the 'first child' reference if we haven't already if (node.Child == null) { node.Child = childNode; } } } return(node); } // Dummy! Node dummy = null; var identity = Assimp.Matrix4x4.Identity; return(ConvertHierarchyNodeRecursively(aiNode, ref dummy, null, ref identity)); }
private static void ProcessMeshParameters(List <Param> parameters, ref MeshRenderState state) { foreach (var param in parameters) { switch (param.Type) { case 0: { var param0 = ( UnknownParam )param; state.Param0Value1 = param0.Value1; state.Param0Value2 = param0.Value2; } break; case MeshStateParamType.IndexAttributeFlags: state.IndexFlags = ((GC.IndexAttributeFlagsParam)param).Flags; break; case MeshStateParamType.Lighting: { var lightingParams = ( LightingParams )param; state.LightingParam1 = lightingParams.Value1; state.LightingParam2 = lightingParams.Value2; } break; case ( MeshStateParamType )3: { var param3 = ( UnknownParam )param; state.Param3Value1 = param3.Value1; state.Param3Value2 = param3.Value2; } break; case MeshStateParamType.BlendAlpha: { var blendAlphaParam = ( BlendAlphaParam )param; state.BlendAlphaFlags = blendAlphaParam.Flags; } break; case MeshStateParamType.AmbientColor: { var ambientColorParam = ( AmbientColorParam )param; state.AmbientColor = ambientColorParam.Color; } break; case ( MeshStateParamType )6: { var param6 = ( UnknownParam )param; state.Param6Value1 = param6.Value1; state.Param6Value2 = param6.Value2; } break; case ( MeshStateParamType )7: { var param7 = ( UnknownParam )param; state.Param7Value1 = param7.Value1; state.Param7Value2 = param7.Value2; } break; case MeshStateParamType.Texture: { var textureParams = ( TextureParams )param; state.TextureId = textureParams.TextureId; state.TileMode = textureParams.TileMode; } break; case ( MeshStateParamType )9: { var param9 = ( UnknownParam )param; state.Param9Value1 = param9.Value1; state.Param9Value2 = param9.Value2; } break; case MeshStateParamType.MipMap: { var mipMapParams = ( MipMapParams )param; state.MipMapParam1 = mipMapParams.Value1; state.MipMapParam2 = mipMapParams.Value2; } break; default: Debugger.Break(); break; } } }
private void ConvertMeshes(List <Mesh> meshes, VertexPositionBuffer positionBuffer, VertexNormalBuffer normalBuffer, VertexColorBuffer colorBuffer, VertexUVBuffer uvBuffer) { var state = new MeshRenderState(); for (var i = 0; i < meshes.Count; i++) { var mesh = meshes[i]; if (mesh.Parameters != null && mesh.Parameters.Count > 0) { ProcessMeshParameters(mesh.Parameters, ref state); } var stateCopy = state; stateCopy.TextureId = 0; stateCopy.TileMode = 0; //stateCopy.IndexFlags = 0; stateCopy.AmbientColor = new Color(); sUniqueStates.Add(stateCopy); var aiMesh = new Assimp.Mesh(); var vertexCache = new List <Vertex>(); Debug.Assert(state.IndexFlags.HasFlag(IndexAttributeFlags.HasPosition) ? positionBuffer != null : true); Debug.Assert(state.IndexFlags.HasFlag(IndexAttributeFlags.HasNormal) ? normalBuffer != null : true); Debug.Assert(state.IndexFlags.HasFlag(IndexAttributeFlags.HasColor) ? colorBuffer != null : true); Debug.Assert(state.IndexFlags.HasFlag(IndexAttributeFlags.HasUV) ? uvBuffer != null : true); // Extract all vertices used by the triangles, and build a new vertex list // with each vertex attribute clumped together var aiFace = new Assimp.Face(); foreach (var index in mesh.DisplayLists.SelectMany(x => x.ToTriangles())) { var vertex = new Vertex(); if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasPosition)) { vertex.Position = positionBuffer.Elements[index.PositionIndex]; } if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasNormal)) { vertex.Normal = normalBuffer.Elements[index.NormalIndex]; } if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasColor)) { vertex.Color = colorBuffer.Elements[index.ColorIndex]; } if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasUV)) { vertex.UV = uvBuffer.Elements[index.UVIndex]; } // Find index of this vertex in the list in case it already exists var vertexIndex = vertexCache.IndexOf(vertex); if (vertexIndex == -1) { vertexIndex = vertexCache.Count; vertexCache.Add(vertex); } aiFace.Indices.Add(vertexIndex); if (aiFace.IndexCount == 3) { // Done with this face, move on to the next one aiMesh.Faces.Add(aiFace); aiFace = new Assimp.Face(); } } // Convert vertices aiMesh.Vertices.AddRange(vertexCache.Select(x => ToAssimp(x.Position))); if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasNormal)) { aiMesh.Normals.AddRange(vertexCache.Select(x => ToAssimp(x.Normal))); } if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasUV)) { aiMesh.TextureCoordinateChannels[0].AddRange(vertexCache.Select(x => ToAssimp(UVCodec.Decode1023(x.UV)))); } if (state.IndexFlags.HasFlag(IndexAttributeFlags.HasColor)) { aiMesh.VertexColorChannels[0].AddRange(vertexCache.Select(x => ToAssimp(x.Color))); } // Convert material if (!mConvertedMaterialCache.TryGetValue(state, out var aiMaterialIndex)) { // Not in cache, so create a new one and add it aiMaterialIndex = Scene.MaterialCount; Scene.Materials.Add(CreateMaterial(Color.Gray, Color.Gray, Color.Gray, FormatTextureName(state.TextureId), false, false, state.TileMode.HasFlag(TileMode.MirrorU), state.TileMode.HasFlag(TileMode.MirrorV), state.BlendAlphaFlags.HasFlag(BlendAlphaFlags.UseAlpha))); mConvertedMaterialCache[state] = aiMaterialIndex; } aiMesh.MaterialIndex = aiMaterialIndex; // Add mesh to scene. Scene.Meshes.Add(aiMesh); } }
private static IGeometry ConvertDisplayGeometry(Assimp.Node curAiNode, Assimp.Matrix4x4 nodeWorldTransform, List <Assimp.Mesh> aiMeshes, List <MaterialBuildInfo> materialBuildInfos) { var nodeInverseWorldTransform = nodeWorldTransform; nodeInverseWorldTransform.Inverse(); var nodeInverseTransposeWorldTransform = nodeInverseWorldTransform; nodeInverseTransposeWorldTransform.Transpose(); var geometry = new Geometry(); // Convert meshes var vertexPositions = new List <Assimp.Vector3D>(); var vertexNormals = new List <Assimp.Vector3D>(); var vertexUVs = new List <Assimp.Vector3D>(); var vertexColors = new List <Assimp.Color4D>(); var lastRenderState = new MeshRenderState(); foreach (var aiMeshIndex in curAiNode.MeshIndices) { var aiMesh = aiMeshes[aiMeshIndex]; var material = materialBuildInfos[aiMesh.MaterialIndex]; var mesh = new Mesh(); var renderState = new MeshRenderState(); renderState.IndexFlags = IndexAttributeFlags.HasPosition; var useColors = false; var hasColors = aiMesh.HasVertexColors(0); var hasUVs = aiMesh.HasTextureCoords(0); var hasNormals = aiMesh.HasNormals; if (hasColors || !hasNormals) { renderState.IndexFlags |= IndexAttributeFlags.HasColor; useColors = true; } else { renderState.IndexFlags |= IndexAttributeFlags.HasNormal; } if (hasUVs) { renderState.IndexFlags |= IndexAttributeFlags.HasUV; } // Convert faces var triangleIndices = new Index[aiMesh.FaceCount * 3]; for (var i = 0; i < aiMesh.Faces.Count; i++) { var aiFace = aiMesh.Faces[i]; for (var j = 0; j < 3; j++) { var triangleIndicesIndex = (i * 3) + 2 - j; if (j >= aiFace.IndexCount) { triangleIndices[triangleIndicesIndex] = triangleIndices[triangleIndicesIndex + 1]; continue; } int aiFaceIndex = aiFace.Indices[j]; var position = aiMesh.Vertices[aiFaceIndex]; var positionIndex = vertexPositions.AddUnique(position); if (positionIndex > byte.MaxValue) { renderState.IndexFlags |= IndexAttributeFlags.Position16BitIndex; } var normalIndex = 0; var colorIndex = 0; var uvIndex = 0; if (useColors) { var color = hasColors ? aiMesh.VertexColorChannels[0][aiFaceIndex] : new Assimp.Color4D(); colorIndex = vertexColors.AddUnique(color); if (colorIndex > byte.MaxValue) { renderState.IndexFlags |= IndexAttributeFlags.Color16BitIndex; } } else { var normal = aiMesh.Normals[aiFaceIndex]; normalIndex = vertexNormals.AddUnique(normal); if (normalIndex > byte.MaxValue) { renderState.IndexFlags |= IndexAttributeFlags.Normal16BitIndex; } } if (hasUVs) { var uv = aiMesh.TextureCoordinateChannels[0][aiFaceIndex]; uvIndex = vertexUVs.AddUnique(uv); if (uvIndex > byte.MaxValue) { renderState.IndexFlags |= IndexAttributeFlags.UV16BitIndex; } } triangleIndices[triangleIndicesIndex] = new Index { PositionIndex = ( ushort )positionIndex, NormalIndex = ( ushort )normalIndex, ColorIndex = ( ushort )colorIndex, UVIndex = ( ushort )uvIndex }; } } // Build display list var displayList = new GXDisplayList(GXPrimitive.Triangles, triangleIndices); mesh.DisplayLists.Add(displayList); // Set up render params if (renderState.IndexFlags != lastRenderState.IndexFlags) { mesh.Parameters.Add(new IndexAttributeFlagsParam(renderState.IndexFlags)); } // Set up render lighting params { if (useColors) { renderState.LightingValue1 = 0x0b11; } else { renderState.LightingValue2 = 0x0011; } renderState.LightingValue2 = 1; if (renderState.LightingValue1 != lastRenderState.LightingValue1 || renderState.LightingValue2 != lastRenderState.LightingValue2) { mesh.Parameters.Add(new LightingParams() { Value1 = renderState.LightingValue1, Value2 = renderState.LightingValue2 }); } } // Set up render texture params { renderState.TextureId = ( ushort )material.TextureId; renderState.TileMode = TileMode.WrapU | TileMode.WrapV; if (renderState.TextureId != lastRenderState.TextureId || renderState.TileMode != lastRenderState.TileMode) { mesh.Parameters.Add(new TextureParams(renderState.TextureId, renderState.TileMode)); } } // Set up render mipmap params { renderState.MipMapValue1 = 0x104a; renderState.MipMapValue2 = 0; if (renderState.MipMapValue1 != lastRenderState.MipMapValue1 || renderState.MipMapValue2 != lastRenderState.MipMapValue2) { mesh.Parameters.Add(new MipMapParams { Value1 = renderState.MipMapValue1, Value2 = renderState.MipMapValue2 }); } } //if ( material.UseAlpha ) //{ // mesh.Parameters.Add( new BlendAlphaParam() { Flags = BlendAlphaFlags.UseAlpha } ); // geometry.TranslucentMeshes.Add( mesh ); //} //else //{ // geometry.OpaqueMeshes.Add( mesh ); //} geometry.OpaqueMeshes.Add(mesh); lastRenderState = renderState; } // Build vertex buffers if (vertexPositions.Count > 0) { Debug.Assert(vertexPositions.Count <= ushort.MaxValue); var localVertexPositions = vertexPositions.Select(x => { Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeWorldTransform); return(AssimpHelper.FromAssimp(x)); }).ToArray(); geometry.VertexBuffers.Add(new VertexPositionBuffer(localVertexPositions)); geometry.Bounds = BoundingSphere.Calculate(localVertexPositions); } if (vertexNormals.Count > 0) { Debug.Assert(vertexNormals.Count <= ushort.MaxValue); geometry.VertexBuffers.Add(new VertexNormalBuffer(vertexNormals.Select(x => { Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref x, ref nodeInverseTransposeWorldTransform); return(AssimpHelper.FromAssimp(x)); }).ToArray())); } if (vertexColors.Count > 0) { Debug.Assert(vertexColors.Count <= ushort.MaxValue); geometry.VertexBuffers.Add(new VertexColorBuffer(vertexColors.Select(AssimpHelper.FromAssimp).ToArray())); } if (vertexUVs.Count > 0) { Debug.Assert(vertexUVs.Count <= ushort.MaxValue); geometry.VertexBuffers.Add(new VertexUVBuffer(vertexUVs.Select(x => UVCodec.Encode255(AssimpHelper .FromAssimpAsVector2(x))) .ToArray())); } return(geometry); }