private void SetBuffers() { float[] vertices = Meshes.SelectMany(x => x.Vertices).SelectMany(x => x.FloatArray()).ToArray(); uint[] elements = Meshes.SelectMany(x => x.Elements).Select(x => Convert.ToUInt32(x)).ToArray(); int vertexOffset = 0; int elementOffset = 0; foreach (var m in Meshes) { m.ElementOffset = elementOffset; m.ElementCount = m.Elements.Count; elementOffset += m.Elements.Count; for (int i = 0; i < m.Elements.Count; i++) { elements[m.ElementOffset + i] += Convert.ToUInt32(vertexOffset); } vertexOffset += m.Vertices.Count; } GL.BindVertexArray(vertexArrayObject); vertexBufferId = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferId); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(float)), vertices, BufferUsageHint.StaticDraw); elementBufferId = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ElementArrayBuffer, elementBufferId); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(elements.Length * sizeof(uint)), elements, BufferUsageHint.StaticDraw); }
public void RecomputeVertexNormals() { var verts = Meshes.SelectMany(m => m.Vertices).Select(v => v.Position).ToList(); var closeVertices = new List <CloseVertex>(); foreach (var vert in verts) { if (!closeVertices.Where(cv => cv == vert).Any()) { closeVertices.Add(new CloseVertex() { Position = vert }); } } // Get all mesh all triangles // Compute their normals // They reference close verts to average out the normals // Then individual verts reference close verts foreach (var mesh in Meshes) { foreach (var triangle in mesh.Triangles) { var v1 = mesh.Vertices[triangle.v1].Position; var v2 = mesh.Vertices[triangle.v2].Position; var v3 = mesh.Vertices[triangle.v3].Position; var normal = CalcFaceNormal(mesh, triangle); var cv1 = closeVertices.Where(cv => cv == v1).Single(); var cv2 = closeVertices.Where(cv => cv == v2).Single(); var cv3 = closeVertices.Where(cv => cv == v3).Single(); cv1.NormalSum += normal; cv2.NormalSum += normal; cv3.NormalSum += normal; } } foreach (var mesh in Meshes) { for (var v = 0; v < mesh.Vertices.Count; v++) { var vertex = mesh.Vertices[v]; var cVertex = closeVertices.Where(cv => cv == vertex.Position).Single(); var len = Math.Sqrt(cVertex.NormalSum.X * cVertex.NormalSum.X + cVertex.NormalSum.Y * cVertex.NormalSum.Y + cVertex.NormalSum.Z * cVertex.NormalSum.Z); var vertexNormal = new Vector3(cVertex.NormalSum / (float)len); mesh.Vertices[v].Normal = vertexNormal; } } }
private void UpdateBoundingSphere() { IEnumerable <GcmfTriangleStrip> allTriangleStrip = Meshes.SelectMany( m => m.Obj1StripsCcw.Union(m.Obj1StripsCw).Union(m.Obj2StripsCcw).Union(m.Obj2StripsCw)); IEnumerable <GcmfVertex> allVertices = allTriangleStrip.SelectMany(ts => ts); IEnumerable <Vector3> allVertexPositions = allVertices.Select(v => v.Position); BoundingSphere boundingSphere = BoundingSphere.FromPoints(allVertexPositions); BoundingSphereCenter = boundingSphere.Center; BoundingSphereRadius = boundingSphere.Radius; }
public void Save(Stream destination, ObjectDatabase objectDatabase, TextureDatabase textureDatabase, BoneDatabase boneDatabase, bool leaveOpen = false) { if (objectDatabase != null) { foreach (var mesh in Meshes) { mesh.Id = objectDatabase.GetMesh(mesh.Name)?.Id ?? mesh.Id; } } if (boneDatabase != null) { string fileName = destination is FileStream fileStream ? Path.GetFileName(fileStream.Name) : string.Empty; var skeletonEntry = boneDatabase.Skeletons.FirstOrDefault(x => fileName.StartsWith(x.Name, StringComparison.OrdinalIgnoreCase)) ?? boneDatabase.Skeletons.FirstOrDefault(x => x.Name.Equals("CMN", StringComparison.OrdinalIgnoreCase)) ?? boneDatabase.Skeletons[0]; if (skeletonEntry != null) { foreach (var skin in Meshes.Where(x => x.Skin != null).Select(x => x.Skin)) { foreach (var bone in skin.Bones) { int index = skin.ExData?.BoneNames?.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)) ?? -1; if (index == -1) { index = skeletonEntry.BoneNames1.FindIndex(x => x.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)); } else { index = 0x8000 | index; } if (index == -1) { continue; } foreach (var childBone in skin.Bones.Where(x => x.ParentId == bone.Id)) { childBone.ParentId = index; } bone.Id = index; } } } } if (textureDatabase != null && TextureSet != null) { int id = textureDatabase.Textures.Max(x => x.Id) + 1; var idDictionary = new Dictionary <int, int>(TextureSet.Textures.Count); foreach (var texture in TextureSet.Textures) { var textureEntry = !string.IsNullOrEmpty(texture.Name) ? textureDatabase.GetTexture(texture.Name) : textureDatabase.GetTexture(texture.Id); if (textureEntry == null) { textureDatabase.Textures.Add( textureEntry = new TextureEntry { Name = texture.Name, Id = id++ }); if (string.IsNullOrEmpty(textureEntry.Name)) { textureEntry.Name = $"Unnamed {textureEntry.Id}"; } } idDictionary[texture.Id] = textureEntry.Id; texture.Name = textureEntry.Name; texture.Id = textureEntry.Id; } foreach (var materialTexture in Meshes.SelectMany(x => x.Materials) .SelectMany(x => x.MaterialTextures)) { if (idDictionary.TryGetValue(materialTexture.TextureId, out id)) { materialTexture.TextureId = id; } } TextureIds.Clear(); TextureIds.AddRange(TextureSet.Textures.Select(x => x.Id)); } Save(destination, leaveOpen); }
public Mesh ToUnityMesh() { return(TriangleMesh.ToUnityMesh(Meshes.SelectMany(mesh => mesh.TriangleMeshes))); }
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); }