private static MESH_CULLING SerializeMeshCulling(MESH_FILE file, ShaderDataHelper shaderData, MeshCulling meshCulling) { var culling = new MESH_CULLING { Type = (int)meshCulling.Type, FromVertex = meshCulling.FromVertex, VertexCount = meshCulling.VertexCount, FromIndex = meshCulling.FromIndex, IndexCount = meshCulling.IndexCount }; if (meshCulling.ReplacementMesh != null) { culling.AlternateMesh = SerializeMeshGeometry(file, shaderData, meshCulling.ReplacementMesh); } if (meshCulling.Studs != null && meshCulling.Studs.Any()) { culling.Studs = meshCulling.Studs.Select(x => x.Serialize()).ToArray(); } if (meshCulling.AdjacentStuds != null && meshCulling.AdjacentStuds.Any()) { culling.AdjacentStuds = meshCulling.AdjacentStuds.Select(x => x.Serialize()).ToArray(); } return(culling); }
private static MESH_CULLING ReadCullingInfo(BinaryReaderEx br, MESH_FILE meshFile, long boneMappingPosition) { long startPosition = br.BaseStream.Position; int cullingDataSize = br.ReadInt32(); var culling = new MESH_CULLING() { Type = br.ReadInt32(), FromVertex = br.ReadInt32(), VertexCount = br.ReadInt32(), FromIndex = br.ReadInt32(), IndexCount = br.ReadInt32() }; int alternateMeshOffset = br.ReadInt32(); int connectorReferenceFlag = br.ReadInt32(); if (connectorReferenceFlag >= 1) { culling.Studs = ReadCustom2DFieldReferences(br); } else { culling.Studs = new CUSTOM2DFIELD_REFERENCE[0]; } if (connectorReferenceFlag >= 2) { culling.AdjacentStuds = ReadCustom2DFieldReferences(br); } else { culling.AdjacentStuds = new CUSTOM2DFIELD_REFERENCE[0]; } if (connectorReferenceFlag > 2) { Trace.WriteLine($"Unexpected connector reference flag: {connectorReferenceFlag}"); } if (alternateMeshOffset != 0) { if (startPosition + alternateMeshOffset != br.BaseStream.Position) { Trace.WriteLine("Incorrect data size read"); br.BaseStream.Position = startPosition + alternateMeshOffset; } long meshDataSize = cullingDataSize - alternateMeshOffset; culling.AlternateMesh = ReadAlternateMesh(br, meshFile, boneMappingPosition, meshDataSize); } return(culling); }
private static void SetShaderData(MESH_FILE file, MESH_DATA data, MeshGeometry mesh) { for (int i = 0; i < data.Indices.Length; i++) { var index = data.Indices[i]; if (index.AverageNormalIndex >= 0 && index.AverageNormalIndex < file.AverageNormals.Length) { mesh.Indices[i].AverageNormal = file.AverageNormals[index.AverageNormalIndex]; } if (file.GetShaderDataFromOffset(index.REShaderOffset, out ROUNDEDGE_SHADER_DATA shaderData)) { mesh.Indices[i].RoundEdgeData = new RoundEdgeData(shaderData.Coords.Take(6).ToArray()); } } }
private static MESH_DATA SerializeMeshGeometry(MESH_FILE file, ShaderDataHelper shaderData, MeshGeometry meshGeometry) { var meshData = MESH_DATA.Create(meshGeometry.VertexCount, meshGeometry.IndexCount, file.IsTextured, file.IsFlexible); var vertIndexer = new ListIndexer <Vertex>(meshGeometry.Vertices); bool isTextured = meshGeometry.IsTextured; for (int i = 0; i < meshGeometry.Vertices.Count; i++) { var vert = meshGeometry.Vertices[i]; meshData.Positions[i] = vert.Position; meshData.Normals[i] = vert.Normal; if (file.IsTextured) { meshData.UVs[i] = isTextured ? vert.TexCoord : new Vector2(0f); } if (file.IsFlexible && vert.BoneWeights.Any()) { var boneWeights = vert.BoneWeights.Select(x => new MESH_BONE_WEIGHT { BoneID = x.BoneID, Weight = x.Weight }); meshData.Bones[i] = new MESH_BONE_MAPPING(boneWeights); } } for (int i = 0; i < meshGeometry.Indices.Count; i++) { var idx = meshGeometry.Indices[i]; meshData.Indices[i].VertexIndex = vertIndexer.IndexOf(idx.Vertex); meshData.Indices[i].AverageNormalIndex = shaderData.AvgNormals.IndexOf(idx.AverageNormal); int reIdx = shaderData.RoundEdgeData.IndexOf(idx.RoundEdgeData); int reOffset = reIdx * 12 + ((int)Math.Floor(reIdx / 21d) * 4); meshData.Indices[i].REShaderOffset = reOffset; } return(meshData); }
private static MESH_FILE BuildMeshFile(MeshFile mesh) { if (!mesh.Cullings.Any(x => x.Type == MeshCullingType.MainModel)) { if (Debugger.IsAttached) { throw new InvalidOperationException("Mesh does not contain culling information"); } else { Debug.WriteLine("Mesh has no culling information!"); } } var file = new MESH_FILE { Header = MESH_HEADER.Create(mesh) }; var avgNormals = mesh.GetAverageNormals().DistinctValues().ToList(); var outlines = mesh.GetRoundEdgeShaderData().EqualsDistinct().Select(x => x.Clone()).ToList(); for (int i = 20; i < outlines.Count; i += 21) { outlines[i].PackData(); } var shaderData = new ShaderDataHelper(avgNormals, outlines); file.AverageNormals = avgNormals.ToArray(); file.RoundEdgeShaderData = outlines.Select(x => new ROUNDEDGE_SHADER_DATA(x.Coords)).ToArray(); file.Geometry = SerializeMeshGeometry(file, shaderData, mesh.Geometry); file.Cullings = new MESH_CULLING[mesh.Cullings.Count]; for (int i = 0; i < mesh.Cullings.Count; i++) { file.Cullings[i] = SerializeMeshCulling(file, shaderData, mesh.Cullings[i]); } return(file); }
private static MESH_DATA ReadAlternateMesh(BinaryReaderEx br, MESH_FILE mesh, long boneMappingPosition, long meshDataSize) { long startPosition = br.Position; int vertexCount = br.ReadInt32(); int indexCount = br.ReadInt32(); var geom = MESH_DATA.Create(vertexCount, indexCount, mesh.IsTextured, mesh.IsFlexible); for (int i = 0; i < vertexCount; i++) { geom.Positions[i] = new Vector3(br.ReadSingles(3)); } for (int i = 0; i < vertexCount; i++) { geom.Normals[i] = new Vector3(br.ReadSingles(3)); } if (mesh.IsTextured) { for (int i = 0; i < vertexCount; i++) { geom.UVs[i] = new Vector2(br.ReadSingles(2)); } } for (int i = 0; i < indexCount; i++) { geom.Indices[i] = new MESH_INDEX() { VertexIndex = br.ReadInt32() } } ; for (int i = 0; i < indexCount; i++) { geom.Indices[i].AverageNormalIndex = br.ReadInt32(); } for (int i = 0; i < indexCount; i++) { geom.Indices[i].REShaderOffset = br.ReadInt32(); } if (mesh.IsFlexible) { Trace.WriteLine("WARNING Flexible alternate mesh encountered!"); if (br.Position < startPosition + meshDataSize) { var dataOffsets = new List <int>(); for (int i = 0; i < vertexCount; i++) { dataOffsets.Add(br.ReadInt32()); } long dataEndPosition = br.Position; for (int i = 0; i < vertexCount; i++) { geom.Bones[i] = ReadBoneMapping(br, boneMappingPosition, dataOffsets[i]); } br.Position = dataEndPosition; } else { Trace.WriteLine("Flexible alternate mesh does not seem to be supported!"); geom.Bones = new MESH_BONE_MAPPING[0]; } } return(geom); }
public static MESH_FILE ReadMeshFile(Stream stream) { stream.Position = 0; var meshFile = new MESH_FILE(); using (var br = new BinaryReaderEx(stream, Encoding.UTF8, true)) { var fileHeader = br.ReadStruct <MESH_HEADER>(); if (fileHeader.Header != "10GB") { throw new IOException("The file is not a LDD mesh file (*.g)"); } meshFile.Header = fileHeader; meshFile.Geometry = MESH_DATA.Create(fileHeader); // Vertices & triangles { for (int i = 0; i < fileHeader.VertexCount; i++) { meshFile.Geometry.Positions[i] = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); } for (int i = 0; i < fileHeader.VertexCount; i++) { meshFile.Geometry.Normals[i] = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); } if (fileHeader.MeshType == (int)MeshType.StandardTextured || fileHeader.MeshType == (int)MeshType.FlexibleTextured) { for (int i = 0; i < fileHeader.VertexCount; i++) { meshFile.Geometry.UVs[i] = new Vector2(br.ReadSingle(), br.ReadSingle()); } } for (int i = 0; i < fileHeader.IndexCount; i++) { meshFile.Geometry.Indices[i] = new MESH_INDEX { VertexIndex = br.ReadInt32() } } ; } // Edge shader data (brick outlines) { int shaderDataLength = br.ReadInt32(); int valueCounter = 0; var shaderData = new List <ROUNDEDGE_SHADER_DATA>(); while (valueCounter < shaderDataLength) { bool endOfRow = 256 - ((valueCounter + 12) % 256) < 12; int valueCount = endOfRow ? 16 : 12; int remainingData = shaderDataLength - valueCounter; if (valueCount > remainingData) { if (endOfRow && 12 <= remainingData) { valueCount = 12; } else { Trace.WriteLine("Shader data length error!!!"); stream.Skip(remainingData * 4); break; } } shaderData.Add(new ROUNDEDGE_SHADER_DATA(br.ReadSingles(valueCount))); valueCounter += valueCount; } meshFile.RoundEdgeShaderData = shaderData.ToArray(); for (int i = 0; i < fileHeader.IndexCount; i++) { meshFile.Geometry.Indices[i].REShaderOffset = br.ReadInt32(); } } // Average Normals { int averageNormalsCount = br.ReadInt32(); //we skip the first item because it looks like an header and the maximum referenced value seems always one less the specified length var dataHeader = new Vector3(br.ReadSingles(3)); if (dataHeader.X != 83 || dataHeader.Y != 0 || dataHeader.Z != 0) { Trace.WriteLine($"Unexpected average normal header: {dataHeader}"); } meshFile.AverageNormals = new Vector3[averageNormalsCount - 1]; for (int i = 0; i < averageNormalsCount - 1; i++) { meshFile.AverageNormals[i] = new Vector3(br.ReadSingles(3)); } for (int i = 0; i < fileHeader.IndexCount; i++) { meshFile.Geometry.Indices[i].AverageNormalIndex = br.ReadInt32(); } } long boneMappingPosition = 0; // Flex data if (fileHeader.MeshType == (int)MeshType.Flexible || fileHeader.MeshType == (int)MeshType.FlexibleTextured) { int dataSize = br.ReadInt32(); boneMappingPosition = stream.Position; stream.Seek(dataSize, SeekOrigin.Current); //skip over the data var dataOffsets = new List <int>(); for (int i = 0; i < fileHeader.VertexCount; i++) { dataOffsets.Add(br.ReadInt32()); } long dataEndPosition = stream.Position; for (int i = 0; i < fileHeader.VertexCount; i++) { meshFile.Geometry.Bones[i] = ReadBoneMapping(br, boneMappingPosition, dataOffsets[i]); } stream.Position = dataEndPosition; } int cullingInfoCount = br.ReadInt32(); int cullingInfoSize = br.ReadInt32(); meshFile.Cullings = new MESH_CULLING[cullingInfoCount]; for (int i = 0; i < cullingInfoCount; i++) { meshFile.Cullings[i] = ReadCullingInfo(br, meshFile, boneMappingPosition); } } return(meshFile); }
public static void WriteMeshFile(Stream stream, MESH_FILE meshFile) { using (var bw = new BinaryWriterEx(stream, Encoding.UTF8, true)) { bw.WriteStruct(meshFile.Header); void WriteVector3(Vector3 v) { bw.WriteSingle(v.X); bw.WriteSingle(v.Y); bw.WriteSingle(v.Z); } void WriteVector2(Vector2 v) { bw.WriteSingle(v.X); bw.WriteSingle(v.Y); } foreach (var pos in meshFile.Geometry.Positions) { WriteVector3(pos); } foreach (var norm in meshFile.Geometry.Normals) { WriteVector3(norm); } if (meshFile.Geometry.UVs != null && meshFile.Geometry.UVs.Length > 0) { foreach (var uv in meshFile.Geometry.UVs) { WriteVector2(uv); } } for (int i = 0; i < meshFile.Header.IndexCount; i++) { bw.WriteInt32(meshFile.Geometry.Indices[i].VertexIndex); } //Round Edge Shader Data { var allShaderData = meshFile.RoundEdgeShaderData.SelectMany(x => x.Coords).ToList(); bw.WriteInt32(allShaderData.Count * 2); foreach (var shaderCoord in allShaderData) { WriteVector2(shaderCoord); } for (int i = 0; i < meshFile.Header.IndexCount; i++) { bw.WriteInt32(meshFile.Geometry.Indices[i].REShaderOffset); } } //Average Normals { bw.WriteInt32(meshFile.AverageNormals.Length + 1); WriteVector3(new Vector3(83, 0, 0)); foreach (var avgNorm in meshFile.AverageNormals) { WriteVector3(avgNorm); } for (int i = 0; i < meshFile.Header.IndexCount; i++) { bw.WriteInt32(meshFile.Geometry.Indices[i].AverageNormalIndex); } } if (meshFile.Geometry.Bones != null && meshFile.Geometry.Bones.Length > 0) { //TODO: If flexible alternate meshes are supported, improve this code to include alternate meshes bones // and a way to retrieve the correct offsets in WriteCullingInfo var allBones = meshFile.Geometry.Bones.SelectMany(x => x.BoneWeights).ToList(); int boneDataSize = (allBones.Count * 8) + (meshFile.Header.VertexCount * 4); bw.WriteInt32(boneDataSize); var dataOffsets = new List <int>(); int totalOffset = 0; for (int i = 0; i < meshFile.Header.VertexCount; i++) { var vertexBones = meshFile.Geometry.Bones[i]; bw.WriteInt32(vertexBones.BoneWeights.Length); for (int j = 0; j < vertexBones.BoneWeights.Length; j++) { bw.WriteInt32(vertexBones.BoneWeights[j].BoneID); bw.WriteSingle(vertexBones.BoneWeights[j].Weight); } //bone count (4 bytes) + bones data (id + weight = 8 bytes) dataOffsets.Add(totalOffset); totalOffset += 4 + (vertexBones.BoneWeights.Length * 8); } for (int i = 0; i < meshFile.Header.VertexCount; i++) { bw.WriteInt32(dataOffsets[i]); } } //Mesh Culling Info { bw.WriteInt32(meshFile.Cullings.Length); WriteSizedBlock(bw, () => { for (int i = 0; i < meshFile.Cullings.Length; i++) { WriteCullingInfo(bw, meshFile.Cullings[i]); } }, false); } } }