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
 | 
			
		||||
        {
 | 
			
		||||
            private readonly bool[] _litLayers;
 | 
			
		||||
            public List<byte[]> Pixels { get; set; }
 | 
			
		||||
 | 
			
		||||
            public int Layers;
 | 
			
		||||
| 
						 | 
				
			
			@ -163,10 +164,12 @@ public class WorldRep : IChunk
 | 
			
		|||
            {
 | 
			
		||||
                var layers = 1 + BitOperations.PopCount(bitmask);
 | 
			
		||||
                var length = bytesPerPixel * width * height;
 | 
			
		||||
                _litLayers = new bool[33];
 | 
			
		||||
                Pixels = new List<byte[]>();
 | 
			
		||||
                for (var i = 0; i < layers; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    Pixels.Add(reader.ReadBytes(length));
 | 
			
		||||
                    _litLayers[i] = true;
 | 
			
		||||
                }
 | 
			
		||||
                Layers = layers;
 | 
			
		||||
                Width = width;
 | 
			
		||||
| 
						 | 
				
			
			@ -246,6 +249,8 @@ public class WorldRep : IChunk
 | 
			
		|||
                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)
 | 
			
		||||
| 
						 | 
				
			
			@ -260,12 +265,7 @@ public class WorldRep : IChunk
 | 
			
		|||
                // the hue/saturation of coloured lights. Got to make sure we
 | 
			
		||||
                // maintain the colour ratios.
 | 
			
		||||
                var c = color * strength;
 | 
			
		||||
                var ratio = 0.0f;
 | 
			
		||||
                foreach (var e in new float[] { c.X, c.Y, c.Z })
 | 
			
		||||
                {
 | 
			
		||||
                    ratio = Math.Max(ratio, e / 255.0f);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var ratio = Math.Max(Math.Max(Math.Max(0.0f, c.X), c.Y), c.Z) / 255.0f;
 | 
			
		||||
                if (ratio > 1.0f)
 | 
			
		||||
                {
 | 
			
		||||
                    c /= ratio;
 | 
			
		||||
| 
						 | 
				
			
			@ -276,9 +276,18 @@ public class WorldRep : IChunk
 | 
			
		|||
 | 
			
		||||
            public void Reset(Vector3 ambientLight, bool hdr)
 | 
			
		||||
            {
 | 
			
		||||
                Layers = 0;
 | 
			
		||||
                Layers = 33;
 | 
			
		||||
                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++)
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
            {
 | 
			
		||||
                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 DarkObject
 | 
			
		||||
    private class DarkObject
 | 
			
		||||
    {
 | 
			
		||||
        public int objectId;
 | 
			
		||||
        public int parentId;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ public class ObjectHierarchy
 | 
			
		|||
            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))
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,11 +79,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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +103,7 @@ public class ObjectHierarchy
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,6 +74,7 @@ class Program
 | 
			
		|||
            return;
 | 
			
		||||
        var ambient = rendParams.ambientLight * 255;
 | 
			
		||||
        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("Update Anim Mapping", () => SetAnimLightCellMaps(mis, worldRep, lights));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,30 +86,6 @@ class Program
 | 
			
		|||
        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(
 | 
			
		||||
        DbFile mis,
 | 
			
		||||
        WorldRep worldRep,
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +170,7 @@ class Program
 | 
			
		|||
                var light = new Light
 | 
			
		||||
                {
 | 
			
		||||
                    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,
 | 
			
		||||
                    r2 = float.MaxValue,
 | 
			
		||||
                    lightTableIndex = worldRep.LightingTable.LightCount,
 | 
			
		||||
| 
						 | 
				
			
			@ -266,7 +243,7 @@ class Program
 | 
			
		|||
                    var light = new Light
 | 
			
		||||
                    {
 | 
			
		||||
                        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,
 | 
			
		||||
                        radius = propLight.Radius,
 | 
			
		||||
                        r2 = propLight.Radius * propLight.Radius,
 | 
			
		||||
| 
						 | 
				
			
			@ -303,7 +280,7 @@ class Program
 | 
			
		|||
                    var light = new Light
 | 
			
		||||
                    {
 | 
			
		||||
                        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,
 | 
			
		||||
                        radius = propAnimLight.Radius,
 | 
			
		||||
                        r2 = propAnimLight.Radius * propAnimLight.Radius,
 | 
			
		||||
| 
						 | 
				
			
			@ -369,9 +346,8 @@ class Program
 | 
			
		|||
        var indices = new List<int>();
 | 
			
		||||
 | 
			
		||||
        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 numRenderPolys = cell.RenderPolyCount;
 | 
			
		||||
            var numPortalPolys = cell.PortalPolyCount;
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +360,7 @@ class Program
 | 
			
		|||
 | 
			
		||||
            var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
 | 
			
		||||
            var cellIdxOffset = 0;
 | 
			
		||||
            for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
			
		||||
            for (var polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                var poly = cell.Polys[polyIdx];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +372,7 @@ class Program
 | 
			
		|||
                    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 + j);
 | 
			
		||||
| 
						 | 
				
			
			@ -413,42 +389,6 @@ class Program
 | 
			
		|||
    private static void CastSceneParallel(Raytracer scene, WorldRep wr, Light[] lights, Vector3 ambientLight)
 | 
			
		||||
    {
 | 
			
		||||
        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 =>
 | 
			
		||||
        {
 | 
			
		||||
            // Reset cell AnimLight palette
 | 
			
		||||
| 
						 | 
				
			
			@ -468,7 +408,7 @@ class Program
 | 
			
		|||
 | 
			
		||||
            var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys);
 | 
			
		||||
            var cellIdxOffset = 0;
 | 
			
		||||
            for (int polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
			
		||||
            for (var polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                var poly = cell.Polys[polyIdx];
 | 
			
		||||
                var plane = cell.Planes[poly.PlaneId];
 | 
			
		||||
| 
						 | 
				
			
			@ -505,13 +445,6 @@ class Program
 | 
			
		|||
                foreach (var light in lights)
 | 
			
		||||
                {
 | 
			
		||||
                    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
 | 
			
		||||
                    // 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);
 | 
			
		||||
                                    }
 | 
			
		||||
                                    info.AnimLightBitmask |= 1u << paletteIdx;
 | 
			
		||||
 | 
			
		||||
                                    if (layer >= lightmap.Layers)
 | 
			
		||||
                                    {
 | 
			
		||||
                                        lightmap.AddLayer();
 | 
			
		||||
                                    }
 | 
			
		||||
                                    layer = paletteIdx + 1;
 | 
			
		||||
                                }
 | 
			
		||||
                                var strength = CalculateLightStrengthAtPoint(light, pos, plane);
 | 
			
		||||
                                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)
 | 
			
		||||
    {
 | 
			
		||||
        // Calculate light strength at a given point. As far as I can tell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,33 @@ using System.Numerics;
 | 
			
		|||
 | 
			
		||||
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 const float Epsilon = 0.001f;
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +76,7 @@ public static class MathUtils
 | 
			
		|||
        return d2 < r2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Should automagically handle max float radii
 | 
			
		||||
    public static bool Intersects(Sphere sphere, Sphere other)
 | 
			
		||||
    {
 | 
			
		||||
        var rsum = sphere.Radius + other.Radius;
 | 
			
		||||
		Loading…
	
		Reference in New Issue