Add RendParams and writers for each chunk

This commit is contained in:
Jarrod Doyle 2024-09-22 11:14:34 +01:00
parent cf7ad880fb
commit 4bc55b9130
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
9 changed files with 690 additions and 34 deletions

View File

@ -46,6 +46,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 +113,34 @@ public class BrList : IChunk
txs = Array.Empty<TexInfo>();
}
}
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 +157,9 @@ public class BrList : IChunk
public void WriteData(BinaryWriter writer)
{
throw new System.NotImplementedException();
foreach (var brush in Brushes)
{
brush.Write(writer);
}
}
}

View File

@ -1,7 +1,3 @@
using System;
using System.IO;
using System.Text;
namespace KeepersCompound.LGS.Database.Chunks;
public class GamFile : IChunk
@ -16,6 +12,6 @@ public class GamFile : IChunk
public void WriteData(BinaryWriter writer)
{
throw new System.NotImplementedException();
writer.WriteNullString(fileName, 256);
}
}

View File

@ -32,6 +32,11 @@ public record LinkId
{
return _data;
}
public void Write(BinaryWriter writer)
{
writer.Write(_data);
}
}
public class LinkChunk : IChunk, IMergable
@ -50,6 +55,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 +79,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 +104,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 +128,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)

View File

@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Numerics;
namespace KeepersCompound.LGS.Database.Chunks;
@ -14,6 +12,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<T> : IChunk, IMergable where T : Property, new()
@ -34,7 +38,10 @@ public class PropertyChunk<T> : 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 +59,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 +76,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 +93,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 +110,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 +129,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 +147,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 +164,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 +190,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 +212,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 +234,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 +262,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 +289,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 +317,17 @@ 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 PropLightColor : Property
@ -241,6 +341,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 +362,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 +385,12 @@ public class PropSpotlightAndAmbient : Property
OuterAngle = reader.ReadSingle();
SpotBrightness = reader.ReadSingle();
}
}
// TODO: Work out what this property actually does
public class PropLightBasedAlpha : Property
public override void Write(BinaryWriter writer)
{
public bool Enabled;
public Vector2 AlphaRange;
public Vector2 LightRange;
public override void Read(BinaryReader reader)
{
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);
}
}

View File

@ -0,0 +1,80 @@
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);
}
}
}

View File

@ -1,7 +1,3 @@
using System;
using System.IO;
using System.Text;
namespace KeepersCompound.LGS.Database.Chunks;
public class TxList : IChunk
@ -16,6 +12,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 +29,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 +48,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);
}
}
}

View File

@ -39,6 +39,17 @@ 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
@ -62,6 +73,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,6 +104,18 @@ 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
@ -104,6 +138,18 @@ public class WorldRep : IChunk
DynamicLightPtr = reader.ReadUInt32();
AnimLightBitmask = reader.ReadUInt32();
}
public readonly 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
@ -186,6 +232,21 @@ public class WorldRep : IChunk
return bytes;
}
// TODO: This ONLY works for rgba (bpp = 4)!!!
public void AddLight(int layer, int x, int y, byte r, byte g, byte b)
{
var idx = (x + y * Width + layer * Width * Height) * Bpp;
Pixels[idx] = (byte)Math.Min(Pixels[idx] + b, 255);
Pixels[idx + 1] = (byte)Math.Min(Pixels[idx + 1] + g, 255);
Pixels[idx + 2] = (byte)Math.Min(Pixels[idx + 2] + r, 255);
Pixels[idx + 3] = 255;
}
public readonly void Write(BinaryWriter writer)
{
writer.Write(Pixels);
}
}
public byte VertexCount { get; set; }
@ -277,11 +338,65 @@ public class WorldRep : IChunk
LightIndices[i] = reader.ReadUInt16();
}
}
public readonly 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 ChunkHeader Header { get; set; }
public WrHeader DataHeader { get; set; }
public Cell[] Cells { get; set; }
private byte[] _unreadData;
public void ReadData(BinaryReader reader, DbFile.TableOfContents.Entry entry)
{
@ -295,10 +410,17 @@ public class WorldRep : IChunk
}
// 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);
}
writer.Write(_unreadData);
}
}

View File

@ -52,6 +52,13 @@ public class DbFile
// return $"Name: {Name}, Offset: {O}"
return base.ToString();
}
public readonly void Write(BinaryWriter writer)
{
writer.WriteNullString(Name, 12);
writer.Write(Offset);
writer.Write(Size);
}
}
public uint ItemCount { get; }
@ -65,6 +72,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 +89,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 +108,22 @@ public class DbFile
}
}
public void Save(string filename)
{
// !HACK: Right now we don't need to adjust TOC offset or anything because we're only
// overwriting data, not writing new lengths of data
using var stream = File.Open(filename, FileMode.Create);
using var writer = new BinaryWriter(stream, Encoding.UTF8, false);
Header.Write(writer);
foreach (var (name, chunk) in Chunks)
{
chunk.Write(writer);
}
Toc.Write(writer);
}
private static IChunk NewChunk(string entryName)
{
return entryName switch
@ -101,6 +134,7 @@ public class DbFile
"TXLIST" => new TxList(),
"WREXT" => new WorldRep(),
"BRLIST" => new BrList(),
"RENDPARAMS" => new RendParams(),
"P$ModelName" => new PropertyChunk<PropLabel>(),
"P$Scale" => new PropertyChunk<PropVector>(),
"P$RenderTyp" => new PropertyChunk<PropRenderType>(),

View File

@ -1,2 +1,243 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
using System.Numerics;
using KeepersCompound.LGS.Database;
using KeepersCompound.LGS.Database.Chunks;
using TinyEmbree;
namespace KeepersCompound.Lightmapper;
class Program
{
// Super simple for now
private record Light
{
public Vector3 position;
public Vector3 color;
public float radius;
}
static void Main(string[] args)
{
var misPath = "/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/FMs/JAYRUDE_Tests/lm_test.cow";
var mis = new DbFile(misPath);
var hierarchy = BuildHierarchy(misPath, mis);
// Get list of brush lights, and object lights (ignore anim lights for now)
var lights = new List<Light>();
if (mis.Chunks.TryGetValue("BRLIST", out var brListRaw))
{
var brList = (BrList)brListRaw;
foreach (var brush in brList.Brushes)
{
if (brush.media == BrList.Brush.Media.Light)
{
var sz = brush.size;
lights.Add(new Light
{
position = brush.position,
color = HsbToRgb(360 * sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
radius = float.MaxValue
});
}
}
// TODO: object lights
}
// Build embree mesh
if (!mis.Chunks.TryGetValue("WREXT", out var wrRaw))
return;
var worldRep = (WorldRep)wrRaw;
var scene = new Raytracer();
scene.AddMesh(BuildWrMesh(worldRep));
scene.CommitScene();
// For each lightmap pixel cast against all the brush and object lights
if (!mis.Chunks.TryGetValue("RENDPARAMS", out var rendParamsRaw))
return;
var ambient = ((RendParams)rendParamsRaw).ambientLight * 255;
CastScene(scene, worldRep, [.. lights], ambient);
var dir = Path.GetDirectoryName(misPath);
var filename = Path.GetFileNameWithoutExtension(misPath);
mis.Save(Path.Join(dir, $"{filename}-lit.cow"));
}
// Expects Hue to be 0-360, saturation 0-1, brightness 0-255
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
private static Vector3 HsbToRgb(float hue, float saturation, float brightness)
{
var hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
var f = hue / 60 - Math.Floor(hue / 60);
var v = Convert.ToInt32(brightness);
var p = Convert.ToInt32(brightness * (1 - saturation));
var q = Convert.ToInt32(brightness * (1 - f * saturation));
var t = Convert.ToInt32(brightness * (1 - (1 - f) * saturation));
return hi switch
{
0 => new Vector3(v, t, p),
1 => new Vector3(q, v, p),
2 => new Vector3(p, v, t),
3 => new Vector3(p, q, v),
4 => new Vector3(t, p, v),
_ => new Vector3(v, p, q),
};
}
private static ObjectHierarchy BuildHierarchy(string misPath, DbFile misFile)
{
ObjectHierarchy objHierarchy;
if (misFile.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk))
{
var dir = Path.GetDirectoryName(misPath);
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
var name = ((GamFile)gamFileChunk).fileName;
var paths = Directory.GetFiles(dir!, name, options);
if (paths.Length > 0)
{
objHierarchy = new ObjectHierarchy(misFile, new DbFile(paths[0]));
}
else
{
objHierarchy = new ObjectHierarchy(misFile);
}
}
else
{
objHierarchy = new ObjectHierarchy(misFile);
}
return objHierarchy;
}
private static TriangleMesh BuildWrMesh(WorldRep worldRep)
{
var vertices = new List<Vector3>();
var indices = new List<int>();
var cells = worldRep.Cells;
for (var cellIdx = 0; cellIdx < cells.Length; cellIdx++)
{
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;
}
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
var cellIdxOffset = 0;
for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
{
var poly = cell.Polys[polyIdx];
var meshIndexOffset = vertices.Count;
var numPolyVertices = poly.VertexCount;
for (var j = 0; j < numPolyVertices; j++)
{
var vertex = cell.Vertices[cell.Indices[cellIdxOffset + j]];
// Console.WriteLine($"Cell: {cellIdx}, Poly: {polyIdx}, V: {j}, Vert: {vertex}");
vertices.Add(vertex);
}
for (int j = 1; j < numPolyVertices - 1; j++)
{
indices.Add(meshIndexOffset);
indices.Add(meshIndexOffset + j);
indices.Add(meshIndexOffset + j + 1);
}
cellIdxOffset += cell.Polys[polyIdx].VertexCount;
}
}
return new TriangleMesh([.. vertices], [.. indices]);
}
private static void CastScene(Raytracer scene, WorldRep wr, Light[] lights, Vector3 ambientLight)
{
var cells = wr.Cells;
for (var cellIdx = 0; cellIdx < cells.Length; cellIdx++)
{
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;
}
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
{
var poly = cell.Polys[polyIdx];
var plane = cell.Planes[poly.PlaneId];
var renderPoly = cell.RenderPolys[polyIdx];
var info = cell.LightList[polyIdx];
var lightmap = cell.Lightmaps[polyIdx];
// Clear existing lightmap data
for (var i = 0; i < lightmap.Pixels.Length; i++)
{
lightmap.Pixels[i] = 0;
}
for (var y = 0; y < lightmap.Height; y++)
{
for (var x = 0; x < lightmap.Width; x++)
{
lightmap.AddLight(0, x, y, (byte)ambientLight.X, (byte)ambientLight.Y, (byte)ambientLight.Z);
}
}
foreach (var light in lights)
{
Console.WriteLine("Doing a light...");
// Check if plane normal is facing towards the light
var direction = renderPoly.Center - light.position;
Console.WriteLine($"Light Pos: {light.position}, poly center: {renderPoly.Center}");
Console.WriteLine($"Dir: {direction}");
// if (Vector3.Dot(plane.Normal, direction) < 0)
{
// Cast from the light to the center (later each pixel)
var hit = scene.Trace(new Ray
{
Origin = light.position,
Direction = Vector3.Normalize(direction)
});
// cheeky epsilon
var goodHit = hit && Math.Abs(hit.Distance - direction.Length()) < 0.001;
Console.WriteLine($"Did we hit? {goodHit}");
Console.WriteLine($"Distance: {hit.Distance}, target dist: {direction.Length()}");
Console.WriteLine($"Pos: {hit.Position}, Target Pos: {renderPoly.Center}");
// Iterate all pixels
var color = goodHit ? light.color : ambientLight;
for (var y = 0; y < lightmap.Height; y++)
{
for (var x = 0; x < lightmap.Width; x++)
{
lightmap.AddLight(0, x, y, (byte)color.X, (byte)color.Y, (byte)color.Z);
}
}
}
}
// // TODO: Get world position of each pixel?
// var poly = cell.Polys[polyIdx];
// poly.
}
}
}
}