Compare commits
	
		
			8 Commits
		
	
	
		
			940c78e1cf
			...
			c162a5028d
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						c162a5028d | |
| 
							
							
								
								 | 
						0a41d78cac | |
| 
							
							
								
								 | 
						3df1b728e6 | |
| 
							
							
								
								 | 
						ef95b07926 | |
| 
							
							
								
								 | 
						466b7a4c0b | |
| 
							
							
								
								 | 
						7ff91b2fda | |
| 
							
							
								
								 | 
						0c26aa1f0e | |
| 
							
							
								
								 | 
						cd13cee48f | 
| 
						 | 
					@ -152,6 +152,7 @@ public class WorldRep : IChunk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public class Lightmap
 | 
					        public class Lightmap
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            private readonly bool[] _litLayers;
 | 
				
			||||||
            public List<byte[]> Pixels { get; set; }
 | 
					            public List<byte[]> Pixels { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public int Layers;
 | 
					            public int Layers;
 | 
				
			||||||
| 
						 | 
					@ -163,10 +164,12 @@ public class WorldRep : IChunk
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var layers = 1 + BitOperations.PopCount(bitmask);
 | 
					                var layers = 1 + BitOperations.PopCount(bitmask);
 | 
				
			||||||
                var length = bytesPerPixel * width * height;
 | 
					                var length = bytesPerPixel * width * height;
 | 
				
			||||||
 | 
					                _litLayers = new bool[33];
 | 
				
			||||||
                Pixels = new List<byte[]>();
 | 
					                Pixels = new List<byte[]>();
 | 
				
			||||||
                for (var i = 0; i < layers; i++)
 | 
					                for (var i = 0; i < layers; i++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Pixels.Add(reader.ReadBytes(length));
 | 
					                    Pixels.Add(reader.ReadBytes(length));
 | 
				
			||||||
 | 
					                    _litLayers[i] = true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Layers = layers;
 | 
					                Layers = layers;
 | 
				
			||||||
                Width = width;
 | 
					                Width = width;
 | 
				
			||||||
| 
						 | 
					@ -246,6 +249,8 @@ public class WorldRep : IChunk
 | 
				
			||||||
                pLayer[idx + 1] = (byte)Math.Clamp(pLayer[idx + 1] + g, 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 + 2] = (byte)Math.Clamp(pLayer[idx + 2] + b, 0, 255);
 | 
				
			||||||
                pLayer[idx + 3] = 255;
 | 
					                pLayer[idx + 3] = 255;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                _litLayers[layer] = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr)
 | 
					            public void AddLight(int layer, int x, int y, Vector3 color, float strength, bool hdr)
 | 
				
			||||||
| 
						 | 
					@ -260,12 +265,7 @@ public class WorldRep : IChunk
 | 
				
			||||||
                // the hue/saturation of coloured lights. Got to make sure we
 | 
					                // the hue/saturation of coloured lights. Got to make sure we
 | 
				
			||||||
                // maintain the colour ratios.
 | 
					                // maintain the colour ratios.
 | 
				
			||||||
                var c = color * strength;
 | 
					                var c = color * strength;
 | 
				
			||||||
                var ratio = 0.0f;
 | 
					                var ratio = Math.Max(Math.Max(Math.Max(0.0f, c.X), c.Y), c.Z) / 255.0f;
 | 
				
			||||||
                foreach (var e in new float[] { c.X, c.Y, c.Z })
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    ratio = Math.Max(ratio, e / 255.0f);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (ratio > 1.0f)
 | 
					                if (ratio > 1.0f)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    c /= ratio;
 | 
					                    c /= ratio;
 | 
				
			||||||
| 
						 | 
					@ -276,9 +276,18 @@ public class WorldRep : IChunk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public void Reset(Vector3 ambientLight, bool hdr)
 | 
					            public void Reset(Vector3 ambientLight, bool hdr)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Layers = 0;
 | 
					                Layers = 33;
 | 
				
			||||||
                Pixels.Clear();
 | 
					                Pixels.Clear();
 | 
				
			||||||
                AddLayer();
 | 
					                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 y = 0; y < Height; y++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -289,22 +298,14 @@ public class WorldRep : IChunk
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public void AddLayer()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var bytesPerLayer = Width * Height * Bpp;
 | 
					 | 
				
			||||||
                Pixels.Add(new byte[bytesPerLayer]);
 | 
					 | 
				
			||||||
                for (var j = 0; j < bytesPerLayer; j++)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Pixels[Layers][j] = 0;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Layers++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public void Write(BinaryWriter writer)
 | 
					            public void Write(BinaryWriter writer)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var layer in Pixels)
 | 
					                for (var i = 0; i < Layers; i++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    writer.Write(layer);
 | 
					                    if (_litLayers[i])
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        writer.Write(Pixels[i]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ namespace KeepersCompound.LGS.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ObjectHierarchy
 | 
					public class ObjectHierarchy
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class DarkObject
 | 
					    private class DarkObject
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public int objectId;
 | 
					        public int objectId;
 | 
				
			||||||
        public int parentId;
 | 
					        public int parentId;
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ public class ObjectHierarchy
 | 
				
			||||||
            properties = new Dictionary<string, Property>();
 | 
					            properties = new Dictionary<string, Property>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public T GetProperty<T>(string propName) where T : Property
 | 
					        public T? GetProperty<T>(string propName) where T : Property
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (properties.TryGetValue(propName, out var prop))
 | 
					            if (properties.TryGetValue(propName, out var prop))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -79,11 +79,12 @@ public class ObjectHierarchy
 | 
				
			||||||
            foreach (var prop in chunk.properties)
 | 
					            foreach (var prop in chunk.properties)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var id = prop.objectId;
 | 
					                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);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,7 +103,7 @@ public class ObjectHierarchy
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: Work out if there's some nice way to automatically decide if we inherit
 | 
					    // TODO: Work out if there's some nice way to automatically decide if we inherit
 | 
				
			||||||
    public T GetProperty<T>(int objectId, string propName, bool inherit = true) where T : Property
 | 
					    public T? GetProperty<T>(int objectId, string propName, bool inherit = true) where T : Property
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (!_objects.ContainsKey(objectId))
 | 
					        if (!_objects.ContainsKey(objectId))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,6 +74,7 @@ class Program
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        var ambient = rendParams.ambientLight * 255;
 | 
					        var ambient = rendParams.ambientLight * 255;
 | 
				
			||||||
        var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign));
 | 
					        var lights = Timing.TimeStage("Gather Lights", () => BuildLightList(mis, hierarchy, campaign));
 | 
				
			||||||
 | 
					        Timing.TimeStage("Set Light Indices", () => SetCellLightIndices(worldRep, [.. lights]));
 | 
				
			||||||
        Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient));
 | 
					        Timing.TimeStage("Light", () => CastSceneParallel(scene, worldRep, [.. lights], ambient));
 | 
				
			||||||
        Timing.TimeStage("Update Anim Mapping", () => SetAnimLightCellMaps(mis, worldRep, lights));
 | 
					        Timing.TimeStage("Update Anim Mapping", () => SetAnimLightCellMaps(mis, worldRep, lights));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,30 +86,6 @@ class Program
 | 
				
			||||||
        Console.WriteLine($"Lit {lights.Count} light");
 | 
					        Console.WriteLine($"Lit {lights.Count} light");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Expects Hue and Saturation are 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)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        hue *= 360;
 | 
					 | 
				
			||||||
        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 void SetAnimLightCellMaps(
 | 
					    private static void SetAnimLightCellMaps(
 | 
				
			||||||
        DbFile mis,
 | 
					        DbFile mis,
 | 
				
			||||||
        WorldRep worldRep,
 | 
					        WorldRep worldRep,
 | 
				
			||||||
| 
						 | 
					@ -193,7 +170,7 @@ class Program
 | 
				
			||||||
                var light = new Light
 | 
					                var light = new Light
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    position = brush.position,
 | 
					                    position = brush.position,
 | 
				
			||||||
                    color = HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
 | 
					                    color = Utils.HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
 | 
				
			||||||
                    radius = float.MaxValue,
 | 
					                    radius = float.MaxValue,
 | 
				
			||||||
                    r2 = float.MaxValue,
 | 
					                    r2 = float.MaxValue,
 | 
				
			||||||
                    lightTableIndex = worldRep.LightingTable.LightCount,
 | 
					                    lightTableIndex = worldRep.LightingTable.LightCount,
 | 
				
			||||||
| 
						 | 
					@ -266,7 +243,7 @@ class Program
 | 
				
			||||||
                    var light = new Light
 | 
					                    var light = new Light
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        position = baseLight.position + propLight.Offset,
 | 
					                        position = baseLight.position + propLight.Offset,
 | 
				
			||||||
                        color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
 | 
					                        color = Utils.HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
 | 
				
			||||||
                        innerRadius = propLight.InnerRadius,
 | 
					                        innerRadius = propLight.InnerRadius,
 | 
				
			||||||
                        radius = propLight.Radius,
 | 
					                        radius = propLight.Radius,
 | 
				
			||||||
                        r2 = propLight.Radius * propLight.Radius,
 | 
					                        r2 = propLight.Radius * propLight.Radius,
 | 
				
			||||||
| 
						 | 
					@ -303,7 +280,7 @@ class Program
 | 
				
			||||||
                    var light = new Light
 | 
					                    var light = new Light
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        position = baseLight.position + propAnimLight.Offset,
 | 
					                        position = baseLight.position + propAnimLight.Offset,
 | 
				
			||||||
                        color = HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness),
 | 
					                        color = Utils.HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness),
 | 
				
			||||||
                        innerRadius = propAnimLight.InnerRadius,
 | 
					                        innerRadius = propAnimLight.InnerRadius,
 | 
				
			||||||
                        radius = propAnimLight.Radius,
 | 
					                        radius = propAnimLight.Radius,
 | 
				
			||||||
                        r2 = propAnimLight.Radius * propAnimLight.Radius,
 | 
					                        r2 = propAnimLight.Radius * propAnimLight.Radius,
 | 
				
			||||||
| 
						 | 
					@ -369,9 +346,8 @@ class Program
 | 
				
			||||||
        var indices = new List<int>();
 | 
					        var indices = new List<int>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var cells = worldRep.Cells;
 | 
					        var cells = worldRep.Cells;
 | 
				
			||||||
        for (var cellIdx = 0; cellIdx < cells.Length; cellIdx++)
 | 
					        foreach (var cell in cells)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var cell = cells[cellIdx];
 | 
					 | 
				
			||||||
            var numPolys = cell.PolyCount;
 | 
					            var numPolys = cell.PolyCount;
 | 
				
			||||||
            var numRenderPolys = cell.RenderPolyCount;
 | 
					            var numRenderPolys = cell.RenderPolyCount;
 | 
				
			||||||
            var numPortalPolys = cell.PortalPolyCount;
 | 
					            var numPortalPolys = cell.PortalPolyCount;
 | 
				
			||||||
| 
						 | 
					@ -384,7 +360,7 @@ class Program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
 | 
					            var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
 | 
				
			||||||
            var cellIdxOffset = 0;
 | 
					            var cellIdxOffset = 0;
 | 
				
			||||||
            for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
					            for (var polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var poly = cell.Polys[polyIdx];
 | 
					                var poly = cell.Polys[polyIdx];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -396,7 +372,7 @@ class Program
 | 
				
			||||||
                    vertices.Add(vertex);
 | 
					                    vertices.Add(vertex);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (int j = 1; j < numPolyVertices - 1; j++)
 | 
					                for (var j = 1; j < numPolyVertices - 1; j++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    indices.Add(meshIndexOffset);
 | 
					                    indices.Add(meshIndexOffset);
 | 
				
			||||||
                    indices.Add(meshIndexOffset + j);
 | 
					                    indices.Add(meshIndexOffset + j);
 | 
				
			||||||
| 
						 | 
					@ -413,42 +389,6 @@ class Program
 | 
				
			||||||
    private static void CastSceneParallel(Raytracer scene, WorldRep wr, Light[] lights, Vector3 ambientLight)
 | 
					    private static void CastSceneParallel(Raytracer scene, WorldRep wr, Light[] lights, Vector3 ambientLight)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var hdr = wr.DataHeader.LightmapFormat == 2;
 | 
					        var hdr = wr.DataHeader.LightmapFormat == 2;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // We set up light indices in a separate loop because the actual lighting
 | 
					 | 
				
			||||||
        // phase takes a lot of shortcuts that we don't want
 | 
					 | 
				
			||||||
        Parallel.ForEach(wr.Cells, cell =>
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            cell.LightIndexCount = 0;
 | 
					 | 
				
			||||||
            cell.LightIndices.Clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // The OG lightmapper uses the cell traversal to work out all the cells that
 | 
					 | 
				
			||||||
            // are actually visited. We're a lot more coarse and just say if a cell is
 | 
					 | 
				
			||||||
            // in range then we potentially affect the lighting in the cell and add it 
 | 
					 | 
				
			||||||
            // to the list. Cells already contain their sphere bounds so we just use
 | 
					 | 
				
			||||||
            // that for now, but a tighter AABB is another option.
 | 
					 | 
				
			||||||
            var cellSphere = new MathUtils.Sphere(cell.SphereCenter, cell.SphereRadius);
 | 
					 | 
				
			||||||
            foreach (var light in lights)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // If the light had radius 0 (represented here with max float) then we
 | 
					 | 
				
			||||||
                // always add it to the list
 | 
					 | 
				
			||||||
                // TODO: Neaten this up
 | 
					 | 
				
			||||||
                if (light.radius == float.MaxValue)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    cell.LightIndexCount++;
 | 
					 | 
				
			||||||
                    cell.LightIndices.Add((ushort)light.lightTableIndex);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var lightSphere = new MathUtils.Sphere(light.position, light.radius);
 | 
					 | 
				
			||||||
                    if (MathUtils.Intersects(cellSphere, lightSphere))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        cell.LightIndexCount++;
 | 
					 | 
				
			||||||
                        cell.LightIndices.Add((ushort)light.lightTableIndex);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Parallel.ForEach(wr.Cells, cell =>
 | 
					        Parallel.ForEach(wr.Cells, cell =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // Reset cell AnimLight palette
 | 
					            // Reset cell AnimLight palette
 | 
				
			||||||
| 
						 | 
					@ -468,7 +408,7 @@ class Program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
 | 
					            var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
 | 
				
			||||||
            var cellIdxOffset = 0;
 | 
					            var cellIdxOffset = 0;
 | 
				
			||||||
            for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
					            for (var polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var poly = cell.Polys[polyIdx];
 | 
					                var poly = cell.Polys[polyIdx];
 | 
				
			||||||
                var plane = cell.Planes[poly.PlaneId];
 | 
					                var plane = cell.Planes[poly.PlaneId];
 | 
				
			||||||
| 
						 | 
					@ -505,13 +445,6 @@ class Program
 | 
				
			||||||
                foreach (var light in lights)
 | 
					                foreach (var light in lights)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var layer = 0;
 | 
					                    var layer = 0;
 | 
				
			||||||
                    if (light.anim)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        // Because we're building the AnimLightBitmask in this loop we
 | 
					 | 
				
			||||||
                        // know there aren't any layers set above us. So the target layer
 | 
					 | 
				
			||||||
                        // is just the number of set bits + 1.
 | 
					 | 
				
			||||||
                        layer = BitOperations.PopCount(info.AnimLightBitmask) + 1;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Check if plane normal is facing towards the light
 | 
					                    // Check if plane normal is facing towards the light
 | 
				
			||||||
                    // If it's not then we're never going to be (directly) lit by this
 | 
					                    // If it's not then we're never going to be (directly) lit by this
 | 
				
			||||||
| 
						 | 
					@ -595,11 +528,7 @@ class Program
 | 
				
			||||||
                                        cell.AnimLights.Add((ushort)light.lightTableIndex);
 | 
					                                        cell.AnimLights.Add((ushort)light.lightTableIndex);
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    info.AnimLightBitmask |= 1u << paletteIdx;
 | 
					                                    info.AnimLightBitmask |= 1u << paletteIdx;
 | 
				
			||||||
 | 
					                                    layer = paletteIdx + 1;
 | 
				
			||||||
                                    if (layer >= lightmap.Layers)
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        lightmap.AddLayer();
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                var strength = CalculateLightStrengthAtPoint(light, pos, plane);
 | 
					                                var strength = CalculateLightStrengthAtPoint(light, pos, plane);
 | 
				
			||||||
                                lightmap.AddLight(layer, x, y, light.color, strength, hdr);
 | 
					                                lightmap.AddLight(layer, x, y, light.color, strength, hdr);
 | 
				
			||||||
| 
						 | 
					@ -613,6 +542,32 @@ class Program
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void SetCellLightIndices(WorldRep wr, Light[] lights)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // We set up light indices in separately from lighting because the actual
 | 
				
			||||||
 | 
					        // lighting phase takes a lot of shortcuts that we don't want
 | 
				
			||||||
 | 
					        Parallel.ForEach(wr.Cells, cell =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cell.LightIndexCount = 0;
 | 
				
			||||||
 | 
					            cell.LightIndices.Clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // The OG lightmapper uses the cell traversal to work out all the cells that
 | 
				
			||||||
 | 
					            // are actually visited. We're a lot more coarse and just say if a cell is
 | 
				
			||||||
 | 
					            // in range then we potentially affect the lighting in the cell and add it 
 | 
				
			||||||
 | 
					            // to the list. Cells already contain their sphere bounds so we just use
 | 
				
			||||||
 | 
					            // that for now, but a tighter AABB is another option.
 | 
				
			||||||
 | 
					            var cellSphere = new MathUtils.Sphere(cell.SphereCenter, cell.SphereRadius);
 | 
				
			||||||
 | 
					            foreach (var light in lights)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (MathUtils.Intersects(cellSphere, new MathUtils.Sphere(light.position, light.radius)))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    cell.LightIndexCount++;
 | 
				
			||||||
 | 
					                    cell.LightIndices.Add((ushort)light.lightTableIndex);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static float CalculateLightStrengthAtPoint(Light light, Vector3 point, Plane plane)
 | 
					    private static float CalculateLightStrengthAtPoint(Light light, Vector3 point, Plane plane)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Calculate light strength at a given point. As far as I can tell
 | 
					        // Calculate light strength at a given point. As far as I can tell
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,33 @@ using System.Numerics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace KeepersCompound.Lightmapper;
 | 
					namespace KeepersCompound.Lightmapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public static class Utils
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Expects Hue and Saturation are 0-1, Brightness 0-255
 | 
				
			||||||
 | 
					    // https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
 | 
				
			||||||
 | 
					    public static Vector3 HsbToRgb(float hue, float saturation, float brightness)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        hue *= 360;
 | 
				
			||||||
 | 
					        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),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public static class MathUtils
 | 
					public static class MathUtils
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public const float Epsilon = 0.001f;
 | 
					    public const float Epsilon = 0.001f;
 | 
				
			||||||
| 
						 | 
					@ -49,6 +76,7 @@ public static class MathUtils
 | 
				
			||||||
        return d2 < r2;
 | 
					        return d2 < r2;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should automagically handle max float radii
 | 
				
			||||||
    public static bool Intersects(Sphere sphere, Sphere other)
 | 
					    public static bool Intersects(Sphere sphere, Sphere other)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var rsum = sphere.Radius + other.Radius;
 | 
					        var rsum = sphere.Radius + other.Radius;
 | 
				
			||||||
		Loading…
	
		Reference in New Issue