Add code to terrain packet compression/decompression to handle LayerType.LandExtended.

Passed flag "largeRegion" around which properly parses ParcelIDs in layer packet headers.
Some notes on functions that won't work for varregions (like GlobalPosToRegionHandle).
This commit is contained in:
Robert Adams
2023-06-23 12:28:16 -07:00
parent babd04c145
commit d2455d591a
6 changed files with 66 additions and 33 deletions

View File

@@ -206,6 +206,7 @@ namespace OpenMetaverse.Assets
private static bool LoadTerrain(string filePath, byte[] data, TerrainLoadedCallback terrainCallback, long bytesRead, long totalBytes)
{
// TODO: This needs to be re-written to read data from a saved varregion (sizeX != 256)
float[,] terrain = new float[256, 256];
bool loaded = false;
@@ -350,10 +351,10 @@ namespace OpenMetaverse.Assets
BinaryWriter bs = new BinaryWriter(s);
int y;
for (y = 0; y < 256; y++)
for (y = 0; y < sim.SizeY; y++)
{
int x;
for (x = 0; x < 256; x++)
for (x = 0; x < sim.SizeX; x++)
{
float height;
sim.TerrainHeightAtPoint(x, y, out height);

View File

@@ -158,14 +158,15 @@ namespace OpenMetaverse
public uint GlobalY;
/// <summary>Get the Local X position of the item</summary>
public uint LocalX { get { return GlobalX % 256; } }
public uint LocalX { get { return GlobalX % Simulator.DefaultRegionSizeX; } }
/// <summary>Get the Local Y position of the item</summary>
public uint LocalY { get { return GlobalY % 256; } }
public uint LocalY { get { return GlobalY % Simulator.DefaultRegionSizeY; } }
/// <summary>Get the Handle of the region</summary>
public ulong RegionHandle
{
get { return Utils.UIntsToLong((uint)(GlobalX - (GlobalX % 256)), (uint)(GlobalY - (GlobalY % 256))); }
get { return Utils.UIntsToLong((uint)(GlobalX - (GlobalX % Simulator.DefaultRegionSizeX)),
(uint)(GlobalY - (GlobalY % Simulator.DefaultRegionSizeY))); }
}
}

View File

@@ -149,6 +149,11 @@ namespace OpenMetaverse
/// Given an X/Y location in absolute (grid-relative) terms, a region
/// handle is returned along with the local X/Y location in that region
/// </summary>
/// <remarks>
/// NOTE: this does not work for varregions -- the region handle is correct but
/// the local X,Y are wrong. TODO: create new function
/// that takes a reference to the region and thus can calculate region local address.
/// </remarks>
/// <param name="globalX">The absolute X location, a number such as
/// 255360.35</param>
/// <param name="globalY">The absolute Y location, a number such as
@@ -160,8 +165,8 @@ namespace OpenMetaverse
/// <returns>A 64-bit region handle that can be used to teleport to</returns>
public static ulong GlobalPosToRegionHandle(float globalX, float globalY, out float localX, out float localY)
{
uint x = ((uint)globalX / 256) * 256;
uint y = ((uint)globalY / 256) * 256;
uint x = ((uint)globalX / Simulator.DefaultRegionSizeX) * Simulator.DefaultRegionSizeX;
uint y = ((uint)globalY / Simulator.DefaultRegionSizeY) * Simulator.DefaultRegionSizeY;
localX = globalX - (float)x;
localY = globalY - (float)y;
return Utils.UIntsToLong(x, y);

View File

@@ -546,8 +546,8 @@ namespace OpenMetaverse
if (client.Settings.STORE_LAND_PATCHES)
{
Terrain = new TerrainPatch[16 * 16];
WindSpeeds = new Vector2[16 * 16];
Terrain = new TerrainPatch[sizeX/16 * sizeY/16];
WindSpeeds = new Vector2[sizeX/16 * sizeY/16];
}
}

View File

@@ -59,18 +59,19 @@ namespace OpenMetaverse
public int Range;
public int QuantWBits;
public int PatchIDs;
public bool LargeRegion; // true if PatchIDs are 32 bits and not 10
public uint WordBits;
public int X
{
get { return PatchIDs >> 5; }
set { PatchIDs += (value << 5); }
get { return PatchIDs >> (LargeRegion ? 16 : 5); }
set { PatchIDs += (value << (LargeRegion ? 16 : 5)); }
}
public int Y
{
get { return PatchIDs & 0x1F; }
set { PatchIDs |= value & 0x1F; }
get { return PatchIDs & (LargeRegion ? 0xffff : 0x1F); }
set { PatchIDs |= value & (LargeRegion ? 0xffff : 0x1F); }
}
}
@@ -148,14 +149,18 @@ namespace OpenMetaverse
/// simulator heightmap and an array of indices of patches to compress
/// </summary>
/// <param name="heightmap">A 256 * 256 array of floating point values
/// specifying the height at each meter in the simulator</param>
/// specifying the height at each meter in the simulator.
/// This can be larger if it is a varregion.</param>
/// <param name="patches">Array of indexes in the 16x16 grid of patches
/// for this simulator. For example if 1 and 17 are specified, patches
/// x=1,y=0 and x=1,y=1 are sent</param>
/// <returns></returns>
public static LayerDataPacket CreateLandPacket(float[] heightmap, int[] patches)
{
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = (byte) TerrainPatch.LayerType.Land}};
var layerType = TerrainPatch.LayerType.Land;
if (heightmap.Length > Simulator.DefaultRegionSizeY * Simulator.DefaultRegionSizeY)
layerType = TerrainPatch.LayerType.LandExtended;
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = (byte) layerType}};
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
{
@@ -235,15 +240,18 @@ namespace OpenMetaverse
return layer;
}
public static void CreatePatch(BitPack output, float[] patchData, int x, int y)
public static void CreatePatch(BitPack output, float[] patchData, int x, int y, bool largeRegion = false)
{
if (patchData.Length != 16 * 16)
throw new ArgumentException("Patch data must be a 16x16 array");
TerrainPatch.Header header = PrescanPatch(patchData);
header.LargeRegion = largeRegion;
header.QuantWBits = 136;
header.PatchIDs = (y & 0x1F);
header.PatchIDs += (x << 5);
if (largeRegion)
header.PatchIDs = (x << 16) | (y & 0xffff);
else
header.PatchIDs = (x << 5) | (y & 0x1f);
// NOTE: No idea what prequant and postquant should be or what they do
int[] patch = CompressPatch(patchData, header, 10);
@@ -251,15 +259,18 @@ namespace OpenMetaverse
EncodePatch(output, patch, 0, wbits);
}
public static void CreatePatch(BitPack output, float[,] patchData, int x, int y)
public static void CreatePatch(BitPack output, float[,] patchData, int x, int y, bool largeRegion = false)
{
if (patchData.Length != 16 * 16)
throw new ArgumentException("Patch data must be a 16x16 array");
var header = PrescanPatch(patchData);
header.LargeRegion = largeRegion;
header.QuantWBits = 136;
header.PatchIDs = (y & 0x1F);
header.PatchIDs += (x << 5);
if (largeRegion)
header.PatchIDs = (x << 16) | (y & 0xffff);
else
header.PatchIDs = (x << 5) | (y & 0x1f);
// NOTE: No idea what prequant and postquant should be or what they do
int[] patch = CompressPatch(patchData, header, 10);
@@ -277,7 +288,7 @@ namespace OpenMetaverse
/// from 0 to 15</param>
/// <param name="y">Y offset of the patch to create, valid values are
/// from 0 to 15</param>
public static void CreatePatchFromHeightmap(BitPack output, float[] heightmap, int x, int y)
public static void CreatePatchFromHeightmap(BitPack output, float[] heightmap, int x, int y, bool largeRegion = false)
{
if (heightmap.Length != 256 * 256)
throw new ArgumentException("Heightmap data must be 256x256");
@@ -286,9 +297,12 @@ namespace OpenMetaverse
throw new ArgumentException("X and Y patch offsets must be from 0 to 15");
var header = PrescanPatch(heightmap, x, y);
header.LargeRegion = largeRegion;
header.QuantWBits = 136;
header.PatchIDs = (y & 0x1F);
header.PatchIDs += (x << 5);
if (largeRegion)
header.PatchIDs = (x << 16) | (y & 0xffff);
else
header.PatchIDs = (x << 5) | (y & 0x1f);
// NOTE: No idea what prequant and postquant should be or what they do
int[] patch = CompressPatch(heightmap, x, y, header, 10);
@@ -296,6 +310,8 @@ namespace OpenMetaverse
EncodePatch(output, patch, 0, wbits);
}
// Scan the height map to get the range of values so the values can be compressed.
// Returns a TerrainPatch.Header with the range information filled in.
private static TerrainPatch.Header PrescanPatch(float[] patch)
{
TerrainPatch.Header header = new TerrainPatch.Header();
@@ -318,6 +334,8 @@ namespace OpenMetaverse
return header;
}
// Scan the height map to get the range of values so the values can be compressed.
// Returns a TerrainPatch.Header with the range information filled in.
private static TerrainPatch.Header PrescanPatch(float[,] patch)
{
TerrainPatch.Header header = new TerrainPatch.Header();
@@ -340,6 +358,8 @@ namespace OpenMetaverse
return header;
}
// Scan the height map to get the range of values so the values can be compressed.
// Returns a TerrainPatch.Header with the range information filled in.
private static TerrainPatch.Header PrescanPatch(float[] heightmap, int patchX, int patchY)
{
TerrainPatch.Header header = new TerrainPatch.Header();
@@ -362,7 +382,7 @@ namespace OpenMetaverse
return header;
}
public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack)
public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack, bool largeRegion = false)
{
TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)};
@@ -377,7 +397,9 @@ namespace OpenMetaverse
header.Range = bitpack.UnpackBits(16);
// Patch IDs (10 bits)
header.PatchIDs = bitpack.UnpackBits(10);
header.LargeRegion = largeRegion;
header.PatchIDs = bitpack.UnpackBits(largeRegion ? 32 : 10);
// Word bits
header.WordBits = (uint)((header.QuantWBits & 0x0f) + 2);
@@ -428,7 +450,7 @@ namespace OpenMetaverse
output.PackBits(header.QuantWBits, 8);
output.PackFloat(header.DCOffset);
output.PackBits(header.Range, 16);
output.PackBits(header.PatchIDs, 10);
output.PackBits(header.PatchIDs, header.LargeRegion ? 32 : 10); // the IDs are larger for large regions
return wbits;
}

View File

@@ -67,7 +67,7 @@ namespace OpenMetaverse
Client.Network.RegisterCallback(PacketType.LayerData, LayerDataHandler);
}
private void DecompressLand(Simulator simulator, BitPack bitpack, TerrainPatch.GroupHeader group)
private void DecompressLand(Simulator simulator, BitPack bitpack, TerrainPatch.GroupHeader group, bool largeRegion = false)
{
int x;
int y;
@@ -76,7 +76,7 @@ namespace OpenMetaverse
while (true)
{
TerrainPatch.Header header = TerrainCompressor.DecodePatchHeader(bitpack);
TerrainPatch.Header header = TerrainCompressor.DecodePatchHeader(bitpack, largeRegion);
if (header.QuantWBits == TerrainCompressor.END_OF_PATCHES)
break;
@@ -84,7 +84,7 @@ namespace OpenMetaverse
x = header.X;
y = header.Y;
if (x >= TerrainCompressor.PATCHES_PER_EDGE || y >= TerrainCompressor.PATCHES_PER_EDGE)
if (!largeRegion && (x >= TerrainCompressor.PATCHES_PER_EDGE || y >= TerrainCompressor.PATCHES_PER_EDGE))
{
Logger.Log(String.Format(
"Invalid LayerData land packet, x={0}, y={1}, dc_offset={2}, range={3}, quant_wbits={4}, patchids={5}, count={6}",
@@ -117,7 +117,7 @@ namespace OpenMetaverse
}
}
private void DecompressWind(Simulator simulator, BitPack bitpack, TerrainPatch.GroupHeader group)
private void DecompressWind(Simulator simulator, BitPack bitpack, TerrainPatch.GroupHeader group, bool largeRegion = false)
{
int[] patches = new int[32 * 32];
@@ -131,12 +131,12 @@ namespace OpenMetaverse
// wind_direction = vec2(x,y)
// X values
TerrainPatch.Header header = TerrainCompressor.DecodePatchHeader(bitpack);
TerrainPatch.Header header = TerrainCompressor.DecodePatchHeader(bitpack, largeRegion);
TerrainCompressor.DecodePatch(patches, bitpack, header, group.PatchSize);
float[] xvalues = TerrainCompressor.DecompressPatch(patches, header, group);
// Y values
header = TerrainCompressor.DecodePatchHeader(bitpack);
header = TerrainCompressor.DecodePatchHeader(bitpack, largeRegion);
TerrainCompressor.DecodePatch(patches, bitpack, header, group.PatchSize);
float[] yvalues = TerrainCompressor.DecompressPatch(patches, header, group);
@@ -172,6 +172,10 @@ namespace OpenMetaverse
if (m_LandPatchReceivedEvent != null || Client.Settings.STORE_LAND_PATCHES)
DecompressLand(e.Simulator, bitpack, header);
break;
case TerrainPatch.LayerType.LandExtended:
if (m_LandPatchReceivedEvent != null || Client.Settings.STORE_LAND_PATCHES)
DecompressLand(e.Simulator, bitpack, header, true);
break;
case TerrainPatch.LayerType.Water:
Logger.Log("Got a Water LayerData packet, implement me!", Helpers.LogLevel.Error, Client);
break;