diff --git a/LibreMetaverse/Assets/Archiving/OarFile.cs b/LibreMetaverse/Assets/Archiving/OarFile.cs index 99eb2a55..fc8d57fc 100644 --- a/LibreMetaverse/Assets/Archiving/OarFile.cs +++ b/LibreMetaverse/Assets/Archiving/OarFile.cs @@ -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); diff --git a/LibreMetaverse/GridManager.cs b/LibreMetaverse/GridManager.cs index e273a077..03f75a48 100644 --- a/LibreMetaverse/GridManager.cs +++ b/LibreMetaverse/GridManager.cs @@ -158,14 +158,15 @@ namespace OpenMetaverse public uint GlobalY; /// Get the Local X position of the item - public uint LocalX { get { return GlobalX % 256; } } + public uint LocalX { get { return GlobalX % Simulator.DefaultRegionSizeX; } } /// Get the Local Y position of the item - public uint LocalY { get { return GlobalY % 256; } } + public uint LocalY { get { return GlobalY % Simulator.DefaultRegionSizeY; } } /// Get the Handle of the region 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))); } } } diff --git a/LibreMetaverse/Helpers.cs b/LibreMetaverse/Helpers.cs index 9f3b09b2..45c6076f 100644 --- a/LibreMetaverse/Helpers.cs +++ b/LibreMetaverse/Helpers.cs @@ -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 /// + /// + /// 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. + /// /// The absolute X location, a number such as /// 255360.35 /// The absolute Y location, a number such as @@ -160,8 +165,8 @@ namespace OpenMetaverse /// A 64-bit region handle that can be used to teleport to 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); diff --git a/LibreMetaverse/Simulator.cs b/LibreMetaverse/Simulator.cs index 79438ca0..2f82738c 100644 --- a/LibreMetaverse/Simulator.cs +++ b/LibreMetaverse/Simulator.cs @@ -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]; } } diff --git a/LibreMetaverse/TerrainCompressor.cs b/LibreMetaverse/TerrainCompressor.cs index 43ffd4ae..d0d03504 100644 --- a/LibreMetaverse/TerrainCompressor.cs +++ b/LibreMetaverse/TerrainCompressor.cs @@ -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 /// /// A 256 * 256 array of floating point values - /// specifying the height at each meter in the simulator + /// specifying the height at each meter in the simulator. + /// This can be larger if it is a varregion. /// 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 /// 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 /// Y offset of the patch to create, valid values are /// from 0 to 15 - 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; } diff --git a/LibreMetaverse/TerrainManager.cs b/LibreMetaverse/TerrainManager.cs index 8a112e94..71183ea8 100644 --- a/LibreMetaverse/TerrainManager.cs +++ b/LibreMetaverse/TerrainManager.cs @@ -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;