From 770f4e22d518993fc2262ae0db36806414c3b76f Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Mon, 5 Aug 2024 18:46:57 +0100 Subject: [PATCH] Split meshes into a surface for each poly --- project/code/Mission.cs | 90 ++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/project/code/Mission.cs b/project/code/Mission.cs index 62c39b2..9ba5924 100644 --- a/project/code/Mission.cs +++ b/project/code/Mission.cs @@ -1,4 +1,5 @@ using Godot; +using Godot.NativeInterop; using KeepersCompound.LGS; using KeepersCompound.LGS.Database; using KeepersCompound.LGS.Database.Chunks; @@ -102,35 +103,26 @@ public partial class Mission : Node3D // You'd think these would be the same number, but apparently not // I think it's because water counts as a render poly and a portal poly var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys); + var surfaceArrays = BuildSurfaceArrays(cell, maxPolyIdx, out var packingRects); - var packingRects = new PackingRectangle[maxPolyIdx]; - var rectIdToUvIdxMap = new List(); - - var vertices = new List(); - var normals = new List(); - var indices = new List(); - var lightmapUvs = new List(); - GenerateMeshContent(cell, maxPolyIdx, packingRects, rectIdToUvIdxMap, vertices, normals, indices, lightmapUvs); - - Image lightmap = BuildLightmap(cell, packingRects, rectIdToUvIdxMap, lightmapUvs); - + Image lightmap = BuildLightmap(cell, packingRects, surfaceArrays); var material = new StandardMaterial3D { AlbedoTexture = ImageTexture.CreateFromImage(lightmap), TextureFilter = BaseMaterial3D.TextureFilterEnum.Nearest, }; - MeshInstance3D mesh = GenerateMesh(vertices, normals, indices, lightmapUvs, material); - OccluderInstance3D occluderInstance = GenerateOccluder(vertices, indices); + MeshInstance3D mesh = GenerateMesh(surfaceArrays, material); + // OccluderInstance3D occluderInstance = GenerateOccluder(vertices, indices); var cellNode = new Node3D(); cellNode.AddChild(mesh); - cellNode.AddChild(occluderInstance); + // cellNode.AddChild(occluderInstance); AddChild(cellNode); } - private static Image BuildLightmap(WorldRep.Cell cell, PackingRectangle[] packingRects, List rectIdToUvIdxMap, List lightmapUvs) + private static Image BuildLightmap(WorldRep.Cell cell, PackingRectangle[] packingRects, List surfaceArrays) { RectanglePacker.Pack(packingRects, out var bounds); var image = Image.Create((int)bounds.Width, (int)bounds.Height, false, Image.Format.Rgba8); @@ -158,10 +150,10 @@ public partial class Mission : Node3D } // Transform UVs - var lmUvIdxs = rectIdToUvIdxMap[rect.Id]; - foreach (var idx in lmUvIdxs) + var lightmapUvs = surfaceArrays[rect.Id][(int)Mesh.ArrayType.TexUV].As(); + for (var i = 0; i < lightmapUvs.Length; i++) { - var uv = lightmapUvs[idx]; + var uv = lightmapUvs[i]; var u = uv.X; var v = uv.Y; @@ -174,21 +166,31 @@ public partial class Mission : Node3D // Transform! u = (rect.X + rect.Width * u) / (int)bounds.Width; v = (rect.Y + rect.Height * v) / (int)bounds.Height; - lightmapUvs[idx] = new Vector2(u, v); + lightmapUvs[i] = new Vector2(u, v); } + surfaceArrays[rect.Id][(int)Mesh.ArrayType.TexUV] = lightmapUvs; } return image; } - private static void GenerateMeshContent(WorldRep.Cell cell, int maxPolyIdx, PackingRectangle[] packingRects, List rectIdToUvIdxMap, List vertices, List normals, List indices, List lightmapUvs) + private static List BuildSurfaceArrays( + WorldRep.Cell cell, + int maxPolyIdx, + out PackingRectangle[] packingRects) { + packingRects = new PackingRectangle[maxPolyIdx]; + + var surfacesArrays = new List(); var cellIdxOffset = 0; for (int i = 0; i < maxPolyIdx; i++) { - var poly = cell.Polys[i]; - var meshIdxOffset = vertices.Count; + var vertices = new List(); + var normals = new List(); + var indices = new List(); + var uvs = new List(); + var poly = cell.Polys[i]; var normal = cell.Planes[poly.PlaneId].Normal.ToGodotVec3(); var numPolyVertices = poly.VertexCount; for (var j = 0; j < numPolyVertices; j++) @@ -201,20 +203,29 @@ public partial class Mission : Node3D // Simple triangulation. Polys are always convex so we can just do a fan for (int j = 1; j < numPolyVertices - 1; j++) { - indices.Add(meshIdxOffset); - indices.Add(meshIdxOffset + j); - indices.Add(meshIdxOffset + j + 1); + indices.Add(0); + indices.Add(j); + indices.Add(j + 1); } // UVs var renderPoly = cell.RenderPolys[i]; var light = cell.LightList[i]; packingRects[i] = new PackingRectangle(0, 0, light.Width, light.Height, i); - var uvIdxs = CalcBaseUV(cell, poly, renderPoly, light, lightmapUvs, cellIdxOffset); - rectIdToUvIdxMap.Add(uvIdxs); + CalcBaseUV(cell, poly, renderPoly, light, uvs, cellIdxOffset); cellIdxOffset += poly.VertexCount; + + var array = new Godot.Collections.Array(); + array.Resize((int)Mesh.ArrayType.Max); + array[(int)Mesh.ArrayType.Vertex] = vertices.ToArray(); + array[(int)Mesh.ArrayType.Normal] = normals.ToArray(); + array[(int)Mesh.ArrayType.Index] = indices.ToArray(); + array[(int)Mesh.ArrayType.TexUV] = uvs.ToArray(); + surfacesArrays.Add(array); } + + return surfacesArrays; } private static OccluderInstance3D GenerateOccluder(List vertices, List indices) @@ -229,18 +240,14 @@ public partial class Mission : Node3D return occluderInstance; } - private static MeshInstance3D GenerateMesh(List vertices, List normals, List indices, List lightmapUvs, StandardMaterial3D material) + private static MeshInstance3D GenerateMesh(List surfaceArrays, StandardMaterial3D material) { var arrMesh = new ArrayMesh(); - var arrays = new Godot.Collections.Array(); - arrays.Resize((int)Mesh.ArrayType.Max); - arrays[(int)Mesh.ArrayType.Vertex] = vertices.ToArray(); - arrays[(int)Mesh.ArrayType.Normal] = normals.ToArray(); - arrays[(int)Mesh.ArrayType.Index] = indices.ToArray(); - arrays[(int)Mesh.ArrayType.TexUV] = lightmapUvs.ToArray(); - - arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays); - arrMesh.SurfaceSetMaterial(0, material); + for (var i = 0; i < surfaceArrays.Count; i++) + { + arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArrays[i]); + arrMesh.SurfaceSetMaterial(i, material); + } var meshInstance = new MeshInstance3D { @@ -250,7 +257,7 @@ public partial class Mission : Node3D return meshInstance; } - private static int[] CalcBaseUV( + private static void CalcBaseUV( WorldRep.Cell cell, WorldRep.Cell.Poly poly, WorldRep.Cell.RenderPoly renderPoly, @@ -275,15 +282,12 @@ public partial class Mission : Node3D var lmVBase = lmVScale * (baseV + (0.5f - light.Bases.Item2) / 4.0f); var anchor = cell.Vertices[cell.Indices[cellIdxOffset + 0]].ToGodotVec3(); // TODO: This probably shouldn't be hardcoded idx 0 - var uvIdxs = new int[poly.VertexCount]; if (uv == 0.0) { var lmUVec = texU * lmUScale / uu; var lmVVec = texV * lmVScale / vv; for (var i = 0; i < poly.VertexCount; i++) { - uvIdxs[i] = lightmapUvs.Count; - var v = cell.Vertices[cell.Indices[cellIdxOffset + i]].ToGodotVec3(); var delta = new Vector3(v.X - anchor.X, v.Y - anchor.Y, v.Z - anchor.Z); var lmUV = new Vector2(delta.Dot(lmUVec) + lmUBase, delta.Dot(lmVVec) + lmVBase); @@ -299,8 +303,6 @@ public partial class Mission : Node3D var lmUvv = lmVScale * denom * uv; for (var i = 0; i < poly.VertexCount; i++) { - uvIdxs[i] = lightmapUvs.Count; - var v = cell.Vertices[cell.Indices[cellIdxOffset + i]].ToGodotVec3(); var delta = new Vector3(v.X - anchor.X, v.Y - anchor.Y, v.Z - anchor.Z); var du = delta.Dot(texU); @@ -309,8 +311,6 @@ public partial class Mission : Node3D lightmapUvs.Add(lmUV); } } - - return uvIdxs; } private void LoadTextures(TxList textureList)