/* * Copyright (c) OpenSim project, http://sim.opensecondlife.org/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ using System; using System.Collections.Generic; //using libsecondlife; using libsecondlife.Packets; namespace OpenSim { /// /// Description of TerrainDecoder. /// public class TerrainDecode { public enum LayerType : byte { Land = 0x4C, Water = 0x57, Wind = 0x37, Cloud = 0x38 } public struct GroupHeader { public int Stride; public int PatchSize; public LayerType Type; } public struct PatchHeader { public float DCOffset; public int Range; public int QuantWBits; public int PatchIDs; public uint WordBits; } public class Patch { public float[] Heightmap; } /// /// /// /// /// /// /// /// // public delegate void LandPatchCallback(Simulator simulator, int x, int y, int width, float[] data); /// /// /// //public event LandPatchCallback OnLandPatch; private Random RandomClass = new Random(); private const byte END_OF_PATCHES = 97; private const int PATCHES_PER_EDGE = 16; private const float OO_SQRT2 = 0.7071067811865475244008443621049f; //private SecondLife Client; private Dictionary SimPatches = new Dictionary(); private float[] DequantizeTable16 = new float[16 * 16]; private float[] DequantizeTable32 = new float[32 * 32]; private float[] ICosineTable16 = new float[16 * 16]; private float[] ICosineTable32 = new float[32 * 32]; private int[] DeCopyMatrix16 = new int[16 * 16]; private int[] DeCopyMatrix32 = new int[32 * 32]; /// /// /// /// public TerrainDecode() { // Initialize the decompression tables BuildDequantizeTable16(); BuildDequantizeTable32(); SetupICosines16(); SetupICosines32(); BuildDecopyMatrix16(); BuildDecopyMatrix32(); } private void BuildDequantizeTable16() { for (int j = 0; j < 16; j++) { for (int i = 0; i < 16; i++) { DequantizeTable16[j * 16 + i] = 1.0f + 2.0f * (float)(i + j); } } } private void BuildDequantizeTable32() { for (int j = 0; j < 32; j++) { for (int i = 0; i < 32; i++) { DequantizeTable32[j * 32 + i] = 1.0f + 2.0f * (float)(i + j); } } } private void SetupICosines16() { const float hposz = (float)Math.PI * 0.5f / 16.0f; for (int u = 0; u < 16; u++) { for (int n = 0; n < 16; n++) { ICosineTable16[u * 16 + n] = (float)Math.Cos((2.0f * (float)n + 1.0f) * (float)u * hposz); } } } private void SetupICosines32() { const float hposz = (float)Math.PI * 0.5f / 32.0f; for (int u = 0; u < 32; u++) { for (int n = 0; n < 32; n++) { ICosineTable32[u * 32 + n] = (float)Math.Cos((2.0f * (float)n + 1.0f) * (float)u * hposz); } } } private void BuildDecopyMatrix16() { bool diag = false; bool right = true; int i = 0; int j = 0; int count = 0; while (i < 16 && j < 16) { DeCopyMatrix16[j * 16 + i] = count++; if (!diag) { if (right) { if (i < 16 - 1) i++; else j++; right = false; diag = true; } else { if (j < 16 - 1) j++; else i++; right = true; diag = true; } } else { if (right) { i++; j--; if (i == 16 - 1 || j == 0) diag = false; } else { i--; j++; if (j == 16 - 1 || i == 0) diag = false; } } } } private void BuildDecopyMatrix32() { bool diag = false; bool right = true; int i = 0; int j = 0; int count = 0; while (i < 32 && j < 32) { DeCopyMatrix32[j * 32 + i] = count++; if (!diag) { if (right) { if (i < 32 - 1) i++; else j++; right = false; diag = true; } else { if (j < 32 - 1) j++; else i++; right = true; diag = true; } } else { if (right) { i++; j--; if (i == 32 - 1 || j == 0) diag = false; } else { i--; j++; if (j == 32 - 1 || i == 0) diag = false; } } } } private void EncodePatchHeader(BitPacker bitpack, PatchHeader header) { bitpack.PackBits(header.QuantWBits,8); if (header.QuantWBits == END_OF_PATCHES) return; bitpack.PackFloat(header.DCOffset); bitpack.PackBits(header.Range,16); bitpack.PackBits(header.PatchIDs,10); } public void DCTLine16(float[] In, float[] Out, int line) { int N =16; int lineSize = line * 16; for(int k = 0; k < N;k++) { float sum = 0.0f; for(int n = 0; n < N; n++) { float num = (float)(Math.PI*k*(2.0f*n+1)/(2*N)); float cosine = (float)Math.Cos(num); float product = In[lineSize +n] * cosine; sum += product; } float alpha; if(k == 0) { alpha = (float)(1.0f/Math.Sqrt(2)); } else { alpha = 1; } Out[lineSize + k] =(float)( sum * alpha ); } } public void DCTColumn16(float[] In, float[] Out, int Column) { int N =16; int uSize; for(int k = 0; k < N; k++){ float sum = 0.0f; for(int n = 0; n < N; n++) { uSize = n * 16; float num = (float)(Math.PI*k*(2.0f*n+1)/(2*N)); float cosine = (float)Math.Cos(num); float product = In[uSize + Column] * cosine; sum += product; } float alpha; if(k == 0) { alpha = (float)(1.0f/Math.Sqrt(2)); } else { alpha = 1; } Out[16 * k + Column] = (float)( sum * alpha * (2.0f /N)); } } private void EncodePatch(int[] patches, BitPacker bitpack, int size) { int lastnum =0; for(int n = 0; n < size * size; n++) { if(patches[n]!=0) lastnum=n; } for (int n = 0; n < lastnum+1; n++) { if(patches[n] != 0) { bitpack.PackBits(1,1); //value or EOB bitpack.PackBits(1,1); //value if(patches[n] > 0) { bitpack.PackBits(0,1); // positive bitpack.PackBits(patches[n],13); } else { bitpack.PackBits(1,1); // negative int temp = patches[n] * -1; bitpack.PackBits(temp,13); } } else { bitpack.PackBits(0,1); // no value } } bitpack.PackBits(1,1); //value or EOB bitpack.PackBits(0,1); // EOB } public int[] CompressPatch(float[] patches) { int size = 16; float[] block = new float[size * size]; int[] output = new int[size * size]; int prequant = (139 >> 4) + 2; int quantize = 1 << prequant; float ooq = 1.0f / (float)quantize; float mult = ooq * (float)1; float addval = mult * (float)(1 << (prequant - 1)) + 20.4989f; if (size == 16) { for (int n = 0; n < 16 * 16; n++) { block[n] = (float)((patches[n] - addval)/ mult); } float[] ftemp = new float[32 * 32]; for (int o = 0; o < 16; o++) this.DCTColumn16(block, ftemp, o); for (int o = 0; o < 16; o++) this.DCTLine16(ftemp, block, o); } for (int j = 0; j < block.Length; j++) { output[DeCopyMatrix16[j]] = (int)(block[j] / DequantizeTable16[j]); } return output; } public Packet CreateLayerPacket(float[] heightmap, int minX, int minY, int maxX, int maxY) { //int minX = 0, maxX = 2, minY = 0, maxY = 1; //these should be passed to this function LayerDataPacket layer = new LayerDataPacket(); byte[] Encoded = new byte[2048]; layer.LayerID.Type = 76; GroupHeader header = new GroupHeader(); header.Stride = 264; header.PatchSize = 16; header.Type = LayerType.Land; BitPacker newpack = new BitPacker(Encoded,0); newpack.PackBits(header.Stride,16); newpack.PackBits(header.PatchSize,8); newpack.PackBits((int)header.Type,8); float[] height; for(int y = minY; y< maxY; y++) { for(int x = minX ; x < maxX ; x++) { height = new float[256]; Array.Copy(heightmap, (4096 *y) +(x *256), height, 0, 256); this.CreatePatch(height, newpack, x, y); } } PatchHeader headers = new PatchHeader(); headers.QuantWBits = END_OF_PATCHES; this.EncodePatchHeader(newpack, headers); int lastused=0; for(int i = 0; i < 2048 ; i++) { if(Encoded[i] !=0) lastused = i; } byte[] data = new byte[lastused+1]; Array.Copy(Encoded, data, lastused+1); layer.LayerData.Data =data; return(layer); } public void CreatePatch(float[] heightmap, BitPacker newpack, int x, int y) { PatchHeader header = new PatchHeader(); header.DCOffset = 20.4989f; header.QuantWBits = 139; header.Range = 1; header.PatchIDs = (y & 0x1F); header.PatchIDs += x <<5 ; this.EncodePatchHeader(newpack, header); int[] newpatch = this.CompressPatch(heightmap); this.EncodePatch(newpatch, newpack, 16); } } //*************************************************** public class BitPacker { private const int MAX_BITS = 8; private byte[] Data; public int bytePos; public int bitPos; /// /// Default constructor, initialize the bit packer / bit unpacker /// with a byte array and starting position /// /// Byte array to pack bits in to or unpack from /// Starting position in the byte array public BitPacker(byte[] data, int pos) { Data = data; bytePos = pos; } /// /// Pack a floating point value in to the data /// /// Floating point value to pack public void PackFloat(float data) { byte[] input = BitConverter.GetBytes(data); PackBitArray(input, 32); } /// /// Pack part or all of an integer in to the data /// /// Integer containing the data to pack /// Number of bits of the integer to pack public void PackBits(int data, int totalCount) { byte[] input = BitConverter.GetBytes(data); PackBitArray(input, totalCount); } /// /// Unpacking a floating point value from the data /// /// Unpacked floating point value public float UnpackFloat() { byte[] output = UnpackBitsArray(32); if (!BitConverter.IsLittleEndian) Array.Reverse(output); return BitConverter.ToSingle(output, 0); } /// /// Unpack a variable number of bits from the data in to integer format /// /// Number of bits to unpack /// An integer containing the unpacked bits /// This function is only useful up to 32 bits public int UnpackBits(int totalCount) { byte[] output = UnpackBitsArray(totalCount); if (!BitConverter.IsLittleEndian) Array.Reverse(output); return BitConverter.ToInt32(output, 0); } private void PackBitArray(byte[] data, int totalCount) { int count = 0; int curBytePos = 0; int curBitPos = 0; while (totalCount > 0) { if (totalCount > (MAX_BITS )) { count = MAX_BITS ; totalCount -= MAX_BITS ; } else { count = totalCount; totalCount = 0; } while (count > 0) { switch(count) { case 1: if ((data[curBytePos] & (0x01)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 2: if ((data[curBytePos] & (0x02)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 3: if ((data[curBytePos] & (0x04)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 4: if ((data[curBytePos] & (0x08)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 5: if ((data[curBytePos] & (0x10)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 6: if ((data[curBytePos] & (0x20)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 7: if ((data[curBytePos] & (0x40)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; case 8: if ((data[curBytePos] & (0x80)) != 0) { Data[bytePos] |= (byte)(0x80 >> bitPos); } break; } bitPos++; --count; ++curBitPos; if (bitPos >= MAX_BITS) { bitPos = 0; ++bytePos; } if (curBitPos >= MAX_BITS) { curBitPos = 0; ++curBytePos; } } } } private byte[] UnpackBitsArray(int totalCount) { int count = 0; byte[] output = new byte[4]; int curBytePos = 0; int curBitPos = 0; while (totalCount > 0) { if (totalCount > MAX_BITS) { count = MAX_BITS; totalCount -= MAX_BITS; } else { count = totalCount; totalCount = 0; } while (count > 0) { // Shift the previous bits output[curBytePos] <<= 1; // Grab one bit if ((Data[bytePos] & (0x80 >> bitPos++)) != 0) ++output[curBytePos]; --count; ++curBitPos; if (bitPos >= MAX_BITS) { bitPos = 0; ++bytePos; } if (curBitPos >= MAX_BITS) { curBitPos = 0; ++curBytePos; } } } return output; } } }