Files
DTrierFlood_New/Assets/FloodSWE/Scripts/Streaming/HeightmapPacket.cs

184 lines
5.0 KiB
C#

using System;
using System.IO;
using System.IO.Compression;
using FloodSWE.TileGraph;
using UnityEngine;
namespace FloodSWE.Streaming
{
[Serializable]
public struct HeightmapPacket
{
public int FrameId;
public TileId Tile;
public int Resolution;
public float MinHeight;
public float MaxHeight;
public byte[] Payload;
public bool IsValid
{
get { return Resolution > 0 && Payload != null && Payload.Length > 0; }
}
public int SampleCount
{
get { return Resolution * Resolution; }
}
public static HeightmapPacket FromHeights(int frameId, TileId tile, int resolution, float[] heights)
{
if (heights == null)
{
throw new ArgumentNullException(nameof(heights));
}
int expected = resolution * resolution;
if (expected <= 0 || heights.Length != expected)
{
throw new ArgumentException("Height array size does not match resolution.");
}
float minHeight = heights[0];
float maxHeight = heights[0];
for (int i = 1; i < heights.Length; i++)
{
float h = heights[i];
if (h < minHeight)
{
minHeight = h;
}
else if (h > maxHeight)
{
maxHeight = h;
}
}
float range = maxHeight - minHeight;
if (range <= 0.0f)
{
range = 0.001f;
maxHeight = minHeight + range;
}
float maxRange = 65.535f;
if (range > maxRange)
{
maxHeight = minHeight + maxRange;
range = maxRange;
}
ushort[] quantized = new ushort[expected];
for (int i = 0; i < heights.Length; i++)
{
float delta = Mathf.Clamp(heights[i] - minHeight, 0.0f, range);
int mm = Mathf.RoundToInt(delta * 1000.0f);
quantized[i] = (ushort)Mathf.Clamp(mm, 0, ushort.MaxValue);
}
byte[] payload = CompressUShorts(quantized);
return new HeightmapPacket
{
FrameId = frameId,
Tile = tile,
Resolution = resolution,
MinHeight = minHeight,
MaxHeight = maxHeight,
Payload = payload
};
}
public bool TryDecodeHeights(out float[] heights)
{
heights = null;
if (!IsValid)
{
return false;
}
int expectedBytes = SampleCount * sizeof(ushort);
if (!TryDecompress(Payload, expectedBytes, out byte[] raw))
{
return false;
}
if (raw.Length < expectedBytes)
{
return false;
}
ushort[] quantized = new ushort[SampleCount];
Buffer.BlockCopy(raw, 0, quantized, 0, expectedBytes);
heights = new float[SampleCount];
for (int i = 0; i < quantized.Length; i++)
{
heights[i] = MinHeight + (quantized[i] * 0.001f);
}
return true;
}
private static byte[] CompressUShorts(ushort[] values)
{
byte[] raw = new byte[values.Length * sizeof(ushort)];
Buffer.BlockCopy(values, 0, raw, 0, raw.Length);
using (var output = new MemoryStream())
{
using (var deflate = new DeflateStream(output, System.IO.Compression.CompressionLevel.Fastest, true))
{
deflate.Write(raw, 0, raw.Length);
}
return output.ToArray();
}
}
private static bool TryDecompress(byte[] payload, int expectedBytes, out byte[] raw)
{
raw = null;
if (payload == null || payload.Length == 0)
{
return false;
}
byte[] buffer = new byte[expectedBytes];
int offset = 0;
try
{
using (var input = new MemoryStream(payload))
using (var deflate = new DeflateStream(input, CompressionMode.Decompress))
{
while (offset < expectedBytes)
{
int read = deflate.Read(buffer, offset, expectedBytes - offset);
if (read == 0)
{
break;
}
offset += read;
}
}
}
catch (Exception)
{
return false;
}
if (offset < expectedBytes)
{
return false;
}
raw = buffer;
return true;
}
}
}