thief-mission-viewer/project/code/TMV/Mission.cs

660 lines
19 KiB
C#
Raw Normal View History

2024-07-21 21:13:31 +00:00
using Godot;
using KeepersCompound.LGS;
2024-07-21 21:13:31 +00:00
using KeepersCompound.LGS.Database;
using KeepersCompound.LGS.Database.Chunks;
2024-08-03 09:24:45 +00:00
using KeepersCompound.TMV.UI;
2024-07-21 21:13:31 +00:00
using System;
using System.Collections.Generic;
using System.IO;
2024-07-21 21:13:31 +00:00
2024-08-11 08:18:02 +00:00
namespace KeepersCompound.TMV;
2024-07-21 21:13:31 +00:00
public partial class Mission : Node3D
{
2024-08-11 11:28:24 +00:00
private readonly struct LightmapRectData
{
public readonly int cellIndex;
public readonly int lightmapIndex;
public readonly int textureId;
public readonly int uvStart;
public readonly int uvEnd;
public LightmapRectData(int cellIndex, int lightmapIndex, int textureId, int uvStart, int uvEnd)
{
this.cellIndex = cellIndex;
this.lightmapIndex = lightmapIndex;
this.textureId = textureId;
this.uvStart = uvStart;
this.uvEnd = uvEnd;
}
}
2024-09-07 07:10:14 +00:00
private const string OBJECT_MODELS_GROUP = "ObjectModels";
2024-07-21 21:13:31 +00:00
[Export(PropertyHint.GlobalFile, "*.mis")]
public string FileName { get; set; }
[Export]
public bool Build = false;
[Export]
public bool Clear = false;
2024-08-03 15:09:51 +00:00
[Export]
public bool Dump = false;
public int LightmapLayers = 33;
2024-07-21 21:13:31 +00:00
string _missionName;
2024-07-21 21:13:31 +00:00
DbFile _file;
2024-08-11 14:23:56 +00:00
TextureLoader _textureLoader;
2024-08-28 06:44:07 +00:00
List<ShaderMaterial> _materials;
Vector2I _lmLayerMask;
2024-07-21 21:13:31 +00:00
public override void _Ready()
{
2024-08-28 06:44:07 +00:00
_materials = new List<ShaderMaterial>();
_lmLayerMask = new Vector2I(~0, ~0);
var lightmapToggler = GetNode<Control>("%LightmapToggler") as LightmapLayerToggler;
lightmapToggler.Setup(this);
2024-09-05 18:44:19 +00:00
var resourceSelector = GetNode<Control>("%ResourceSelector") as ResourceSelector;
resourceSelector.ResourceSelected += (string campaign, string mission) =>
2024-08-03 09:24:45 +00:00
{
_missionName = mission;
2024-09-19 20:16:07 +00:00
var context = Context.Instance;
context.SetCampaign(campaign);
FileName = context.CampaignResources.GetResourcePath(ResourceType.Mission, mission);
2024-08-03 09:24:45 +00:00
Build = true;
};
2024-07-21 21:13:31 +00:00
}
public override void _Process(double delta)
{
if (Build)
{
2024-08-29 18:27:18 +00:00
Timing.Reset();
2024-08-26 14:24:07 +00:00
Timing.TimeStage("Build", () => RebuildMap());
2024-08-29 18:27:18 +00:00
Timing.LogAll();
2024-07-21 21:13:31 +00:00
Build = false;
}
if (Clear)
{
ClearMap();
Clear = false;
}
2024-07-21 21:13:31 +00:00
}
public override void _ShortcutInput(InputEvent @event)
2024-07-21 21:13:31 +00:00
{
if (@event is InputEventKey keyEvent && keyEvent.Pressed)
{
if (keyEvent.Keycode == Key.R)
{
Build = true;
}
2024-09-07 07:10:14 +00:00
if (keyEvent.Keycode == Key.L)
2024-08-28 06:44:07 +00:00
{
ToggleLightmap();
}
2024-09-07 07:10:14 +00:00
if (keyEvent.Keycode == Key.O)
{
ToggleObjectRendering();
}
2024-07-21 21:13:31 +00:00
}
}
public void ClearMap()
2024-07-21 21:13:31 +00:00
{
foreach (var node in GetChildren())
{
node.QueueFree();
}
2024-08-28 06:44:07 +00:00
_materials.Clear();
}
public void RebuildMap()
{
ClearMap();
2024-07-21 21:13:31 +00:00
2024-09-19 20:16:07 +00:00
_textureLoader = new TextureLoader();
2024-08-26 12:25:06 +00:00
Timing.TimeStage("DbFile Parse", () => _file = new(FileName));
Timing.TimeStage("Register Textures", () => UseChunk<TxList>("TXLIST", RegisterTextures));
2024-08-26 12:25:06 +00:00
Timing.TimeStage("Build WR", () => UseChunk<WorldRep>("WREXT", BuildWrMeshes));
if (_file.Chunks.TryGetValue("BRLIST", out var brList))
{
2024-08-26 14:24:07 +00:00
var objHierarchy = Timing.TimeStage("Hierarchy", BuildHierarchy);
Timing.TimeStage("Object Placement", () => PlaceObjects((BrList)brList, objHierarchy));
}
}
2024-09-07 07:10:14 +00:00
public void ToggleObjectRendering()
{
// It might be "better" to use a parent node and just toggle that, but
// the performance of this is completely fine so we'll stick with it
foreach (var node in GetTree().GetNodesInGroup(OBJECT_MODELS_GROUP))
{
var node3d = node as Node3D;
node3d.Visible = !node3d.Visible;
}
}
2024-08-28 06:44:07 +00:00
public void ToggleLightmap()
{
if (_lmLayerMask == Vector2I.Zero)
{
_lmLayerMask = new Vector2I(~0, ~0);
}
else
{
_lmLayerMask = Vector2I.Zero;
}
foreach (var mat in _materials)
{
mat.SetShaderParameter("lightmap_bitmask", _lmLayerMask);
}
}
public void ToggleLmLayer(uint layer)
{
if (layer >= 64)
{
throw new ArgumentOutOfRangeException(nameof(layer));
}
static int ToggleBit(int bitIdx, int val)
{
var mask = 1 << bitIdx;
var isEnabled = (val & mask) != 0;
if (isEnabled)
{
val &= ~mask;
}
else
{
val |= mask;
}
return val;
}
if (layer < 32)
{
_lmLayerMask.X = ToggleBit((int)layer, _lmLayerMask.X);
}
else
{
_lmLayerMask.Y = ToggleBit((int)layer - 32, _lmLayerMask.Y);
}
foreach (var mat in _materials)
{
mat.SetShaderParameter("lightmap_bitmask", _lmLayerMask);
}
}
// TODO: Make this less of a mess
2024-08-26 14:24:07 +00:00
private ObjectHierarchy BuildHierarchy()
{
ObjectHierarchy objHierarchy;
if (_file.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk))
{
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
var name = ((GamFile)gamFileChunk).fileName;
var paths = Directory.GetFiles(FileName.GetBaseDir(), name, options);
if (!paths.IsEmpty())
{
objHierarchy = new ObjectHierarchy(_file, new DbFile(paths[0]));
}
else
{
objHierarchy = new ObjectHierarchy(_file);
}
}
else
{
objHierarchy = new ObjectHierarchy(_file);
}
return objHierarchy;
2024-08-14 17:39:13 +00:00
}
2024-08-03 15:09:51 +00:00
2024-08-14 17:39:13 +00:00
private void UseChunk<T>(string name, Action<T> action)
{
if (_file.Chunks.TryGetValue(name, out var value))
{
action((T)value);
}
else
{
GD.Print($"No chunk found/loaded: {name}");
}
2024-07-21 21:13:31 +00:00
}
private void PlaceObjects(BrList brList, ObjectHierarchy objHierarchy)
{
foreach (var brush in brList.Brushes)
{
if (brush.media != BrList.Brush.Media.Object)
{
continue;
}
var id = (int)brush.brushInfo;
2024-09-07 09:48:30 +00:00
var modelNameProp = objHierarchy.GetProperty<PropLabel>(id, "P$ModelName");
2024-09-07 10:18:40 +00:00
var scaleProp = objHierarchy.GetProperty<PropVector>(id, "P$Scale");
var renderTypeProp = objHierarchy.GetProperty<PropRenderType>(id, "P$RenderTyp");
2024-12-07 13:20:03 +00:00
var jointPosProp = objHierarchy.GetProperty<PropJointPos>(id, "P$JointPos");
2024-08-31 11:00:07 +00:00
var txtRepl0 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr0");
var txtRepl1 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr1");
var txtRepl2 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr2");
var txtRepl3 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr3");
var renderAlpha = objHierarchy.GetProperty<PropFloat>(id, "P$RenderAlp");
var renderMode = renderTypeProp == null ? PropRenderType.Mode.Normal : renderTypeProp.mode;
2024-08-25 10:54:33 +00:00
if (modelNameProp == null || renderMode == PropRenderType.Mode.NotRendered)
{
continue;
}
// Let's try and place an object :)
2024-09-07 09:48:30 +00:00
var modelName = modelNameProp.value + ".bin";
var pos = brush.position.ToGodotVec3();
var rot = brush.angle.ToGodotVec3(false);
2024-09-07 10:18:40 +00:00
var scale = scaleProp == null ? Vector3.One : scaleProp.value.ToGodotVec3(false);
var meshDetails = Timing.TimeStage("Get Models", () => Context.Instance.ModelLoader.Load(modelName));
if (meshDetails.Length != 0)
{
var model = new Node3D();
model.Position = pos;
model.RotationDegrees = rot;
model.Scale = scale;
2024-12-07 13:20:03 +00:00
var joints = jointPosProp != null ? jointPosProp.Positions : [0, 0, 0, 0, 0, 0];
var meshes = ModelLoader.TransformMeshes(joints, meshDetails);
2024-08-31 11:00:07 +00:00
bool GetTextReplPath(PropString prop, out string path)
{
path = "";
if (prop == null)
{
return false;
}
path = prop.value;
2024-09-19 20:16:07 +00:00
var campaignResources = Context.Instance.CampaignResources;
2024-08-31 11:00:07 +00:00
if (path.StartsWith("fam", StringComparison.OrdinalIgnoreCase))
{
var resType = ResourceType.Texture;
2024-09-19 20:16:07 +00:00
path = campaignResources.GetResourcePath(resType, prop.value);
}
else
{
var resType = ResourceType.ObjectTexture;
2024-09-07 07:01:15 +00:00
var convertedValue = ResourcePathManager.ConvertSeparator(prop.value);
var resName = Path.GetFileNameWithoutExtension(convertedValue);
2024-09-19 20:16:07 +00:00
path = campaignResources.GetResourcePath(resType, resName);
2024-08-31 11:00:07 +00:00
}
return path != null;
}
var repls = new PropString[] { txtRepl0, txtRepl1, txtRepl2, txtRepl3 };
foreach (var meshInstance in meshes)
2024-08-31 11:00:07 +00:00
{
for (var i = 0; i < 4; i++)
2024-08-31 11:00:07 +00:00
{
if (GetTextReplPath(repls[i], out var path))
2024-08-31 11:00:07 +00:00
{
var overrideMat = new StandardMaterial3D
{
AlbedoTexture = TextureLoader.LoadTexture(path),
Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass,
};
2024-08-31 11:00:07 +00:00
var surfaceCount = meshInstance.Mesh.GetSurfaceCount();
for (var idx = 0; idx < surfaceCount; idx++)
2024-08-31 11:00:07 +00:00
{
var surfaceMat = meshInstance.Mesh.SurfaceGetMaterial(idx);
if (surfaceMat.HasMeta($"TxtRepl{i}"))
{
meshInstance.SetSurfaceOverrideMaterial(idx, overrideMat);
}
2024-08-31 11:00:07 +00:00
}
}
}
if (renderAlpha != null)
{
meshInstance.Transparency = 1.0f - renderAlpha.value;
}
model.AddChild(meshInstance);
}
2024-09-07 07:10:14 +00:00
model.AddToGroup(OBJECT_MODELS_GROUP);
AddChild(model);
}
}
}
2024-08-14 17:39:13 +00:00
private void BuildWrMeshes(WorldRep worldRep)
2024-07-21 21:13:31 +00:00
{
2024-08-14 17:39:13 +00:00
var cells = worldRep.Cells;
var lmHdr = worldRep.DataHeader.LightmapFormat == 2;
GD.Print($"HDR Lightmap: {lmHdr}");
2024-08-28 20:39:30 +00:00
var packingRects = new List<RectPacker.Rect>();
2024-08-11 11:28:24 +00:00
var surfaceDataMap = new Dictionary<int, MeshSurfaceData>();
var rectDataMap = new Dictionary<int, LightmapRectData>();
2024-07-21 21:13:31 +00:00
2024-08-11 11:28:24 +00:00
for (var cellIdx = 0; cellIdx < cells.Length; cellIdx++)
{
2024-08-11 11:28:24 +00:00
var cell = cells[cellIdx];
var numPolys = cell.PolyCount;
var numRenderPolys = cell.RenderPolyCount;
var numPortalPolys = cell.PortalPolyCount;
// There's nothing to render
if (numRenderPolys == 0 || numPortalPolys >= numPolys)
{
continue;
}
// 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 cellIdxOffset = 0;
for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
{
2024-08-26 15:53:01 +00:00
var lightmapRectData = ProcessCellSurfaceData(surfaceDataMap, worldRep, cellIdx, polyIdx, cellIdxOffset);
2024-08-11 11:28:24 +00:00
rectDataMap.Add(packingRects.Count, lightmapRectData);
2024-08-11 11:28:24 +00:00
var light = cell.LightList[polyIdx];
2024-08-28 20:39:30 +00:00
var rect = new RectPacker.Rect
{
Width = light.Width,
Height = light.Height,
Id = packingRects.Count,
};
2024-08-11 11:28:24 +00:00
packingRects.Add(rect);
2024-08-03 16:22:38 +00:00
2024-08-11 11:28:24 +00:00
cellIdxOffset += cell.Polys[polyIdx].VertexCount;
}
}
2024-08-05 18:25:44 +00:00
2024-08-26 13:03:50 +00:00
var lightmapTexture = Timing.TimeStage("Build Lightmap", () =>
{
return BuildLightmapTexture(cells, packingRects.ToArray(), rectDataMap, surfaceDataMap);
});
2024-08-11 14:43:44 +00:00
foreach (var (textureId, surface) in surfaceDataMap)
2024-08-03 16:24:04 +00:00
{
2024-08-11 11:28:24 +00:00
if (surface.Empty)
2024-08-05 18:25:44 +00:00
{
2024-08-11 11:28:24 +00:00
continue;
2024-08-05 18:25:44 +00:00
}
2024-08-11 14:43:44 +00:00
var albedoTexture = _textureLoader.Get(textureId);
2024-08-28 06:44:07 +00:00
var mat = BuildMaterial(albedoTexture, lightmapTexture, lmHdr, _lmLayerMask);
_materials.Add(mat);
2024-08-11 14:43:44 +00:00
2024-08-11 11:28:24 +00:00
var mesh = new ArrayMesh();
2024-08-11 14:43:44 +00:00
mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surface.BuildSurfaceArray());
2024-08-28 06:44:07 +00:00
mesh.SurfaceSetMaterial(0, mat);
2024-08-11 11:28:24 +00:00
var meshInstance = new MeshInstance3D { Mesh = mesh };
AddChild(meshInstance);
}
}
2024-08-26 15:53:01 +00:00
private LightmapRectData ProcessCellSurfaceData(Dictionary<int, MeshSurfaceData> surfaceDataMap, WorldRep worldRep, int cellIdx, int polyIdx, int indicesOffset)
2024-08-11 11:28:24 +00:00
{
2024-08-26 15:53:01 +00:00
var cell = worldRep.Cells[cellIdx];
2024-08-11 11:28:24 +00:00
var poly = cell.Polys[polyIdx];
var normal = cell.Planes[poly.PlaneId].Normal.ToGodotVec3();
var vertices = new List<Vector3>();
var textureUvs = new List<Vector2>();
var lightmapUvs = new List<Vector2>();
var numPolyVertices = poly.VertexCount;
for (var j = 0; j < numPolyVertices; j++)
{
var vertex = cell.Vertices[cell.Indices[indicesOffset + j]];
vertices.Add(vertex.ToGodotVec3());
2024-08-05 18:25:44 +00:00
}
2024-08-03 16:24:04 +00:00
2024-08-11 11:28:24 +00:00
var renderPoly = cell.RenderPolys[polyIdx];
var light = cell.LightList[polyIdx];
var lightmapScale = worldRep.DataHeader.LightmapScaleMultiplier();
2024-08-26 15:53:01 +00:00
var textureId = CalcBaseUV(cell, poly, renderPoly, light, textureUvs, lightmapUvs, lightmapScale, indicesOffset);
2024-08-11 11:28:24 +00:00
if (!surfaceDataMap.ContainsKey(textureId))
{
2024-08-11 14:43:44 +00:00
surfaceDataMap.Add(textureId, new MeshSurfaceData());
2024-08-11 11:28:24 +00:00
}
var surfaceData = surfaceDataMap[textureId];
var (start, end) = surfaceData.AddPolygon(vertices, normal, textureUvs, lightmapUvs);
2024-08-03 16:24:04 +00:00
2024-08-11 11:28:24 +00:00
if (polyIdx >= cell.Lightmaps.Length) GD.Print("HUH");
2024-08-03 16:24:04 +00:00
2024-08-11 11:28:24 +00:00
return new LightmapRectData(cellIdx, polyIdx, textureId, start, end);
2024-08-03 16:24:04 +00:00
}
private static Texture2DArray BuildLightmapTexture(
WorldRep.Cell[] cells,
2024-08-28 20:39:30 +00:00
RectPacker.Rect[] packingRects,
Dictionary<int, LightmapRectData> rectDataMap,
Dictionary<int, MeshSurfaceData> surfaceDataMap)
2024-08-03 16:24:04 +00:00
{
2024-08-26 13:26:45 +00:00
var bounds = Timing.TimeStage("RectPack", () =>
{
return RectPacker.Pack(packingRects);
2024-08-26 13:26:45 +00:00
});
2024-08-28 20:39:30 +00:00
GD.Print($"Packed {packingRects.Length} rects in ({bounds.Width}, {bounds.Height})");
var lightmapFormat = Image.Format.Rgba8;
var lmLayerCount = 33; // TODO: Use LightmapLayers
var lmImages = new Godot.Collections.Array<Image>();
lmImages.Resize(lmLayerCount);
for (var i = 0; i < lmLayerCount; i++)
{
2024-08-29 15:59:41 +00:00
lmImages[i] = Image.CreateEmpty(bounds.Width, bounds.Height, false, lightmapFormat);
}
2024-07-30 20:50:34 +00:00
foreach (var rect in packingRects)
{
2024-08-11 11:28:24 +00:00
if (!rectDataMap.ContainsKey(rect.Id)) GD.Print("Invalid rectDataMap key");
var info = rectDataMap[rect.Id];
if (info.cellIndex >= cells.Length) GD.Print($"CellIndex too big: {info.cellIndex}/{cells.Length}");
if (info.lightmapIndex >= cells[info.cellIndex].Lightmaps.Length) GD.Print($"LightmapIndex too big: {info.lightmapIndex}/{cells[info.cellIndex].Lightmaps.Length}");
2024-08-11 11:28:24 +00:00
var lightmap = cells[info.cellIndex].Lightmaps[info.lightmapIndex];
var width = lightmap.Width;
var height = lightmap.Height;
var layerCount = lightmap.Layers;
var srcRect = new Rect2I(0, 0, width, height);
2024-08-29 15:59:41 +00:00
var dst = new Vector2I(rect.X, rect.Y);
2024-08-29 18:59:01 +00:00
Timing.TimeStage("Blit LM Data", () =>
{
2024-08-29 18:59:01 +00:00
for (var i = 0; i < layerCount; i++)
{
var bytes = Timing.TimeStage("Get LM Data", () => { return lightmap.AsBytesRgba(i); });
var cellLm = Image.CreateFromData(width, height, false, lightmapFormat, bytes);
lmImages[i].BlitRect(cellLm, srcRect, dst);
}
});
2024-07-30 20:50:34 +00:00
2024-08-29 18:59:01 +00:00
Timing.TimeStage("Transform LM UVs", () =>
2024-07-30 20:50:34 +00:00
{
2024-08-29 18:59:01 +00:00
if (!surfaceDataMap.ContainsKey(info.textureId)) GD.Print("Invalid SurfaceDataMap key");
surfaceDataMap[info.textureId].TransformUv2s(info.uvStart, info.uvEnd, (uv) =>
{
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) / bounds.Width;
v = (rect.Y + rect.Height * v) / bounds.Height;
return new Vector2(u, v);
});
2024-08-11 11:28:24 +00:00
});
}
2024-07-22 19:34:12 +00:00
2024-09-17 16:15:28 +00:00
for (var i = 0; i < lmLayerCount; i++)
{
lmImages[i].GenerateMipmaps();
}
var lightmapTexture = new Texture2DArray();
lightmapTexture.CreateFromImages(lmImages);
return lightmapTexture;
2024-07-21 21:13:31 +00:00
}
2024-07-30 20:50:34 +00:00
2024-08-11 11:28:24 +00:00
private int CalcBaseUV(
2024-07-30 20:50:34 +00:00
WorldRep.Cell cell,
WorldRep.Cell.Poly poly,
WorldRep.Cell.RenderPoly renderPoly,
WorldRep.Cell.LightmapInfo light,
List<Vector2> textureUvs,
2024-07-30 20:50:34 +00:00
List<Vector2> lightmapUvs,
2024-08-26 15:53:01 +00:00
float lightmapScale,
2024-07-30 20:50:34 +00:00
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 textureId = renderPoly.TextureId;
2024-08-29 18:59:01 +00:00
var texture = Timing.TimeStage("Load Textures", () => { return _textureLoader.Get(textureId); });
var texU = renderPoly.TextureVectors.Item1.ToGodotVec3();
var texV = renderPoly.TextureVectors.Item2.ToGodotVec3();
var baseU = renderPoly.TextureBases.Item1;
var baseV = renderPoly.TextureBases.Item2;
2024-07-30 20:50:34 +00:00
2024-08-26 15:53:01 +00:00
var txUScale = 64.0f / lightmapScale / texture.GetWidth();
var txVScale = 64.0f / lightmapScale / texture.GetHeight();
2024-07-30 20:50:34 +00:00
var lmUScale = 4.0f / light.Width;
var lmVScale = 4.0f / light.Height;
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);
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)
{
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++)
{
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 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);
textureUvs.Add(txUV);
2024-07-30 20:50:34 +00:00
lightmapUvs.Add(lmUV);
}
}
else
{
var denom = 1.0f / (uu * vv - uv * uv);
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++)
{
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);
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);
textureUvs.Add(txUV);
2024-07-30 20:50:34 +00:00
lightmapUvs.Add(lmUV);
}
}
2024-08-11 11:28:24 +00:00
return textureId;
2024-07-30 20:50:34 +00:00
}
2024-08-03 15:09:51 +00:00
private void RegisterTextures(TxList textureList)
2024-08-03 15:09:51 +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];
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]}/";
}
path += item.Name;
2024-08-03 15:09:51 +00:00
if (!_textureLoader.Register(i, path))
2024-08-03 15:09:51 +00:00
{
GD.Print($"Failed to register texture: {path}");
2024-08-03 15:09:51 +00:00
}
}
2024-08-14 17:39:13 +00:00
if (Dump) DumpTextureList(textureList);
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-08-11 14:43:44 +00:00
const string MATERIAL_PATH = "res://project/materials/base.tres";
2024-08-28 06:44:07 +00:00
private static ShaderMaterial BuildMaterial(
Texture2D albedoTexture,
Texture2DArray lightmapTexture,
bool lmHdr,
Vector2I layerMask)
2024-08-11 14:43:44 +00:00
{
var material = ResourceLoader.Load<ShaderMaterial>(MATERIAL_PATH).Duplicate() as ShaderMaterial;
material.SetShaderParameter("texture_albedo", albedoTexture);
material.SetShaderParameter("lightmap_albedo", lightmapTexture);
material.SetShaderParameter("lightmap_2x", lmHdr);
2024-08-28 06:44:07 +00:00
material.SetShaderParameter("lightmap_bitmask", layerMask);
2024-08-11 14:43:44 +00:00
return material;
}
2024-07-21 21:13:31 +00:00
}