public MeshExportOptions(Matrix4x4 matrix, MeshNormalExportMode normalExportMode = MeshNormalExportMode.Unchanged, bool invertNormals = false, bool reverseFaceWinding = false) { Matrix = matrix; NormalExportMode = normalExportMode; InvertNormals = invertNormals; ReverseFaceWinding = reverseFaceWinding; }
private IModel ImportMesh(byte[] bytes) { int stride = 1; MeshNormalExportMode shadingMode = MeshNormalExportMode.Unchanged; HWBinaryResourceChunk headerChunk = GetFirstChunkOfType(HWBinaryResourceChunkType.XTD_XTDHeader); float tileScale = BinaryUtils.ReadFloatBigEndian(bytes, (int)headerChunk.Offset + 12); HWBinaryResourceChunk atlasChunk = GetFirstChunkOfType(HWBinaryResourceChunkType.XTD_AtlasChunk); int gridSize = (int)Math.Round(Math.Sqrt((atlasChunk.Size - 32) / 8)); // Subtract the min/range vector sizes, divide by position + normal size, and sqrt for grid size int positionOffset = (int)atlasChunk.Offset + 32; int normalOffset = positionOffset + gridSize * gridSize * 4; if (gridSize % stride != 0) { throw new Exception( $"Grid size {gridSize} is not evenly divisible by stride {stride} - choose a different stride value."); } // These are stored as ZYX, 4 bytes per component Vector3 PosCompMin = BinaryUtils .ReadVector3BigEndian( bytes, (int)atlasChunk.Offset) .ReverseComponents(); Vector3 PosCompRange = BinaryUtils .ReadVector3BigEndian(bytes, (int)atlasChunk.Offset + 16) .ReverseComponents(); var finModel = new ModelImpl(gridSize * gridSize); var finMesh = finModel.Skin.AddMesh(); var finVertices = new IVertex[gridSize * gridSize]; // Read vertex offsets/normals and add them to the mesh for (int x = 0; x < gridSize; x += stride) { for (int z = 0; z < gridSize; z += stride) { int index = ConvertGridPositionToIndex(new Tuple <int, int>(x, z), gridSize); int offset = index * 4; // Get offset position and normal for this vertex Vector3 position = ReadVector3Compressed(bytes, positionOffset + offset) * PosCompRange - PosCompMin; // Positions are relative to the terrain grid, so shift them by the grid position position += new Vector3(x, 0, z) * tileScale; Vector3 normal = ConvertDirectionVector( Vector3.Normalize( ReadVector3Compressed(bytes, normalOffset + offset) * 2.0f - Vector3.One)); // Simple UV based on original, non-warped terrain grid Vector3 texCoord = new Vector3(x / ((float)gridSize - 1), z / ((float)gridSize - 1), 0); var finVertex = finModel.Skin .AddVertex(position.X, position.Y, position.Z) .SetLocalNormal(normal.X, normal.Y, normal.Z) .SetUv(texCoord.X, texCoord.Y); finVertices[GetVertexIndex(x, z, gridSize)] = finVertex; } } // Generate faces based on terrain grid for (int x = 0; x < gridSize - stride; x += stride) { var triangles = new List <(IVertex, IVertex, IVertex)>(); for (int z = 0; z < gridSize - stride; z += stride) { var a = finVertices[GetVertexIndex(x, z, gridSize)]; var b = finVertices[GetVertexIndex(x + stride, z, gridSize)]; var c = finVertices[GetVertexIndex(x, z + stride, gridSize)]; var d = finVertices[GetVertexIndex(x + stride, z + stride, gridSize)]; triangles.Add((a, b, c)); triangles.Add((d, c, b)); } finMesh.AddTriangles(triangles.ToArray()); triangles.Clear(); } return(finModel); }