From 3ee172f16c0303b64ef52798c006e28108a17cf1 Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Fri, 29 Nov 2024 11:41:38 +0000 Subject: [PATCH] Update LGS lib (from lightmapper --- project/code/LGS/Database/Chunks/BrList.cs | 43 +- project/code/LGS/Database/Chunks/GamFile.cs | 4 +- project/code/LGS/Database/Chunks/Link.cs | 31 +- project/code/LGS/Database/Chunks/LmParams.cs | 75 +++ project/code/LGS/Database/Chunks/Property.cs | 244 +++++++++- .../code/LGS/Database/Chunks/RendParams.cs | 80 ++++ project/code/LGS/Database/Chunks/TxList.cs | 21 +- project/code/LGS/Database/Chunks/WorldRep.cs | 449 ++++++++++++++++-- project/code/LGS/Database/File.cs | 76 ++- project/code/LGS/Database/ObjectHierarchy.cs | 38 +- project/code/LGS/Extensions.cs | 30 ++ project/code/LGS/ModelFile.cs | 46 +- .../scenes/asset_browser/asset_browser.tscn | 8 +- project/scenes/main.tscn | 5 +- 14 files changed, 1060 insertions(+), 90 deletions(-) create mode 100644 project/code/LGS/Database/Chunks/LmParams.cs create mode 100644 project/code/LGS/Database/Chunks/RendParams.cs diff --git a/project/code/LGS/Database/Chunks/BrList.cs b/project/code/LGS/Database/Chunks/BrList.cs index 5d65316..eaa7038 100644 --- a/project/code/LGS/Database/Chunks/BrList.cs +++ b/project/code/LGS/Database/Chunks/BrList.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Numerics; -using System.Text; namespace KeepersCompound.LGS.Database.Chunks; @@ -46,6 +45,15 @@ public class BrList : IChunk x = reader.ReadUInt16(); y = reader.ReadUInt16(); } + + public void Write(BinaryWriter writer) + { + writer.Write(id); + writer.Write(rot); + writer.Write(scale); + writer.Write(x); + writer.Write(y); + } }; public short id; @@ -104,6 +112,34 @@ public class BrList : IChunk txs = Array.Empty(); } } + + public void Write(BinaryWriter writer) + { + writer.Write(id); + writer.Write(time); + writer.Write(brushInfo); + writer.Write(textureId); + writer.Write((byte)media); + writer.Write(flags); + writer.WriteVec3(position); + writer.WriteVec3(size); + writer.WriteRotation(angle); + writer.Write(currentFaceIndex); + writer.Write(gridLineSpacing); + writer.WriteVec3(gridPhaseShift); + writer.WriteRotation(gridOrientation); + writer.Write(gridEnabled); + writer.Write(numFaces); + writer.Write(edgeSelected); + writer.Write(pointSelected); + writer.Write(useFlag); + writer.Write(groupId); + writer.Write(new byte[4]); + foreach (var info in txs) + { + info.Write(writer); + } + } } public ChunkHeader Header { get; set; } @@ -120,6 +156,9 @@ public class BrList : IChunk public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + foreach (var brush in Brushes) + { + brush.Write(writer); + } } } \ No newline at end of file diff --git a/project/code/LGS/Database/Chunks/GamFile.cs b/project/code/LGS/Database/Chunks/GamFile.cs index 9d6e9aa..150e455 100644 --- a/project/code/LGS/Database/Chunks/GamFile.cs +++ b/project/code/LGS/Database/Chunks/GamFile.cs @@ -1,6 +1,4 @@ -using System; using System.IO; -using System.Text; namespace KeepersCompound.LGS.Database.Chunks; @@ -16,6 +14,6 @@ public class GamFile : IChunk public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + writer.WriteNullString(fileName, 256); } } \ No newline at end of file diff --git a/project/code/LGS/Database/Chunks/Link.cs b/project/code/LGS/Database/Chunks/Link.cs index b079e64..d8108f1 100644 --- a/project/code/LGS/Database/Chunks/Link.cs +++ b/project/code/LGS/Database/Chunks/Link.cs @@ -1,4 +1,3 @@ - using System.Collections.Generic; using System.IO; @@ -32,6 +31,11 @@ public record LinkId { return _data; } + + public void Write(BinaryWriter writer) + { + writer.Write(_data); + } } public class LinkChunk : IChunk, IMergable @@ -50,6 +54,14 @@ public class LinkChunk : IChunk, IMergable destination = reader.ReadInt32(); relation = reader.ReadUInt16(); } + + public void Write(BinaryWriter writer) + { + linkId.Write(writer); + writer.Write(source); + writer.Write(destination); + writer.Write(relation); + } } public ChunkHeader Header { get; set; } @@ -66,7 +78,10 @@ public class LinkChunk : IChunk, IMergable public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + foreach (var link in links) + { + link.Write(writer); + } } public void Merge(IMergable other) @@ -88,6 +103,12 @@ public class LinkDataMetaProp : IChunk, IMergable linkId = new LinkId(reader.ReadUInt32()); priority = reader.ReadInt32(); } + + public void Write(BinaryWriter writer) + { + linkId.Write(writer); + writer.Write(priority); + } } public ChunkHeader Header { get; set; } @@ -106,7 +127,11 @@ public class LinkDataMetaProp : IChunk, IMergable public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + writer.Write(DataSize); + foreach (var data in linkData) + { + data.Write(writer); + } } public void Merge(IMergable other) diff --git a/project/code/LGS/Database/Chunks/LmParams.cs b/project/code/LGS/Database/Chunks/LmParams.cs new file mode 100644 index 0000000..8cfe322 --- /dev/null +++ b/project/code/LGS/Database/Chunks/LmParams.cs @@ -0,0 +1,75 @@ +using System.IO; + +namespace KeepersCompound.LGS.Database.Chunks; + +public enum SoftnessMode +{ + Standard, + HighFourPoint, + HighFivePoint, + HighNinePoint, + MediumFourPoint, + MediumFivePoint, + MediumNinePoint, + LowFourPoint, +} + +public class LmParams : IChunk +{ + public enum LightingMode + { + Quick, + Raycast, + Objcast, + } + + public enum DepthMode + { + Lm16, + Lm32, + Lm32x, + } + + public ChunkHeader Header { get; set; } + public float Attenuation { get; set; } + public float Saturation { get; set; } + public LightingMode ShadowType { get; set; } + public SoftnessMode ShadowSoftness { get; set; } + public float CenterWeight { get; set; } + public DepthMode ShadowDepth { get; set; } + public bool LightmappedWater { get; set; } + public int LightmapScale { get; set; } + + private int _dataSize; + private uint _unknown; + + public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry) + { + _dataSize = reader.ReadInt32(); + Attenuation = reader.ReadSingle(); + Saturation = reader.ReadSingle(); + ShadowType = (LightingMode)reader.ReadUInt32(); + ShadowSoftness = (SoftnessMode)reader.ReadUInt32(); + CenterWeight = reader.ReadSingle(); + ShadowDepth = (DepthMode)reader.ReadUInt32(); + LightmappedWater = reader.ReadBoolean(); + reader.ReadBytes(3); + LightmapScale = reader.ReadInt32(); + _unknown = reader.ReadUInt32(); + } + + public void WriteData(BinaryWriter writer) + { + writer.Write(_dataSize); + writer.Write(Attenuation); + writer.Write(Saturation); + writer.Write((uint)ShadowType); + writer.Write((uint)ShadowSoftness); + writer.Write(CenterWeight); + writer.Write((uint)ShadowDepth); + writer.Write(LightmappedWater); + writer.Write(new byte[3]); + writer.Write(LightmapScale); + writer.Write(_unknown); + } +} \ No newline at end of file diff --git a/project/code/LGS/Database/Chunks/Property.cs b/project/code/LGS/Database/Chunks/Property.cs index 56aa7d8..37c0596 100644 --- a/project/code/LGS/Database/Chunks/Property.cs +++ b/project/code/LGS/Database/Chunks/Property.cs @@ -14,6 +14,12 @@ public class Property objectId = reader.ReadInt32(); length = (int)reader.ReadUInt32(); } + + public virtual void Write(BinaryWriter writer) + { + writer.Write(objectId); + writer.Write((uint)length); + } } public class PropertyChunk : IChunk, IMergable where T : Property, new() @@ -34,7 +40,10 @@ public class PropertyChunk : IChunk, IMergable where T : Property, new() public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + foreach (var prop in properties) + { + prop.Write(writer); + } } public void Merge(IMergable other) @@ -52,6 +61,12 @@ public class PropGeneric : Property base.Read(reader); data = reader.ReadBytes(length); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(data); + } } public class PropBool : Property @@ -63,6 +78,12 @@ public class PropBool : Property base.Read(reader); value = reader.ReadInt32() != 0; } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(value ? 1 : 0); + } } public class PropInt : Property @@ -74,6 +95,12 @@ public class PropInt : Property base.Read(reader); value = reader.ReadInt32(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(value); + } } public class PropLabel : Property @@ -85,6 +112,12 @@ public class PropLabel : Property base.Read(reader); value = reader.ReadNullString(length); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.WriteNullString(value, length); + } } public class PropString : Property @@ -98,6 +131,13 @@ public class PropString : Property stringLength = reader.ReadInt32(); value = reader.ReadNullString(stringLength); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(stringLength); + writer.WriteNullString(value, stringLength); + } } public class PropFloat : Property @@ -109,6 +149,12 @@ public class PropFloat : Property base.Read(reader); value = reader.ReadSingle(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(value); + } } public class PropVector : Property @@ -120,6 +166,12 @@ public class PropVector : Property base.Read(reader); value = reader.ReadVec3(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.WriteVec3(value); + } } public class PropRenderType : Property @@ -140,6 +192,12 @@ public class PropRenderType : Property base.Read(reader); mode = (Mode)reader.ReadUInt32(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write((uint)mode); + } } public class PropSlayResult : Property @@ -156,6 +214,12 @@ public class PropSlayResult : Property base.Read(reader); effect = (Effect)reader.ReadUInt32(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write((uint)effect); + } } public class PropInventoryType : Property @@ -172,6 +236,12 @@ public class PropInventoryType : Property base.Read(reader); type = (Slot)reader.ReadUInt32(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write((uint)type); + } } public class PropCollisionType : Property @@ -194,6 +264,19 @@ public class PropCollisionType : Property NoResult = (flags & (0x1 << 4)) != 0; FullCollisionSound = (flags & (0x1 << 5)) != 0; } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + var flags = 0u; + if (Bounce) flags += 1; + if (DestroyOnImpact) flags += 2; + if (SlayOnImpact) flags += 4; + if (NoCollisionSound) flags += 8; + if (NoResult) flags += 16; + if (FullCollisionSound) flags += 32; + writer.Write(flags); + } } public class PropPosition : Property @@ -208,6 +291,14 @@ public class PropPosition : Property reader.ReadBytes(4); // Runtime Cell/Hint in editor Rotation = reader.ReadRotation(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.WriteVec3(Location); + writer.Write(new byte[4]); + writer.WriteRotation(Rotation); + } } public class PropLight : Property @@ -228,6 +319,123 @@ public class PropLight : Property reader.ReadBytes(3); InnerRadius = reader.ReadSingle(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(Brightness); + writer.WriteVec3(Offset); + writer.Write(Radius); + writer.Write(QuadLit); + writer.Write(new byte[3]); + writer.Write(InnerRadius); + } +} + +public class PropAnimLight : Property +{ + public enum AnimMode + { + FlipMinMax, + SlideSmoothly, + Random, + MinBrightness, + MaxBrightness, + ZeroBrightness, + SmoothlyBrighten, + SmoothlyDim, + RandomButCoherent, + FlickerMinMax, + } + + // Standard light props + public float Brightness; + public Vector3 Offset; + public float Radius; + public float InnerRadius; + public bool QuadLit; + public bool Dynamic; + + // Animation + public AnimMode Mode; + public int MsToBrighten; + public int MsToDim; + public float MinBrightness; + public float MaxBrightness; + + // Start state + public float CurrentBrightness; + public bool Rising; + public int Timer; + public bool Inactive; + + // World rep info + public bool Refresh; // Not relevant to us. It's used to tell dromed it needs to relight + public ushort LightTableMapIndex; + public ushort LightTableLightIndex; + public ushort CellsReached; + + private int _unknown; + + public override void Read(BinaryReader reader) + { + base.Read(reader); + Brightness = reader.ReadSingle(); + Offset = reader.ReadVec3(); + Refresh = reader.ReadBoolean(); + reader.ReadBytes(3); + LightTableMapIndex = reader.ReadUInt16(); + CellsReached = reader.ReadUInt16(); + LightTableLightIndex = reader.ReadUInt16(); + Mode = (AnimMode)reader.ReadUInt16(); + MsToBrighten = reader.ReadInt32(); + MsToDim = reader.ReadInt32(); + MinBrightness = reader.ReadSingle(); + MaxBrightness = reader.ReadSingle(); + CurrentBrightness = reader.ReadSingle(); + Rising = reader.ReadBoolean(); + reader.ReadBytes(3); + Timer = reader.ReadInt32(); + Inactive = reader.ReadBoolean(); + reader.ReadBytes(3); + Radius = reader.ReadSingle(); + _unknown = reader.ReadInt32(); + QuadLit = reader.ReadBoolean(); + reader.ReadBytes(3); + InnerRadius = reader.ReadSingle(); + Dynamic = reader.ReadBoolean(); + reader.ReadBytes(3); + } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(Brightness); + writer.WriteVec3(Offset); + writer.Write(Refresh); + writer.Write(new byte[3]); + writer.Write(LightTableMapIndex); + writer.Write(CellsReached); + writer.Write(LightTableLightIndex); + writer.Write((ushort)Mode); + writer.Write(MsToBrighten); + writer.Write(MsToDim); + writer.Write(MinBrightness); + writer.Write(MaxBrightness); + writer.Write(CurrentBrightness); + writer.Write(Rising); + writer.Write(new byte[3]); + writer.Write(Timer); + writer.Write(Inactive); + writer.Write(new byte[3]); + writer.Write(Radius); + writer.Write(_unknown); + writer.Write(QuadLit); + writer.Write(new byte[3]); + writer.Write(InnerRadius); + writer.Write(Dynamic); + writer.Write(new byte[3]); + } } public class PropLightColor : Property @@ -241,6 +449,13 @@ public class PropLightColor : Property Hue = reader.ReadSingle(); Saturation = reader.ReadSingle(); } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(Hue); + writer.Write(Saturation); + } } public class PropSpotlight : Property @@ -255,6 +470,14 @@ public class PropSpotlight : Property OuterAngle = reader.ReadSingle(); reader.ReadBytes(4); // Z is unused } + + public override void Write(BinaryWriter writer) + { + base.Write(writer); + writer.Write(InnerAngle); + writer.Write(OuterAngle); + writer.Write(new byte[4]); + } } public class PropSpotlightAndAmbient : Property @@ -270,21 +493,12 @@ public class PropSpotlightAndAmbient : Property OuterAngle = reader.ReadSingle(); SpotBrightness = reader.ReadSingle(); } -} -// TODO: Work out what this property actually does -public class PropLightBasedAlpha : Property -{ - public bool Enabled; - public Vector2 AlphaRange; - public Vector2 LightRange; - - public override void Read(BinaryReader reader) + public override void Write(BinaryWriter writer) { - base.Read(reader); - Enabled = reader.ReadBoolean(); - reader.ReadBytes(3); - AlphaRange = reader.ReadVec2(); - LightRange = new Vector2(reader.ReadInt32(), reader.ReadInt32()); + base.Write(writer); + writer.Write(InnerAngle); + writer.Write(OuterAngle); + writer.Write(SpotBrightness); } } diff --git a/project/code/LGS/Database/Chunks/RendParams.cs b/project/code/LGS/Database/Chunks/RendParams.cs new file mode 100644 index 0000000..5a43c79 --- /dev/null +++ b/project/code/LGS/Database/Chunks/RendParams.cs @@ -0,0 +1,80 @@ +using System.IO; +using System.Numerics; + +namespace KeepersCompound.LGS.Database.Chunks; + +public class RendParams : IChunk +{ + public enum SunlightMode + { + SingleUnshadowed, + QuadObjcastShadows, + QuadUnshadowed, + SingleObjcastShadows, + } + + public ChunkHeader Header { get; set; } + + public string palette; + public Vector3 ambientLight; + public int useSunlight; + public SunlightMode sunlightMode; + public Vector3 sunlightDirection; + public float sunlightHue; + public float sunlightSaturation; + public float sunlightBrightness; + public float viewDistance; + public Vector3[] ambientLightZones; + public float globalAiVisBias; + public float[] ambientZoneAiVisBiases; + + public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry) + { + palette = reader.ReadNullString(16); + ambientLight = reader.ReadVec3(); + useSunlight = reader.ReadInt32(); + sunlightMode = (SunlightMode)reader.ReadUInt32(); + sunlightDirection = reader.ReadVec3(); + sunlightHue = reader.ReadSingle(); + sunlightSaturation = reader.ReadSingle(); + sunlightBrightness = reader.ReadSingle(); + reader.ReadBytes(24); + viewDistance = reader.ReadSingle(); + reader.ReadBytes(12); + ambientLightZones = new Vector3[8]; + for (var i = 0; i < ambientLightZones.Length; i++) + { + ambientLightZones[i] = reader.ReadVec3(); + } + globalAiVisBias = reader.ReadSingle(); + ambientZoneAiVisBiases = new float[8]; + for (var i = 0; i < ambientZoneAiVisBiases.Length; i++) + { + ambientZoneAiVisBiases[i] = reader.ReadSingle(); + } + } + + public void WriteData(BinaryWriter writer) + { + writer.WriteNullString(palette, 16); + writer.WriteVec3(ambientLight); + writer.Write(useSunlight); + writer.Write((uint)sunlightMode); + writer.WriteVec3(sunlightDirection); + writer.Write(sunlightHue); + writer.Write(sunlightSaturation); + writer.Write(sunlightBrightness); + writer.Write(new byte[24]); + writer.Write(viewDistance); + writer.Write(new byte[12]); + foreach (var lightZone in ambientLightZones) + { + writer.WriteVec3(lightZone); + } + writer.Write(globalAiVisBias); + foreach (var visBias in ambientZoneAiVisBiases) + { + writer.Write(visBias); + } + } +} \ No newline at end of file diff --git a/project/code/LGS/Database/Chunks/TxList.cs b/project/code/LGS/Database/Chunks/TxList.cs index 5aaafd7..4601f67 100644 --- a/project/code/LGS/Database/Chunks/TxList.cs +++ b/project/code/LGS/Database/Chunks/TxList.cs @@ -1,6 +1,4 @@ -using System; using System.IO; -using System.Text; namespace KeepersCompound.LGS.Database.Chunks; @@ -16,6 +14,12 @@ public class TxList : IChunk Tokens = reader.ReadBytes(4); Name = reader.ReadNullString(16); } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(Tokens); + writer.WriteNullString(Name, 16); + } } @@ -27,7 +31,6 @@ public class TxList : IChunk public string[] Tokens { get; set; } public Item[] Items { get; set; } - public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry) { BlockSize = reader.ReadInt32(); @@ -47,6 +50,16 @@ public class TxList : IChunk public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + writer.Write(BlockSize); + writer.Write(ItemCount); + writer.Write(TokenCount); + foreach (var token in Tokens) + { + writer.WriteNullString(token, 16); + } + foreach (var item in Items) + { + item.Write(writer); + } } } \ No newline at end of file diff --git a/project/code/LGS/Database/Chunks/WorldRep.cs b/project/code/LGS/Database/Chunks/WorldRep.cs index ef442e4..034bf5f 100644 --- a/project/code/LGS/Database/Chunks/WorldRep.cs +++ b/project/code/LGS/Database/Chunks/WorldRep.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Numerics; @@ -39,9 +40,20 @@ public class WorldRep : IChunk _ => 1.0f, }; } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(Size); + writer.Write(Version); + writer.Write(Flags); + writer.Write(LightmapFormat); + writer.Write(LightmapScale); + writer.Write(DataSize); + writer.Write(CellCount); + } } - public struct Cell + public class Cell { public struct Poly { @@ -62,6 +74,17 @@ public class WorldRep : IChunk MotionIndex = reader.ReadByte(); reader.ReadByte(); } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(Flags); + writer.Write(VertexCount); + writer.Write(PlaneId); + writer.Write(ClutId); + writer.Write(Destination); + writer.Write(MotionIndex); + writer.Write((byte)0); + } } public struct RenderPoly @@ -82,9 +105,21 @@ public class WorldRep : IChunk TextureMagnitude = reader.ReadSingle(); Center = reader.ReadVec3(); } + + public readonly void Write(BinaryWriter writer) + { + writer.WriteVec3(TextureVectors.Item1); + writer.WriteVec3(TextureVectors.Item2); + writer.Write(TextureBases.Item1); + writer.Write(TextureBases.Item2); + writer.Write(TextureId); + writer.Write(CachedSurface); + writer.Write(TextureMagnitude); + writer.WriteVec3(Center); + } } - public struct LightmapInfo + public class LightmapInfo { public (short, short) Bases { get; set; } public short PaddedWidth { get; set; } @@ -104,11 +139,24 @@ public class WorldRep : IChunk DynamicLightPtr = reader.ReadUInt32(); AnimLightBitmask = reader.ReadUInt32(); } + + public void Write(BinaryWriter writer) + { + writer.Write(Bases.Item1); + writer.Write(Bases.Item2); + writer.Write(PaddedWidth); + writer.Write(Height); + writer.Write(Width); + writer.Write(DataPtr); + writer.Write(DynamicLightPtr); + writer.Write(AnimLightBitmask); + } } - public struct Lightmap + public class Lightmap { - public byte[] Pixels { get; set; } + private readonly bool[] _litLayers; + public List Pixels { get; set; } public int Layers; public int Width; @@ -117,44 +165,52 @@ public class WorldRep : IChunk public Lightmap(BinaryReader reader, byte width, byte height, uint bitmask, int bytesPerPixel) { - var count = 1 + BitOperations.PopCount(bitmask); - var length = bytesPerPixel * width * height * count; - Pixels = reader.ReadBytes(length); - Layers = count; + var layers = 1 + BitOperations.PopCount(bitmask); + var length = bytesPerPixel * width * height; + _litLayers = new bool[33]; + Pixels = new List(); + for (var i = 0; i < layers; i++) + { + Pixels.Add(reader.ReadBytes(length)); + _litLayers[i] = true; + } + Layers = layers; Width = width; Height = height; Bpp = bytesPerPixel; } - public readonly Vector4 GetPixel(uint layer, uint x, uint y) + public Vector4 GetPixel(uint layer, uint x, uint y) { if (layer >= Layers || x >= Width || y >= Height) { return Vector4.Zero; } - var idx = 0 + x * Bpp + y * Bpp * Width + layer * Bpp * Width * Height; + var pLayer = Pixels[(int)layer]; + var idx = x * Bpp + y * Bpp * Width; switch (Bpp) { case 1: - var raw1 = Pixels[idx]; + var raw1 = pLayer[idx]; return new Vector4(raw1, raw1, raw1, 255) / 255.0f; case 2: - var raw2 = Pixels[idx] + (Pixels[idx + 1] << 8); + var raw2 = pLayer[idx] + (pLayer[idx + 1] << 8); return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f; case 4: - return new Vector4(Pixels[idx + 2], Pixels[idx + 1], Pixels[idx], Pixels[idx + 3]) / 255.0f; + return new Vector4(pLayer[idx + 2], pLayer[idx + 1], pLayer[idx], pLayer[idx + 3]) / 255.0f; default: return Vector4.Zero; } } - public readonly byte[] AsBytesRgba(int layer) + public byte[] AsBytesRgba(int layer) { ArgumentOutOfRangeException.ThrowIfLessThan(layer, 0, nameof(layer)); ArgumentOutOfRangeException.ThrowIfGreaterThan(layer, Layers, nameof(layer)); - var pIdx = layer * Bpp * Width * Height; + var pLayer = Pixels[layer]; + var pIdx = 0; var length = 4 * Width * Height; var bytes = new byte[length]; for (var i = 0; i < length; i += 4, pIdx += Bpp) @@ -162,30 +218,99 @@ public class WorldRep : IChunk switch (Bpp) { case 1: - var raw1 = Pixels[pIdx]; + var raw1 = pLayer[pIdx]; bytes[i] = raw1; bytes[i + 1] = raw1; bytes[i + 2] = raw1; bytes[i + 3] = 255; break; case 2: - var raw2 = Pixels[pIdx] + (Pixels[pIdx + 1] << 8); + var raw2 = pLayer[pIdx] + (pLayer[pIdx + 1] << 8); bytes[i] = (byte)(255 * (raw2 & 31) / 31.0f); bytes[i + 1] = (byte)(255 * ((raw2 >> 5) & 31) / 31.0f); bytes[i + 2] = (byte)(255 * ((raw2 >> 10) & 31) / 31.0f); bytes[i + 3] = 255; break; case 4: - bytes[i] = Pixels[pIdx + 2]; - bytes[i + 1] = Pixels[pIdx + 1]; - bytes[i + 2] = Pixels[pIdx]; - bytes[i + 3] = Pixels[pIdx + 3]; + bytes[i] = pLayer[pIdx + 2]; + bytes[i + 1] = pLayer[pIdx + 1]; + bytes[i + 2] = pLayer[pIdx]; + bytes[i + 3] = pLayer[pIdx + 3]; break; } } return bytes; } + + // TODO: This ONLY works for rgba (bpp = 4)!!! + public void AddLight(int layer, int x, int y, float r, float g, float b) + { + 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; + + _litLayers[layer] = true; + } + + public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr) + { + if (hdr) + { + strength /= 2.0f; + } + + // We need to make sure we don't go over (255, 255, 255). + // If we just do Max(color, (255, 255, 255)) then we change + // the hue/saturation of coloured lights. Got to make sure we + // maintain the colour ratios. + var c = color * strength; + var ratio = Math.Max(Math.Max(Math.Max(0.0f, c.X), c.Y), c.Z) / 255.0f; + if (ratio > 1.0f) + { + c /= ratio; + } + + AddLight(layer, x, y, c.Z, c.Y, c.X); + } + + public void Reset(Vector3 ambientLight, bool hdr) + { + Layers = 33; + Pixels.Clear(); + var bytesPerLayer = Width * Height * Bpp; + for (var i = 0; i < Layers; i++) + { + Pixels.Add(new byte[bytesPerLayer]); + for (var j = 0; j < bytesPerLayer; j++) + { + Pixels[i][j] = 0; + } + _litLayers[i] = false; + } + + for (var y = 0; y < Height; y++) + { + for (var x = 0; x < Width; x++) + { + AddLight(0, x, y, ambientLight, 1.0f, hdr); + } + } + } + + public void Write(BinaryWriter writer) + { + for (var i = 0; i < Layers; i++) + { + if (_litLayers[i]) + { + writer.Write(Pixels[i]); + } + } + } } public byte VertexCount { get; set; } @@ -207,11 +332,11 @@ public class WorldRep : IChunk public uint IndexCount { get; set; } public byte[] Indices { get; set; } public Plane[] Planes { get; set; } - public ushort[] AnimLights { get; set; } + public List AnimLights { get; set; } public LightmapInfo[] LightList { get; set; } public Lightmap[] Lightmaps { get; set; } public int LightIndexCount { get; set; } - public ushort[] LightIndices { get; set; } + public List LightIndices { get; set; } public Cell(BinaryReader reader, int bpp) { @@ -254,10 +379,10 @@ public class WorldRep : IChunk { Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle()); } - AnimLights = new ushort[AnimLightCount]; + AnimLights = new List(AnimLightCount); for (var i = 0; i < AnimLightCount; i++) { - AnimLights[i] = reader.ReadUInt16(); + AnimLights.Add(reader.ReadUInt16()); } LightList = new LightmapInfo[RenderPolyCount]; for (var i = 0; i < RenderPolyCount; i++) @@ -271,17 +396,269 @@ public class WorldRep : IChunk Lightmaps[i] = new Lightmap(reader, info.Width, info.Height, info.AnimLightBitmask, bpp); } LightIndexCount = reader.ReadInt32(); - LightIndices = new ushort[LightIndexCount]; + LightIndices = new List(LightIndexCount); for (var i = 0; i < LightIndexCount; i++) { - LightIndices[i] = reader.ReadUInt16(); + LightIndices.Add(reader.ReadUInt16()); } } + + public void Write(BinaryWriter writer) + { + writer.Write(VertexCount); + writer.Write(PolyCount); + writer.Write(RenderPolyCount); + writer.Write(PortalPolyCount); + writer.Write(PlaneCount); + writer.Write(Medium); + writer.Write(Flags); + writer.Write(PortalVertices); + writer.Write(NumVList); + writer.Write(AnimLightCount); + writer.Write(MotionIndex); + writer.WriteVec3(SphereCenter); + writer.Write(SphereRadius); + foreach (var vertex in Vertices) + { + writer.WriteVec3(vertex); + } + foreach (var poly in Polys) + { + poly.Write(writer); + } + foreach (var renderPoly in RenderPolys) + { + renderPoly.Write(writer); + } + writer.Write(IndexCount); + writer.Write(Indices); + foreach (var plane in Planes) + { + writer.WriteVec3(plane.Normal); + writer.Write(plane.D); + } + foreach (var animLight in AnimLights) + { + writer.Write(animLight); + } + foreach (var lightmapInfo in LightList) + { + lightmapInfo.Write(writer); + } + foreach (var lightmap in Lightmaps) + { + lightmap.Write(writer); + } + writer.Write(LightIndexCount); + foreach (var lightIndex in LightIndices) + { + writer.Write(lightIndex); + } + } + } + + public struct BspTree + { + public struct Node + { + int parentIndex; // TODO: Split the flags out of this + int cellId; + int planeId; + uint insideIndex; + uint outsideIndex; + + public Node(BinaryReader reader) + { + parentIndex = reader.ReadInt32(); + cellId = reader.ReadInt32(); + planeId = reader.ReadInt32(); + insideIndex = reader.ReadUInt32(); + outsideIndex = reader.ReadUInt32(); + } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(parentIndex); + writer.Write(cellId); + writer.Write(planeId); + writer.Write(insideIndex); + writer.Write(outsideIndex); + } + } + + public uint PlaneCount; + public uint NodeCount; + public Plane[] Planes; + public Node[] Nodes; + + public BspTree(BinaryReader reader) + { + PlaneCount = reader.ReadUInt32(); + Planes = new Plane[PlaneCount]; + for (var i = 0; i < PlaneCount; i++) + { + Planes[i] = new Plane(reader.ReadVec3(), reader.ReadSingle()); + } + + NodeCount = reader.ReadUInt32(); + Nodes = new Node[NodeCount]; + for (var i = 0; i < NodeCount; i++) + { + Nodes[i] = new Node(reader); + } + } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(PlaneCount); + foreach (var plane in Planes) + { + writer.WriteVec3(plane.Normal); + writer.Write(plane.D); + } + writer.Write(NodeCount); + foreach (var node in Nodes) + { + node.Write(writer); + } + } + } + + public class LightTable + { + public struct LightData + { + public Vector3 Location; + public Vector3 Direction; + public Vector3 Color; + public float InnerAngle; // I'm pretty sure these are the spotlight angles + public float OuterAngle; + public float Radius; + + public LightData(BinaryReader reader) + { + Location = reader.ReadVec3(); + Direction = reader.ReadVec3(); + Color = reader.ReadVec3(); + InnerAngle = reader.ReadSingle(); + OuterAngle = reader.ReadSingle(); + Radius = reader.ReadSingle(); + } + + public readonly void Write(BinaryWriter writer) + { + writer.WriteVec3(Location); + writer.WriteVec3(Direction); + writer.WriteVec3(Color); + writer.Write(InnerAngle); + writer.Write(OuterAngle); + writer.Write(Radius); + } + } + + public struct AnimCellMap + { + public ushort CellIndex; + public ushort LightIndex; + + public AnimCellMap(BinaryReader reader) + { + CellIndex = reader.ReadUInt16(); + LightIndex = reader.ReadUInt16(); + } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(CellIndex); + writer.Write(LightIndex); + } + } + + public int LightCount; + public int DynamicLightCount; + public int AnimMapCount; + public List Lights; + public LightData[] ScratchpadLights; + public List AnimCellMaps; + + // TODO: Support olddark + public LightTable(BinaryReader reader) + { + LightCount = reader.ReadInt32(); + DynamicLightCount = reader.ReadInt32(); + var totalLightCount = LightCount + DynamicLightCount; + Lights = new List(totalLightCount); + for (var i = 0; i < totalLightCount; i++) + { + Lights.Add(new LightData(reader)); + } + ScratchpadLights = new LightData[32]; + for (var i = 0; i < 32; i++) + { + ScratchpadLights[i] = new LightData(reader); + } + AnimMapCount = reader.ReadInt32(); + AnimCellMaps = new List(AnimMapCount); + for (var i = 0; i < AnimMapCount; i++) + { + AnimCellMaps.Add(new AnimCellMap(reader)); + } + } + + public void Write(BinaryWriter writer) + { + writer.Write(LightCount); + writer.Write(DynamicLightCount); + foreach (var light in Lights) + { + light.Write(writer); + } + foreach (var light in ScratchpadLights) + { + light.Write(writer); + } + writer.Write(AnimMapCount); + foreach (var map in AnimCellMaps) + { + map.Write(writer); + } + } + + public void Reset() + { + // Seems light we store something for sunlight at index 0 even if sunlight isn't being used + // TODO: Work out what to actually *put* here + LightCount = 1; + Lights.Clear(); + Lights.Add(new LightData()); + + DynamicLightCount = 0; + AnimMapCount = 0; + AnimCellMaps.Clear(); + } + + public void AddLight(LightData data, bool dynamicLight = false) + { + if (dynamicLight) + { + DynamicLightCount++; + } + else + { + LightCount++; + } + + Lights.Add(data); + } } public ChunkHeader Header { get; set; } public WrHeader DataHeader { get; set; } public Cell[] Cells { get; set; } + public BspTree Bsp { get; set; } + public LightTable LightingTable { get; set; } + private byte[] _unknown; + private byte[] _unreadData; public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry) { @@ -294,11 +671,27 @@ public class WorldRep : IChunk Cells[i] = new Cell(reader, bpp); } + Bsp = new BspTree(reader); + + // TODO: Work out what this is + _unknown = reader.ReadBytes(Cells.Length); + LightingTable = new LightTable(reader); + // TODO: All the other info lol + var length = entry.Offset + entry.Size + 24 - reader.BaseStream.Position; + _unreadData = reader.ReadBytes((int)length); } public void WriteData(BinaryWriter writer) { - throw new System.NotImplementedException(); + DataHeader.Write(writer); + foreach (var cell in Cells) + { + cell.Write(writer); + } + Bsp.Write(writer); + writer.Write(_unknown); + LightingTable.Write(writer); + writer.Write(_unreadData); } } \ No newline at end of file diff --git a/project/code/LGS/Database/File.cs b/project/code/LGS/Database/File.cs index 89ed448..8abbb63 100644 --- a/project/code/LGS/Database/File.cs +++ b/project/code/LGS/Database/File.cs @@ -1,4 +1,3 @@ - using System; using System.Collections.Generic; using System.IO; @@ -32,13 +31,13 @@ public class DbFile } } - public readonly struct TableOfContents + public struct TableOfContents { - public readonly struct Entry + public struct Entry { - public string Name { get; } - public uint Offset { get; } - public uint Size { get; } + public string Name; + public uint Offset; + public uint Size; public Entry(BinaryReader reader) { @@ -47,10 +46,16 @@ public class DbFile Size = reader.ReadUInt32(); } - public override string ToString() + public override readonly string ToString() { - // return $"Name: {Name}, Offset: {O}" - return base.ToString(); + return $"Name: {Name}, Offset: {Offset}, Size: {Size}"; + } + + public readonly void Write(BinaryWriter writer) + { + writer.WriteNullString(Name, 12); + writer.Write(Offset); + writer.Write(Size); } } @@ -65,6 +70,15 @@ public class DbFile Items.Add(new Entry(reader)); Items.Sort((a, b) => a.Offset.CompareTo(b.Offset)); } + + public readonly void Write(BinaryWriter writer) + { + writer.Write(ItemCount); + foreach (var entry in Items) + { + entry.Write(writer); + } + } } public FHeader Header { get; private set; } @@ -73,6 +87,7 @@ public class DbFile public DbFile(string filename) { + // TODO: Throw rather than return if (!File.Exists(filename)) return; using MemoryStream stream = new(File.ReadAllBytes(filename)); @@ -91,6 +106,43 @@ public class DbFile } } + public void Save(string filename) + { + using var stream = File.Open(filename, FileMode.Create); + using var writer = new BinaryWriter(stream, Encoding.UTF8, false); + + Header.Write(writer); + for (var i = 0; i < Toc.ItemCount; i++) + { + var item = Toc.Items[i]; + var pos = stream.Position; + + var chunk = Chunks[item.Name]; + chunk.Write(writer); + var size = stream.Position - pos - 24; + + item.Offset = (uint)pos; + item.Size = (uint)size; + Toc.Items[i] = item; + } + var tocOffset = (uint)stream.Position; + Toc.Write(writer); + + stream.Seek(0, SeekOrigin.Begin); + writer.Write(tocOffset); + } + + public bool TryGetChunk(string name, out T chunk) + { + if (Chunks.TryGetValue(name, out var rawChunk)) + { + chunk = (T)rawChunk; + return true; + } + chunk = default; + return false; + } + private static IChunk NewChunk(string entryName) { return entryName switch @@ -101,6 +153,8 @@ public class DbFile "TXLIST" => new TxList(), "WREXT" => new WorldRep(), "BRLIST" => new BrList(), + "LM_PARAM" => new LmParams(), + "RENDPARAMS" => new RendParams(), "P$ModelName" => new PropertyChunk(), "P$Scale" => new PropertyChunk(), "P$RenderTyp" => new PropertyChunk(), @@ -108,6 +162,10 @@ public class DbFile "P$OTxtRepr1" => new PropertyChunk(), "P$OTxtRepr2" => new PropertyChunk(), "P$OTxtRepr3" => new PropertyChunk(), + "P$Light" => new PropertyChunk(), + "P$AnimLight" => new PropertyChunk(), + "P$LightColo" => new PropertyChunk(), + "P$Spotlight" => new PropertyChunk(), "P$RenderAlp" => new PropertyChunk(), "LD$MetaProp" => new LinkDataMetaProp(), _ when entryName.StartsWith("L$") => new LinkChunk(), diff --git a/project/code/LGS/Database/ObjectHierarchy.cs b/project/code/LGS/Database/ObjectHierarchy.cs index e76d3cc..f5935df 100644 --- a/project/code/LGS/Database/ObjectHierarchy.cs +++ b/project/code/LGS/Database/ObjectHierarchy.cs @@ -1,4 +1,3 @@ - using System; using System.Collections.Generic; using KeepersCompound.LGS.Database.Chunks; @@ -7,7 +6,7 @@ namespace KeepersCompound.LGS.Database; public class ObjectHierarchy { - public class DarkObject + private class DarkObject { public int objectId; public int parentId; @@ -20,7 +19,7 @@ public class ObjectHierarchy properties = new Dictionary(); } - public T GetProperty(string propName) where T : Property + public T? GetProperty(string propName) where T : Property { if (properties.TryGetValue(propName, out var prop)) { @@ -38,18 +37,17 @@ public class ObjectHierarchy T GetMergedChunk(string name) where T : IMergable { - if (db.Chunks.TryGetValue(name, out var rawChunk)) + if (!db.TryGetChunk(name, out var chunk)) { - var chunk = (T)rawChunk; - if (gam != null && gam.Chunks.TryGetValue(name, out var rawGamChunk)) - { - var gamChunk = (T)rawGamChunk; - chunk.Merge(gamChunk); - } - return chunk; + throw new ArgumentException($"No chunk with name ({name}) found", nameof(name)); } - throw new ArgumentException($"No chunk with name ({name}) found", nameof(name)); + if (gam != null && gam.TryGetChunk(name, out var gamChunk)) + { + gamChunk.Merge(chunk); + return gamChunk; + } + return chunk; } // Add parentages @@ -83,11 +81,12 @@ public class ObjectHierarchy foreach (var prop in chunk.properties) { var id = prop.objectId; - if (!_objects.ContainsKey(id)) + if (!_objects.TryGetValue(id, out var value)) { - _objects.Add(id, new DarkObject(id)); + value = new DarkObject(id); + _objects.Add(id, value); } - _objects[id].properties.TryAdd(name, prop); + value.properties.TryAdd(name, prop); } } @@ -99,9 +98,14 @@ public class ObjectHierarchy AddProp("P$OTxtRepr2"); AddProp("P$OTxtRepr3"); AddProp("P$RenderAlp"); + AddProp("P$Light"); + AddProp("P$AnimLight"); + AddProp("P$LightColo"); + AddProp("P$Spotlight"); } - public T GetProperty(int objectId, string propName) where T : Property + // TODO: Work out if there's some nice way to automatically decide if we inherit + public T? GetProperty(int objectId, string propName, bool inherit = true) where T : Property { if (!_objects.ContainsKey(objectId)) { @@ -117,7 +121,7 @@ public class ObjectHierarchy } var prop = obj.GetProperty(propName); - if (prop != null) + if (prop != null || !inherit) { return prop; } diff --git a/project/code/LGS/Extensions.cs b/project/code/LGS/Extensions.cs index 078e5a9..e04d682 100644 --- a/project/code/LGS/Extensions.cs +++ b/project/code/LGS/Extensions.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Numerics; using System.Text; @@ -12,16 +13,37 @@ public static class Extensions return raw * 360 / (ushort.MaxValue + 1); } + public static void WriteRotation(this BinaryWriter writer, Vector3 rotation) + { + var raw = rotation * (ushort.MaxValue + 1) / 360; + writer.Write((ushort)raw.X); + writer.Write((ushort)raw.Y); + writer.Write((ushort)raw.Z); + } + public static Vector3 ReadVec3(this BinaryReader reader) { return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } + public static void WriteVec3(this BinaryWriter writer, Vector3 vec) + { + writer.Write(vec.X); + writer.Write(vec.Y); + writer.Write(vec.Z); + } + public static Vector2 ReadVec2(this BinaryReader reader) { return new Vector2(reader.ReadSingle(), reader.ReadSingle()); } + public static void WriteVec2(this BinaryWriter writer, Vector2 vec) + { + writer.Write(vec.X); + writer.Write(vec.Y); + } + public static string ReadNullString(this BinaryReader reader, int length) { var tmpName = Encoding.UTF8.GetString(reader.ReadBytes(length)); @@ -29,4 +51,12 @@ public static class Extensions if (idx >= 0) tmpName = tmpName[..idx]; return tmpName; } + + public static void WriteNullString(this BinaryWriter writer, string nullString, int length) + { + var writeBytes = new byte[length]; + var stringBytes = Encoding.UTF8.GetBytes(nullString); + stringBytes[..Math.Min(length, stringBytes.Length)].CopyTo(writeBytes, 0); + writer.Write(writeBytes); + } } diff --git a/project/code/LGS/ModelFile.cs b/project/code/LGS/ModelFile.cs index 64bb42a..e6e0bea 100644 --- a/project/code/LGS/ModelFile.cs +++ b/project/code/LGS/ModelFile.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using System.Numerics; using System.Text; @@ -153,6 +152,30 @@ public class ModelFile } } + public enum VhotId + { + LightPosition = 1, + LightDirection = 8, + Anchor = 2, + Particle1 = 3, + Particle2 = 4, + Particle3 = 5, + Particle4 = 6, + Particle5 = 7, + } + + public struct VHot + { + public int Id; + public Vector3 Position; + + public VHot(BinaryReader reader) + { + Id = reader.ReadInt32(); + Position = reader.ReadVec3(); + } + } + public BHeader BinHeader { get; set; } public MHeader Header { get; set; } public Vector3[] Vertices { get; } @@ -160,6 +183,7 @@ public class ModelFile public Vector3[] Normals { get; } public Polygon[] Polygons { get; } public Material[] Materials { get; } + public VHot[] VHots { get; } public ModelFile(string filename) { @@ -202,5 +226,25 @@ public class ModelFile { Materials[i] = new Material(reader); } + stream.Seek(Header.VHotOffset, SeekOrigin.Begin); + VHots = new VHot[Header.VHotCount]; + for (var i = 0; i < VHots.Length; i++) + { + VHots[i] = new VHot(reader); + } + } + + public bool TryGetVhot(VhotId id, out VHot vhot) + { + foreach (var v in VHots) + { + if (v.Id == (int)id) + { + vhot = v; + return true; + } + } + vhot = new VHot(); + return false; } } \ No newline at end of file diff --git a/project/scenes/asset_browser/asset_browser.tscn b/project/scenes/asset_browser/asset_browser.tscn index 9c88948..9570341 100644 --- a/project/scenes/asset_browser/asset_browser.tscn +++ b/project/scenes/asset_browser/asset_browser.tscn @@ -10,7 +10,6 @@ [ext_resource type="Texture2D" uid="uid://bfswg75r148mr" path="res://project/assets/icons/ActionCopy.svg" id="7_2pq2g"] [node name="AssetBrowser" type="Control"] -visible = false layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -22,9 +21,10 @@ size_flags_vertical = 3 script = ExtResource("1_5rr8c") [node name="TabContainer" type="TabContainer" parent="."] -layout_mode = 2 -offset_right = 1152.0 -offset_bottom = 648.0 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 current_tab = 0 diff --git a/project/scenes/main.tscn b/project/scenes/main.tscn index 796da0b..b0c81a4 100644 --- a/project/scenes/main.tscn +++ b/project/scenes/main.tscn @@ -1,9 +1,8 @@ -[gd_scene load_steps=7 format=3 uid="uid://boxi211q3kx6c"] +[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="PackedScene" uid="uid://bfxdpxkcgwlkx" path="res://project/scenes/ui/resource_selector.tscn" id="3_kdn7u"] -[ext_resource type="PackedScene" uid="uid://byknmqac1a5vn" path="res://project/scenes/asset_browser/asset_browser.tscn" id="3_noiti"] [ext_resource type="PackedScene" uid="uid://0h2w7w84vbea" path="res://project/scenes/ui/lightmap_layer_toggler.tscn" id="4_naip8"] [sub_resource type="Environment" id="Environment_cckyk"] @@ -29,7 +28,5 @@ unique_name_in_owner = true unique_name_in_owner = true visible = false -[node name="AssetBrowser" parent="UI" instance=ExtResource("3_noiti")] - [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_cckyk")