Files
DTrierFlood_New/Assets/FloodSWE/Scripts/IO/SweTileLoader.cs

358 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using UnityEngine;
namespace FloodSWE.IO
{
public sealed class SweTileLoader : MonoBehaviour
{
public enum SourceMode
{
Resources = 0,
StreamingAssets = 1,
AbsolutePath = 2,
}
[Header("Tile Index")]
public TextAsset tileIndexCsv;
public SourceMode sourceMode = SourceMode.Resources;
public string tileIndexPath = "export_swe/swe_tile_index.csv";
public string fileRoot = "";
public string resourcesRoot = "export_swe";
[Header("Runtime")]
public bool cacheTextures = true;
private readonly Dictionary<TileKey, TileRecord> records = new Dictionary<TileKey, TileRecord>();
private readonly Dictionary<string, Texture2D> textureCache = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);
private bool indexLoaded;
public bool TryLoadTile(string lod, int tileX, int tileY, out SweTileData data)
{
data = default;
EnsureIndexLoaded();
TileKey key = new TileKey(lod, tileX, tileY);
if (!records.TryGetValue(key, out TileRecord record))
{
return false;
}
Texture2D height = LoadTexture(record.HeightPath);
Texture2D porosity = LoadTexture(record.PorosityPath);
Texture2D buildings = LoadTexture(record.BuildingPath);
data = new SweTileData(record, height, porosity, buildings);
return height != null || porosity != null || buildings != null;
}
public bool ApplyToSimulator(string lod, int tileX, int tileY, SweTileSimulator simulator)
{
if (simulator == null)
{
return false;
}
if (!TryLoadTile(lod, tileX, tileY, out SweTileData data))
{
return false;
}
if (data.Height != null)
{
simulator.terrainHeight = data.Height;
}
if (data.Porosity != null)
{
simulator.porosity = data.Porosity;
}
return true;
}
private void EnsureIndexLoaded()
{
if (indexLoaded)
{
return;
}
records.Clear();
string text = tileIndexCsv != null ? tileIndexCsv.text : LoadTextFromPath();
if (string.IsNullOrWhiteSpace(text))
{
Debug.LogWarning("SweTileLoader: tile index CSV is empty.");
indexLoaded = true;
return;
}
string[] lines = text.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 1)
{
indexLoaded = true;
return;
}
int headerColumns = 0;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i].Trim();
if (line.Length == 0)
{
continue;
}
string[] parts = line.Split(',');
if (i == 0)
{
headerColumns = parts.Length;
continue;
}
if (parts.Length < headerColumns)
{
continue;
}
string lod = parts[0].Trim();
if (!int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out int tileX))
{
continue;
}
if (!int.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out int tileY))
{
continue;
}
TileKey key = new TileKey(lod, tileX, tileY);
records[key] = new TileRecord(
lod,
tileX,
tileY,
NormalizePath(parts[9]),
NormalizePath(parts[10]),
NormalizePath(parts[11])
);
}
indexLoaded = true;
}
private string LoadTextFromPath()
{
if (string.IsNullOrWhiteSpace(tileIndexPath))
{
return null;
}
string path = tileIndexPath;
if (sourceMode == SourceMode.StreamingAssets)
{
path = Path.Combine(Application.streamingAssetsPath, tileIndexPath);
}
if (!File.Exists(path))
{
Debug.LogWarning($"SweTileLoader: CSV not found at {path}");
return null;
}
return File.ReadAllText(path);
}
private Texture2D LoadTexture(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return null;
}
if (cacheTextures && textureCache.TryGetValue(path, out Texture2D cached))
{
return cached;
}
Texture2D texture = null;
switch (sourceMode)
{
case SourceMode.Resources:
texture = Resources.Load<Texture2D>(ToResourcesPath(path));
break;
case SourceMode.StreamingAssets:
case SourceMode.AbsolutePath:
texture = LoadTextureFromFile(path);
break;
}
if (cacheTextures && texture != null)
{
textureCache[path] = texture;
}
return texture;
}
private string NormalizePath(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
{
return "";
}
string path = raw.Trim().Replace("\\", "/");
if (sourceMode == SourceMode.AbsolutePath)
{
return path;
}
if (sourceMode == SourceMode.StreamingAssets)
{
return string.IsNullOrWhiteSpace(fileRoot) ? path : Path.Combine(fileRoot, path).Replace("\\", "/");
}
return path;
}
private string ToResourcesPath(string path)
{
string normalized = path.Replace("\\", "/");
if (!string.IsNullOrWhiteSpace(resourcesRoot))
{
int idx = normalized.IndexOf(resourcesRoot, StringComparison.OrdinalIgnoreCase);
if (idx >= 0)
{
normalized = normalized.Substring(idx);
}
}
if (normalized.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
{
normalized = normalized.Substring("Assets/".Length);
}
if (normalized.StartsWith("Resources/", StringComparison.OrdinalIgnoreCase))
{
normalized = normalized.Substring("Resources/".Length);
}
if (normalized.EndsWith(".exr", StringComparison.OrdinalIgnoreCase))
{
normalized = normalized.Substring(0, normalized.Length - 4);
}
return normalized;
}
private Texture2D LoadTextureFromFile(string path)
{
string resolved = path;
if (sourceMode == SourceMode.StreamingAssets)
{
resolved = Path.Combine(Application.streamingAssetsPath, path);
}
if (!File.Exists(resolved))
{
Debug.LogWarning($"SweTileLoader: texture not found {resolved}");
return null;
}
byte[] bytes = File.ReadAllBytes(resolved);
Texture2D texture = new Texture2D(2, 2, TextureFormat.RFloat, false, true);
if (!ImageConversion.LoadImage(texture, bytes, false))
{
Destroy(texture);
Debug.LogWarning($"SweTileLoader: failed to decode {resolved}");
return null;
}
texture.wrapMode = TextureWrapMode.Clamp;
texture.filterMode = FilterMode.Point;
texture.name = Path.GetFileNameWithoutExtension(resolved);
return texture;
}
private readonly struct TileKey : IEquatable<TileKey>
{
public readonly string Lod;
public readonly int X;
public readonly int Y;
public TileKey(string lod, int x, int y)
{
Lod = lod ?? "";
X = x;
Y = y;
}
public bool Equals(TileKey other)
{
return X == other.X && Y == other.Y && string.Equals(Lod, other.Lod, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object obj)
{
return obj is TileKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 31 + X;
hash = hash * 31 + Y;
hash = hash * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(Lod);
return hash;
}
}
}
internal readonly struct TileRecord
{
public readonly string Lod;
public readonly int X;
public readonly int Y;
public readonly string HeightPath;
public readonly string PorosityPath;
public readonly string BuildingPath;
public TileRecord(string lod, int x, int y, string heightPath, string porosityPath, string buildingPath)
{
Lod = lod;
X = x;
Y = y;
HeightPath = heightPath;
PorosityPath = porosityPath;
BuildingPath = buildingPath;
}
}
}
public readonly struct SweTileData
{
public readonly string Lod;
public readonly int TileX;
public readonly int TileY;
public readonly Texture2D Height;
public readonly Texture2D Porosity;
public readonly Texture2D Buildings;
public SweTileData(
in SweTileLoader.TileRecord record,
Texture2D height,
Texture2D porosity,
Texture2D buildings)
{
Lod = record.Lod;
TileX = record.X;
TileY = record.Y;
Height = height;
Porosity = porosity;
Buildings = buildings;
}
}
}