OpenSimMirror/world/TerrainDecoder.cs

684 lines
21 KiB
C#
Raw Permalink Normal View History

2007-03-04 12:40:55 +00:00
/*
* 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 <organization> 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 <copyright holder> ``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 <copyright holder> 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
{
/// <summary>
/// Description of TerrainDecoder.
/// </summary>
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;
}
/// <summary>
///
/// </summary>
/// <param name="simulator"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="data"></param>
// public delegate void LandPatchCallback(Simulator simulator, int x, int y, int width, float[] data);
/// <summary>
///
/// </summary>
//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<ulong, Patch[]> SimPatches = new Dictionary<ulong, Patch[]>();
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];
/// <summary>
///
/// </summary>
/// <param name="client"></param>
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;
/// <summary>
/// Default constructor, initialize the bit packer / bit unpacker
/// with a byte array and starting position
/// </summary>
/// <param name="data">Byte array to pack bits in to or unpack from</param>
/// <param name="pos">Starting position in the byte array</param>
public BitPacker(byte[] data, int pos)
{
Data = data;
bytePos = pos;
}
/// <summary>
/// Pack a floating point value in to the data
/// </summary>
/// <param name="data">Floating point value to pack</param>
public void PackFloat(float data)
{
byte[] input = BitConverter.GetBytes(data);
PackBitArray(input, 32);
}
/// <summary>
/// Pack part or all of an integer in to the data
/// </summary>
/// <param name="data">Integer containing the data to pack</param>
/// <param name="totalCount">Number of bits of the integer to pack</param>
public void PackBits(int data, int totalCount)
{
byte[] input = BitConverter.GetBytes(data);
PackBitArray(input, totalCount);
}
/// <summary>
/// Unpacking a floating point value from the data
/// </summary>
/// <returns>Unpacked floating point value</returns>
public float UnpackFloat()
{
byte[] output = UnpackBitsArray(32);
if (!BitConverter.IsLittleEndian) Array.Reverse(output);
return BitConverter.ToSingle(output, 0);
}
/// <summary>
/// Unpack a variable number of bits from the data in to integer format
/// </summary>
/// <param name="totalCount">Number of bits to unpack</param>
/// <returns>An integer containing the unpacked bits</returns>
/// <remarks>This function is only useful up to 32 bits</remarks>
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;
}
}
}