From d55231178e5e82220227214c9e9422466f3ad175 Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Wed, 28 Aug 2024 21:39:30 +0100 Subject: [PATCH] Use custom naive rectpacker --- project/code/TMV/Mission.cs | 18 +++-- project/code/TMV/RectPacker.cs | 119 +++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 project/code/TMV/RectPacker.cs diff --git a/project/code/TMV/Mission.cs b/project/code/TMV/Mission.cs index aa4e52e..7b1e10e 100644 --- a/project/code/TMV/Mission.cs +++ b/project/code/TMV/Mission.cs @@ -3,7 +3,6 @@ using KeepersCompound.LGS; using KeepersCompound.LGS.Database; using KeepersCompound.LGS.Database.Chunks; using KeepersCompound.TMV.UI; -using RectpackSharp; using System; using System.Collections.Generic; using System.IO; @@ -261,7 +260,7 @@ public partial class Mission : Node3D var lmHdr = worldRep.DataHeader.LightmapFormat == 2; GD.Print($"HDR Lightmap: {lmHdr}"); - var packingRects = new List(); + var packingRects = new List(); var surfaceDataMap = new Dictionary(); var rectDataMap = new Dictionary(); @@ -288,7 +287,12 @@ public partial class Mission : Node3D rectDataMap.Add(packingRects.Count, lightmapRectData); var light = cell.LightList[polyIdx]; - var rect = new PackingRectangle(0, 0, light.Width, light.Height, packingRects.Count); + var rect = new RectPacker.Rect + { + Width = light.Width, + Height = light.Height, + Id = packingRects.Count, + }; packingRects.Add(rect); cellIdxOffset += cell.Polys[polyIdx].VertexCount; @@ -355,16 +359,17 @@ public partial class Mission : Node3D private static Texture2DArray BuildLightmapTexture( WorldRep.Cell[] cells, - PackingRectangle[] packingRects, + RectPacker.Rect[] packingRects, Dictionary rectDataMap, Dictionary surfaceDataMap) { var bounds = Timing.TimeStage("RectPack", () => { - RectanglePacker.Pack(packingRects, out var bounds, PackingHints.FindBest, 0); + var bounds = RectPacker.Pack(packingRects); return bounds; }); - GD.Print($"Creating lightmap with bounds: ({bounds.Width}, {bounds.Height})"); + bounds = RectPacker.TightBounds(packingRects); + GD.Print($"Packed {packingRects.Length} rects in ({bounds.Width}, {bounds.Height})"); var lightmapFormat = Image.Format.Rgba8; var lmLayerCount = 33; // TODO: Use LightmapLayers @@ -413,6 +418,7 @@ public partial class Mission : Node3D return new Vector2(u, v); }); } + lmImages[0].SavePng("user://lm.png"); var lightmapTexture = new Texture2DArray(); lightmapTexture.CreateFromImages(lmImages); diff --git a/project/code/TMV/RectPacker.cs b/project/code/TMV/RectPacker.cs new file mode 100644 index 0000000..d7ddef7 --- /dev/null +++ b/project/code/TMV/RectPacker.cs @@ -0,0 +1,119 @@ +using System; + +namespace KeepersCompound.TMV; + +public static class RectPacker +{ + public struct Rect + { + public int Id; + public ushort X; + public ushort Y; + public ushort Width; + public ushort Height; + } + + enum ExpandDir + { + X, + Y, + } + + public static Rect Pack(Rect[] rectangles, ushort minWidth = 512, ushort minHeight = 512) + { + ArgumentNullException.ThrowIfNull(rectangles); + ArgumentOutOfRangeException.ThrowIfZero(minWidth); + ArgumentOutOfRangeException.ThrowIfZero(minHeight); + + var bounds = new Rect + { + Width = minWidth, + Height = minHeight + }; + if (rectangles.Length == 0) + { + return bounds; + } + + Array.Sort(rectangles, (a, b) => a.Height.CompareTo(b.Height)); + + // Naively try to pack into the row, wrapping when we reach the end + // and expanding the bounds when needed + ushort rowStartX = 0; + ushort rowMaxH = 0; + ushort x = 0; + ushort y = 0; + var expandDir = ExpandDir.Y; + var rectCount = rectangles.Length; + for (var i = 0; i < rectCount; i++) + { + var rect = rectangles[i]; + if (rect.Width == 0 || rect.Height == 0) + { + throw new ArgumentOutOfRangeException(nameof(rectangles), rectangles, $"{nameof(rectangles)} contains rect with width or height 0."); + } + + // uh oh we've hit the edge, wrap to next row + if (x + rect.Width > bounds.Width) + { + // we just loop to next row + x = rowStartX; + y += rowMaxH; + rowMaxH = 0; + } + + // uh oh we've hit the edge, expand + if (y + rect.Height > bounds.Height) + { + if (expandDir == ExpandDir.X) + { + // We can expand to the right + x = bounds.Width; + rowStartX = bounds.Width; + y = 0; + rowMaxH = 0; + bounds.Width *= 2; + expandDir = ExpandDir.Y; + } + else if (expandDir == ExpandDir.Y) + { + rowStartX = 0; + bounds.Height *= 2; + expandDir = ExpandDir.X; + } + } + + rect.X = x; + rect.Y = y; + rectangles[i] = rect; + + x += rect.Width; + if (rect.Height > rowMaxH) + { + rowMaxH = rect.Height; + } + } + + return bounds; + } + + public static Rect TightBounds(Rect[] rectangles) + { + var bounds = new Rect(); + foreach (var rect in rectangles) + { + var w = rect.X + rect.Width; + if (w > bounds.Width) + { + bounds.Width = (ushort)w; + } + + var h = rect.Y + rect.Height; + if (h > bounds.Height) + { + bounds.Height = (ushort)h; + } + } + return bounds; + } +}