Split meshes into a surface for each poly
This commit is contained in:
parent
714c9804d8
commit
770f4e22d5
|
@ -1,4 +1,5 @@
|
||||||
using Godot;
|
using Godot;
|
||||||
|
using Godot.NativeInterop;
|
||||||
using KeepersCompound.LGS;
|
using KeepersCompound.LGS;
|
||||||
using KeepersCompound.LGS.Database;
|
using KeepersCompound.LGS.Database;
|
||||||
using KeepersCompound.LGS.Database.Chunks;
|
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
|
// 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
|
// I think it's because water counts as a render poly and a portal poly
|
||||||
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
|
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
|
||||||
|
var surfaceArrays = BuildSurfaceArrays(cell, maxPolyIdx, out var packingRects);
|
||||||
|
|
||||||
var packingRects = new PackingRectangle[maxPolyIdx];
|
Image lightmap = BuildLightmap(cell, packingRects, surfaceArrays);
|
||||||
var rectIdToUvIdxMap = new List<int[]>();
|
|
||||||
|
|
||||||
var vertices = new List<Vector3>();
|
|
||||||
var normals = new List<Vector3>();
|
|
||||||
var indices = new List<int>();
|
|
||||||
var lightmapUvs = new List<Vector2>();
|
|
||||||
GenerateMeshContent(cell, maxPolyIdx, packingRects, rectIdToUvIdxMap, vertices, normals, indices, lightmapUvs);
|
|
||||||
|
|
||||||
Image lightmap = BuildLightmap(cell, packingRects, rectIdToUvIdxMap, lightmapUvs);
|
|
||||||
|
|
||||||
var material = new StandardMaterial3D
|
var material = new StandardMaterial3D
|
||||||
{
|
{
|
||||||
AlbedoTexture = ImageTexture.CreateFromImage(lightmap),
|
AlbedoTexture = ImageTexture.CreateFromImage(lightmap),
|
||||||
TextureFilter = BaseMaterial3D.TextureFilterEnum.Nearest,
|
TextureFilter = BaseMaterial3D.TextureFilterEnum.Nearest,
|
||||||
};
|
};
|
||||||
|
|
||||||
MeshInstance3D mesh = GenerateMesh(vertices, normals, indices, lightmapUvs, material);
|
MeshInstance3D mesh = GenerateMesh(surfaceArrays, material);
|
||||||
OccluderInstance3D occluderInstance = GenerateOccluder(vertices, indices);
|
// OccluderInstance3D occluderInstance = GenerateOccluder(vertices, indices);
|
||||||
|
|
||||||
var cellNode = new Node3D();
|
var cellNode = new Node3D();
|
||||||
cellNode.AddChild(mesh);
|
cellNode.AddChild(mesh);
|
||||||
cellNode.AddChild(occluderInstance);
|
// cellNode.AddChild(occluderInstance);
|
||||||
|
|
||||||
AddChild(cellNode);
|
AddChild(cellNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Image BuildLightmap(WorldRep.Cell cell, PackingRectangle[] packingRects, List<int[]> rectIdToUvIdxMap, List<Vector2> lightmapUvs)
|
private static Image BuildLightmap(WorldRep.Cell cell, PackingRectangle[] packingRects, List<Godot.Collections.Array> surfaceArrays)
|
||||||
{
|
{
|
||||||
RectanglePacker.Pack(packingRects, out var bounds);
|
RectanglePacker.Pack(packingRects, out var bounds);
|
||||||
var image = Image.Create((int)bounds.Width, (int)bounds.Height, false, Image.Format.Rgba8);
|
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
|
// Transform UVs
|
||||||
var lmUvIdxs = rectIdToUvIdxMap[rect.Id];
|
var lightmapUvs = surfaceArrays[rect.Id][(int)Mesh.ArrayType.TexUV].As<Vector2[]>();
|
||||||
foreach (var idx in lmUvIdxs)
|
for (var i = 0; i < lightmapUvs.Length; i++)
|
||||||
{
|
{
|
||||||
var uv = lightmapUvs[idx];
|
var uv = lightmapUvs[i];
|
||||||
var u = uv.X;
|
var u = uv.X;
|
||||||
var v = uv.Y;
|
var v = uv.Y;
|
||||||
|
|
||||||
|
@ -174,21 +166,31 @@ public partial class Mission : Node3D
|
||||||
// Transform!
|
// Transform!
|
||||||
u = (rect.X + rect.Width * u) / (int)bounds.Width;
|
u = (rect.X + rect.Width * u) / (int)bounds.Width;
|
||||||
v = (rect.Y + rect.Height * v) / (int)bounds.Height;
|
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;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateMeshContent(WorldRep.Cell cell, int maxPolyIdx, PackingRectangle[] packingRects, List<int[]> rectIdToUvIdxMap, List<Vector3> vertices, List<Vector3> normals, List<int> indices, List<Vector2> lightmapUvs)
|
private static List<Godot.Collections.Array> BuildSurfaceArrays(
|
||||||
|
WorldRep.Cell cell,
|
||||||
|
int maxPolyIdx,
|
||||||
|
out PackingRectangle[] packingRects)
|
||||||
{
|
{
|
||||||
|
packingRects = new PackingRectangle[maxPolyIdx];
|
||||||
|
|
||||||
|
var surfacesArrays = new List<Godot.Collections.Array>();
|
||||||
var cellIdxOffset = 0;
|
var cellIdxOffset = 0;
|
||||||
for (int i = 0; i < maxPolyIdx; i++)
|
for (int i = 0; i < maxPolyIdx; i++)
|
||||||
{
|
{
|
||||||
var poly = cell.Polys[i];
|
var vertices = new List<Vector3>();
|
||||||
var meshIdxOffset = vertices.Count;
|
var normals = new List<Vector3>();
|
||||||
|
var indices = new List<int>();
|
||||||
|
var uvs = new List<Vector2>();
|
||||||
|
|
||||||
|
var poly = cell.Polys[i];
|
||||||
var normal = cell.Planes[poly.PlaneId].Normal.ToGodotVec3();
|
var normal = cell.Planes[poly.PlaneId].Normal.ToGodotVec3();
|
||||||
var numPolyVertices = poly.VertexCount;
|
var numPolyVertices = poly.VertexCount;
|
||||||
for (var j = 0; j < numPolyVertices; j++)
|
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
|
// Simple triangulation. Polys are always convex so we can just do a fan
|
||||||
for (int j = 1; j < numPolyVertices - 1; j++)
|
for (int j = 1; j < numPolyVertices - 1; j++)
|
||||||
{
|
{
|
||||||
indices.Add(meshIdxOffset);
|
indices.Add(0);
|
||||||
indices.Add(meshIdxOffset + j);
|
indices.Add(j);
|
||||||
indices.Add(meshIdxOffset + j + 1);
|
indices.Add(j + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UVs
|
// UVs
|
||||||
var renderPoly = cell.RenderPolys[i];
|
var renderPoly = cell.RenderPolys[i];
|
||||||
var light = cell.LightList[i];
|
var light = cell.LightList[i];
|
||||||
packingRects[i] = new PackingRectangle(0, 0, light.Width, light.Height, i);
|
packingRects[i] = new PackingRectangle(0, 0, light.Width, light.Height, i);
|
||||||
var uvIdxs = CalcBaseUV(cell, poly, renderPoly, light, lightmapUvs, cellIdxOffset);
|
CalcBaseUV(cell, poly, renderPoly, light, uvs, cellIdxOffset);
|
||||||
rectIdToUvIdxMap.Add(uvIdxs);
|
|
||||||
|
|
||||||
cellIdxOffset += poly.VertexCount;
|
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<Vector3> vertices, List<int> indices)
|
private static OccluderInstance3D GenerateOccluder(List<Vector3> vertices, List<int> indices)
|
||||||
|
@ -229,18 +240,14 @@ public partial class Mission : Node3D
|
||||||
return occluderInstance;
|
return occluderInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MeshInstance3D GenerateMesh(List<Vector3> vertices, List<Vector3> normals, List<int> indices, List<Vector2> lightmapUvs, StandardMaterial3D material)
|
private static MeshInstance3D GenerateMesh(List<Godot.Collections.Array> surfaceArrays, StandardMaterial3D material)
|
||||||
{
|
{
|
||||||
var arrMesh = new ArrayMesh();
|
var arrMesh = new ArrayMesh();
|
||||||
var arrays = new Godot.Collections.Array();
|
for (var i = 0; i < surfaceArrays.Count; i++)
|
||||||
arrays.Resize((int)Mesh.ArrayType.Max);
|
{
|
||||||
arrays[(int)Mesh.ArrayType.Vertex] = vertices.ToArray();
|
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArrays[i]);
|
||||||
arrays[(int)Mesh.ArrayType.Normal] = normals.ToArray();
|
arrMesh.SurfaceSetMaterial(i, material);
|
||||||
arrays[(int)Mesh.ArrayType.Index] = indices.ToArray();
|
}
|
||||||
arrays[(int)Mesh.ArrayType.TexUV] = lightmapUvs.ToArray();
|
|
||||||
|
|
||||||
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
|
|
||||||
arrMesh.SurfaceSetMaterial(0, material);
|
|
||||||
|
|
||||||
var meshInstance = new MeshInstance3D
|
var meshInstance = new MeshInstance3D
|
||||||
{
|
{
|
||||||
|
@ -250,7 +257,7 @@ public partial class Mission : Node3D
|
||||||
return meshInstance;
|
return meshInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] CalcBaseUV(
|
private static void CalcBaseUV(
|
||||||
WorldRep.Cell cell,
|
WorldRep.Cell cell,
|
||||||
WorldRep.Cell.Poly poly,
|
WorldRep.Cell.Poly poly,
|
||||||
WorldRep.Cell.RenderPoly renderPoly,
|
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 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 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)
|
if (uv == 0.0)
|
||||||
{
|
{
|
||||||
var lmUVec = texU * lmUScale / uu;
|
var lmUVec = texU * lmUScale / uu;
|
||||||
var lmVVec = texV * lmVScale / vv;
|
var lmVVec = texV * lmVScale / vv;
|
||||||
for (var i = 0; i < poly.VertexCount; i++)
|
for (var i = 0; i < poly.VertexCount; i++)
|
||||||
{
|
{
|
||||||
uvIdxs[i] = lightmapUvs.Count;
|
|
||||||
|
|
||||||
var v = cell.Vertices[cell.Indices[cellIdxOffset + i]].ToGodotVec3();
|
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 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);
|
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;
|
var lmUvv = lmVScale * denom * uv;
|
||||||
for (var i = 0; i < poly.VertexCount; i++)
|
for (var i = 0; i < poly.VertexCount; i++)
|
||||||
{
|
{
|
||||||
uvIdxs[i] = lightmapUvs.Count;
|
|
||||||
|
|
||||||
var v = cell.Vertices[cell.Indices[cellIdxOffset + i]].ToGodotVec3();
|
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 delta = new Vector3(v.X - anchor.X, v.Y - anchor.Y, v.Z - anchor.Z);
|
||||||
var du = delta.Dot(texU);
|
var du = delta.Dot(texU);
|
||||||
|
@ -309,8 +311,6 @@ public partial class Mission : Node3D
|
||||||
lightmapUvs.Add(lmUV);
|
lightmapUvs.Add(lmUV);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uvIdxs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadTextures(TxList textureList)
|
private void LoadTextures(TxList textureList)
|
||||||
|
|
Loading…
Reference in New Issue