예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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());
                }
            }
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
                }
            }
        }