Compare commits

...

4 Commits

Author SHA1 Message Date
Jarrod Doyle 8bd2e53d7e
Update to Godot 4.4.1 2025-04-11 16:31:14 +01:00
Jarrod Doyle 1177474fd6
Apply subobj hierarchy to model 2024-12-23 11:44:13 +00:00
Jarrod Doyle 202edd424b
Fix extraction issue 2024-12-23 11:43:33 +00:00
Jarrod Doyle f924470e8d
Port LGS changes from lightmapper 2024-12-22 08:50:45 +00:00
48 changed files with 159 additions and 66 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.3.0">
<Project Sdk="Godot.NET.Sdk/4.4.1">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@ -12,7 +12,7 @@ config_version=5
config/name="Thief Mission Viewer"
run/main_scene="res://project/scenes/main.tscn"
config/features=PackedStringArray("4.3", "C#", "Forward Plus")
config/features=PackedStringArray("4.4", "C#", "Forward Plus")
config/icon="res://icon.svg"
[autoload]

View File

@ -0,0 +1 @@
uid://ddyj2xlmr0rfe

View File

@ -0,0 +1 @@
uid://b8n4k0ssnlvu2

View File

@ -0,0 +1 @@
uid://b1bo0tyeqog6h

View File

@ -0,0 +1 @@
uid://64d8uwu3n6hq

View File

@ -0,0 +1 @@
uid://biy323fj0ke5v

View File

@ -0,0 +1 @@
uid://bn66ccq4fqwqm

View File

@ -0,0 +1 @@
uid://dpjlv5b4wrwq

View File

@ -0,0 +1 @@
uid://cngbjlen8cwhu

View File

@ -0,0 +1 @@
uid://cjb2tw7hxompq

View File

@ -17,7 +17,7 @@ public class RendParams : IChunk
public string palette;
public Vector3 ambientLight;
public int useSunlight;
public bool useSunlight;
public SunlightMode sunlightMode;
public Vector3 sunlightDirection;
public float sunlightHue;
@ -32,7 +32,8 @@ public class RendParams : IChunk
{
palette = reader.ReadNullString(16);
ambientLight = reader.ReadVec3();
useSunlight = reader.ReadInt32();
useSunlight = reader.ReadBoolean();
reader.ReadBytes(3);
sunlightMode = (SunlightMode)reader.ReadUInt32();
sunlightDirection = reader.ReadVec3();
sunlightHue = reader.ReadSingle();
@ -59,6 +60,7 @@ public class RendParams : IChunk
writer.WriteNullString(palette, 16);
writer.WriteVec3(ambientLight);
writer.Write(useSunlight);
writer.Write(new byte[3]);
writer.Write((uint)sunlightMode);
writer.WriteVec3(sunlightDirection);
writer.Write(sunlightHue);

View File

@ -0,0 +1 @@
uid://c2byqoq1n4dhj

View File

@ -0,0 +1 @@
uid://dhaj07j75kai8

View File

@ -246,12 +246,30 @@ public class WorldRep : IChunk
// TODO: This ONLY works for rgba (bpp = 4)!!!
public void AddLight(int layer, int x, int y, float r, float g, float b)
{
ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, nameof(layer));
ArgumentOutOfRangeException.ThrowIfGreaterThan(layer, Layers, nameof(layer));
var idx = (x + y * Width) * Bpp;
var pLayer = Pixels[layer];
pLayer[idx] = (byte)Math.Clamp(pLayer[idx] + r, 0, 255);
pLayer[idx + 1] = (byte)Math.Clamp(pLayer[idx + 1] + g, 0, 255);
pLayer[idx + 2] = (byte)Math.Clamp(pLayer[idx + 2] + b, 0, 255);
pLayer[idx + 3] = 255;
switch (Bpp)
{
// 1bpp isn't really supported (does nd dromed even produce it?)
case 2:
var raw2 = pLayer[idx] + (pLayer[idx + 1] << 8);
var newR = (int)Math.Clamp((raw2 & 31) + r * 32f / 255f, 0, 31);
var newG = (int)Math.Clamp(((raw2 >> 5) & 31) + g * 32f / 255f, 0, 31);
var newB = (int)Math.Clamp(((raw2 >> 10) & 31) + b * 32f / 255f, 0, 31);
raw2 = newR + (newG << 5) + (newB << 10);
pLayer[idx] = (byte)(raw2 & 0xff);
pLayer[idx + 1] = (byte)((raw2 >> 8) & 0xff);
break;
case 4:
pLayer[idx] = (byte)Math.Clamp(pLayer[idx] + b, 0, 255);
pLayer[idx + 1] = (byte)Math.Clamp(pLayer[idx + 1] + g, 0, 255);
pLayer[idx + 2] = (byte)Math.Clamp(pLayer[idx + 2] + r, 0, 255);
pLayer[idx + 3] = 255;
break;
}
_litLayers[layer] = true;
}
@ -274,7 +292,7 @@ public class WorldRep : IChunk
c /= ratio;
}
AddLight(layer, x, y, c.Z, c.Y, c.X);
AddLight(layer, x, y, c.X, c.Y, c.Z);
}
public void Reset(Vector3 ambientLight, bool hdr)

View File

@ -0,0 +1 @@
uid://bgrwq2pe10awh

View File

@ -159,6 +159,8 @@ public class DbFile
"P$Scale" => new PropertyChunk<PropVector>(),
"P$RenderTyp" => new PropertyChunk<PropRenderType>(),
"P$JointPos" => new PropertyChunk<PropJointPos>(),
"P$Immobile" => new PropertyChunk<PropBool>(),
"P$StatShad" => new PropertyChunk<PropBool>(),
"P$OTxtRepr0" => new PropertyChunk<PropString>(),
"P$OTxtRepr1" => new PropertyChunk<PropString>(),
"P$OTxtRepr2" => new PropertyChunk<PropString>(),

View File

@ -0,0 +1 @@
uid://b4qbr6xsdhvgn

View File

@ -94,6 +94,8 @@ public class ObjectHierarchy
AddProp<PropVector>("P$Scale");
AddProp<PropRenderType>("P$RenderTyp");
AddProp<PropJointPos>("P$JointPos");
AddProp<PropBool>("P$Immobile");
AddProp<PropBool>("P$StatShad");
AddProp<PropString>("P$OTxtRepr0");
AddProp<PropString>("P$OTxtRepr1");
AddProp<PropString>("P$OTxtRepr2");

View File

@ -0,0 +1 @@
uid://fxq8jvky04oh

View File

@ -0,0 +1 @@
uid://cn6fjsjh2ne1c

View File

@ -0,0 +1 @@
uid://dnm5qcj36s51w

View File

@ -0,0 +1 @@
uid://bfv1v2tgj21sq

View File

@ -151,38 +151,44 @@ public class ResourcePathManager
throw new InvalidOperationException("Failed to find all installation config paths.");
}
// Get the paths of the base Fam and Obj resources so we can extract them.
// We need to know where all the texture and object resources are
var installCfgLines = File.ReadAllLines(configPaths[(int)ConfigFile.Install]);
FindConfigVar(installCfgLines, "resname_base", out var resPaths);
var baseFamPath = "";
var baseObjPath = "";
if (!FindConfigVar(installCfgLines, "resname_base", out var resPaths))
{
throw new InvalidOperationException("Failed to find resnames in install config");
}
var zipPaths = new List<string>();
foreach (var resPath in resPaths.Split('+'))
{
var dir = Path.Join(installPath, ConvertSeparator(resPath));
if (!Directory.Exists(dir))
{
continue;
}
foreach (var path in Directory.GetFiles(dir))
{
var name = Path.GetFileName(path).ToLower();
if (name == "fam.crf" && baseFamPath == "")
if (name is "fam.crf" or "obj.crf")
{
baseFamPath = path;
}
else if (name == "obj.crf" && baseObjPath == "")
{
baseObjPath = path;
zipPaths.Add(path);
}
}
}
// Do the extraction bro
(string, string)[] resources = [("fam", baseFamPath), ("obj", baseObjPath)];
foreach (var (extractName, zipPath) in resources)
// The path order is a priority order, so we don't want to overwrite any files when extracting
// TODO: Check if there's any problems caused by case sensitivity
if (Directory.Exists(_extractionPath))
{
var extractPath = Path.Join(_extractionPath, extractName);
if (Directory.Exists(extractPath))
{
Directory.Delete(extractPath, true);
}
ZipFile.OpenRead(zipPath).ExtractToDirectory(extractPath);
Directory.Delete(_extractionPath, true);
}
foreach (var zipPath in zipPaths)
{
var resType = Path.GetFileNameWithoutExtension(zipPath);
var extractPath = Path.Join(_extractionPath, resType);
ZipFile.OpenRead(zipPath).ExtractToDirectory(extractPath, false);
}
FindConfigVar(installCfgLines, "load_path", out var omsPath);

View File

@ -0,0 +1 @@
uid://do02v3prhultx

View File

@ -0,0 +1 @@
uid://8slua3vhscjw

View File

@ -0,0 +1 @@
uid://cbko1cr0u6nu4

View File

@ -265,14 +265,12 @@ public partial class Mission : Node3D
var meshDetails = Timing.TimeStage("Get Models", () => Context.Instance.ModelLoader.Load(modelName));
if (meshDetails.Length != 0)
{
var model = new Node3D();
var joints = jointPosProp != null ? jointPosProp.Positions : [0, 0, 0, 0, 0, 0];
var model = ModelLoader.TransformMeshes(joints, meshDetails);
model.Position = pos;
model.RotationDegrees = rot;
model.Scale = scale;
var joints = jointPosProp != null ? jointPosProp.Positions : [0, 0, 0, 0, 0, 0];
var meshes = ModelLoader.TransformMeshes(joints, meshDetails);
bool GetTextReplPath(PropString prop, out string path)
{
path = "";
@ -301,26 +299,29 @@ public partial class Mission : Node3D
}
var repls = new PropString[] { txtRepl0, txtRepl1, txtRepl2, txtRepl3 };
foreach (var meshInstance in meshes)
foreach (var child in model.FindChildren("*", "MeshInstance3D", true, false))
{
var meshInstance = child as MeshInstance3D;
for (var i = 0; i < 4; i++)
{
if (GetTextReplPath(repls[i], out var path))
if (!GetTextReplPath(repls[i], out var path))
{
var overrideMat = new StandardMaterial3D
{
AlbedoTexture = TextureLoader.LoadTexture(path),
Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass,
};
continue;
}
var surfaceCount = meshInstance.Mesh.GetSurfaceCount();
for (var idx = 0; idx < surfaceCount; idx++)
var overrideMat = new StandardMaterial3D
{
AlbedoTexture = TextureLoader.LoadTexture(path),
Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass,
};
var surfaceCount = meshInstance.Mesh.GetSurfaceCount();
for (var idx = 0; idx < surfaceCount; idx++)
{
var surfaceMat = meshInstance.Mesh.SurfaceGetMaterial(idx);
if (surfaceMat.HasMeta($"TxtRepl{i}"))
{
var surfaceMat = meshInstance.Mesh.SurfaceGetMaterial(idx);
if (surfaceMat.HasMeta($"TxtRepl{i}"))
{
meshInstance.SetSurfaceOverrideMaterial(idx, overrideMat);
}
meshInstance.SetSurfaceOverrideMaterial(idx, overrideMat);
}
}
}
@ -329,8 +330,6 @@ public partial class Mission : Node3D
{
meshInstance.Transparency = 1.0f - renderAlpha.value;
}
model.AddChild(meshInstance);
}
model.AddToGroup(OBJECT_MODELS_GROUP);

View File

@ -0,0 +1 @@
uid://ci24h68n7606n

View File

@ -19,13 +19,8 @@ public partial class Model : Node3D
}
Context.Instance.SetCampaign(campaignName);
var model = new Node3D();
var meshDetails = Context.Instance.ModelLoader.Load(modelPath);
var meshes = ModelLoader.TransformMeshes([0, 0, 0, 0, 0, 0], meshDetails);
foreach (var meshInstance in meshes)
{
model.AddChild(meshInstance);
}
var model = ModelLoader.TransformMeshes([0, 0, 0, 0, 0, 0], meshDetails);
AddChild(model);
}
}

View File

@ -0,0 +1 @@
uid://6v4368eednxc

View File

@ -8,11 +8,12 @@ namespace KeepersCompound.TMV;
// TODO: Work out a way to share base game models again in the cache
public class ModelLoader
{
public struct MeshDetails(int jointIdx, Transform3D transform, MeshInstance3D mesh)
public struct MeshDetails(int jointIdx, Transform3D transform, MeshInstance3D mesh, int parentIdx)
{
public readonly int JointIdx = jointIdx;
public readonly Transform3D Transform = transform;
public readonly MeshInstance3D Mesh = mesh;
public int ParentIdx = parentIdx;
}
private readonly Dictionary<(string, string), MeshDetails[]> _cache = new();
@ -40,15 +41,20 @@ public class ModelLoader
return details;
}
public static MeshInstance3D[] TransformMeshes(float[] joints, MeshDetails[] meshDetails)
public static Node3D TransformMeshes(float[] joints, MeshDetails[] meshDetails)
{
for (var i = 0; i < joints.Length; i++)
{
joints[i] = float.DegreesToRadians(joints[i]);
}
var meshes = new List<MeshInstance3D>();
foreach (var details in meshDetails)
{
var mesh = details.Mesh.Duplicate() as MeshInstance3D;
if (details.JointIdx != -1)
{
var ang = float.DegreesToRadians(joints[details.JointIdx]);
var ang = joints[details.JointIdx];
var r1 = new Quaternion(new Vector3(0, 0, 1), ang);
var r2 = details.Transform.Basis.GetRotationQuaternion();
var basis = new Basis(r2 * r1);
@ -62,7 +68,22 @@ public class ModelLoader
meshes.Add(mesh);
}
return [..meshes];
var root = new Node3D();
for (var i = 0; i < meshes.Count; i++)
{
var details = meshDetails[i];
var parentId = details.ParentIdx;
if (parentId == -1)
{
root.AddChild(meshes[i]);
}
else
{
meshes[parentId].AddChild(meshes[i]);
}
}
return root;
}
public static MeshDetails[] LoadModel(string modelName)
@ -186,7 +207,19 @@ public class ModelLoader
var pos = -modelFile.Header.Center.ToGodotVec3();
var meshInstance = new MeshInstance3D { Mesh = mesh, Position = pos };
meshDetails[i] = new MeshDetails(jointIdx, transform, meshInstance);
meshDetails[i] = new MeshDetails(jointIdx, transform, meshInstance, -1);
}
// Build parentage
for (var i = 0; i < objCount; i++)
{
var subObj = modelFile.Objects[i];
var childIdx = subObj.Child;
while (childIdx != -1)
{
meshDetails[childIdx].ParentIdx = i;
childIdx = modelFile.Objects[childIdx].Next;
}
}
return meshDetails;

View File

@ -0,0 +1 @@
uid://c8dcucbcfsnd0

View File

@ -0,0 +1 @@
uid://wwf2ie0kv0kp

View File

@ -0,0 +1 @@
uid://dfvg4cc3huaai

View File

@ -0,0 +1 @@
uid://cmt5nfysu7p6h

View File

@ -0,0 +1 @@
uid://ndn3nkupjo76

View File

@ -0,0 +1 @@
uid://bul3d5u1jbg3g

View File

@ -0,0 +1 @@
uid://beoujsbxyth70

View File

@ -0,0 +1 @@
uid://cbsjt5rmif150

View File

@ -0,0 +1 @@
uid://c8c7y5qlasj1x

View File

@ -0,0 +1 @@
uid://dy6yk7onoihyd

View File

@ -0,0 +1 @@
uid://bghumseqcj7g2

View File

@ -0,0 +1 @@
uid://c032d00skt5fu

View File

@ -1,8 +1,8 @@
[gd_scene load_steps=9 format=3 uid="uid://byknmqac1a5vn"]
[ext_resource type="Texture2D" uid="uid://b208ufsau5jhb" path="res://project/jorge.png" id="1_1vlw2"]
[ext_resource type="Script" path="res://project/code/TMV/UI/AssetBrowser.cs" id="1_5rr8c"]
[ext_resource type="Script" path="res://project/code/TMV/UI/TextureBrowser.cs" id="1_72xft"]
[ext_resource type="Script" uid="uid://beoujsbxyth70" path="res://project/code/TMV/UI/AssetBrowser.cs" id="1_5rr8c"]
[ext_resource type="Script" uid="uid://dy6yk7onoihyd" path="res://project/code/TMV/UI/TextureBrowser.cs" id="1_72xft"]
[ext_resource type="Texture2D" uid="uid://beb4tj06ivjae" path="res://project/assets/icons/Search.svg" id="1_ityvd"]
[ext_resource type="Texture2D" uid="uid://dx8paqeom7dtb" path="res://project/assets/icons/Sort.svg" id="3_0k1fm"]
[ext_resource type="Texture2D" uid="uid://dqefy830o4foj" path="res://project/assets/icons/Folder.svg" id="3_xdjfn"]
@ -82,6 +82,7 @@ item_count = 4
popup/item_0/text = "Name"
popup/item_0/checkable = 2
popup/item_0/checked = true
popup/item_0/id = 0
popup/item_1/text = "Family"
popup/item_1/checkable = 2
popup/item_1/id = 1
@ -228,6 +229,7 @@ size_flags_horizontal = 3
selected = 0
item_count = 2
popup/item_0/text = "Linear"
popup/item_0/id = 0
popup/item_1/text = "Nearest"
popup/item_1/id = 1
@ -241,6 +243,7 @@ size_flags_horizontal = 3
selected = 0
item_count = 2
popup/item_0/text = "Scaled"
popup/item_0/id = 0
popup/item_1/text = "Tiled"
popup/item_1/id = 1

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=6 format=3 uid="uid://boxi211q3kx6c"]
[ext_resource type="Script" path="res://project/code/TMV/Mission.cs" id="1_3gnqe"]
[ext_resource type="Script" path="res://project/code/camera.gd" id="2_w5otl"]
[ext_resource type="Script" uid="uid://ci24h68n7606n" path="res://project/code/TMV/Mission.cs" id="1_3gnqe"]
[ext_resource type="Script" uid="uid://c032d00skt5fu" path="res://project/code/camera.gd" id="2_w5otl"]
[ext_resource type="PackedScene" uid="uid://bfxdpxkcgwlkx" path="res://project/scenes/ui/resource_selector.tscn" id="3_kdn7u"]
[ext_resource type="PackedScene" uid="uid://0h2w7w84vbea" path="res://project/scenes/ui/lightmap_layer_toggler.tscn" id="4_naip8"]
@ -13,7 +13,6 @@ ssao_enabled = true
[node name="Main" type="Node3D"]
[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_3gnqe")
[node name="Camera3D" type="Camera3D" parent="."]

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=5 format=3 uid="uid://iegbijrr5amb"]
[ext_resource type="Script" path="res://project/code/TMV/Model.cs" id="1_dax7s"]
[ext_resource type="Script" path="res://project/code/camera.gd" id="2_ov7rc"]
[ext_resource type="Script" uid="uid://6v4368eednxc" path="res://project/code/TMV/Model.cs" id="1_dax7s"]
[ext_resource type="Script" uid="uid://c032d00skt5fu" path="res://project/code/camera.gd" id="2_ov7rc"]
[ext_resource type="PackedScene" uid="uid://bfxdpxkcgwlkx" path="res://project/scenes/ui/resource_selector.tscn" id="3_yp5sl"]
[sub_resource type="Environment" id="Environment_e4172"]

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=3 format=3 uid="uid://bfxdpxkcgwlkx"]
[ext_resource type="Script" path="res://project/code/TMV/UI/ResourceSelector.cs" id="1_4lx6q"]
[ext_resource type="Script" uid="uid://c8c7y5qlasj1x" path="res://project/code/TMV/UI/ResourceSelector.cs" id="1_4lx6q"]
[sub_resource type="LabelSettings" id="LabelSettings_4v24o"]
font_size = 20