Use custom naive rectpacker
This commit is contained in:
parent
0ceb011af9
commit
d55231178e
|
@ -3,7 +3,6 @@ using KeepersCompound.LGS;
|
||||||
using KeepersCompound.LGS.Database;
|
using KeepersCompound.LGS.Database;
|
||||||
using KeepersCompound.LGS.Database.Chunks;
|
using KeepersCompound.LGS.Database.Chunks;
|
||||||
using KeepersCompound.TMV.UI;
|
using KeepersCompound.TMV.UI;
|
||||||
using RectpackSharp;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -261,7 +260,7 @@ public partial class Mission : Node3D
|
||||||
var lmHdr = worldRep.DataHeader.LightmapFormat == 2;
|
var lmHdr = worldRep.DataHeader.LightmapFormat == 2;
|
||||||
GD.Print($"HDR Lightmap: {lmHdr}");
|
GD.Print($"HDR Lightmap: {lmHdr}");
|
||||||
|
|
||||||
var packingRects = new List<PackingRectangle>();
|
var packingRects = new List<RectPacker.Rect>();
|
||||||
var surfaceDataMap = new Dictionary<int, MeshSurfaceData>();
|
var surfaceDataMap = new Dictionary<int, MeshSurfaceData>();
|
||||||
var rectDataMap = new Dictionary<int, LightmapRectData>();
|
var rectDataMap = new Dictionary<int, LightmapRectData>();
|
||||||
|
|
||||||
|
@ -288,7 +287,12 @@ public partial class Mission : Node3D
|
||||||
rectDataMap.Add(packingRects.Count, lightmapRectData);
|
rectDataMap.Add(packingRects.Count, lightmapRectData);
|
||||||
|
|
||||||
var light = cell.LightList[polyIdx];
|
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);
|
packingRects.Add(rect);
|
||||||
|
|
||||||
cellIdxOffset += cell.Polys[polyIdx].VertexCount;
|
cellIdxOffset += cell.Polys[polyIdx].VertexCount;
|
||||||
|
@ -355,16 +359,17 @@ public partial class Mission : Node3D
|
||||||
|
|
||||||
private static Texture2DArray BuildLightmapTexture(
|
private static Texture2DArray BuildLightmapTexture(
|
||||||
WorldRep.Cell[] cells,
|
WorldRep.Cell[] cells,
|
||||||
PackingRectangle[] packingRects,
|
RectPacker.Rect[] packingRects,
|
||||||
Dictionary<int, LightmapRectData> rectDataMap,
|
Dictionary<int, LightmapRectData> rectDataMap,
|
||||||
Dictionary<int, MeshSurfaceData> surfaceDataMap)
|
Dictionary<int, MeshSurfaceData> surfaceDataMap)
|
||||||
{
|
{
|
||||||
var bounds = Timing.TimeStage("RectPack", () =>
|
var bounds = Timing.TimeStage("RectPack", () =>
|
||||||
{
|
{
|
||||||
RectanglePacker.Pack(packingRects, out var bounds, PackingHints.FindBest, 0);
|
var bounds = RectPacker.Pack(packingRects);
|
||||||
return bounds;
|
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 lightmapFormat = Image.Format.Rgba8;
|
||||||
var lmLayerCount = 33; // TODO: Use LightmapLayers
|
var lmLayerCount = 33; // TODO: Use LightmapLayers
|
||||||
|
@ -413,6 +418,7 @@ public partial class Mission : Node3D
|
||||||
return new Vector2(u, v);
|
return new Vector2(u, v);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
lmImages[0].SavePng("user://lm.png");
|
||||||
|
|
||||||
var lightmapTexture = new Texture2DArray();
|
var lightmapTexture = new Texture2DArray();
|
||||||
lightmapTexture.CreateFromImages(lmImages);
|
lightmapTexture.CreateFromImages(lmImages);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue