varregion: add new TerrainData and TerrainCompressor routines. TerrainCompressor needed to replace the one in libopenmetaverse that doesn't know about the larger terrain packets.
parent
8c432c7c9b
commit
9b150194f6
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* 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 OpenSimulator Project 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 THE DEVELOPERS ``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 THE CONTRIBUTORS 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 System.IO;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Framework
|
||||
{
|
||||
public abstract class TerrainData
|
||||
{
|
||||
// Terrain always is a square
|
||||
public int SizeX { get; protected set; }
|
||||
public int SizeY { get; protected set; }
|
||||
public int SizeZ { get; protected set; }
|
||||
|
||||
public abstract float this[int x, int y] { get; set; }
|
||||
// Someday terrain will have caves
|
||||
public abstract float this[int x, int y, int z] { get; set; }
|
||||
|
||||
// Return a representation of this terrain for storing as a blob in the database.
|
||||
// Returns 'true' to say blob was stored in the 'out' locations.
|
||||
public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob);
|
||||
}
|
||||
|
||||
// The terrain is stored as a blob in the database with a 'revision' field.
|
||||
// Some implementations of terrain storage would fill the revision field with
|
||||
// the time the terrain was stored. When real revisions were added and this
|
||||
// feature removed, that left some old entries with the time in the revision
|
||||
// field.
|
||||
// Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
|
||||
// left over and it is presumed to be 'Legacy256'.
|
||||
// Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
|
||||
// If a revision does not match any of these, it is assumed to be Legacy256.
|
||||
public enum DBTerrainRevision
|
||||
{
|
||||
// Terrain is 'double[256,256]'
|
||||
Legacy256 = 11,
|
||||
// Terrain is 'int32, int32, float[,]' where the shorts are X and Y dimensions
|
||||
// The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
|
||||
Variable2D = 22,
|
||||
// A revision that is not listed above or any revision greater than this value is 'Legacy256'.
|
||||
RevisionHigh = 1234
|
||||
}
|
||||
|
||||
// Version of terrain that is a heightmap.
|
||||
// This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge
|
||||
// of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer.
|
||||
public class HeightmapTerrainData : TerrainData
|
||||
{
|
||||
// TerrainData.this[x, y]
|
||||
public override float this[int x, int y]
|
||||
{
|
||||
get { return m_heightmap[x * SizeX + y]; }
|
||||
set { m_heightmap[x * SizeX + y] = value; }
|
||||
}
|
||||
|
||||
// TerrainData.this[x, y, z]
|
||||
public override float this[int x, int y, int z]
|
||||
{
|
||||
get { return this[x, y]; }
|
||||
set { this[x, y] = value; }
|
||||
}
|
||||
|
||||
// TerrainData.GetDatabaseBlob
|
||||
// The user wants something to store in the database.
|
||||
public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
|
||||
{
|
||||
DBRevisionCode = (int)DBTerrainRevision.Legacy256;
|
||||
blob = LegacyTerrainSerialization();
|
||||
return false;
|
||||
}
|
||||
private float[] m_heightmap;
|
||||
|
||||
// To keep with the legacy theme, this can be created with the way terrain
|
||||
// used to passed around as.
|
||||
public HeightmapTerrainData(double[,] pTerrain)
|
||||
{
|
||||
SizeX = pTerrain.GetLength(0);
|
||||
SizeY = pTerrain.GetLength(1);
|
||||
SizeZ = (int)Constants.RegionHeight;
|
||||
|
||||
int idx = 0;
|
||||
m_heightmap = new float[SizeX * SizeY];
|
||||
for (int ii = 0; ii < SizeX; ii++)
|
||||
{
|
||||
for (int jj = 0; jj < SizeY; jj++)
|
||||
{
|
||||
m_heightmap[idx++] = (float)pTerrain[ii, jj];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HeightmapTerrainData(float[] pHeightmap, int pX, int pY, int pZ)
|
||||
{
|
||||
m_heightmap = pHeightmap;
|
||||
SizeX = pX;
|
||||
SizeY = pY;
|
||||
SizeZ = pZ;
|
||||
}
|
||||
|
||||
// Just create an array of doubles. Presumes the caller implicitly knows the size.
|
||||
public Array LegacyTerrainSerialization()
|
||||
{
|
||||
Array ret = null;
|
||||
using (MemoryStream str = new MemoryStream(SizeX * SizeY * sizeof(double)))
|
||||
{
|
||||
using (BinaryWriter bw = new BinaryWriter(str))
|
||||
{
|
||||
// TODO: COMPATIBILITY - Add byte-order conversions
|
||||
for (int ii = 0; ii < m_heightmap.Length; ii++)
|
||||
{
|
||||
double height = (double)m_heightmap[ii];
|
||||
if (height == 0.0)
|
||||
height = double.Epsilon;
|
||||
|
||||
bw.Write(height);
|
||||
}
|
||||
}
|
||||
ret = str.ToArray();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,958 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* 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 OpenSimulator Project 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 THE DEVELOPERS ``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 THE CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
/* Freely adapted from the Aurora version of the terrain compressor.
|
||||
* Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using log4net;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public static class OpenSimTerrainCompressor
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static string LogHeader = "[TERRAIN COMPRESSOR]";
|
||||
|
||||
public const int END_OF_PATCHES = 97;
|
||||
|
||||
private const float OO_SQRT2 = 0.7071067811865475244008443621049f;
|
||||
private const int STRIDE = 264;
|
||||
|
||||
private const int ZERO_CODE = 0x0;
|
||||
private const int ZERO_EOB = 0x2;
|
||||
private const int POSITIVE_VALUE = 0x6;
|
||||
private const int NEGATIVE_VALUE = 0x7;
|
||||
|
||||
private static readonly float[] DequantizeTable16 =
|
||||
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
private static readonly float[] DequantizeTable32 =
|
||||
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
private static readonly float[] CosineTable16 = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
//private static readonly float[] CosineTable32 = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize];
|
||||
private static readonly int[] CopyMatrix16 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
private static readonly int[] CopyMatrix32 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
private static readonly float[] QuantizeTable16 =
|
||||
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
static OpenSimTerrainCompressor()
|
||||
{
|
||||
// Initialize the decompression tables
|
||||
BuildDequantizeTable16();
|
||||
SetupCosines16();
|
||||
BuildCopyMatrix16();
|
||||
BuildQuantizeTable16();
|
||||
}
|
||||
|
||||
// Unused: left for historical reference.
|
||||
public static LayerDataPacket CreateLayerDataPacket(TerrainPatch[] patches, byte type, int pRegionSizeX,
|
||||
int pRegionSizeY)
|
||||
{
|
||||
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
|
||||
|
||||
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
|
||||
{Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
|
||||
|
||||
// Should be enough to fit even the most poorly packed data
|
||||
byte[] data = new byte[patches.Length*Constants.TerrainPatchSize*Constants.TerrainPatchSize*2];
|
||||
BitPack bitpack = new BitPack(data, 0);
|
||||
bitpack.PackBits(header.Stride, 16);
|
||||
bitpack.PackBits(header.PatchSize, 8);
|
||||
bitpack.PackBits(type, 8);
|
||||
|
||||
foreach (TerrainPatch t in patches)
|
||||
CreatePatch(bitpack, t.Data, t.X, t.Y, pRegionSizeX, pRegionSizeY);
|
||||
|
||||
bitpack.PackBits(END_OF_PATCHES, 8);
|
||||
|
||||
layer.LayerData.Data = new byte[bitpack.BytePos + 1];
|
||||
Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
// Legacy land packet generation gluing old land representation (heights) to compressed representation.
|
||||
// This is an intermediate step in converting terrain into a variable sized heightmap. Some of the
|
||||
// routines (like IClientAPI) only pass the float array of heights around. This entry
|
||||
// converts that legacy representation into the more compact represenation used in
|
||||
// TerrainChannel. Someday fix the plumbing between here and the scene.
|
||||
public static LayerDataPacket CreateLandPacket(float[] heightmap, int patchX, int patchY, uint sizeX, uint sizeY)
|
||||
{
|
||||
int[] xPieces = new int[1];
|
||||
int[] yPieces = new int[1];
|
||||
|
||||
short[] newmap = new short[heightmap.Length];
|
||||
for (int ii = 0; ii < heightmap.Length; ii++)
|
||||
newmap[ii] = TerrainChannel.ToCompressedHeight(heightmap[ii]);
|
||||
|
||||
xPieces[0] = patchX; // patch X dimension
|
||||
yPieces[0] = patchY;
|
||||
|
||||
m_log.DebugFormat("{0} CreateLandPacket. patchX={1}, patchY={2}, sizeX={3}, sizeY={4}",
|
||||
LogHeader, patchX, patchY, sizeX, sizeY);
|
||||
|
||||
return CreateLandPacket(newmap, xPieces, yPieces, (int)TerrainPatch.LayerType.Land, sizeX, sizeY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a LayerData packet for compressed land data given a full
|
||||
/// 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>
|
||||
/// <param name="x">
|
||||
/// 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>
|
||||
/// <param name="y">
|
||||
/// 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>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="pRegionSizeX"></param>
|
||||
/// <param name="pRegionSizeY"></param>
|
||||
/// <returns></returns>
|
||||
public static LayerDataPacket CreateLandPacket(short[] heightmap, int[] x, int[] y, byte type,
|
||||
uint pRegionSizeX, uint pRegionSizeY)
|
||||
{
|
||||
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
|
||||
|
||||
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
|
||||
{Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
|
||||
|
||||
byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2];
|
||||
BitPack bitpack = new BitPack(data, 0);
|
||||
bitpack.PackBits(header.Stride, 16);
|
||||
bitpack.PackBits(header.PatchSize, 8);
|
||||
bitpack.PackBits(type, 8);
|
||||
|
||||
for (int i = 0; i < x.Length; i++)
|
||||
CreatePatchFromHeightmap(bitpack, heightmap, x[i], y[i], pRegionSizeX, pRegionSizeY);
|
||||
|
||||
bitpack.PackBits(END_OF_PATCHES, 8);
|
||||
|
||||
layer.LayerData.Data = new byte[bitpack.BytePos + 1];
|
||||
Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
// Unused: left for historical reference.
|
||||
public static void CreatePatch(BitPack output, float[] patchData, int x, int y, int pRegionSizeX, int pRegionSizeY)
|
||||
{
|
||||
TerrainPatch.Header header = PrescanPatch(patchData);
|
||||
header.QuantWBits = 136;
|
||||
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
|
||||
{
|
||||
header.PatchIDs = (y & 0xFFFF);
|
||||
header.PatchIDs += (x << 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
header.PatchIDs = (y & 0x1F);
|
||||
header.PatchIDs += (x << 5);
|
||||
}
|
||||
|
||||
// NOTE: No idea what prequant and postquant should be or what they do
|
||||
|
||||
int wbits;
|
||||
int[] patch = CompressPatch(patchData, header, 10, out wbits);
|
||||
wbits = EncodePatchHeader(output, header, patch, Constants.RegionSize, Constants.RegionSize, wbits);
|
||||
EncodePatch(output, patch, 0, wbits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a patch of terrain to a BitPacker
|
||||
/// </summary>
|
||||
/// <param name="output">BitPacker to write the patch to</param>
|
||||
/// <param name="heightmap">
|
||||
/// Heightmap of the simulator. Presumed to be an sizeX*sizeY array.
|
||||
/// </param>
|
||||
/// <param name="patchX">
|
||||
/// X offset of the patch to create.
|
||||
/// </param>
|
||||
/// <param name="patchY">
|
||||
/// Y offset of the patch to create.
|
||||
/// </param>
|
||||
/// <param name="pRegionSizeX"></param>
|
||||
/// <param name="pRegionSizeY"></param>
|
||||
public static void CreatePatchFromHeightmap(BitPack output, short[] heightmap, int patchX, int patchY,
|
||||
uint pRegionSizeX, uint pRegionSizeY)
|
||||
{
|
||||
TerrainPatch.Header header = PrescanPatch(heightmap, patchX, patchY, pRegionSizeX, pRegionSizeY);
|
||||
header.QuantWBits = 136;
|
||||
|
||||
// If larger than legacy region size, pack patch X and Y info differently.
|
||||
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
|
||||
{
|
||||
header.PatchIDs = (patchY & 0xFFFF);
|
||||
header.PatchIDs += (patchX << 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
header.PatchIDs = (patchY & 0x1F);
|
||||
header.PatchIDs += (patchX << 5);
|
||||
}
|
||||
|
||||
// NOTE: No idea what prequant and postquant should be or what they do
|
||||
int wbits;
|
||||
int[] patch = CompressPatch(heightmap, patchX, patchY, header, 10, pRegionSizeX, pRegionSizeY, out wbits);
|
||||
wbits = EncodePatchHeader(output, header, patch, pRegionSizeX, pRegionSizeY, wbits);
|
||||
EncodePatch(output, patch, 0, wbits);
|
||||
}
|
||||
|
||||
private static TerrainPatch.Header PrescanPatch(float[] patch)
|
||||
{
|
||||
TerrainPatch.Header header = new TerrainPatch.Header();
|
||||
float zmax = -99999999.0f;
|
||||
float zmin = 99999999.0f;
|
||||
|
||||
for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
|
||||
{
|
||||
float val = patch[i];
|
||||
if (val > zmax) zmax = val;
|
||||
if (val < zmin) zmin = val;
|
||||
}
|
||||
|
||||
header.DCOffset = zmin;
|
||||
header.Range = (int) ((zmax - zmin) + 1.0f);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
// Scan the height info we're returning and return a patch packet header for this patch.
|
||||
// TODO. Why are patches ordered Y,X rather than X,Y?
|
||||
private static TerrainPatch.Header PrescanPatch(short[] heightmap, int patchX, int patchY,
|
||||
uint pRegionSizeX, uint pRegionSizeY)
|
||||
{
|
||||
TerrainPatch.Header header = new TerrainPatch.Header();
|
||||
short zmax = -32767;
|
||||
short zmin = 32767;
|
||||
|
||||
for (int j = patchY*16; j < (patchY + 1)*16; j++)
|
||||
{
|
||||
for (int i = patchX*16; i < (patchX + 1)*16; i++)
|
||||
{
|
||||
short val = heightmap[j*pRegionSizeX + i];
|
||||
if (val > zmax) zmax = val;
|
||||
if (val < zmin) zmin = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Since the the min and max values are the shorts, rescale to be real values.
|
||||
// TODO: all this logic should go into the class wrapping the short values.
|
||||
header.DCOffset = TerrainChannel.FromCompressedHeight(zmin);
|
||||
header.Range = (int)(TerrainChannel.FromCompressedHeight(zmax) - TerrainChannel.FromCompressedHeight(zmin) + 1.0f);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack)
|
||||
{
|
||||
TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)};
|
||||
|
||||
// Quantized word bits
|
||||
if (header.QuantWBits == END_OF_PATCHES)
|
||||
return header;
|
||||
|
||||
// DC offset
|
||||
header.DCOffset = bitpack.UnpackFloat();
|
||||
|
||||
// Range
|
||||
header.Range = bitpack.UnpackBits(16);
|
||||
|
||||
// Patch IDs (10 bits)
|
||||
header.PatchIDs = bitpack.UnpackBits(10);
|
||||
|
||||
// Word bits
|
||||
header.WordBits = (uint) ((header.QuantWBits & 0x0f) + 2);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private static int EncodePatchHeader(BitPack output, TerrainPatch.Header header, int[] patch, uint pRegionSizeX,
|
||||
uint pRegionSizeY, int wbits)
|
||||
{
|
||||
/*
|
||||
int temp;
|
||||
int wbits = (header.QuantWBits & 0x0f) + 2;
|
||||
uint maxWbits = (uint)wbits + 5;
|
||||
uint minWbits = ((uint)wbits >> 1);
|
||||
int wbitsMaxValue;
|
||||
*/
|
||||
// goal is to determ minimum number of bits to use so all data fits
|
||||
/*
|
||||
wbits = (int)minWbits;
|
||||
wbitsMaxValue = (1 << wbits);
|
||||
|
||||
for (int i = 0; i < patch.Length; i++)
|
||||
{
|
||||
temp = patch[i];
|
||||
if (temp != 0)
|
||||
{
|
||||
// Get the absolute value
|
||||
if (temp < 0) temp *= -1;
|
||||
|
||||
no coments..
|
||||
|
||||
for (int j = (int)maxWbits; j > (int)minWbits; j--)
|
||||
{
|
||||
if ((temp & (1 << j)) != 0)
|
||||
{
|
||||
if (j > wbits) wbits = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (temp > wbitsMaxValue)
|
||||
{
|
||||
wbits++;
|
||||
if (wbits == maxWbits)
|
||||
goto Done;
|
||||
wbitsMaxValue = 1 << wbits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Done:
|
||||
|
||||
// wbits += 1;
|
||||
*/
|
||||
// better check
|
||||
if (wbits > 17)
|
||||
wbits = 16;
|
||||
else if (wbits < 3)
|
||||
wbits = 3;
|
||||
|
||||
header.QuantWBits &= 0xf0;
|
||||
|
||||
header.QuantWBits |= (wbits - 2);
|
||||
|
||||
output.PackBits(header.QuantWBits, 8);
|
||||
output.PackFloat(header.DCOffset);
|
||||
output.PackBits(header.Range, 16);
|
||||
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
|
||||
output.PackBits(header.PatchIDs, 32);
|
||||
else
|
||||
output.PackBits(header.PatchIDs, 10);
|
||||
|
||||
return wbits;
|
||||
}
|
||||
|
||||
private static void IDCTColumn16(float[] linein, float[] lineout, int column)
|
||||
{
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
float total = OO_SQRT2*linein[column];
|
||||
|
||||
for (int u = 1; u < Constants.TerrainPatchSize; u++)
|
||||
{
|
||||
int usize = u*Constants.TerrainPatchSize;
|
||||
total += linein[usize + column]*CosineTable16[usize + n];
|
||||
}
|
||||
|
||||
lineout[Constants.TerrainPatchSize*n + column] = total;
|
||||
}
|
||||
}
|
||||
|
||||
private static void IDCTLine16(float[] linein, float[] lineout, int line)
|
||||
{
|
||||
const float oosob = 2.0f/Constants.TerrainPatchSize;
|
||||
int lineSize = line*Constants.TerrainPatchSize;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
float total = OO_SQRT2*linein[lineSize];
|
||||
|
||||
for (int u = 1; u < Constants.TerrainPatchSize; u++)
|
||||
{
|
||||
total += linein[lineSize + u]*CosineTable16[u*Constants.TerrainPatchSize + n];
|
||||
}
|
||||
|
||||
lineout[lineSize + n] = total*oosob;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private static void DCTLine16(float[] linein, float[] lineout, int line)
|
||||
{
|
||||
float total = 0.0f;
|
||||
int lineSize = line * Constants.TerrainPatchSize;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[lineSize + n];
|
||||
}
|
||||
|
||||
lineout[lineSize] = OO_SQRT2 * total;
|
||||
|
||||
int uptr = 0;
|
||||
for (int u = 1; u < Constants.TerrainPatchSize; u++)
|
||||
{
|
||||
total = 0.0f;
|
||||
uptr += Constants.TerrainPatchSize;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[lineSize + n] * CosineTable16[uptr + n];
|
||||
}
|
||||
|
||||
lineout[lineSize + u] = total;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private static void DCTLine16(float[] linein, float[] lineout, int line)
|
||||
{
|
||||
// outputs transpose data (lines exchanged with coluns )
|
||||
// so to save a bit of cpu when doing coluns
|
||||
float total = 0.0f;
|
||||
int lineSize = line*Constants.TerrainPatchSize;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[lineSize + n];
|
||||
}
|
||||
|
||||
lineout[line] = OO_SQRT2*total;
|
||||
|
||||
for (int u = Constants.TerrainPatchSize;
|
||||
u < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
|
||||
u += Constants.TerrainPatchSize)
|
||||
{
|
||||
total = 0.0f;
|
||||
for (int ptrn = lineSize, ptru = u; ptrn < lineSize + Constants.TerrainPatchSize; ptrn++,ptru++)
|
||||
{
|
||||
total += linein[ptrn]*CosineTable16[ptru];
|
||||
}
|
||||
|
||||
lineout[line + u] = total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
private static void DCTColumn16(float[] linein, int[] lineout, int column)
|
||||
{
|
||||
float total = 0.0f;
|
||||
// const float oosob = 2.0f / Constants.TerrainPatchSize;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[Constants.TerrainPatchSize * n + column];
|
||||
}
|
||||
|
||||
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
|
||||
lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * QuantizeTable16[column]);
|
||||
|
||||
for (int uptr = Constants.TerrainPatchSize; uptr < Constants.TerrainPatchSize * Constants.TerrainPatchSize; uptr += Constants.TerrainPatchSize)
|
||||
{
|
||||
total = 0.0f;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[Constants.TerrainPatchSize * n + column] * CosineTable16[uptr + n];
|
||||
}
|
||||
|
||||
// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
|
||||
lineout[CopyMatrix16[uptr + column]] = (int)(total * QuantizeTable16[uptr + column]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private static void DCTColumn16(float[] linein, int[] lineout, int column)
|
||||
{
|
||||
// input columns are in fact stored in lines now
|
||||
|
||||
float total = 0.0f;
|
||||
// const float oosob = 2.0f / Constants.TerrainPatchSize;
|
||||
int inlinesptr = Constants.TerrainPatchSize*column;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[inlinesptr + n];
|
||||
}
|
||||
|
||||
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
|
||||
lineout[CopyMatrix16[column]] = (int) (OO_SQRT2*total*QuantizeTable16[column]);
|
||||
|
||||
for (int uptr = Constants.TerrainPatchSize;
|
||||
uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
|
||||
uptr += Constants.TerrainPatchSize)
|
||||
{
|
||||
total = 0.0f;
|
||||
|
||||
for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
|
||||
{
|
||||
total += linein[n]*CosineTable16[ptru];
|
||||
}
|
||||
|
||||
// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
|
||||
lineout[CopyMatrix16[uptr + column]] = (int) (total*QuantizeTable16[uptr + column]);
|
||||
}
|
||||
}
|
||||
|
||||
private static int DCTColumn16Wbits(float[] linein, int[] lineout, int column, int wbits, int maxwbits)
|
||||
{
|
||||
// input columns are in fact stored in lines now
|
||||
|
||||
bool dowbits = wbits != maxwbits;
|
||||
int wbitsMaxValue = 1 << wbits;
|
||||
|
||||
float total = 0.0f;
|
||||
// const float oosob = 2.0f / Constants.TerrainPatchSize;
|
||||
int inlinesptr = Constants.TerrainPatchSize*column;
|
||||
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
total += linein[inlinesptr + n];
|
||||
}
|
||||
|
||||
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
|
||||
int tmp = (int) (OO_SQRT2*total*QuantizeTable16[column]);
|
||||
lineout[CopyMatrix16[column]] = tmp;
|
||||
|
||||
if (dowbits)
|
||||
{
|
||||
if (tmp < 0) tmp *= -1;
|
||||
while (tmp > wbitsMaxValue)
|
||||
{
|
||||
wbits++;
|
||||
wbitsMaxValue = 1 << wbits;
|
||||
if (wbits == maxwbits)
|
||||
{
|
||||
dowbits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int uptr = Constants.TerrainPatchSize;
|
||||
uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
|
||||
uptr += Constants.TerrainPatchSize)
|
||||
{
|
||||
total = 0.0f;
|
||||
|
||||
for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
|
||||
{
|
||||
total += linein[n]*CosineTable16[ptru];
|
||||
}
|
||||
|
||||
tmp = (int) (total*QuantizeTable16[uptr + column]);
|
||||
lineout[CopyMatrix16[uptr + column]] = tmp;
|
||||
|
||||
if (dowbits)
|
||||
{
|
||||
if (tmp < 0) tmp *= -1;
|
||||
while (tmp > wbitsMaxValue)
|
||||
{
|
||||
wbits++;
|
||||
wbitsMaxValue = 1 << wbits;
|
||||
if (wbits == maxwbits)
|
||||
{
|
||||
dowbits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wbits;
|
||||
}
|
||||
|
||||
public static void DecodePatch(int[] patches, BitPack bitpack, TerrainPatch.Header header, int size)
|
||||
{
|
||||
for (int n = 0; n < size*size; n++)
|
||||
{
|
||||
// ?
|
||||
int temp = bitpack.UnpackBits(1);
|
||||
if (temp != 0)
|
||||
{
|
||||
// Value or EOB
|
||||
temp = bitpack.UnpackBits(1);
|
||||
if (temp != 0)
|
||||
{
|
||||
// Value
|
||||
temp = bitpack.UnpackBits(1);
|
||||
if (temp != 0)
|
||||
{
|
||||
// Negative
|
||||
temp = bitpack.UnpackBits((int) header.WordBits);
|
||||
patches[n] = temp*-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Positive
|
||||
temp = bitpack.UnpackBits((int) header.WordBits);
|
||||
patches[n] = temp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the rest to zero
|
||||
// TODO: This might not be necessary
|
||||
for (int o = n; o < size*size; o++)
|
||||
{
|
||||
patches[o] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
patches[n] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EncodePatch(BitPack output, int[] patch, int postquant, int wbits)
|
||||
{
|
||||
int maxwbitssize = (1 << wbits) - 1;
|
||||
|
||||
if (postquant > Constants.TerrainPatchSize*Constants.TerrainPatchSize || postquant < 0)
|
||||
{
|
||||
Logger.Log("Postquant is outside the range of allowed values in EncodePatch()", Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (postquant != 0) patch[Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant] = 0;
|
||||
|
||||
for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
|
||||
{
|
||||
int temp = patch[i];
|
||||
|
||||
if (temp == 0)
|
||||
{
|
||||
bool eob = true;
|
||||
|
||||
for (int j = i; j < Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant; j++)
|
||||
{
|
||||
if (patch[j] != 0)
|
||||
{
|
||||
eob = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eob)
|
||||
{
|
||||
output.PackBits(ZERO_EOB, 2);
|
||||
return;
|
||||
}
|
||||
output.PackBits(ZERO_CODE, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temp < 0)
|
||||
{
|
||||
temp *= -1;
|
||||
|
||||
if (temp > maxwbitssize) temp = maxwbitssize;
|
||||
|
||||
output.PackBits(NEGATIVE_VALUE, 3);
|
||||
output.PackBits(temp, wbits);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temp > maxwbitssize) temp = maxwbitssize;
|
||||
|
||||
output.PackBits(POSITIVE_VALUE, 3);
|
||||
output.PackBits(temp, wbits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] DecompressPatch(int[] patches, TerrainPatch.Header header, TerrainPatch.GroupHeader group)
|
||||
{
|
||||
float[] block = new float[group.PatchSize*group.PatchSize];
|
||||
float[] output = new float[group.PatchSize*group.PatchSize];
|
||||
int prequant = (header.QuantWBits >> 4) + 2;
|
||||
int quantize = 1 << prequant;
|
||||
float ooq = 1.0f/quantize;
|
||||
float mult = ooq*header.Range;
|
||||
float addval = mult*(1 << (prequant - 1)) + header.DCOffset;
|
||||
|
||||
if (group.PatchSize == Constants.TerrainPatchSize)
|
||||
{
|
||||
for (int n = 0; n < Constants.TerrainPatchSize*Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
block[n] = patches[CopyMatrix16[n]]*DequantizeTable16[n];
|
||||
}
|
||||
|
||||
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
IDCTColumn16(block, ftemp, o);
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
IDCTLine16(ftemp, block, o);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int n = 0; n < Constants.TerrainPatchSize*2*Constants.TerrainPatchSize*2; n++)
|
||||
{
|
||||
block[n] = patches[CopyMatrix32[n]]*DequantizeTable32[n];
|
||||
}
|
||||
|
||||
Logger.Log("Implement IDCTPatchLarge", Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
for (int j = 0; j < block.Length; j++)
|
||||
{
|
||||
output[j] = block[j]*mult + addval;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static int[] CompressPatch(float[] patchData, TerrainPatch.Header header, int prequant, out int wbits)
|
||||
{
|
||||
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
int wordsize = (prequant - 2) & 0x0f;
|
||||
float oozrange = 1.0f/header.Range;
|
||||
float range = (1 << prequant);
|
||||
float premult = oozrange*range;
|
||||
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
|
||||
|
||||
header.QuantWBits = wordsize;
|
||||
header.QuantWBits |= wordsize << 4;
|
||||
|
||||
int k = 0;
|
||||
for (int j = 0; j < Constants.TerrainPatchSize; j++)
|
||||
{
|
||||
for (int i = 0; i < Constants.TerrainPatchSize; i++)
|
||||
block[k++] = patchData[j*Constants.TerrainPatchSize + i]*premult - sub;
|
||||
}
|
||||
|
||||
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
|
||||
int maxWbits = prequant + 5;
|
||||
wbits = (prequant >> 1);
|
||||
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
DCTLine16(block, ftemp, o);
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
|
||||
|
||||
return itemp;
|
||||
}
|
||||
|
||||
private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant, out int wbits)
|
||||
{
|
||||
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
float oozrange = 1.0f/header.Range;
|
||||
float range = (1 << prequant);
|
||||
float premult = oozrange*range;
|
||||
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
|
||||
int wordsize = (prequant - 2) & 0x0f;
|
||||
|
||||
header.QuantWBits = wordsize;
|
||||
header.QuantWBits |= wordsize << 4;
|
||||
|
||||
int k = 0;
|
||||
for (int j = 0; j < Constants.TerrainPatchSize; j++)
|
||||
{
|
||||
for (int i = 0; i < Constants.TerrainPatchSize; i++)
|
||||
block[k++] = patchData[j, i]*premult - sub;
|
||||
}
|
||||
|
||||
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
int maxWbits = prequant + 5;
|
||||
wbits = (prequant >> 1);
|
||||
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
DCTLine16(block, ftemp, o);
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
|
||||
|
||||
return itemp;
|
||||
}
|
||||
|
||||
private static int[] CompressPatch(short[] heightmap, int patchX, int patchY, TerrainPatch.Header header,
|
||||
int prequant, uint pRegionSizeX, uint pRegionSizeY, out int wbits)
|
||||
{
|
||||
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
int wordsize = prequant;
|
||||
float oozrange = 1.0f/header.Range;
|
||||
float range = (1 << prequant);
|
||||
float premult = oozrange*range;
|
||||
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
|
||||
|
||||
header.QuantWBits = wordsize - 2;
|
||||
header.QuantWBits |= (prequant - 2) << 4;
|
||||
|
||||
int k = 0;
|
||||
|
||||
premult /= Constants.TerrainCompression; // put here short to float factor
|
||||
|
||||
int jPatchLimit = patchY;
|
||||
if (patchY >= (pRegionSizeY / Constants.TerrainPatchSize))
|
||||
{
|
||||
jPatchLimit = (int)(pRegionSizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
|
||||
}
|
||||
jPatchLimit = (jPatchLimit + 1) * Constants.TerrainPatchSize;
|
||||
|
||||
int iPatchLimit = patchX;
|
||||
if (patchX >= (pRegionSizeX / Constants.TerrainPatchSize))
|
||||
{
|
||||
iPatchLimit = (int)(pRegionSizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
|
||||
}
|
||||
iPatchLimit = (iPatchLimit + 1) * Constants.TerrainPatchSize;
|
||||
|
||||
for (int j = patchY * Constants.TerrainPatchSize; j < jPatchLimit; j++)
|
||||
{
|
||||
for (int i = patchX * Constants.TerrainPatchSize; i < iPatchLimit; i++)
|
||||
{
|
||||
block[k++] = (heightmap[j*pRegionSizeX + i])*premult - sub;
|
||||
}
|
||||
}
|
||||
|
||||
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
|
||||
|
||||
int maxWbits = prequant + 5;
|
||||
wbits = (prequant >> 1);
|
||||
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
DCTLine16(block, ftemp, o);
|
||||
for (int o = 0; o < Constants.TerrainPatchSize; o++)
|
||||
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
|
||||
|
||||
return itemp;
|
||||
}
|
||||
|
||||
#region Initialization
|
||||
|
||||
private static void BuildDequantizeTable16()
|
||||
{
|
||||
for (int j = 0; j < Constants.TerrainPatchSize; j++)
|
||||
{
|
||||
for (int i = 0; i < Constants.TerrainPatchSize; i++)
|
||||
{
|
||||
DequantizeTable16[j*Constants.TerrainPatchSize + i] = 1.0f + 2.0f*(i + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildQuantizeTable16()
|
||||
{
|
||||
const float oosob = 2.0f/Constants.TerrainPatchSize;
|
||||
for (int j = 0; j < Constants.TerrainPatchSize; j++)
|
||||
{
|
||||
for (int i = 0; i < Constants.TerrainPatchSize; i++)
|
||||
{
|
||||
// QuantizeTable16[j * Constants.TerrainPatchSize + i] = 1.0f / (1.0f + 2.0f * ((float)i + (float)j));
|
||||
QuantizeTable16[j*Constants.TerrainPatchSize + i] = oosob/(1.0f + 2.0f*(i + (float) j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupCosines16()
|
||||
{
|
||||
const float hposz = (float) Math.PI*0.5f/Constants.TerrainPatchSize;
|
||||
|
||||
for (int u = 0; u < Constants.TerrainPatchSize; u++)
|
||||
{
|
||||
for (int n = 0; n < Constants.TerrainPatchSize; n++)
|
||||
{
|
||||
CosineTable16[u*Constants.TerrainPatchSize + n] = (float) Math.Cos((2.0f*n + 1.0f)*u*hposz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildCopyMatrix16()
|
||||
{
|
||||
bool diag = false;
|
||||
bool right = true;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int count = 0;
|
||||
|
||||
while (i < Constants.TerrainPatchSize && j < Constants.TerrainPatchSize)
|
||||
{
|
||||
CopyMatrix16[j*Constants.TerrainPatchSize + i] = count++;
|
||||
|
||||
if (!diag)
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
if (i < Constants.TerrainPatchSize - 1) i++;
|
||||
else j++;
|
||||
|
||||
right = false;
|
||||
diag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j < Constants.TerrainPatchSize - 1) j++;
|
||||
else i++;
|
||||
|
||||
right = true;
|
||||
diag = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
i++;
|
||||
j--;
|
||||
if (i == Constants.TerrainPatchSize - 1 || j == 0) diag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
j++;
|
||||
if (j == Constants.TerrainPatchSize - 1 || i == 0) diag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Initialization
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue