Add working lightmaps
This commit is contained in:
parent
185af065bc
commit
e8b77b42bb
|
@ -6,4 +6,7 @@
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
<RootNamespace>ThiefMissionViewer</RootNamespace>
|
<RootNamespace>ThiefMissionViewer</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="rectpacksharp" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -117,6 +117,44 @@ public class WorldRep : IChunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly Vector4[] AsRgba(uint layer)
|
||||||
|
{
|
||||||
|
if (layer >= Pixels.GetLength(0))
|
||||||
|
{
|
||||||
|
|
||||||
|
return System.Array.Empty<Vector4>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var height = Pixels.GetLength(1);
|
||||||
|
var width = Pixels.GetLength(2);
|
||||||
|
var bpp = Pixels.GetLength(3);
|
||||||
|
var colours = new Vector4[height * width];
|
||||||
|
for (var y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
var idx = x + y * width;
|
||||||
|
|
||||||
|
switch (bpp)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
var raw1 = Pixels[layer, y, x, 0];
|
||||||
|
colours[idx] = new Vector4(raw1, raw1, raw1, 255) / 255.0f;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
var raw2 = Pixels[layer, y, x, 0] + (Pixels[layer, y, x, 1] << 8);
|
||||||
|
colours[idx] = new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 32) / 32.0f;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
colours[idx] = new Vector4(Pixels[layer, y, x, 0], Pixels[layer, y, x, 1], Pixels[layer, y, x, 2], Pixels[layer, y, x, 3]) / 255.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return colours;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte VertexCount { get; set; }
|
public byte VertexCount { get; set; }
|
||||||
|
|
|
@ -2,7 +2,7 @@ using Godot;
|
||||||
using KeepersCompound.LGS;
|
using KeepersCompound.LGS;
|
||||||
using KeepersCompound.LGS.Database;
|
using KeepersCompound.LGS.Database;
|
||||||
using KeepersCompound.LGS.Database.Chunks;
|
using KeepersCompound.LGS.Database.Chunks;
|
||||||
using Microsoft.VisualBasic;
|
using RectpackSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ public partial class Mission : Node3D
|
||||||
var numRenderPolys = cell.RenderPolyCount;
|
var numRenderPolys = cell.RenderPolyCount;
|
||||||
var numPortalPolys = cell.PortalPolyCount;
|
var numPortalPolys = cell.PortalPolyCount;
|
||||||
|
|
||||||
|
// There's nothing to render
|
||||||
if (numRenderPolys == 0 || numPortalPolys >= numPolys)
|
if (numRenderPolys == 0 || numPortalPolys >= numPolys)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -89,26 +90,29 @@ public partial class Mission : Node3D
|
||||||
var vertices = new List<Vector3>();
|
var vertices = new List<Vector3>();
|
||||||
var normals = new List<Vector3>();
|
var normals = new List<Vector3>();
|
||||||
var indices = new List<int>();
|
var indices = new List<int>();
|
||||||
|
var lightmapUvs = new List<Vector2>();
|
||||||
var cellIdxOffset = 0;
|
var cellIdxOffset = 0;
|
||||||
|
|
||||||
|
// 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 maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
|
||||||
|
var packingRects = new PackingRectangle[maxPolyIdx];
|
||||||
|
var rectIdToUvIdxMap = new List<int[]>();
|
||||||
for (int i = 0; i < maxPolyIdx; i++)
|
for (int i = 0; i < maxPolyIdx; i++)
|
||||||
{
|
{
|
||||||
var poly = cell.Polys[i];
|
var poly = cell.Polys[i];
|
||||||
var plane = cell.Planes[poly.PlaneId];
|
|
||||||
var normal = plane.Normal;
|
|
||||||
var gNormal = normal.ToGodotVec3(4.0f);
|
|
||||||
|
|
||||||
var numPolyVertices = poly.VertexCount;
|
|
||||||
var meshIdxOffset = vertices.Count;
|
var meshIdxOffset = vertices.Count;
|
||||||
|
|
||||||
|
var normal = cell.Planes[poly.PlaneId].Normal.ToGodotVec3(4.0f);
|
||||||
|
var numPolyVertices = poly.VertexCount;
|
||||||
for (var j = 0; j < numPolyVertices; j++)
|
for (var j = 0; j < numPolyVertices; j++)
|
||||||
{
|
{
|
||||||
var vertex = cell.Vertices[cell.Indices[cellIdxOffset + j]];
|
var vertex = cell.Vertices[cell.Indices[cellIdxOffset + j]];
|
||||||
vertices.Add(vertex.ToGodotVec3(4.0f));
|
vertices.Add(vertex.ToGodotVec3(4.0f));
|
||||||
normals.Add(gNormal);
|
normals.Add(normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple triangulation
|
// 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(meshIdxOffset);
|
||||||
|
@ -116,18 +120,75 @@ public partial class Mission : Node3D
|
||||||
indices.Add(meshIdxOffset + j + 1);
|
indices.Add(meshIdxOffset + 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);
|
||||||
|
|
||||||
cellIdxOffset += poly.VertexCount;
|
cellIdxOffset += poly.VertexCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RectanglePacker.Pack(packingRects, out var bounds);
|
||||||
|
var image = Image.Create((int)bounds.Width, (int)bounds.Height, false, Image.Format.Rgba8);
|
||||||
|
foreach (var rect in packingRects)
|
||||||
|
{
|
||||||
|
// Build lightmap
|
||||||
|
var lightmap = cell.Lightmaps[rect.Id];
|
||||||
|
// TODO: Handle animlight layers
|
||||||
|
var colours = lightmap.AsRgba(0);
|
||||||
|
var height = lightmap.Pixels.GetLength(1);
|
||||||
|
var width = lightmap.Pixels.GetLength(2);
|
||||||
|
for (var y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
var rawColour = colours[x + y * width];
|
||||||
|
image.SetPixel((int)rect.X + x, (int)rect.Y + y, new Color(rawColour.X, rawColour.Y, rawColour.Z, rawColour.W));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform UVs
|
||||||
|
var lmUvIdxs = rectIdToUvIdxMap[rect.Id];
|
||||||
|
foreach (var idx in lmUvIdxs)
|
||||||
|
{
|
||||||
|
var uv = lightmapUvs[idx];
|
||||||
|
var u = uv.X;
|
||||||
|
var v = uv.Y;
|
||||||
|
|
||||||
|
// Clamp uv range to [0..1]
|
||||||
|
u %= 1;
|
||||||
|
v %= 1;
|
||||||
|
if (u < 0) u = Math.Abs(u);
|
||||||
|
if (v < 0) v = Math.Abs(v);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var cellNode = new Node3D();
|
var cellNode = new Node3D();
|
||||||
|
|
||||||
|
var material = new StandardMaterial3D
|
||||||
|
{
|
||||||
|
AlbedoTexture = ImageTexture.CreateFromImage(image),
|
||||||
|
TextureFilter = BaseMaterial3D.TextureFilterEnum.Nearest,
|
||||||
|
};
|
||||||
|
|
||||||
var arrMesh = new ArrayMesh();
|
var arrMesh = new ArrayMesh();
|
||||||
var arrays = new Godot.Collections.Array();
|
var arrays = new Godot.Collections.Array();
|
||||||
arrays.Resize((int)Mesh.ArrayType.Max);
|
arrays.Resize((int)Mesh.ArrayType.Max);
|
||||||
arrays[(int)Mesh.ArrayType.Vertex] = vertices.ToArray();
|
arrays[(int)Mesh.ArrayType.Vertex] = vertices.ToArray();
|
||||||
arrays[(int)Mesh.ArrayType.Normal] = normals.ToArray();
|
arrays[(int)Mesh.ArrayType.Normal] = normals.ToArray();
|
||||||
arrays[(int)Mesh.ArrayType.Index] = indices.ToArray();
|
arrays[(int)Mesh.ArrayType.Index] = indices.ToArray();
|
||||||
|
arrays[(int)Mesh.ArrayType.TexUV] = lightmapUvs.ToArray();
|
||||||
|
|
||||||
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
|
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
|
||||||
|
arrMesh.SurfaceSetMaterial(0, material);
|
||||||
|
|
||||||
var meshInstance = new MeshInstance3D
|
var meshInstance = new MeshInstance3D
|
||||||
{
|
{
|
||||||
|
@ -153,9 +214,72 @@ public partial class Mission : Node3D
|
||||||
Position = cell.SphereCenter.ToGodotVec3(4.0f),
|
Position = cell.SphereCenter.ToGodotVec3(4.0f),
|
||||||
OmniRange = cell.SphereRadius * (r.NextSingle() + 1.0f) * 0.5f,
|
OmniRange = cell.SphereRadius * (r.NextSingle() + 1.0f) * 0.5f,
|
||||||
};
|
};
|
||||||
cellNode.AddChild(light);
|
// cellNode.AddChild(light);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddChild(cellNode);
|
AddChild(cellNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int[] CalcBaseUV(
|
||||||
|
WorldRep.Cell cell,
|
||||||
|
WorldRep.Cell.Poly poly,
|
||||||
|
WorldRep.Cell.RenderPoly renderPoly,
|
||||||
|
WorldRep.Cell.LightmapInfo light,
|
||||||
|
List<Vector2> lightmapUvs,
|
||||||
|
int cellIdxOffset)
|
||||||
|
{
|
||||||
|
// TODO: This is slightly hardcoded for ND. Check other stuff at some point. Should be handled in LG side imo
|
||||||
|
// TODO: This is a mess lol
|
||||||
|
var texU = renderPoly.TextureVectors.Item1.ToGodotVec3(4.0f);
|
||||||
|
var texV = renderPoly.TextureVectors.Item2.ToGodotVec3(4.0f);
|
||||||
|
|
||||||
|
var uu = texU.Dot(texU);
|
||||||
|
var vv = texV.Dot(texV);
|
||||||
|
var uv = texU.Dot(texV);
|
||||||
|
var lmUScale = 4.0f / light.Width;
|
||||||
|
var lmVScale = 4.0f / light.Height;
|
||||||
|
|
||||||
|
var baseU = renderPoly.TextureBases.Item1;
|
||||||
|
var baseV = renderPoly.TextureBases.Item2;
|
||||||
|
var lmUBase = lmUScale * (baseU + (0.5f - light.Bases.Item1) / 4.0f);
|
||||||
|
var lmVBase = lmVScale * (baseV + (0.5f - light.Bases.Item2) / 4.0f);
|
||||||
|
var anchor = cell.Vertices[cell.Indices[cellIdxOffset + 0]].ToGodotVec3(4.0f); // 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(4.0f);
|
||||||
|
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);
|
||||||
|
lightmapUvs.Add(lmUV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var denom = 1.0f / (uu * vv - uv * uv);
|
||||||
|
var lmUu = uu * lmVScale * denom;
|
||||||
|
var lmVv = vv * lmUScale * denom;
|
||||||
|
var lmUvu = lmUScale * denom * uv;
|
||||||
|
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(4.0f);
|
||||||
|
var delta = new Vector3(v.X - anchor.X, v.Y - anchor.Y, v.Z - anchor.Z);
|
||||||
|
var du = delta.Dot(texU);
|
||||||
|
var dv = delta.Dot(texV);
|
||||||
|
var lmUV = new Vector2(lmUBase + lmVv * du - lmUvu * dv, lmVBase + lmUu * dv - lmUvv * du);
|
||||||
|
lightmapUvs.Add(lmUV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uvIdxs;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -8,8 +8,9 @@
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
|
|
||||||
[node name="Mission" type="Node3D" parent="."]
|
[node name="Mission" type="Node3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.110309, 0.187101, -0.461656)
|
||||||
script = ExtResource("1_xhqt7")
|
script = ExtResource("1_xhqt7")
|
||||||
FileName = "/home/jarrod/Dev/thief/de-specs/test_data/rose-garden.mis"
|
FileName = "/home/jarrod/Dev/thief/de-specs/test_data/lm-test.cow"
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
script = ExtResource("2_w5otl")
|
script = ExtResource("2_w5otl")
|
||||||
|
@ -36,6 +37,7 @@ layout_mode = 2
|
||||||
[node name="FileNameLineEdit" type="LineEdit" parent="UI/PanelContainer/MarginContainer/VBoxContainer"]
|
[node name="FileNameLineEdit" type="LineEdit" parent="UI/PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
text = "/home/jarrod/Dev/thief/de-specs/test_data/rose-garden.mis"
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="UI/PanelContainer/MarginContainer/VBoxContainer"]
|
[node name="HBoxContainer" type="HBoxContainer" parent="UI/PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
Loading…
Reference in New Issue