private Vertex[] ReadVertices(GmdFile.Submesh submesh, GmdFile.Mesh mesh, GmdFile file) { var vertices = new Vertex[submesh.VertexCount]; var meshIndex = Array.IndexOf(file.Meshes, mesh); using var memoryStream = new MemoryStream(file.VertexBuffers[meshIndex]); using var bs = new BinaryReader(memoryStream); bs.BaseStream.Seek((submesh.BufferOffset1 + submesh.BufferOffset2) * mesh.Stride, SeekOrigin.Begin); for (int j = 0; j < submesh.VertexCount; j++) { var v = new Vertex(); v.Position = GmdUtils.ReadVector3(bs); if (mesh.HasBones && file.BoneIndices.Length > 0) { v.BoneWeights = new[] { bs.ReadByteAsFloat(), bs.ReadByteAsFloat(), bs.ReadByteAsFloat(), bs.ReadByteAsFloat() }; v.BoneIndices = new[] { file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value, file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value, file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value, file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value }; } v.Normal = GmdUtils.ReadNormal(bs); bs.ReadByte(); if (mesh.HasFlag1) { // var a = bs.ReadByteAsFloat(); // var b = bs.ReadByteAsFloat(); // var c = bs.ReadByteAsFloat(); // var d = bs.ReadByteAsFloat(); var val = bs.ReadInt32(); // Console.WriteLine($"{a:0.00} / {b:0.00} / {c:0.00} / {d:0.00}"); // bs.ReadInt32(); } if (mesh.HasFlag2) { bs.ReadInt32(); } if (mesh.HasFlag3) { bs.ReadInt32(); } for (int l = 0; l < mesh.UvLayers; l++) { var uvs = new Vector2(Half.ToHalf(bs.ReadUInt16()), 1 - Half.ToHalf(bs.ReadUInt16())); switch (l) { case 0: v.Uv0 = uvs; break; case 1: v.Uv1 = uvs; break; case 2: v.Uv2 = uvs; break; case 3: v.Uv3 = uvs; break; default: Console.Error.WriteLine("Too many UV channels!"); break; } } vertices[j] = v; } return(vertices); }
public GmdFile ToGmdFile() { var file = new GmdFile(); file.Name = Name; // TEXTURES var textures = Textures.Concat(new string[] { "_dummy_rd", "dummy_nmap", "dummy_black", "dummy_gray", "dummy_white", "default_z", "noise" }) .Distinct().ToArray(); file.Textures = textures .Select(GmdFile.HashedName.FromString) .ToArray(); var shaders = Materials.Select(mat => mat.Shader).Distinct().ToArray(); var hlSubmeshes = Meshes.SelectMany(m => m.Submeshes).OrderBy(sm => sm.Id).ToArray(); // MATERIALS file.Materials = Materials.Select(mat => { var material = new GmdFile.Material() { Id = mat.Id, TextureIndices = mat.Textures.Select(tex => (int)(ushort)Array.IndexOf(textures, tex)) .Concat(Enumerable.Repeat((int)ushort.MaxValue, 8 - mat.Textures.Length)).ToArray(), ShaderIndex = (uint)Array.IndexOf(shaders, mat.Shader), SubmeshIndex = (uint)Array.FindIndex(hlSubmeshes, m => m.Material == mat) }; /* * 3 => rd (sd_o1dzt_m2dzt) / tr (skin) / dummy_white * 4 => rm / default_z [can be default_z, strange things happen when null] * 5 => rt / noise [can be noise, too smooth when null] * 6 => rs [can be null] * 7 => */ material.TextureIndices[0] = material.TextureIndices[0] >= 255 ? Array.IndexOf(textures, "dummy_black") : material.TextureIndices[0]; material.TextureIndices[1] = material.TextureIndices[1] >= 255 ? Array.IndexOf(textures, "default_z") : material.TextureIndices[1]; material.TextureIndices[2] = material.TextureIndices[2] >= 255 ? Array.IndexOf(textures, "dummy_nmap") : material.TextureIndices[2]; material.TextureIndices[3] = material.TextureIndices[3] >= 255 ? Array.IndexOf(textures, "dummy_white") : material.TextureIndices[3]; material.TextureIndices[4] = material.TextureIndices[4] >= 255 ? Array.IndexOf(textures, "default_z") : material.TextureIndices[4]; material.TextureIndices[5] = material.TextureIndices[5] >= 255 ? Array.IndexOf(textures, "noise") : material.TextureIndices[5]; material.TextureIndices[6] = material.TextureIndices[6] >= 255 ? ushort.MaxValue : material.TextureIndices[6]; //ushort.MaxValue; material.TextureIndices[7] = material.TextureIndices[7] >= 255 ? ushort.MaxValue : material.TextureIndices[7]; //ushort.MaxValue; // ushort.MaxValue material.Initialize(mat.Shader == GmdUtils.TRANSPARENCY_MAT); return(material); }).ToArray(); // SHADERS file.Shaders = shaders.Select(shader => GmdFile.HashedName.FromString(shader)).ToArray(); // MESHES var meshes = new List <GmdFile.Mesh>(); var vertexBuffers = new List <byte[]>(); var submeshes = new List <GmdFile.Submesh>(); var indices = new List <GmdFile.Index>(); var boneIndices = new List <GmdFile.BoneIndex>(); for (int i = 0; i < Meshes.Length; i++) { var mesh = Meshes[i]; var gmdMesh = new GmdFile.Mesh(); var firstVertex = mesh.Submeshes.First().Vertices.First(); gmdMesh.Initialize(); gmdMesh.MeshId = i; gmdMesh.Count = mesh.Submeshes.Sum(m => m.Vertices.Length); gmdMesh.Offset = vertexBuffers.Sum(b => b.Length); gmdMesh.UvLayers = firstVertex.UvLayerCount; gmdMesh.HasBones = firstVertex.BoneIndices != null; gmdMesh.FlagCount = GmdUtils.GetFlagCount(mesh.Submeshes.First().Material.Shader); using var buffer = new MemoryStream(); using var writer = new BinaryWriter(buffer); Console.WriteLine("m" + i); int vertexCount = 0; for (int j = 0; j < mesh.Submeshes.Count; j++) { var submesh = mesh.Submeshes[j]; var gmdSubmesh = new GmdFile.Submesh(); var boneIndexList = submesh.Vertices.SelectMany(v => v.BoneIndices ?? Enumerable.Empty <int>()).Distinct().ToList(); gmdSubmesh.Id = Array.IndexOf(hlSubmeshes, submesh); gmdSubmesh.MeshIndex = i; gmdSubmesh.IndicesCount = submesh.Triangles.GetLength(0) * 3; gmdSubmesh.IndicesOffset = indices.Count; gmdSubmesh.VertexCount = submesh.Vertices.Length; gmdSubmesh.MaterialIndex = (int)submesh.Material.Id; gmdSubmesh.BufferOffset2 = vertexCount; gmdSubmesh.BoneIndexOffset = boneIndices.Count; gmdSubmesh.BoneIndexCount = boneIndexList.Count; gmdMesh.Stride = WriteVertices(writer, submesh, gmdMesh.FlagCount, boneIndexList); boneIndexList.Insert(0, gmdSubmesh.BoneIndexCount); // Expects count in index list vertexCount += submesh.Vertices.Length; // Fill in indices for (int y = 0; y < submesh.Triangles.GetLength(0); y++) { indices.Add(new GmdFile.Index() { Value = submesh.Triangles[y, 0] + gmdSubmesh.BufferOffset2 }); indices.Add(new GmdFile.Index() { Value = submesh.Triangles[y, 1] + gmdSubmesh.BufferOffset2 }); indices.Add(new GmdFile.Index() { Value = submesh.Triangles[y, 2] + gmdSubmesh.BufferOffset2 }); } boneIndices.AddRange(boneIndexList.Select(idx => new GmdFile.BoneIndex() { Value = idx })); submeshes.Add(gmdSubmesh); } gmdMesh.Size = (int)buffer.Length; vertexBuffers.Add(buffer.ToArray()); meshes.Add(gmdMesh); } file.Indices = indices.ToArray(); file.Meshes = meshes.ToArray(); file.Submeshes = submeshes.OrderBy(sm => sm.Id).ToArray(); file.VertexBuffers = vertexBuffers.ToArray(); file.BoneIndices = boneIndices.ToArray(); file.BoneNames = Bones.Select(b => GmdFile.HashedName.FromString(b.Name)).ToArray(); file.BoneTransforms = Bones.Select(bone => { var bt = new GmdFile.BoneTransform(); bt.Position = bone.WorldMatrix.Translation; bt.LocalPosition = bone.Position; bt.LocalRotation = bone.Rotation; bt.Scale = bone.Scale; bt.BoneNo = bt.BoneNo2 = bone.Id; bt.BoneNameIndex = bone.Id; bt.TransformIndex = -1; bt.NextSiblingIndex = -1; bt.NextChildIndex = -1; bt.Footer = new byte[16]; if (bone.Name.Contains("[l0]")) { bt.TransformIndex = 0; bt.NodeType = GmdFile.BoneTransform.NODE_TYPE_TRANSFORM; } // If bone has children, set the nextChildIndex idx if (bone.Children.Count > 0) { bt.NextChildIndex = bone.Children[0].Id; } // If bone has another sibling that comes after itself, set NextSiblingIndex if (bone.Parent != null) { var childIdx = bone.Parent.Children.IndexOf(bone); if (childIdx >= 0 && childIdx + 1 < bone.Parent.Children.Count) { bt.NextSiblingIndex = bone.Parent.Children[childIdx + 1].Id; } } return(bt); }).ToArray(); return(file); }