Apply boundary sink free-outflow on ghost cells
This commit is contained in:
@@ -13,6 +13,7 @@ Texture2D<float2> _VelWest;
|
||||
Texture2D<float> _GhostOverrideMask;
|
||||
Texture2D<float> _GhostOverrideWater;
|
||||
Texture2D<float2> _GhostOverrideVel;
|
||||
Texture2D<float> _GhostFreeOutflowMask;
|
||||
|
||||
RWTexture2D<float> _WaterOut;
|
||||
RWTexture2D<float2> _VelOut;
|
||||
@@ -23,6 +24,7 @@ int _HasSouth;
|
||||
int _HasEast;
|
||||
int _HasWest;
|
||||
int _UseGhostOverride;
|
||||
int _UseGhostFreeOutflow;
|
||||
|
||||
[numthreads(8, 8, 1)]
|
||||
void GhostExchange(uint3 id : SV_DispatchThreadID)
|
||||
@@ -65,7 +67,15 @@ void GhostExchange(uint3 id : SV_DispatchThreadID)
|
||||
usedNeighbor = true;
|
||||
}
|
||||
|
||||
if (!usedNeighbor)
|
||||
bool isBoundary = (g.x == 0 || g.x == size - 1 || g.y == 0 || g.y == size - 1);
|
||||
bool isFreeOutflow = false;
|
||||
if (_UseGhostFreeOutflow == 1 && isBoundary)
|
||||
{
|
||||
float outflowMask = _GhostFreeOutflowMask[g];
|
||||
isFreeOutflow = outflowMask > 0.5;
|
||||
}
|
||||
|
||||
if (!usedNeighbor && !isFreeOutflow)
|
||||
{
|
||||
if (g.x == 0 || g.x == size - 1)
|
||||
{
|
||||
@@ -77,7 +87,7 @@ void GhostExchange(uint3 id : SV_DispatchThreadID)
|
||||
}
|
||||
}
|
||||
|
||||
if (_UseGhostOverride == 1 && (g.x == 0 || g.x == size - 1 || g.y == 0 || g.y == size - 1))
|
||||
if (_UseGhostOverride == 1 && isBoundary)
|
||||
{
|
||||
float mask = _GhostOverrideMask[g];
|
||||
if (mask > 0.5)
|
||||
|
||||
@@ -86,6 +86,9 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
private float[] ghostOverrideWaterData;
|
||||
private Vector2[] ghostOverrideVelocityData;
|
||||
private bool useGhostBoundaryOverrides;
|
||||
private Texture2D ghostFreeOutflowMask;
|
||||
private float[] ghostFreeOutflowMaskData;
|
||||
private bool useGhostFreeOutflowMask;
|
||||
private RenderTexture clampMask;
|
||||
private RenderTexture dummyRw;
|
||||
|
||||
@@ -304,6 +307,59 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
ghostOverrideVelocity.SetPixelData(ghostOverrideVelocityData, 0);
|
||||
ghostOverrideVelocity.Apply(false, false);
|
||||
useGhostBoundaryOverrides = false;
|
||||
EnsureGhostFreeOutflowMask();
|
||||
Array.Clear(ghostFreeOutflowMaskData, 0, ghostFreeOutflowMaskData.Length);
|
||||
ghostFreeOutflowMask.SetPixelData(ghostFreeOutflowMaskData, 0);
|
||||
ghostFreeOutflowMask.Apply(false, false);
|
||||
useGhostFreeOutflowMask = false;
|
||||
}
|
||||
|
||||
public bool SetGhostFreeOutflowCells(int[] ghostIndices)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ghostIndices == null || ghostIndices.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureGhostFreeOutflowMask();
|
||||
Array.Clear(ghostFreeOutflowMaskData, 0, ghostFreeOutflowMaskData.Length);
|
||||
|
||||
bool any = false;
|
||||
int maxIndex = ghostFreeOutflowMaskData.Length - 1;
|
||||
for (int i = 0; i < ghostIndices.Length; i++)
|
||||
{
|
||||
int idx = ghostIndices[i];
|
||||
if (idx < 0 || idx > maxIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ghostFreeOutflowMaskData[idx] = 1.0f;
|
||||
any = true;
|
||||
}
|
||||
|
||||
ghostFreeOutflowMask.SetPixelData(ghostFreeOutflowMaskData, 0);
|
||||
ghostFreeOutflowMask.Apply(false, false);
|
||||
useGhostFreeOutflowMask = any;
|
||||
return any;
|
||||
}
|
||||
|
||||
public void ClearGhostFreeOutflowCells()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureGhostFreeOutflowMask();
|
||||
Array.Clear(ghostFreeOutflowMaskData, 0, ghostFreeOutflowMaskData.Length);
|
||||
ghostFreeOutflowMask.SetPixelData(ghostFreeOutflowMaskData, 0);
|
||||
ghostFreeOutflowMask.Apply(false, false);
|
||||
useGhostFreeOutflowMask = false;
|
||||
}
|
||||
|
||||
public bool TryGetBoundaryIdMasks(out int[] sourceIds, out int[] sinkIds)
|
||||
@@ -602,6 +658,10 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
{
|
||||
Destroy(ghostOverrideVelocity);
|
||||
}
|
||||
if (ghostFreeOutflowMask != null)
|
||||
{
|
||||
Destroy(ghostFreeOutflowMask);
|
||||
}
|
||||
|
||||
resolvedTerrain = null;
|
||||
resolvedPorosity = null;
|
||||
@@ -617,6 +677,9 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
ghostOverrideWaterData = null;
|
||||
ghostOverrideVelocityData = null;
|
||||
useGhostBoundaryOverrides = false;
|
||||
ghostFreeOutflowMask = null;
|
||||
ghostFreeOutflowMaskData = null;
|
||||
useGhostFreeOutflowMask = false;
|
||||
}
|
||||
|
||||
private void ResolveStaticTextures()
|
||||
@@ -867,6 +930,10 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
{
|
||||
EnsureGhostOverrideTextures();
|
||||
}
|
||||
if (ghostFreeOutflowMask == null)
|
||||
{
|
||||
EnsureGhostFreeOutflowMask();
|
||||
}
|
||||
|
||||
ghostExchangeShader.SetInt("_GridRes", gridRes);
|
||||
ghostExchangeShader.SetInt("_HasNorth", 0);
|
||||
@@ -874,6 +941,7 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
ghostExchangeShader.SetInt("_HasEast", 0);
|
||||
ghostExchangeShader.SetInt("_HasWest", 0);
|
||||
ghostExchangeShader.SetInt("_UseGhostOverride", useGhostBoundaryOverrides ? 1 : 0);
|
||||
ghostExchangeShader.SetInt("_UseGhostFreeOutflow", useGhostFreeOutflowMask ? 1 : 0);
|
||||
|
||||
ghostExchangeShader.SetTexture(ghostKernel, "_WaterIn", CurrentWater);
|
||||
ghostExchangeShader.SetTexture(ghostKernel, "_VelIn", CurrentVelocity);
|
||||
@@ -890,6 +958,7 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
ghostExchangeShader.SetTexture(ghostKernel, "_GhostOverrideMask", ghostOverrideMask);
|
||||
ghostExchangeShader.SetTexture(ghostKernel, "_GhostOverrideWater", ghostOverrideWater);
|
||||
ghostExchangeShader.SetTexture(ghostKernel, "_GhostOverrideVel", ghostOverrideVelocity);
|
||||
ghostExchangeShader.SetTexture(ghostKernel, "_GhostFreeOutflowMask", ghostFreeOutflowMask);
|
||||
|
||||
DispatchKernel(ghostExchangeShader, ghostKernel, gridRes + 2, gridRes + 2);
|
||||
}
|
||||
@@ -1344,6 +1413,26 @@ public sealed class SweTileSimulator : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureGhostFreeOutflowMask()
|
||||
{
|
||||
int ghostRes = gridRes + 2;
|
||||
int length = ghostRes * ghostRes;
|
||||
if (ghostFreeOutflowMask == null || ghostFreeOutflowMask.width != ghostRes || ghostFreeOutflowMask.height != ghostRes)
|
||||
{
|
||||
if (ghostFreeOutflowMask != null)
|
||||
{
|
||||
Destroy(ghostFreeOutflowMask);
|
||||
}
|
||||
|
||||
ghostFreeOutflowMask = CreateTextureFromFloats(new float[length], ghostRes, ghostRes, "SWE_GhostFreeOutflowMask");
|
||||
ghostFreeOutflowMaskData = new float[length];
|
||||
}
|
||||
else if (ghostFreeOutflowMaskData == null || ghostFreeOutflowMaskData.Length != length)
|
||||
{
|
||||
ghostFreeOutflowMaskData = new float[length];
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] ReadIdTexture(Texture2D texture)
|
||||
{
|
||||
if (texture == null || !texture.isReadable)
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace FloodSWE.Networking
|
||||
private HashSet<int> activeSourceIds = new HashSet<int>();
|
||||
private HashSet<int> activeSinkIds = new HashSet<int>();
|
||||
private readonly Dictionary<int, int[]> activeBoundaryInflowGhostCells = new Dictionary<int, int[]>();
|
||||
private readonly Dictionary<int, int[]> activeBoundarySinkGhostCells = new Dictionary<int, int[]>();
|
||||
private readonly Dictionary<int, int[]> activeSourceAreaCells = new Dictionary<int, int[]>();
|
||||
private readonly Dictionary<int, int[]> activeSinkCells = new Dictionary<int, int[]>();
|
||||
private bool activeTileHasBoundaryCellGroups;
|
||||
@@ -692,6 +693,7 @@ namespace FloodSWE.Networking
|
||||
activeSourceIds = new HashSet<int>();
|
||||
activeSinkIds = new HashSet<int>();
|
||||
activeBoundaryInflowGhostCells.Clear();
|
||||
activeBoundarySinkGhostCells.Clear();
|
||||
activeSourceAreaCells.Clear();
|
||||
activeSinkCells.Clear();
|
||||
activeTileHasBoundaryCellGroups = false;
|
||||
@@ -714,18 +716,20 @@ namespace FloodSWE.Networking
|
||||
CollectIdsFromRefs(tile.boundary_inflow_ids, activeSourceIds);
|
||||
|
||||
CollectCellGroups(tile.boundary_cells, activeBoundaryInflowGhostCells, activeSourceIds);
|
||||
CollectCellGroups(tile.boundary_sink_cells, activeBoundarySinkGhostCells, activeSinkIds);
|
||||
CollectCellGroups(tile.source_area_cells, activeSourceAreaCells, activeSourceIds);
|
||||
CollectCellGroups(tile.sink_cells, activeSinkCells, activeSinkIds);
|
||||
|
||||
activeTileHasBoundaryCellGroups =
|
||||
activeBoundaryInflowGhostCells.Count > 0 ||
|
||||
activeBoundarySinkGhostCells.Count > 0 ||
|
||||
activeSourceAreaCells.Count > 0 ||
|
||||
activeSinkCells.Count > 0;
|
||||
|
||||
Debug.Log(
|
||||
$"SweServerRuntime: active tile inflowIds={activeSourceIds.Count}, sinkIds={activeSinkIds.Count}, " +
|
||||
$"boundaryGhostGroups={activeBoundaryInflowGhostCells.Count}, sourceAreaGroups={activeSourceAreaCells.Count}, " +
|
||||
$"sinkGroups={activeSinkCells.Count}");
|
||||
$"boundaryInGhostGroups={activeBoundaryInflowGhostCells.Count}, boundaryOutGhostGroups={activeBoundarySinkGhostCells.Count}, " +
|
||||
$"sourceAreaGroups={activeSourceAreaCells.Count}, sinkGroups={activeSinkCells.Count}");
|
||||
}
|
||||
|
||||
private void RecomputeExternalDepthRate()
|
||||
@@ -749,6 +753,8 @@ namespace FloodSWE.Networking
|
||||
var ghostIndices = new List<int>(128);
|
||||
var ghostLevels = new List<float>(128);
|
||||
var ghostVelocities = new List<Vector2>(128);
|
||||
var ghostOutflowCells = new HashSet<int>();
|
||||
var boundarySinkIdsWithGhostOutflow = new HashSet<int>();
|
||||
|
||||
foreach (var pair in activeBoundaryInflowGhostCells)
|
||||
{
|
||||
@@ -786,6 +792,39 @@ namespace FloodSWE.Networking
|
||||
simulator.ClearGhostBoundaryOverrides();
|
||||
}
|
||||
|
||||
foreach (var pair in activeBoundarySinkGhostCells)
|
||||
{
|
||||
int boundaryId = pair.Key;
|
||||
if (boundaryId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
boundarySinkIdsWithGhostOutflow.Add(boundaryId);
|
||||
int[] cells = pair.Value;
|
||||
if (cells == null || cells.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cells.Length; i++)
|
||||
{
|
||||
int idx = cells[i];
|
||||
if (idx >= 0)
|
||||
{
|
||||
ghostOutflowCells.Add(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasGhostOutflow =
|
||||
ghostOutflowCells.Count > 0 &&
|
||||
simulator.SetGhostFreeOutflowCells(new List<int>(ghostOutflowCells).ToArray());
|
||||
if (!hasGhostOutflow)
|
||||
{
|
||||
simulator.ClearGhostFreeOutflowCells();
|
||||
}
|
||||
|
||||
int totalCells = simulator.gridRes * simulator.gridRes;
|
||||
float[] perCell = new float[totalCells];
|
||||
bool hasDepthForcing = false;
|
||||
@@ -834,6 +873,11 @@ namespace FloodSWE.Networking
|
||||
foreach (var pair in activeSinkCells)
|
||||
{
|
||||
int boundaryId = pair.Key;
|
||||
if (boundarySinkIdsWithGhostOutflow.Contains(boundaryId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float rate = 0.0f;
|
||||
if (TryResolveBoundaryProfile("sink", boundaryId, out SweBoundaryProfile profile) && profile != null && profile.enabled)
|
||||
{
|
||||
@@ -897,22 +941,38 @@ namespace FloodSWE.Networking
|
||||
forcedDepthCells = 0;
|
||||
}
|
||||
|
||||
if (!hasGhostForcing && !hasDepthForcing)
|
||||
if (!hasGhostForcing && !hasDepthForcing && !hasGhostOutflow)
|
||||
{
|
||||
lastForcedCellCount = 0;
|
||||
lastForcingStatus = "disabled:no_active_boundary_profiles";
|
||||
return;
|
||||
}
|
||||
|
||||
lastForcedCellCount = ghostIndices.Count + forcedDepthCells;
|
||||
if (hasGhostForcing && hasDepthForcing)
|
||||
lastForcedCellCount = ghostIndices.Count + forcedDepthCells + (hasGhostOutflow ? ghostOutflowCells.Count : 0);
|
||||
if (hasGhostForcing && hasDepthForcing && hasGhostOutflow)
|
||||
{
|
||||
lastForcingStatus = "ghost+localized+free_outflow";
|
||||
}
|
||||
else if (hasGhostForcing && hasDepthForcing)
|
||||
{
|
||||
lastForcingStatus = "ghost+localized";
|
||||
}
|
||||
else if (hasGhostForcing && hasGhostOutflow)
|
||||
{
|
||||
lastForcingStatus = "ghost+free_outflow";
|
||||
}
|
||||
else if (hasDepthForcing && hasGhostOutflow)
|
||||
{
|
||||
lastForcingStatus = "localized+free_outflow";
|
||||
}
|
||||
else if (hasGhostForcing)
|
||||
{
|
||||
lastForcingStatus = "ghost_only";
|
||||
}
|
||||
else if (hasGhostOutflow)
|
||||
{
|
||||
lastForcingStatus = "free_outflow_only";
|
||||
}
|
||||
else
|
||||
{
|
||||
lastForcingStatus = "localized";
|
||||
@@ -921,13 +981,14 @@ namespace FloodSWE.Networking
|
||||
if (verboseDiagnostics)
|
||||
{
|
||||
Debug.Log(
|
||||
$"SweServerRuntime: forcing applied status={lastForcingStatus} ghost={ghostIndices.Count} depth={forcedDepthCells}");
|
||||
$"SweServerRuntime: forcing applied status={lastForcingStatus} ghost={ghostIndices.Count} depth={forcedDepthCells} freeOutflow={ghostOutflowCells.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RecomputeLegacyBoundaryDepthRate()
|
||||
{
|
||||
simulator.ClearGhostBoundaryOverrides();
|
||||
simulator.ClearGhostFreeOutflowCells();
|
||||
|
||||
var sourceRates = new Dictionary<int, float>();
|
||||
foreach (int sourceId in activeSourceIds)
|
||||
|
||||
@@ -253,6 +253,7 @@ namespace FloodSWE.Networking
|
||||
public SweBoundaryTileIdRef[] boundary_inflow_ids;
|
||||
public SweBoundaryTileIdRef[] source_area_ids;
|
||||
public SweBoundaryTileCellGroup[] boundary_cells;
|
||||
public SweBoundaryTileCellGroup[] boundary_sink_cells;
|
||||
public SweBoundaryTileCellGroup[] source_area_cells;
|
||||
public SweBoundaryTileCellGroup[] sink_cells;
|
||||
public string source_id_path;
|
||||
@@ -265,6 +266,7 @@ namespace FloodSWE.Networking
|
||||
get
|
||||
{
|
||||
return (boundary_cells != null && boundary_cells.Length > 0) ||
|
||||
(boundary_sink_cells != null && boundary_sink_cells.Length > 0) ||
|
||||
(source_area_cells != null && source_area_cells.Length > 0) ||
|
||||
(sink_cells != null && sink_cells.Length > 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user