2024-07-21 21:13:31 +00:00
|
|
|
using Godot;
|
2024-08-05 17:46:57 +00:00
|
|
|
using Godot.NativeInterop;
|
2024-07-21 21:13:31 +00:00
|
|
|
using KeepersCompound.LGS;
|
|
|
|
using KeepersCompound.LGS.Database;
|
|
|
|
using KeepersCompound.LGS.Database.Chunks;
|
2024-08-03 09:24:45 +00:00
|
|
|
using KeepersCompound.TMV.UI;
|
2024-07-30 20:50:34 +00:00
|
|
|
using RectpackSharp;
|
2024-07-21 21:13:31 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2024-08-03 15:09:51 +00:00
|
|
|
using System.IO;
|
2024-07-21 21:13:31 +00:00
|
|
|
|
|
|
|
namespace KeepersCompound;
|
|
|
|
|
|
|
|
[Tool]
|
|
|
|
public partial class Mission : Node3D
|
|
|
|
{
|
|
|
|
[Export(PropertyHint.GlobalFile, "*.mis")]
|
|
|
|
public string FileName { get; set; }
|
|
|
|
[Export]
|
|
|
|
public bool Build = false;
|
2024-07-22 18:37:27 +00:00
|
|
|
[Export]
|
|
|
|
public bool Clear = false;
|
2024-08-03 15:09:51 +00:00
|
|
|
[Export]
|
|
|
|
public bool Dump = false;
|
2024-07-21 21:13:31 +00:00
|
|
|
|
|
|
|
DbFile _file;
|
2024-08-05 18:25:44 +00:00
|
|
|
List<ImageTexture> _textures;
|
2024-07-21 21:13:31 +00:00
|
|
|
|
|
|
|
public override void _Ready()
|
|
|
|
{
|
2024-08-05 18:25:44 +00:00
|
|
|
_textures = new List<ImageTexture>();
|
2024-08-03 15:09:51 +00:00
|
|
|
|
2024-08-03 09:24:45 +00:00
|
|
|
var missionSelector = GetNode<Control>("%MissionSelector") as MissionSelector;
|
|
|
|
missionSelector.LoadMission += (string path) =>
|
|
|
|
{
|
|
|
|
FileName = path;
|
|
|
|
Build = true;
|
|
|
|
};
|
2024-07-21 21:13:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override void _Process(double delta)
|
|
|
|
{
|
|
|
|
if (Build)
|
|
|
|
{
|
|
|
|
RebuildMap();
|
|
|
|
Build = false;
|
|
|
|
}
|
2024-07-22 18:37:27 +00:00
|
|
|
if (Clear)
|
|
|
|
{
|
|
|
|
ClearMap();
|
|
|
|
Clear = false;
|
|
|
|
}
|
2024-07-21 21:13:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override void _Input(InputEvent @event)
|
|
|
|
{
|
|
|
|
if (@event is InputEventKey keyEvent && keyEvent.Pressed)
|
|
|
|
{
|
|
|
|
if (keyEvent.Keycode == Key.R)
|
|
|
|
{
|
|
|
|
Build = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-22 18:37:27 +00:00
|
|
|
public void ClearMap()
|
2024-07-21 21:13:31 +00:00
|
|
|
{
|
2024-08-07 16:30:20 +00:00
|
|
|
_textures.Clear();
|
|
|
|
|
2024-07-21 21:13:31 +00:00
|
|
|
foreach (var node in GetChildren())
|
|
|
|
{
|
|
|
|
node.QueueFree();
|
|
|
|
}
|
2024-07-22 18:37:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void RebuildMap()
|
|
|
|
{
|
|
|
|
ClearMap();
|
2024-07-21 21:13:31 +00:00
|
|
|
|
|
|
|
_file = new(FileName);
|
2024-08-03 15:09:51 +00:00
|
|
|
var textureList = (TxList)_file.Chunks["TXLIST"];
|
2024-08-05 18:25:44 +00:00
|
|
|
LoadTextures(textureList);
|
2024-08-03 15:09:51 +00:00
|
|
|
if (Dump) DumpTextureList(textureList);
|
|
|
|
|
2024-07-21 21:13:31 +00:00
|
|
|
var wr = (WorldRep)_file.Chunks["WREXT"];
|
|
|
|
|
|
|
|
foreach (var cell in wr.Cells)
|
|
|
|
{
|
|
|
|
BuildCellMesh(cell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void BuildCellMesh(WorldRep.Cell cell)
|
|
|
|
{
|
|
|
|
var numPolys = cell.PolyCount;
|
|
|
|
var numRenderPolys = cell.RenderPolyCount;
|
|
|
|
var numPortalPolys = cell.PortalPolyCount;
|
|
|
|
|
2024-07-30 20:50:34 +00:00
|
|
|
// There's nothing to render
|
2024-07-22 18:37:27 +00:00
|
|
|
if (numRenderPolys == 0 || numPortalPolys >= numPolys)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-07-30 20:50:34 +00:00
|
|
|
// 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
|
2024-07-22 19:34:12 +00:00
|
|
|
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
|
2024-08-05 17:46:57 +00:00
|
|
|
var surfaceArrays = BuildSurfaceArrays(cell, maxPolyIdx, out var packingRects);
|
2024-08-03 16:22:38 +00:00
|
|
|
|
2024-08-05 17:46:57 +00:00
|
|
|
Image lightmap = BuildLightmap(cell, packingRects, surfaceArrays);
|
2024-08-05 18:25:44 +00:00
|
|
|
|
|
|
|
// !Hack: This should be somewhere else?
|
2024-08-05 20:46:03 +00:00
|
|
|
var materials = new List<Material>();
|
2024-08-05 18:25:44 +00:00
|
|
|
for (var i = 0; i < maxPolyIdx; i++)
|
2024-08-03 16:24:04 +00:00
|
|
|
{
|
2024-08-05 18:25:44 +00:00
|
|
|
var textureId = cell.RenderPolys[i].TextureId;
|
|
|
|
// !HACK: Sky textures :)
|
|
|
|
if (textureId >= _textures.Count)
|
|
|
|
{
|
|
|
|
textureId = 0;
|
|
|
|
}
|
|
|
|
|
2024-08-05 20:46:03 +00:00
|
|
|
var material = ResourceLoader.Load<ShaderMaterial>("res://project/materials/base.tres").Duplicate() as ShaderMaterial;
|
|
|
|
material.SetShaderParameter("texture_albedo", _textures[textureId]);
|
|
|
|
material.SetShaderParameter("lightmap_albedo", ImageTexture.CreateFromImage(lightmap));
|
2024-08-05 18:25:44 +00:00
|
|
|
materials.Add(material);
|
|
|
|
}
|
2024-08-03 16:24:04 +00:00
|
|
|
|
2024-08-05 18:25:44 +00:00
|
|
|
MeshInstance3D mesh = GenerateMesh(surfaceArrays, materials);
|
2024-08-05 17:57:21 +00:00
|
|
|
OccluderInstance3D occluderInstance = GenerateOccluder(surfaceArrays);
|
2024-08-03 16:24:04 +00:00
|
|
|
|
|
|
|
var cellNode = new Node3D();
|
|
|
|
cellNode.AddChild(mesh);
|
2024-08-05 17:57:21 +00:00
|
|
|
cellNode.AddChild(occluderInstance);
|
2024-08-03 16:24:04 +00:00
|
|
|
|
|
|
|
AddChild(cellNode);
|
|
|
|
}
|
|
|
|
|
2024-08-05 17:46:57 +00:00
|
|
|
private static Image BuildLightmap(WorldRep.Cell cell, PackingRectangle[] packingRects, List<Godot.Collections.Array> surfaceArrays)
|
2024-08-03 16:24:04 +00:00
|
|
|
{
|
2024-07-30 20:50:34 +00:00
|
|
|
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
|
2024-08-01 17:23:17 +00:00
|
|
|
var layers = (uint)lightmap.Pixels.GetLength(0);
|
|
|
|
var height = (uint)lightmap.Pixels.GetLength(1);
|
|
|
|
var width = (uint)lightmap.Pixels.GetLength(2);
|
|
|
|
for (uint y = 0; y < height; y++)
|
2024-07-30 20:50:34 +00:00
|
|
|
{
|
2024-08-01 17:23:17 +00:00
|
|
|
for (uint x = 0; x < width; x++)
|
2024-07-30 20:50:34 +00:00
|
|
|
{
|
2024-08-01 17:23:17 +00:00
|
|
|
var rawColour = System.Numerics.Vector4.Zero;
|
|
|
|
for (uint l = 0; l < layers; l++)
|
|
|
|
{
|
|
|
|
rawColour += lightmap.GetPixel(l, x, y);
|
|
|
|
}
|
2024-08-03 15:09:51 +00:00
|
|
|
|
2024-08-03 16:24:04 +00:00
|
|
|
var colour = new Color(MathF.Min(rawColour.X, 1.0f), MathF.Min(rawColour.Y, 1.0f), MathF.Min(rawColour.Z, 1.0f), MathF.Min(rawColour.W, 1.0f));
|
2024-08-01 17:23:17 +00:00
|
|
|
image.SetPixel((int)(rect.X + x), (int)(rect.Y + y), colour);
|
2024-07-30 20:50:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transform UVs
|
2024-08-05 18:49:36 +00:00
|
|
|
var lightmapUvs = surfaceArrays[rect.Id][(int)Mesh.ArrayType.TexUV2].As<Vector2[]>();
|
2024-08-05 17:46:57 +00:00
|
|
|
for (var i = 0; i < lightmapUvs.Length; i++)
|
2024-07-30 20:50:34 +00:00
|
|
|
{
|
2024-08-05 17:46:57 +00:00
|
|
|
var uv = lightmapUvs[i];
|
2024-07-30 20:50:34 +00:00
|
|
|
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;
|
2024-08-05 17:46:57 +00:00
|
|
|
lightmapUvs[i] = new Vector2(u, v);
|
2024-07-30 20:50:34 +00:00
|
|
|
}
|
2024-08-05 18:49:36 +00:00
|
|
|
surfaceArrays[rect.Id][(int)Mesh.ArrayType.TexUV2] = lightmapUvs;
|
2024-07-30 20:50:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-03 16:24:04 +00:00
|
|
|
return image;
|
2024-08-03 16:16:28 +00:00
|
|
|
}
|
|
|
|
|
2024-08-05 18:49:36 +00:00
|
|
|
private List<Godot.Collections.Array> BuildSurfaceArrays(
|
2024-08-05 17:46:57 +00:00
|
|
|
WorldRep.Cell cell,
|
|
|
|
int maxPolyIdx,
|
|
|
|
out PackingRectangle[] packingRects)
|
2024-08-03 16:22:38 +00:00
|
|
|
{
|
2024-08-05 17:46:57 +00:00
|
|
|
packingRects = new PackingRectangle[maxPolyIdx];
|
|
|
|
|
|
|
|
var surfacesArrays = new List<Godot.Collections.Array>();
|
2024-08-03 16:22:38 +00:00
|
|
|
var cellIdxOffset = 0;
|
|
|
|
for (int i = 0; i < maxPolyIdx; i++)
|
|
|
|
{
|
2024-08-05 17:46:57 +00:00
|
|
|
var vertices = new List<Vector3>();
|
|
|
|
var normals = new List<Vector3>();
|
|
|
|
var indices = new List<int>();
|
2024-08-05 18:49:36 +00:00
|
|
|
var textureUvs = new List<Vector2>();
|
|
|
|
var lightmapUvs = new List<Vector2>();
|
2024-08-03 16:22:38 +00:00
|
|
|
|
2024-08-05 17:46:57 +00:00
|
|
|
var poly = cell.Polys[i];
|
2024-08-03 16:22:38 +00:00
|
|
|
var normal = cell.Planes[poly.PlaneId].Normal.ToGodotVec3();
|
|
|
|
var numPolyVertices = poly.VertexCount;
|
|
|
|
for (var j = 0; j < numPolyVertices; j++)
|
|
|
|
{
|
|
|
|
var vertex = cell.Vertices[cell.Indices[cellIdxOffset + j]];
|
|
|
|
vertices.Add(vertex.ToGodotVec3());
|
|
|
|
normals.Add(normal);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simple triangulation. Polys are always convex so we can just do a fan
|
|
|
|
for (int j = 1; j < numPolyVertices - 1; j++)
|
|
|
|
{
|
2024-08-05 17:46:57 +00:00
|
|
|
indices.Add(0);
|
|
|
|
indices.Add(j);
|
|
|
|
indices.Add(j + 1);
|
2024-08-03 16:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UVs
|
|
|
|
var renderPoly = cell.RenderPolys[i];
|
|
|
|
var light = cell.LightList[i];
|
|
|
|
packingRects[i] = new PackingRectangle(0, 0, light.Width, light.Height, i);
|
2024-08-05 18:49:36 +00:00
|
|
|
CalcBaseUV(cell, poly, renderPoly, light, textureUvs, lightmapUvs, cellIdxOffset);
|
2024-08-03 16:22:38 +00:00
|
|
|
|
|
|
|
cellIdxOffset += poly.VertexCount;
|
2024-08-05 17:46:57 +00:00
|
|
|
|
|
|
|
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();
|
2024-08-05 18:49:36 +00:00
|
|
|
array[(int)Mesh.ArrayType.TexUV] = textureUvs.ToArray();
|
|
|
|
array[(int)Mesh.ArrayType.TexUV2] = lightmapUvs.ToArray();
|
2024-08-05 17:46:57 +00:00
|
|
|
surfacesArrays.Add(array);
|
2024-08-03 16:22:38 +00:00
|
|
|
}
|
2024-08-05 17:46:57 +00:00
|
|
|
|
|
|
|
return surfacesArrays;
|
2024-08-03 16:22:38 +00:00
|
|
|
}
|
|
|
|
|
2024-08-05 20:46:03 +00:00
|
|
|
// TODO: This is broke?
|
2024-08-05 17:57:21 +00:00
|
|
|
private static OccluderInstance3D GenerateOccluder(List<Godot.Collections.Array> surfaceArrays)
|
2024-08-03 16:16:28 +00:00
|
|
|
{
|
2024-08-05 17:57:21 +00:00
|
|
|
var vertices = new List<Vector3>();
|
|
|
|
var indices = new List<int>();
|
|
|
|
foreach (var array in surfaceArrays)
|
|
|
|
{
|
|
|
|
var count = vertices.Count;
|
|
|
|
vertices.AddRange(array[(int)Mesh.ArrayType.Vertex].As<Vector3[]>());
|
|
|
|
var surfaceIndices = array[(int)Mesh.ArrayType.Index].As<int[]>();
|
|
|
|
foreach (var idx in surfaceIndices)
|
|
|
|
{
|
|
|
|
indices.Add(count + idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-03 16:16:28 +00:00
|
|
|
var occluder = new ArrayOccluder3D();
|
|
|
|
occluder.SetArrays(vertices.ToArray(), indices.ToArray());
|
|
|
|
var occluderInstance = new OccluderInstance3D
|
|
|
|
{
|
|
|
|
Occluder = occluder,
|
|
|
|
BakeSimplificationDistance = 0.0f
|
|
|
|
};
|
|
|
|
return occluderInstance;
|
|
|
|
}
|
|
|
|
|
2024-08-05 20:46:03 +00:00
|
|
|
private static MeshInstance3D GenerateMesh(List<Godot.Collections.Array> surfaceArrays, List<Material> materials)
|
2024-08-03 16:16:28 +00:00
|
|
|
{
|
2024-07-21 21:13:31 +00:00
|
|
|
var arrMesh = new ArrayMesh();
|
2024-08-05 17:46:57 +00:00
|
|
|
for (var i = 0; i < surfaceArrays.Count; i++)
|
|
|
|
{
|
|
|
|
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArrays[i]);
|
2024-08-05 18:25:44 +00:00
|
|
|
arrMesh.SurfaceSetMaterial(i, materials[i]);
|
2024-08-05 17:46:57 +00:00
|
|
|
}
|
2024-07-22 19:34:12 +00:00
|
|
|
|
2024-07-21 21:13:31 +00:00
|
|
|
var meshInstance = new MeshInstance3D
|
|
|
|
{
|
|
|
|
Mesh = arrMesh,
|
|
|
|
CastShadow = GeometryInstance3D.ShadowCastingSetting.On
|
|
|
|
};
|
2024-08-03 16:16:28 +00:00
|
|
|
return meshInstance;
|
2024-07-21 21:13:31 +00:00
|
|
|
}
|
2024-07-30 20:50:34 +00:00
|
|
|
|
2024-08-05 18:49:36 +00:00
|
|
|
private void CalcBaseUV(
|
2024-07-30 20:50:34 +00:00
|
|
|
WorldRep.Cell cell,
|
|
|
|
WorldRep.Cell.Poly poly,
|
|
|
|
WorldRep.Cell.RenderPoly renderPoly,
|
|
|
|
WorldRep.Cell.LightmapInfo light,
|
2024-08-05 18:49:36 +00:00
|
|
|
List<Vector2> textureUvs,
|
2024-07-30 20:50:34 +00:00
|
|
|
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
|
2024-08-05 18:49:36 +00:00
|
|
|
var textureId = renderPoly.TextureId;
|
|
|
|
// !HACK: Sky textures :)
|
|
|
|
if (textureId >= _textures.Count)
|
|
|
|
{
|
|
|
|
textureId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
var texture = _textures[textureId];
|
2024-08-01 17:29:10 +00:00
|
|
|
var texU = renderPoly.TextureVectors.Item1.ToGodotVec3();
|
|
|
|
var texV = renderPoly.TextureVectors.Item2.ToGodotVec3();
|
2024-08-05 18:49:36 +00:00
|
|
|
var baseU = renderPoly.TextureBases.Item1;
|
|
|
|
var baseV = renderPoly.TextureBases.Item2;
|
2024-07-30 20:50:34 +00:00
|
|
|
|
2024-08-05 18:49:36 +00:00
|
|
|
var txUScale = 64.0f / texture.GetWidth();
|
|
|
|
var txVScale = 64.0f / texture.GetHeight();
|
2024-07-30 20:50:34 +00:00
|
|
|
var lmUScale = 4.0f / light.Width;
|
|
|
|
var lmVScale = 4.0f / light.Height;
|
|
|
|
|
2024-08-05 18:49:36 +00:00
|
|
|
var txUBase = baseU * txUScale;
|
|
|
|
var txVBase = baseV * txVScale;
|
2024-07-30 20:50:34 +00:00
|
|
|
var lmUBase = lmUScale * (baseU + (0.5f - light.Bases.Item1) / 4.0f);
|
|
|
|
var lmVBase = lmVScale * (baseV + (0.5f - light.Bases.Item2) / 4.0f);
|
|
|
|
|
2024-08-05 18:49:36 +00:00
|
|
|
var uu = texU.Dot(texU);
|
|
|
|
var vv = texV.Dot(texV);
|
|
|
|
var uv = texU.Dot(texV);
|
|
|
|
var anchor = cell.Vertices[cell.Indices[cellIdxOffset + 0]].ToGodotVec3(); // TODO: This probably shouldn't be hardcoded idx 0
|
2024-07-30 20:50:34 +00:00
|
|
|
if (uv == 0.0)
|
|
|
|
{
|
2024-08-05 18:49:36 +00:00
|
|
|
var txUVec = texU * txUScale / uu;
|
|
|
|
var txVVec = texV * txVScale / vv;
|
2024-07-30 20:50:34 +00:00
|
|
|
var lmUVec = texU * lmUScale / uu;
|
|
|
|
var lmVVec = texV * lmVScale / vv;
|
|
|
|
for (var i = 0; i < poly.VertexCount; i++)
|
|
|
|
{
|
2024-08-01 17:29:10 +00:00
|
|
|
var v = cell.Vertices[cell.Indices[cellIdxOffset + i]].ToGodotVec3();
|
2024-07-30 20:50:34 +00:00
|
|
|
var delta = new Vector3(v.X - anchor.X, v.Y - anchor.Y, v.Z - anchor.Z);
|
2024-08-05 18:49:36 +00:00
|
|
|
var txUV = new Vector2(delta.Dot(txUVec) + txUBase, delta.Dot(txVVec) + txVBase);
|
2024-07-30 20:50:34 +00:00
|
|
|
var lmUV = new Vector2(delta.Dot(lmUVec) + lmUBase, delta.Dot(lmVVec) + lmVBase);
|
2024-08-05 18:49:36 +00:00
|
|
|
textureUvs.Add(txUV);
|
2024-07-30 20:50:34 +00:00
|
|
|
lightmapUvs.Add(lmUV);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var denom = 1.0f / (uu * vv - uv * uv);
|
2024-08-05 18:49:36 +00:00
|
|
|
|
|
|
|
var txUu = uu * txVScale * denom;
|
|
|
|
var txVv = vv * txUScale * denom;
|
|
|
|
var txUvu = txUScale * denom * uv;
|
|
|
|
var txUvv = txVScale * denom * uv;
|
2024-07-30 20:50:34 +00:00
|
|
|
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++)
|
|
|
|
{
|
2024-08-01 17:29:10 +00:00
|
|
|
var v = cell.Vertices[cell.Indices[cellIdxOffset + i]].ToGodotVec3();
|
2024-07-30 20:50:34 +00:00
|
|
|
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);
|
2024-08-05 18:49:36 +00:00
|
|
|
|
|
|
|
var txUV = new Vector2(txUBase + txVv * du - txUvu * dv, txVBase + txUu * dv - txUvv * du);
|
2024-07-30 20:50:34 +00:00
|
|
|
var lmUV = new Vector2(lmUBase + lmVv * du - lmUvu * dv, lmVBase + lmUu * dv - lmUvv * du);
|
2024-08-05 18:49:36 +00:00
|
|
|
textureUvs.Add(txUV);
|
2024-07-30 20:50:34 +00:00
|
|
|
lightmapUvs.Add(lmUV);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-03 15:09:51 +00:00
|
|
|
|
|
|
|
private void LoadTextures(TxList textureList)
|
|
|
|
{
|
2024-08-07 19:10:45 +00:00
|
|
|
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
|
|
|
|
var dirPaths = Directory.GetDirectories(FileName.GetBaseDir(), "fam", options);
|
|
|
|
var fmFamFiles = Array.Empty<string>();
|
|
|
|
if (!dirPaths.IsEmpty())
|
|
|
|
{
|
|
|
|
fmFamFiles = Directory.GetFiles(dirPaths[0], "*", SearchOption.AllDirectories);
|
|
|
|
}
|
|
|
|
|
2024-08-03 15:09:51 +00:00
|
|
|
// TODO: This has hardcoded .png extension and relies on you placing extracted and converted images in godot user directory
|
2024-08-07 17:40:46 +00:00
|
|
|
// TODO: Use PathJoin
|
2024-08-03 15:09:51 +00:00
|
|
|
var count = textureList.ItemCount;
|
|
|
|
for (var i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
var item = textureList.Items[i];
|
2024-08-07 19:10:45 +00:00
|
|
|
var path = "";
|
2024-08-03 15:09:51 +00:00
|
|
|
for (var j = 0; j < item.Tokens.Length; j++)
|
|
|
|
{
|
|
|
|
var token = item.Tokens[j];
|
|
|
|
if (token == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
path += $"{textureList.Tokens[token - 1]}/";
|
|
|
|
}
|
2024-08-07 19:10:45 +00:00
|
|
|
path += item.Name;
|
2024-08-03 15:09:51 +00:00
|
|
|
|
2024-08-07 19:10:45 +00:00
|
|
|
var foundInFmPath = false;
|
|
|
|
foreach (var famFile in fmFamFiles)
|
2024-08-03 15:09:51 +00:00
|
|
|
{
|
2024-08-07 19:10:45 +00:00
|
|
|
var baseName = famFile.GetBaseName().ToLower();
|
|
|
|
var ext = famFile.GetExtension().ToLower();
|
|
|
|
// TODO: godot supports more than png
|
|
|
|
if (baseName.EndsWith(path.ToLower()) && ext == "png")
|
|
|
|
{
|
|
|
|
foundInFmPath = true;
|
|
|
|
path = famFile;
|
|
|
|
break;
|
|
|
|
}
|
2024-08-03 15:09:51 +00:00
|
|
|
}
|
2024-08-07 19:10:45 +00:00
|
|
|
|
|
|
|
if (!foundInFmPath)
|
2024-08-03 15:09:51 +00:00
|
|
|
{
|
2024-08-07 19:10:45 +00:00
|
|
|
if (File.Exists(ProjectSettings.GlobalizePath($"user://textures/{path}.png")))
|
|
|
|
{
|
|
|
|
path = ProjectSettings.GlobalizePath($"user://textures/{path}.png");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GD.Print($"Failed to find texture: {path}");
|
|
|
|
path = "user://textures/jorge.png";
|
|
|
|
}
|
2024-08-03 15:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Dump) GD.Print($"Loading texture: {path}");
|
2024-08-05 18:25:44 +00:00
|
|
|
_textures.Add(ImageTexture.CreateFromImage(Image.LoadFromFile(path)));
|
2024-08-03 15:09:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void DumpTextureList(TxList textureList)
|
|
|
|
{
|
|
|
|
GD.Print($"TXLIST:\n BlockSize: {textureList.BlockSize}\n ItemCount: {textureList.ItemCount}\n TokenCount: {textureList.TokenCount}\n Tokens:");
|
|
|
|
for (var i = 0; i < textureList.TokenCount; i++)
|
|
|
|
{
|
|
|
|
GD.Print($" {i}: {textureList.Tokens[i]}");
|
|
|
|
}
|
|
|
|
GD.Print($" Items:");
|
|
|
|
for (var i = 0; i < textureList.ItemCount; i++)
|
|
|
|
{
|
|
|
|
var item = textureList.Items[i];
|
|
|
|
GD.Print($" {i}:\n Tokens: [{item.Tokens[0]}, {item.Tokens[1]}, {item.Tokens[2]}, {item.Tokens[3]}]\n Name: {item.Name}");
|
|
|
|
}
|
|
|
|
}
|
2024-07-21 21:13:31 +00:00
|
|
|
}
|