356 lines
12 KiB
C#
356 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace FloodSWE.Networking
|
|
{
|
|
[Serializable]
|
|
public sealed class SweBoundaryManifest
|
|
{
|
|
public int schema_version;
|
|
public string boundary_inflow_mask_dir;
|
|
public string source_area_mask_dir;
|
|
public string sink_mask_dir;
|
|
public string boundary_inflow_params_toml;
|
|
public string source_area_params_toml;
|
|
public string sink_params_toml;
|
|
public SweBoundarySource[] sources;
|
|
public SweBoundarySink[] sinks;
|
|
public SweBoundaryDefinition[] boundaries;
|
|
public SweBoundaryTile[] tiles;
|
|
|
|
[NonSerialized] private Dictionary<string, SweBoundaryTile> tileLookup;
|
|
[NonSerialized] private Dictionary<string, SweBoundaryDefinition> boundaryLookup;
|
|
[NonSerialized] private Dictionary<int, SweBoundarySource> sourceLookup;
|
|
[NonSerialized] private Dictionary<int, SweBoundarySink> sinkLookup;
|
|
|
|
public static bool TryLoad(string json, out SweBoundaryManifest manifest)
|
|
{
|
|
manifest = null;
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
manifest = JsonUtility.FromJson<SweBoundaryManifest>(json);
|
|
if (manifest == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
manifest.BuildLookup();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"SweBoundaryManifest: failed to parse manifest. {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryGetTile(string lod, int tileX, int tileY, out SweBoundaryTile tile)
|
|
{
|
|
tile = null;
|
|
if (tileLookup == null)
|
|
{
|
|
BuildLookup();
|
|
}
|
|
|
|
return tileLookup != null && tileLookup.TryGetValue(MakeTileKey(lod, tileX, tileY), out tile);
|
|
}
|
|
|
|
public bool TryGetSource(int id, out SweBoundarySource source)
|
|
{
|
|
source = null;
|
|
if (sourceLookup == null)
|
|
{
|
|
BuildLookup();
|
|
}
|
|
|
|
return sourceLookup != null && sourceLookup.TryGetValue(id, out source);
|
|
}
|
|
|
|
public bool TryGetBoundary(string kind, int id, out SweBoundaryDefinition boundary)
|
|
{
|
|
boundary = null;
|
|
if (boundaryLookup == null)
|
|
{
|
|
BuildLookup();
|
|
}
|
|
|
|
return boundaryLookup != null && boundaryLookup.TryGetValue(MakeBoundaryKey(kind, id), out boundary);
|
|
}
|
|
|
|
public bool TryGetSink(int id, out SweBoundarySink sink)
|
|
{
|
|
sink = null;
|
|
if (sinkLookup == null)
|
|
{
|
|
BuildLookup();
|
|
}
|
|
|
|
return sinkLookup != null && sinkLookup.TryGetValue(id, out sink);
|
|
}
|
|
|
|
public static int ParseLod(string lod)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(lod))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
string value = lod.Trim().ToLowerInvariant();
|
|
if (value.StartsWith("lod", StringComparison.Ordinal))
|
|
{
|
|
value = value.Substring(3);
|
|
}
|
|
|
|
return int.TryParse(value, out int parsed) ? parsed : 0;
|
|
}
|
|
|
|
private void BuildLookup()
|
|
{
|
|
tileLookup = new Dictionary<string, SweBoundaryTile>(StringComparer.OrdinalIgnoreCase);
|
|
boundaryLookup = new Dictionary<string, SweBoundaryDefinition>(StringComparer.OrdinalIgnoreCase);
|
|
sourceLookup = new Dictionary<int, SweBoundarySource>();
|
|
sinkLookup = new Dictionary<int, SweBoundarySink>();
|
|
if (tiles == null)
|
|
{
|
|
tileLookup = tileLookup ?? new Dictionary<string, SweBoundaryTile>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
if (tiles != null)
|
|
{
|
|
for (int i = 0; i < tiles.Length; i++)
|
|
{
|
|
SweBoundaryTile tile = tiles[i];
|
|
if (tile == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
tileLookup[MakeTileKey(tile.lod, tile.tile_x, tile.tile_y)] = tile;
|
|
}
|
|
}
|
|
|
|
if (boundaries != null && boundaries.Length > 0)
|
|
{
|
|
for (int i = 0; i < boundaries.Length; i++)
|
|
{
|
|
SweBoundaryDefinition boundary = boundaries[i];
|
|
if (boundary == null || boundary.id <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (boundary.default_state == null)
|
|
{
|
|
boundary.default_state = SweBoundaryDefaultState.DefaultFor(boundary.kind, boundary.@params);
|
|
}
|
|
|
|
boundaryLookup[MakeBoundaryKey(boundary.kind, boundary.id)] = boundary;
|
|
}
|
|
}
|
|
|
|
if (sources != null)
|
|
{
|
|
for (int i = 0; i < sources.Length; i++)
|
|
{
|
|
SweBoundarySource source = sources[i];
|
|
if (source != null)
|
|
{
|
|
sourceLookup[source.id] = source;
|
|
string key = MakeBoundaryKey("boundary_inflow", source.id);
|
|
if (!boundaryLookup.ContainsKey(key))
|
|
{
|
|
boundaryLookup[key] = new SweBoundaryDefinition
|
|
{
|
|
kind = "boundary_inflow",
|
|
id = source.id,
|
|
tile_count = source.tile_count,
|
|
total_pixels = source.total_pixels,
|
|
@params = source.@params,
|
|
default_state = SweBoundaryDefaultState.DefaultFor("boundary_inflow", source.@params),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sinks != null)
|
|
{
|
|
for (int i = 0; i < sinks.Length; i++)
|
|
{
|
|
SweBoundarySink sink = sinks[i];
|
|
if (sink != null)
|
|
{
|
|
sinkLookup[sink.id] = sink;
|
|
string key = MakeBoundaryKey("sink", sink.id);
|
|
if (!boundaryLookup.ContainsKey(key))
|
|
{
|
|
boundaryLookup[key] = new SweBoundaryDefinition
|
|
{
|
|
kind = "sink",
|
|
id = sink.id,
|
|
tile_count = sink.tile_count,
|
|
total_pixels = sink.total_pixels,
|
|
@params = sink.@params,
|
|
default_state = SweBoundaryDefaultState.DefaultFor("sink", sink.@params),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static string MakeTileKey(string lod, int tileX, int tileY)
|
|
{
|
|
return $"{lod}|{tileX}|{tileY}";
|
|
}
|
|
|
|
private static string MakeBoundaryKey(string kind, int id)
|
|
{
|
|
string normalized = string.IsNullOrWhiteSpace(kind) ? "boundary_inflow" : kind.Trim().ToLowerInvariant();
|
|
if (normalized == "source")
|
|
{
|
|
normalized = "boundary_inflow";
|
|
}
|
|
return $"{normalized}:{id}";
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundarySource
|
|
{
|
|
public int id;
|
|
public int tile_count;
|
|
public int total_pixels;
|
|
public SweBoundaryParams @params;
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundarySink
|
|
{
|
|
public int id;
|
|
public int tile_count;
|
|
public int total_pixels;
|
|
public SweBoundaryParams @params;
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundaryTile
|
|
{
|
|
public string lod;
|
|
public int tile_x;
|
|
public int tile_y;
|
|
public float tile_size_m;
|
|
public int resolution;
|
|
public float[] bounds;
|
|
public SweBoundaryTileIdRef[] source_ids;
|
|
public SweBoundaryTileIdRef[] sink_ids;
|
|
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;
|
|
public string sink_id_path;
|
|
public string boundary_inflow_id_path;
|
|
public string source_area_id_path;
|
|
|
|
public bool HasCellGroups
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundaryParams
|
|
{
|
|
public string name;
|
|
public string mode;
|
|
public float trigger_level_m;
|
|
public float max_outflow_m3s;
|
|
|
|
public bool IsMode(string value)
|
|
{
|
|
return !string.IsNullOrWhiteSpace(mode) &&
|
|
string.Equals(mode.Trim(), value, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundaryTileIdRef
|
|
{
|
|
public int id;
|
|
public int pixels;
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundaryTileCellGroup
|
|
{
|
|
public int id;
|
|
public int count;
|
|
public int[] cells;
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundaryDefinition
|
|
{
|
|
public string kind;
|
|
public int id;
|
|
public int tile_count;
|
|
public int total_pixels;
|
|
public SweBoundaryParams @params;
|
|
public SweBoundaryDefaultState default_state;
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class SweBoundaryDefaultState
|
|
{
|
|
public bool enabled;
|
|
public float water_level_m;
|
|
public float velocity_u_mps;
|
|
public float velocity_v_mps;
|
|
public float depth_rate_mps;
|
|
|
|
public static SweBoundaryDefaultState DefaultFor(string kind, SweBoundaryParams parameters)
|
|
{
|
|
bool sinkFreeOutflow = string.Equals(kind, "sink", StringComparison.OrdinalIgnoreCase) &&
|
|
parameters != null &&
|
|
parameters.IsMode("free_outflow");
|
|
return new SweBoundaryDefaultState
|
|
{
|
|
enabled = sinkFreeOutflow,
|
|
water_level_m = 0.0f,
|
|
velocity_u_mps = 0.0f,
|
|
velocity_v_mps = 0.0f,
|
|
depth_rate_mps = 0.0f,
|
|
};
|
|
}
|
|
|
|
public SweBoundaryProfile ToProfile(string kind, int id)
|
|
{
|
|
return new SweBoundaryProfile
|
|
{
|
|
boundaryKind = kind,
|
|
boundaryId = id,
|
|
enabled = enabled,
|
|
waterLevelM = water_level_m,
|
|
velocityUMps = velocity_u_mps,
|
|
velocityVMps = velocity_v_mps,
|
|
depthRateMps = depth_rate_mps,
|
|
};
|
|
}
|
|
}
|
|
}
|