diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/MyFreeList.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/MyFreeList.cs index 57cfacd..d2be139 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/MyFreeList.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/MyFreeList.cs @@ -6,7 +6,7 @@ namespace Thermodynamics { public class MyFreeList { - public TItem[] list; + public TItem[] ItemArray; private int m_size; @@ -24,11 +24,11 @@ public class MyFreeList /// public int Count => m_size - m_freePositions.Count; - public int Capacity => list.Length; + public int Capacity => ItemArray.Length; public MyFreeList(int capacity = 16, TItem defaultValue = default(TItem)) { - list = new TItem[16]; + ItemArray = new TItem[16]; m_freePositions = new Queue(capacity / 2); m_default = defaultValue; } @@ -39,9 +39,9 @@ public int Allocate() { return m_freePositions.Dequeue(); } - if (m_size == list.Length) + if (m_size == ItemArray.Length) { - Array.Resize(ref list, list.Length << 1); + Array.Resize(ref ItemArray, ItemArray.Length << 1); } return m_size++; } @@ -49,13 +49,13 @@ public int Allocate() public int Allocate(TItem value) { int num = Allocate(); - list[num] = value; + ItemArray[num] = value; return num; } public void Free(int position) { - list[position] = m_default; + ItemArray[position] = m_default; if (position == m_size) { m_size--; @@ -68,7 +68,7 @@ public void Free(int position) public TItem[] GetInternalArray() { - return list; + return ItemArray; } public bool KeyValid(int key) @@ -80,7 +80,7 @@ public void Clear() { for (int i = 0; i < m_size; i++) { - list[i] = default(TItem); + ItemArray[i] = default(TItem); } m_size = 0; m_freePositions.Clear(); diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs index 05a96b5..de7a6f8 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/Session.cs @@ -14,7 +14,6 @@ using VRage.Game.ModAPI; using VRage.Utils; using VRageMath; -using static VRageRender.MyBillboard; namespace Thermodynamics { @@ -99,7 +98,8 @@ public override void Simulate() $"T: {c.Temperature.ToString("n4")} " + $"dT: {c.DeltaTemperature.ToString("n6")} " + $"Gen: {c.HeatGeneration.ToString("n4")} " + - $"ext: {c.ExposedSurfaces.ToString("n4")} ", 1, "White"); + $"ext: {c.ExposedSurfaces.ToString("n4")} " + + $"kA: {string.Join(", ", c.kA)}", 1, "White"); MyAPIGateway.Utilities.ShowNotification( $"[Calc] m: {c.Mass.ToString("n0")} " + @@ -108,7 +108,6 @@ public override void Simulate() $"em {c.Definition.Emissivity} " + $"pwe: {c.Definition.ProducerWasteEnergy} " + $"cwe: {c.Definition.ConsumerWasteEnergy} " + - $"kA: {(c.Definition.Conductivity * c.Area).ToString("n0")} " + $"tm: {(c.Definition.SpecificHeat * c.Mass).ToString("n0")} " + $"c: {c.C.ToString("n4")} " + $"r: {c.Radiation.ToString("n2")} " + @@ -116,20 +115,21 @@ public override void Simulate() $"prod: {c.EnergyProduction} " + $"cons: {(c.EnergyConsumption + c.ThrustEnergyConsumption)} ", 1, "White"); - //MyAPIGateway.Utilities.ShowNotification($"[Solar] {c.SolarIntensity.ToString("n3")} Average: {g.AverageSolarHeat[0].ToString("n3")}, {tGrid.AverageSolarHeat[1].ToString("n3")}, {tGrid.AverageSolarHeat[2].ToString("n3")}, {tGrid.AverageSolarHeat[3].ToString("n3")}, {tGrid.AverageSolarHeat[4].ToString("n3")}, {tGrid.AverageSolarHeat[5].ToString("n3")}", 1, "White"); - - //Grid.AverageSolarHeat[directionIndex]) - - //MyAPIGateway.Utilities.ShowNotification($"[Grid] Exposed: {tGrid.ExposedNodes.Count} {tGrid.ExposedSurface.Count} inside: {tGrid.InsideNodes.Count} {tGrid.InsideSurface.Count} Rooms: {tGrid.Rooms.Count}", 1, "White"); - //MyAPIGateway.Utilities.ShowNotification($"[Cell] Exposed: {cell.Exposed.Count} {cell.ExposedSurface.Count} Inside: {cell.Inside.Count} {cell.InsideSurface.Count} SurfaceArea: {cell.ExposedSurfaceArea}", 1, "White"); - + int value = g.NodeSurfaces[position]; + MyAPIGateway.Utilities.ShowNotification( + $"[Grid] Exterior: {g.ExteriorNodes.Count} " + + $"Nodes: {g.NodeSurfaces.Count} " + + $"RNodes: {g.Rooms.Count} " + + $"sq: {g.SolidQueue.Count} " + + $"rq: {g.RoomQueue.Count} " + + $"CrawlDone: {g.ThermalCellUpdateComplete} " + + $"sbn: {string.Join(", ", c.TouchingSerfacesByNeighbor)}", 1, "White"); - //MyAPIGateway.Utilities.ShowNotification($"[Cell] Input: {cell.PowerInput} heat: {cell.PowerInput * cell.ConsumerGeneration} heatPerWatt: {cell.ConsumerGeneration}", 1, "White"); - //MyAPIGateway.Utilities.ShowNotification($"[Cell] Output: {cell.PowerOutput} heat: {cell.PowerOutput * cell.ProducerGeneration} heatPerWatt: {cell.ProducerGeneration}", 1, "White"); + MyAPIGateway.Utilities.ShowNotification( + $"[Cell] Airtight out: {((value & 1 << 0) != 0 ? 1:0)}, {((value & 1 << 1) != 0 ? 1:0)}, {((value & 1 << 2) != 0?1:0)}, {((value & 1 << 3) != 0?1:0)}, {((value & 1 << 4) != 0?1:0)}, {((value & 1 << 5) != 0 ? 1 : 0)}, " + + $"in: {((value & 1 << 6) != 0?1:0)}, {((value & 1 << 7) != 0 ? 1 : 0)}, {((value & 1 << 8) != 0 ? 1 : 0)}, {((value & 1 << 9) != 0 ? 1 : 0)}, {((value & 1 << 10) != 0?1:0)}, {((value & 1 << 11) != 0?1:0)}", 1, "White"); - //MyAPIGateway.Utilities.ShowNotification($"[Cell] Exposed: {cell.Exposed.Count} Inside: {cell.Inside.Count} SurfaceArea: {cell.ExposedSurfaceArea}", 1, "White"); - //MyAPIGateway.Utilities.ShowNotification($"[External] {tGrid.Mapper.Blocks.Count} EComplete: {tGrid.Mapper.ExternalRoomUpdateComplete} BComplete: {tGrid.ThermalCellUpdateComplete}", 1, "White"); } } diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs index 8189028..20ced73 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalCell.cs @@ -18,6 +18,9 @@ using VRage.Game.Components.Interfaces; using System.Drawing; using System.Security.AccessControl; +using System.Net; +using VRageRender.Messages; +using static VRage.Game.MyObjectBuilder_CurveDefinition; namespace Thermodynamics { @@ -50,10 +53,11 @@ public class ThermalCell public ThermalCellDefinition Definition; public List Neighbors = new List(); - private List TouchingSerfacesByNeighbor = new List(); + public List TouchingSerfacesByNeighbor = new List(); public int ExposedSurfaces = 0; - public List ExposedSurfaceDirections = new List(); + private int[] ExposedSurfacesByDirection = new int[6]; + //public List ExposedSurfaceDirections = new List(); public ThermalCell(ThermalGrid g, IMySlimBlock b) { @@ -113,7 +117,7 @@ private void SetupListeners() } else if (fat is IMyDoor) { - (fat as IMyDoor).DoorStateChanged += (state) => Grid.ResetMapper(); + (fat as IMyDoor).DoorStateChanged += (state) => Grid.UpdateBlockMapping(ref Block); } else if (fat is IMyLandingGear) { @@ -128,7 +132,7 @@ private void SetupListeners() // if the entity is not MyCubeGrid reset landing gear neighbors because we probably detached if (!(entity is MyCubeGrid)) { - c.ResetNeighbors(); + c.AddAllNeighbors(); return; } @@ -162,13 +166,13 @@ private void SetupListeners() //MyLog.Default.Info($"[{Settings.Name}] testing {temp} {ncell != null}"); if (ncell != null) { - AddNeighbors(c, ncell); + c.AddNeighbor(ncell); } } } } - CalculatekA(); + c.CalculatekA(); }; } @@ -219,7 +223,7 @@ private void GridGroupChanged(IMyMechanicalConnectionBlock block) //MyLog.Default.Info($"[{Settings.Name}] Grid: {cell.Block.CubeGrid.EntityId} cell: {cell.Block.Position} removed connection to, Grid: {ncell.Block.CubeGrid.EntityId} Cell: {ncell.Block.Position}"); - RemoveNeighbors(cell, ncell); + cell.RemoveNeighbor(ncell); break; } } @@ -229,7 +233,7 @@ private void GridGroupChanged(IMyMechanicalConnectionBlock block) //MyLog.Default.Info($"[{Settings.Name}] Grid: {cell.Block.CubeGrid.EntityId} cell: {cell.Block.Position} adding connection to, nGrid: {ncell.Block.CubeGrid.EntityId} nCell: {ncell.Block.Position}"); - AddNeighbors(cell, ncell); + cell.AddNeighbor(ncell); } } @@ -270,12 +274,6 @@ private void PowerProducedChanged(MyDefinitionId resourceTypeId, float oldOutput catch { } } - public void ResetNeighbors() - { - ClearNeighbors(); - AddAllNeighbors(); - } - public void ClearNeighbors() { for (int i = 0; i < Neighbors.Count; i++) @@ -297,6 +295,7 @@ public void ClearNeighbors() public void AddAllNeighbors() { + ClearNeighbors(); //get a list of current neighbors from the grid List neighbors = new List(); Block.GetNeighbours(neighbors); @@ -305,43 +304,85 @@ public void AddAllNeighbors() { IMySlimBlock n = neighbors[i]; ThermalCell ncell = Grid.Get(n.Position); + AddNeighbor(ncell); + } + } + + protected void AddNeighbor(ThermalCell n2) + { + //MyLog.Default.Info($"[Thermals] AddNeighbours"); + Neighbors.Add(n2); + n2.Neighbors.Add(this); - if (!Neighbors.Contains(ncell)) + int area = FindSurfaceArea(n2); + TouchingSerfacesByNeighbor.Add(area); + n2.TouchingSerfacesByNeighbor.Add(area); + + CalculatekA(); + n2.CalculatekA(); + } + + /// + /// Maps out the surface area between two blocks + /// + /// area in grid units (not meters) + protected int FindSurfaceArea(ThermalCell neighbor) + { + int index = Neighbors.IndexOf(neighbor); + int totalArea = 0; + + Vector3I position = Block.Position; + List mounts = new List(); + MyBlockOrientation orientation = Block.Orientation; + MyCubeBlockDefinition def = (MyCubeBlockDefinition)Block.BlockDefinition; + MyCubeGrid.TransformMountPoints(mounts, def, def.MountPoints, ref orientation); + + Vector3I nposition = neighbor.Block.Position; + List neighborMounts = new List(); + MyBlockOrientation neighborOrientation = neighbor.Block.Orientation; + MyCubeBlockDefinition neighborDef = (MyCubeBlockDefinition)neighbor.Block.BlockDefinition; + MyCubeGrid.TransformMountPoints(neighborMounts, neighborDef, neighborDef.MountPoints, ref neighborOrientation); + + Vector3I p = nposition - position; + for (int i = 0; i < mounts.Count; i++) + { + MyCubeBlockDefinition.MountPoint a = mounts[i]; + if (!a.Enabled) { - Neighbors.Add(ncell); - ncell.Neighbors.Add(this); + continue; + } - int area = 1; - if (Grid.Entity.EntityId == ncell.Grid.Entity.EntityId) + Vector3 min = Vector3.Min(a.Start, a.End); + Vector3 max = Vector3.Max(a.Start, a.End); + min -= (Vector3)p; + max -= (Vector3)p; + BoundingBox mainBox = new BoundingBox(min, max); + for (int j = 0; j < neighborMounts.Count; j++) + { + MyCubeBlockDefinition.MountPoint b = neighborMounts[j]; + if (!b.Enabled) { - area = Tools.FindTouchingSurfaceArea(Block.Min, Block.Max + 1, ncell.Block.Min, ncell.Block.Max + 1); + continue; } - TouchingSerfacesByNeighbor.Add(area); - ncell.TouchingSerfacesByNeighbor.Add(area); - ncell.CalculatekA(); - AddNeighbors(this, ncell); - } - } + BoundingBox neighborBox = new BoundingBox(Vector3.Min(b.Start, b.End), Vector3.Max(b.Start, b.End)); + + if (mainBox.Intersects(neighborBox)) + { + BoundingBox box = mainBox.Intersect(neighborBox); - CalculatekA(); - } + Vector3I va = new Vector3I((int)Math.Round(box.Max.X - box.Min.X), (int)Math.Round(box.Max.Y - box.Min.Y), (int)Math.Round(box.Max.Z - box.Min.Z)); - protected void AddNeighbors(ThermalCell n1, ThermalCell n2) - { - n1.Neighbors.Add(n2); - n2.Neighbors.Add(n1); + int area = (va.X == 0) ? 1 : va.X; + area *= (va.Y == 0) ? 1 : va.Y; + area *= (va.Z == 0) ? 1 : va.Z; - int area = 1; - if (n1.Grid.Entity.EntityId == n2.Grid.Entity.EntityId) - { - area = Tools.FindTouchingSurfaceArea(n1.Block.Min, n1.Block.Max + 1, n2.Block.Min, n2.Block.Max + 1); + totalArea += area; + } + } } - n1.TouchingSerfacesByNeighbor.Add(area); - n2.TouchingSerfacesByNeighbor.Add(area); - n1.CalculatekA(); - n2.CalculatekA(); + return totalArea; } protected void CalculatekA() @@ -354,17 +395,17 @@ protected void CalculatekA() } } - protected void RemoveNeighbors(ThermalCell n1, ThermalCell n2) + protected void RemoveNeighbor(ThermalCell n2) { - int i = n1.Neighbors.IndexOf(n2); + int i = Neighbors.IndexOf(n2); if (i != -1) { - n1.Neighbors.RemoveAt(i); - n1.TouchingSerfacesByNeighbor.RemoveAt(i); - n1.CalculatekA(); + Neighbors.RemoveAt(i); + TouchingSerfacesByNeighbor.RemoveAt(i); + CalculatekA(); } - int j = n2.Neighbors.IndexOf(n1); + int j = n2.Neighbors.IndexOf(this); if (i != -1) { n2.Neighbors.RemoveAt(j); @@ -373,7 +414,6 @@ protected void RemoveNeighbors(ThermalCell n1, ThermalCell n2) } } - public float GetTemperature() { return Temperature; @@ -440,6 +480,8 @@ private float CalculateDeltaTemperature() float neighborTemp = Neighbors[i].Frame == Frame ? Neighbors[i].LastTemprature : Neighbors[i].Temperature; deltaTemperature += kA[i] * (neighborTemp - currentTemperature); } + + return deltaTemperature; } @@ -479,23 +521,24 @@ private void UpdateDebugVisuals() } } - public void UpdateSurfaces(ref HashSet exterior, ref Vector3I[] neighbors) - { - ResetExposedSurfaces(); - CalculateExposedSurfaces(ref exterior, ref neighbors); - UpdateExposedSurfaceArea(); - } private void ResetExposedSurfaces() { + //MyLog.Default.Info($"[Thermals] Reset Exposed Surfaces"); ExposedSurfaces = 0; - ExposedSurfaceDirections.Clear(); + ExposedSurfacesByDirection = new int[6]; } - private void CalculateExposedSurfaces(ref HashSet exterior, ref Vector3I[] neighbors) + public void UpdateSurfaces(ref HashSet exterior, ref Dictionary nodeSurfaces) { + //MyLog.Default.Info($"[Thermals] Update Surfaces"); + ResetExposedSurfaces(); + Vector3I min = Block.Min; Vector3I max = Block.Max + 1; + Vector3I[] neighbors = Base6Directions.IntDirections; + MyCubeGrid grid = Grid.Grid; + for (int x = min.X; x < max.X; x++) { @@ -503,40 +546,46 @@ private void CalculateExposedSurfaces(ref HashSet exterior, ref Vector { for (int z = min.Z; z < max.Z; z++) { - ProcessNode(new Vector3I(x, y, z), ref exterior, ref neighbors); + Vector3I node = new Vector3I(x, y, z); + int n = nodeSurfaces[node]; + + for (int i = 0; i < neighbors.Length; i++) + { + Vector3I nodeNei = node + neighbors[i]; + + // neghboring node is not airtight + if ((n & 1 << i) == 0) + { + if (exterior.Contains(nodeNei)) + { + ExposedSurfaces++; + ExposedSurfacesByDirection[i]++; + } + else + { + //TODO: handle internal rooms + } + } + } } } } - } - - private void ProcessNode(Vector3I node, ref HashSet exterior, ref Vector3I[] neighbors) - { - int flag = Grid.BlockNodes[node]; - for (int i = 0; i < 6; i++) - { - ProcessNodeDirection(node, flag, i, ref exterior, ref neighbors); - } + UpdateExposedSurfaceArea(); + //MyLog.Default.Info($"[Thermals] Exposed Surfaces: {ExposedSurfaces}"); } - private void ProcessNodeDirection(Vector3I node, int flag, int i, ref HashSet exterior, ref Vector3I[] neighbors) + + public bool Contains(Vector3I n) { - int d = (1 << i); - int nd = (1 << i + 6); - if ((flag & nd) == 0) - { - Vector3I neighbor = neighbors[i]; - Vector3I n = node + neighbor; + Vector3I min = Block.Min; + Vector3I max = Block.Max + 1; - if (exterior.Contains(n)) - { - ExposedSurfaces++; - if (!ExposedSurfaceDirections.Contains(neighbor)) - ExposedSurfaceDirections.Add(neighbor); - } - } + return n.X >= min.X && n.Y >= min.Y && n.Z >= min.Z && + n.X < max.X && n.Y < max.Y && n.Z < max.Z; } + private void UpdateExposedSurfaceArea() { ExposedSurfaceArea = ExposedSurfaces * Area; @@ -549,18 +598,17 @@ internal float DirectionalRadiationIntensity(ref Vector3 targetDirection, ref Th MatrixD matrix = Grid.FrameMatrix; bool isCube = (Block.Max - Block.Min).Volume() <= 1; - for (int i = 0; i < ExposedSurfaceDirections.Count; i++) + for (int i = 0; i < ExposedSurfacesByDirection.Length; i++) { - intensity += CalculateDirectionIntensity(i, ref targetDirection, ref node, matrix, isCube); + intensity += CalculateDirectionIntensity(i, ExposedSurfacesByDirection[i], ref targetDirection, ref node, matrix, isCube); } return intensity; } - private float CalculateDirectionIntensity(int i, ref Vector3 targetDirection, ref ThermalRadiationNode node, MatrixD matrix, bool isCube) + private float CalculateDirectionIntensity(int directionIndex, int surfaceCount, ref Vector3 targetDirection, ref ThermalRadiationNode node, MatrixD matrix, bool isCube) { - Vector3I direction = ExposedSurfaceDirections[i]; - int directionIndex = Tools.DirectionToIndex(direction); + Vector3I direction = Base6Directions.IntDirections[directionIndex]; Vector3D startDirection = Vector3D.Rotate(direction, matrix); float dot = Vector3.Dot(startDirection, targetDirection); @@ -568,8 +616,8 @@ private float CalculateDirectionIntensity(int i, ref Vector3 targetDirection, re if (isCube) { - node.Sides[directionIndex] += dot; - node.SideSurfaces[directionIndex]++; + node.Sides[directionIndex] += dot*surfaceCount; + node.SideSurfaces[directionIndex] += surfaceCount; return dot; } else diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs index 9c0a9f1..2a4362e 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGrid.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Transactions; @@ -116,15 +117,15 @@ public override bool IsSerialized() Save(); return base.IsSerialized(); } - + private string Pack() { - byte[] bytes = new byte[Thermals.Count * 8]; + byte[] bytes = new byte[Thermals.Count * 6]; int bi = 0; for (int i = 0; i < Thermals.UsedLength; i++) { - ThermalCell c = Thermals.list[i]; + ThermalCell c = Thermals.ItemArray[i]; if (c == null) continue; int id = c.Id; @@ -133,13 +134,14 @@ private string Pack() bytes[bi + 2] = (byte)(id >> 16); bytes[bi + 3] = (byte)(id >> 24); - int t = (int)(c.Temperature * 1000); + short t = (short)(c.Temperature); bytes[bi + 4] = (byte)t; bytes[bi + 5] = (byte)(t >> 8); - bytes[bi + 6] = (byte)(t >> 16); - bytes[bi + 7] = (byte)(t >> 24); - bi += 8; + bi += 6; + + //TODO keep track of this. it might cause weird behaivor. + c.Temperature = t; } return Convert.ToBase64String(bytes); @@ -147,24 +149,26 @@ private string Pack() private void Unpack(string data) { - byte[] bytes = Convert.FromBase64String(data); - - for (int i = 0; i < bytes.Length; i += 8) + try { - int id = bytes[i]; - id |= bytes[i + 1] << 8; - id |= bytes[i + 2] << 16; - id |= bytes[i + 3] << 24; + byte[] bytes = Convert.FromBase64String(data); + + for (int i = 0; i < bytes.Length; i += 6) + { + int id = bytes[i]; + id |= bytes[i + 1] << 8; + id |= bytes[i + 2] << 16; + id |= bytes[i + 3] << 24; - int f = bytes[i + 4]; - f |= bytes[i + 5] << 8; - f |= bytes[i + 6] << 16; - f |= bytes[i + 7] << 24; + int f = bytes[i + 4]; + f |= bytes[i + 5] << 8; - Thermals.list[PositionToIndex[id]].Temperature = f * 0.001f; + Thermals.ItemArray[PositionToIndex[id]].Temperature = f; + } - //MyLog.Default.Info($"[{Settings.Name}] [Unpack] {id} {PositionToIndex[id]} {Thermals.list[PositionToIndex[id]].Block.BlockDefinition.Id} - T: {f * 0.001f}"); + //MyLog.Default.Info($"[{Settings.Name}] [Unpack] {id} {PositionToIndex[id]} {Thermals.list[PositionToIndex[id]].Block.BlockDefinition.Id} - T: {f}"); } + catch{ } } private void Save() @@ -209,14 +213,13 @@ private void BlockAdded(IMySlimBlock b) return; } + AddBlockMapping(ref b); ThermalCell cell = new ThermalCell(this, b); cell.AddAllNeighbors(); int index = Thermals.Allocate(); PositionToIndex.Add(cell.Id, index); - Thermals.list[index] = cell; - - MapBlocks(b, true); + Thermals.ItemArray[index] = cell; } private void BlockRemoved(IMySlimBlock b) @@ -231,9 +234,11 @@ private void BlockRemoved(IMySlimBlock b) //MyLog.Default.Info($"[{Settings.Name}] [{Grid.EntityId}] Removed ({b.Position.Flatten()}) {b.Position}"); + RemoveBlockMapping(ref b); + int flat = b.Position.Flatten(); int index = PositionToIndex[flat]; - ThermalCell cell = Thermals.list[index]; + ThermalCell cell = Thermals.ItemArray[index]; if (RecentlyRemoved.ContainsKey(cell.Id)) { @@ -248,7 +253,6 @@ private void BlockRemoved(IMySlimBlock b) PositionToIndex.Remove(flat); Thermals.Free(index); - ResetMapper(); } private void GridSplit(MyCubeGrid g1, MyCubeGrid g2) @@ -260,7 +264,7 @@ private void GridSplit(MyCubeGrid g1, MyCubeGrid g2) for (int i = 0; i < tg2.Thermals.UsedLength; i++) { - ThermalCell c = tg2.Thermals.list[i]; + ThermalCell c = tg2.Thermals.ItemArray[i]; if (c == null) continue; if (tg1.RecentlyRemoved.ContainsKey(c.Id)) @@ -282,13 +286,13 @@ private void GridMerge(MyCubeGrid g1, MyCubeGrid g2) for (int i = 0; i < tg2.Thermals.UsedLength; i++) { - ThermalCell c = tg2.Thermals.list[i]; + ThermalCell c = tg2.Thermals.ItemArray[i]; if (c == null) continue; int id = c.Block.Position.Flatten(); if (tg1.PositionToIndex.ContainsKey(id)) { - tg1.Thermals.list[tg1.PositionToIndex[id]].Temperature = c.Temperature; + tg1.Thermals.ItemArray[tg1.PositionToIndex[id]].Temperature = c.Temperature; } } @@ -342,10 +346,13 @@ public override void UpdateBeforeSimulation() // prepare for the next simulation after a full iteration if (SimulationIndex == cellCount || SimulationIndex == -1) { + if (!ThermalCellUpdateComplete) + ThermalCellUpdateComplete = true; + // start a new simulation frame SimulationFrame++; - MapExterior(); + MapSurfaces(); PrepareNextSimulationStep(); // reverse the index direction @@ -353,27 +360,18 @@ public override void UpdateBeforeSimulation() // make sure the end cells in the list go once per frame SimulationIndex += Direction; - if (!ThermalCellUpdateComplete) - ThermalCellUpdateComplete = true; - if (!NodeUpdateComplete && - ExteriorQueue.Count == 0) - { - NodeUpdateComplete = true; - ThermalCellUpdateComplete = false; - } } //MyLog.Default.Info($"[{Settings.Name}] Frame: {FrameCount} SimFrame: {SimulationFrame}: Index: {SimulationIndex} Quota: {SimulationQuota} FrameQuota:{FrameQuota}"); - ThermalCell cell = Thermals.list[SimulationIndex]; + ThermalCell cell = Thermals.ItemArray[SimulationIndex]; if (cell != null) { if (!ThermalCellUpdateComplete) - { - //MyLog.Default.Info($"[Thermals] updating serfaces {ExteriorNodes.Count}"); - cell.UpdateSurfaces(ref ExteriorNodes, ref neighbors); + { + cell.UpdateSurfaces(ref ExteriorNodes, ref NodeSurfaces); } cell.Update(); @@ -560,7 +558,7 @@ public ThermalCell Get(Vector3I position) int flat = position.Flatten(); if (PositionToIndex.ContainsKey(flat)) { - return Thermals.list[PositionToIndex[flat]]; + return Thermals.ItemArray[PositionToIndex[flat]]; } return null; diff --git a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs index 76179fd..3354079 100644 --- a/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs +++ b/ThermalDynamics/Data/Scripts/Thermodynamics/ThermalGridMapper.cs @@ -1,7 +1,6 @@ using ProtoBuf.Meta; using Sandbox.Definitions; using Sandbox.Game; -using Sandbox.Definitions; using Sandbox.Game.Entities; using Sandbox.Game.EntityComponents; using Sandbox.ModAPI; @@ -15,58 +14,54 @@ using VRage.ModAPI; using VRage.Utils; using VRageMath; +using System.Linq; +using System.Runtime.CompilerServices; +using VRageRender; namespace Thermodynamics { public partial class ThermalGrid : MyGameLogicComponent { - private Vector3I[] neighbors = new Vector3I[6] - { - new Vector3I(1, 0, 0), // left - new Vector3I(-1, 0, 0), // right - new Vector3I(0, 1, 0), // up - new Vector3I(0, -1, 0), // down - new Vector3I(0, 0, 1), // forward - new Vector3I(0, 0, -1) // backward - }; - - public HashSet ExteriorNodes = new HashSet(); - public Queue ExteriorQueue = new Queue(); - private Vector3I min; private Vector3I max; + /// + /// Room id 0 and 1 are reserved for external nodes and airtight nodes respectively + /// + public int CurrentRoomId = 1; public int NodeCountPerFrame = 1; public bool NodeUpdateComplete = false; - public Dictionary BlockNodes = new Dictionary(); + public Queue ExteriorQueue = new Queue(); + public Queue SolidQueue = new Queue(); + public Queue RoomQueue = new Queue(); + + public Dictionary NodeSurfaces = new Dictionary(); + + public HashSet ExteriorNodes = new HashSet(); + public HashSet SolidNodes = new HashSet(); + //public HashSet InteriorNodes = new HashSet(); + + public Dictionary Rooms = new Dictionary(); - private void MapBlocks(IMySlimBlock block, bool isNew = false, IMySlimBlock removed = null) + private void AddBlockMapping(ref IMySlimBlock block) { - int state = 0; + ResetSpacialMapping(); + Vector3I min = block.Min; Vector3I max = block.Max + 1; + Vector3I[] neighbors = Base6Directions.IntDirections; - if (isNew) - { - if (Vector3I.Min(Grid.Min - 1, this.min) != this.min || - Vector3I.Max(Grid.Max + 1, this.max) != this.max) - ResetMapper(); - } - - HashSet processedNeighbours = new HashSet(); - processedNeighbours.Add(removed); + Queue processQueue = new Queue(); MyCubeBlockDefinition def = block.BlockDefinition as MyCubeBlockDefinition; - Matrix blockMatrix; - block.Orientation.GetMatrix(out blockMatrix); - blockMatrix.TransposeRotationInPlace(); + bool isAirtight = def?.IsAirTight == true; + Matrix matrix; + block.Orientation.GetMatrix(out matrix); + matrix.TransposeRotationInPlace(); + MyCubeBlockDefinition.MountPoint[] mountPoints = def.MountPoints; - bool isAirTight = def?.IsAirTight == true; - if (isAirTight) - { - state = 63; - } + MyLog.Default.Info($"[{Settings.Name}] Block Start"); for (int x = min.X; x < max.X; x++) { @@ -74,143 +69,111 @@ private void MapBlocks(IMySlimBlock block, bool isNew = false, IMySlimBlock remo { for (int z = min.Z; z < max.Z; z++) { - + int state = 0; Vector3I node = new Vector3I(x, y, z); - + + if (isAirtight) + { + state = 4032; + } + for (int i = 0; i < neighbors.Length; i++) { Vector3I n = node + neighbors[i]; - Vector3 direction1 = node - n; - Vector3 direction2 = n - node; + Vector3I towardSelf = node - n; - // neighbour is the same block - if (n.X >= min.X && n.Y >= min.Y && n.Z >= min.Z && - n.X < max.X && n.Y < max.Y && n.Z < max.Z) + if (IsAirtight(ref block, ref def, ref node, ref towardSelf, ref matrix)) { - - if (IsAirtight(ref block, ref def, ref node, ref direction2, ref blockMatrix)) - { - state |= 1 << i; - } - - if (IsAirtight(ref block, ref def, ref n, ref direction1, ref blockMatrix)) - { - state |= 1 << i + 6; - } + state |= 1 << i + 6; } - else + + int ns; + if (NodeSurfaces.TryGetValue(n, out ns)) { - IMySlimBlock nblock = Grid.GetCubeBlock(n); - if (nblock == null) - { - continue; - } - - // update all neighbor blocks if this block was just added to the grid - if (isNew && !processedNeighbours.Contains(nblock)) - { - MapBlocks(nblock); - processedNeighbours.Add(nblock); - } - - MyCubeBlockDefinition ndef = nblock.BlockDefinition as MyCubeBlockDefinition; - Matrix nMatrix; - nblock.Orientation.GetMatrix(out nMatrix); - nMatrix.TransposeRotationInPlace(); - - if (IsAirtight(ref block, ref def, ref node, ref direction2, ref blockMatrix)) - { - state |= 1 << i; - } - - if (ndef?.IsAirTight == true || - IsAirtight(ref nblock, ref ndef, ref n, ref direction1, ref nMatrix)) - { - state |= 1 << i + 6; - } + state |= ((ns & 1 << i + 6) >> 6); + processQueue.Enqueue(n); } } - if (isNew) - { - BlockNodes.Add(node, state); - } - else - { - BlockNodes[node] = state; - } - + NodeSurfaces.Add(node, state); } } } + + while (processQueue.Count != 0) + { + UpdateNodeMapping(processQueue.Dequeue()); + } } - private void UpdateNodeState(ref int state, Vector3I node, IMySlimBlock block, MyCubeBlockDefinition def, Matrix blockMatrix) + public void UpdateBlockMapping(ref IMySlimBlock block) { - for (int i = 0; i < neighbors.Length; i++) - { - Vector3I neighborPosition = node + neighbors[i]; - IMySlimBlock neighborBlock = Grid.GetCubeBlock(neighborPosition); + Vector3I min = block.Min; + Vector3I max = block.Max + 1; + Vector3I[] neighbors = Base6Directions.IntDirections; - // Convert Vector3I to Vector3 for the direction - Vector3 direction = new Vector3(neighbors[i]); + Queue processQueue = new Queue(); - if (neighborPosition.X >= block.Min.X && neighborPosition.Y >= block.Min.Y && neighborPosition.Z >= block.Min.Z && - neighborPosition.X < block.Max.X && neighborPosition.Y < block.Max.Y && neighborPosition.Z < block.Max.Z) - { - // Need to pass a reference to a Vector3, not Vector3I - if (IsAirtight(ref block, ref def, ref neighborPosition, ref direction, ref blockMatrix)) - { - state |= 1 << i; - } - } - else + MyCubeBlockDefinition def = block.BlockDefinition as MyCubeBlockDefinition; + bool isAirtight = def?.IsAirTight == true; + Matrix matrix; + block.Orientation.GetMatrix(out matrix); + matrix.TransposeRotationInPlace(); + + for (int x = min.X; x < max.X; x++) + { + for (int y = min.Y; y < max.Y; y++) { - // Process external or neighboring blocks - if (neighborBlock != null) + for (int z = min.Z; z < max.Z; z++) { - MyCubeBlockDefinition neighborDef = neighborBlock.BlockDefinition as MyCubeBlockDefinition; - // Also convert Vector3I to Vector3 here before passing - if (IsAirtight(ref neighborBlock, ref neighborDef, ref neighborPosition, ref direction, ref blockMatrix)) + int state = 0; + Vector3I node = new Vector3I(x, y, z); + + if (isAirtight) { - state |= 1 << i; + state = 4032; + } + + for (int i = 0; i < neighbors.Length; i++) + { + Vector3I n = node + neighbors[i]; + Vector3I towardSelf = node - n; + + if (IsAirtight(ref block, ref def, ref node, ref towardSelf, ref matrix)) + { + state |= 1 << i + 6; + } + + int ns; + if (NodeSurfaces.TryGetValue(n, out ns)) + { + state |= ((ns & 1 << i + 6) >> 6); + processQueue.Enqueue(n); + } + } + + if (NodeSurfaces[node] != state) + { + ResetSpacialMapping(); + NodeSurfaces[node] = state; } } } } - } - private void ProcessNeighborBlock(Vector3I neighborPosition, bool isNew, ref int state, ref HashSet processedNeighbors, int directionIndex, ref Matrix blockMatrix) - { - IMySlimBlock nblock = Grid.GetCubeBlock(neighborPosition); - if (nblock == null) return; - - if (isNew && !processedNeighbors.Contains(nblock)) + while (processQueue.Count != 0) { - MapBlocks(nblock); - processedNeighbors.Add(nblock); + UpdateNodeMapping(processQueue.Dequeue()); } - - MyCubeBlockDefinition ndef = nblock.BlockDefinition as MyCubeBlockDefinition; - var nMatrix = new Matrix(); - nblock.Orientation.GetMatrix(out nMatrix); - nMatrix.TransposeRotationInPlace(); - - // Convert Vector3I to Vector3 for method calls - Vector3 directionVec = new Vector3(neighbors[directionIndex]); - Vector3 oppositeDirectionVec = new Vector3(neighbors[(directionIndex + 6) % 12]); - - if (IsAirtight(ref nblock, ref ndef, ref neighborPosition, ref directionVec, ref nMatrix)) - state |= 1 << directionIndex; - - if (ndef?.IsAirTight == true || IsAirtight(ref nblock, ref ndef, ref neighborPosition, ref oppositeDirectionVec, ref nMatrix)) - state |= 1 << (directionIndex + 6); } - private void MapBlockRemove(IMySlimBlock block) + private void RemoveBlockMapping(ref IMySlimBlock block) { + ResetSpacialMapping(); + Vector3I min = block.Min; Vector3I max = block.Max + 1; + Vector3I[] neighbors = Base6Directions.IntDirections; for (int x = min.X; x < max.X; x++) { @@ -219,32 +182,46 @@ private void MapBlockRemove(IMySlimBlock block) for (int z = min.Z; z < max.Z; z++) { Vector3I node = new Vector3I(x, y, z); + NodeSurfaces.Remove(node); - int flag = BlockNodes[node]; - - for (int i = 6; i < 12; i++) + for (int i = 0; i < neighbors.Length; i++) { - if ((flag & i) != 0) - { - Vector3I n = node + neighbors[i - 6]; - - if (!(n.X >= min.X && n.Y >= min.Y && n.Z >= min.Z && - n.X < max.X && n.Y < max.Y && n.Z < max.Z)) - { - MapBlocks(Grid.GetCubeBlock(n), false, block); - } + Vector3I n = node + neighbors[i]; + if (!(n.X >= min.X && n.Y >= min.Y && n.Z >= min.Z && + n.X < max.X && n.Y < max.Y && n.Z < max.Z) && + NodeSurfaces.ContainsKey(n)) + { + int oppositeFace = (i % 2 == 0) ? i+1 : i-1; + NodeSurfaces[n] &= ~(1 << oppositeFace); } } - - BlockNodes.Remove(node); } } } } - private bool IsAirtight(ref IMySlimBlock block, ref MyCubeBlockDefinition def, ref Vector3I pos, ref Vector3 normal, ref Matrix matrix) + private void UpdateNodeMapping(Vector3I node) { + Vector3I[] neighbors = Base6Directions.IntDirections; + + int state = NodeSurfaces[node]; + for (int i = 0; i < neighbors.Length; i++) + { + Vector3I n = node + neighbors[i]; + + int ns; + NodeSurfaces.TryGetValue(n, out ns); + state |= ((ns & 1 << i + 6) >> 6); + + } + NodeSurfaces[node] = state; + } + + private bool IsAirtight(ref IMySlimBlock block, ref MyCubeBlockDefinition def, ref Vector3I pos, ref Vector3I normal, ref Matrix matrix) + { + if (def.IsAirTight == true) return true; + Vector3 position = Vector3.Zero; if (block.FatBlock != null) { @@ -254,25 +231,36 @@ private bool IsAirtight(ref IMySlimBlock block, ref MyCubeBlockDefinition def, r IMyDoor door = block.FatBlock as IMyDoor; bool isDoorClosed = door != null && (door.Status == Sandbox.ModAPI.Ingame.DoorStatus.Closed || door.Status == Sandbox.ModAPI.Ingame.DoorStatus.Closing); - Vector3I transformedNormal = Vector3I.Round(Vector3.Transform(normal, matrix)); Vector3 value = Vector3.Transform(position, matrix) + def.Center; Vector3I roundedValue = Vector3I.Round(value); - if (def.IsCubePressurized.ContainsKey(roundedValue) && def.IsCubePressurized[roundedValue].ContainsKey(transformedNormal)) + if (door != null) { - switch (def.IsCubePressurized[roundedValue][transformedNormal]) + switch (def.IsCubePressurized[roundedValue][normal]) { case MyCubeBlockDefinition.MyCubePressurizationMark.NotPressurized: - return false; + return IsDoorAirtight(ref door, ref normal, ref def); case MyCubeBlockDefinition.MyCubePressurizationMark.PressurizedAlways: return true; case MyCubeBlockDefinition.MyCubePressurizationMark.PressurizedClosed: return isDoorClosed; } } + else + { + switch (def.IsCubePressurized[roundedValue][normal]) + { + case MyCubeBlockDefinition.MyCubePressurizationMark.NotPressurized: + return false; + case MyCubeBlockDefinition.MyCubePressurizationMark.PressurizedAlways: + return true; + } + } + // Default to not airtight if the key is not found or if no specific condition matches - return isDoorClosed && door != null && IsDoorAirtight(ref door, ref transformedNormal, ref def); + //return isDoorClosed && door != null && IsDoorAirtight(ref door, ref transformedNormal, ref def); + return false; } private bool IsDoorAirtight(ref IMyDoor door, ref Vector3I normal, ref MyCubeBlockDefinition def) @@ -299,8 +287,8 @@ private bool IsDoorAirtight(ref IMyDoor door, ref Vector3I normal, ref MyCubeBlo MyCubeBlockDefinition.MountPoint[] mountPoints = def.MountPoints; for (int i = 0; i < mountPoints.Length; i++) { - MyCubeBlockDefinition.MountPoint mountPoint = mountPoints[i]; - if (normal == mountPoint.Normal) + //MyCubeBlockDefinition.MountPoint mountPoint = mountPoints[i]; + if (normal == mountPoints[i].Normal) { return false; } @@ -309,72 +297,147 @@ private bool IsDoorAirtight(ref IMyDoor door, ref Vector3I normal, ref MyCubeBlo return true; } - public void ResetMapper() + public void ResetSpacialMapping() { min = Grid.Min - 1; max = Grid.Max + 1; + CurrentRoomId = 0; + ExteriorQueue.Clear(); ExteriorQueue.Enqueue(min); - ExteriorNodes.Clear(); - ExteriorNodes.Add(min); + + SolidQueue.Clear(); + SolidNodes.Clear(); + + RoomQueue.Clear(); + Rooms.Clear(); NodeCountPerFrame = Math.Max((int)((max - min).Size / 60f), 1); NodeUpdateComplete = false; } - public void MapExterior() + public void MapSurfaces() { - if (ExteriorQueue.Count == 0) return; - int loopCount = 0; + if (ExteriorQueue.Count > 0) + { + CrawlOutside(ref loopCount); + } + else if (RoomQueue.Count > 0 || SolidQueue.Count > 0) + { + CrawlInside(ref loopCount); + } + else + { + NodeUpdateComplete = true; + ThermalCellUpdateComplete = false; + } - CrawlOutsideV2(ref loopCount); } - private void CrawlOutsideV2(ref int loopCount) + private void CrawlOutside(ref int loopCount) { + Vector3I[] neighbors = Base6Directions.IntDirections; + while (ExteriorQueue.Count > 0 && loopCount < NodeCountPerFrame) { loopCount++; Vector3I node = ExteriorQueue.Dequeue(); - for (int i = 0; i < neighbors.Length; i++) + for (int i = 0; i < 6; i++) { // get the neighbor location Vector3I n = node + neighbors[i]; // skip if the neighbor is out of bounds or already checked. - if (Vector3I.Min(n, min) != min || Vector3I.Max(n, max) != max || ExteriorNodes.Contains(n)) + if (ExteriorNodes.Contains(n) || Vector3I.Min(n, min) != min || Vector3I.Max(n, max) != max) continue; int flag; - if (BlockNodes.TryGetValue(n, out flag)) + NodeSurfaces.TryGetValue(n, out flag); + + int shift = i + 6 + ((i % 2 == 0) ? 1 : -1); + + // do not queue this node if it is airtight + if ((flag & 1 << shift) != 0) { - // get the alternate direction left/right, up/down - int direction; - if (i % 2 == 0) - { - direction = 1 << (i + 1); - } - else - { - direction = 1 << (i - 1); - } + //make sure something is queued for finding internal rooms + //if (SolidQueue.Count == 0) + //{ + // SolidQueue.Enqueue(n); + // SolidNodes.Add(n); + //} - // do not queue this block if it is airtight - if ((flag & direction) == direction) - { - continue; - } + continue; } // enqueue empty or non airtight nodes - ExteriorNodes.Add(n); ExteriorQueue.Enqueue(n); + ExteriorNodes.Add(n); + } + } + } + + private void CrawlInside(ref int loopCount) + { + Vector3I[] neighbors = Base6Directions.IntDirections; + // crawl over the current room + while (RoomQueue.Count > 0 && loopCount < NodeCountPerFrame) + { + loopCount++; + + Vector3I node = RoomQueue.Dequeue(); + + int flag; + NodeSurfaces.TryGetValue(node, out flag); + + for (int i = 0; i < 6; i++) + { + Vector3I n = node + neighbors[i]; + if (Rooms.ContainsKey(n)) continue; + + if ((flag & 1 << i) == 0) + { + RoomQueue.Enqueue(n); + Rooms.Add(n, CurrentRoomId); + } } } + + // find a new room to crawl over + while (RoomQueue.Count == 0 && SolidQueue.Count > 0 && loopCount < NodeCountPerFrame) + { + loopCount++; + + Vector3I node = SolidQueue.Dequeue(); + int surfaces = NodeSurfaces[node]; + + for (int i = 0; i < neighbors.Length; i++) + { + Vector3I n = node + neighbors[i]; + if (ExteriorNodes.Contains(n) || SolidNodes.Contains(n) || Rooms.ContainsKey(n)) continue; + + if ((surfaces & 1 << i) == 0) + { + RoomQueue.Enqueue(n); + Rooms.Add(n, CurrentRoomId++); + SolidQueue.Enqueue(node); + break; + } + else + { + SolidQueue.Enqueue(n); + } + } + } + + // keep calling this function if there is more work to do + if (loopCount < NodeCountPerFrame && SolidQueue.Count > 0) + { + CrawlInside(ref loopCount); + } } } } \ No newline at end of file