aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/TerrainData.cs152
-rw-r--r--OpenSim/Region/Framework/Scenes/TerrainCompressor.cs958
2 files changed, 1110 insertions, 0 deletions
diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs
new file mode 100644
index 0000000..8cb1aef
--- /dev/null
+++ b/OpenSim/Framework/TerrainData.cs
@@ -0,0 +1,152 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31
32using OpenMetaverse;
33
34namespace OpenSim.Framework
35{
36 public abstract class TerrainData
37 {
38 // Terrain always is a square
39 public int SizeX { get; protected set; }
40 public int SizeY { get; protected set; }
41 public int SizeZ { get; protected set; }
42
43 public abstract float this[int x, int y] { get; set; }
44 // Someday terrain will have caves
45 public abstract float this[int x, int y, int z] { get; set; }
46
47 // Return a representation of this terrain for storing as a blob in the database.
48 // Returns 'true' to say blob was stored in the 'out' locations.
49 public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob);
50 }
51
52 // The terrain is stored as a blob in the database with a 'revision' field.
53 // Some implementations of terrain storage would fill the revision field with
54 // the time the terrain was stored. When real revisions were added and this
55 // feature removed, that left some old entries with the time in the revision
56 // field.
57 // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
58 // left over and it is presumed to be 'Legacy256'.
59 // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
60 // If a revision does not match any of these, it is assumed to be Legacy256.
61 public enum DBTerrainRevision
62 {
63 // Terrain is 'double[256,256]'
64 Legacy256 = 11,
65 // Terrain is 'int32, int32, float[,]' where the shorts are X and Y dimensions
66 // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
67 Variable2D = 22,
68 // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
69 RevisionHigh = 1234
70 }
71
72 // Version of terrain that is a heightmap.
73 // This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge
74 // of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer.
75 public class HeightmapTerrainData : TerrainData
76 {
77 // TerrainData.this[x, y]
78 public override float this[int x, int y]
79 {
80 get { return m_heightmap[x * SizeX + y]; }
81 set { m_heightmap[x * SizeX + y] = value; }
82 }
83
84 // TerrainData.this[x, y, z]
85 public override float this[int x, int y, int z]
86 {
87 get { return this[x, y]; }
88 set { this[x, y] = value; }
89 }
90
91 // TerrainData.GetDatabaseBlob
92 // The user wants something to store in the database.
93 public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
94 {
95 DBRevisionCode = (int)DBTerrainRevision.Legacy256;
96 blob = LegacyTerrainSerialization();
97 return false;
98 }
99 private float[] m_heightmap;
100
101 // To keep with the legacy theme, this can be created with the way terrain
102 // used to passed around as.
103 public HeightmapTerrainData(double[,] pTerrain)
104 {
105 SizeX = pTerrain.GetLength(0);
106 SizeY = pTerrain.GetLength(1);
107 SizeZ = (int)Constants.RegionHeight;
108
109 int idx = 0;
110 m_heightmap = new float[SizeX * SizeY];
111 for (int ii = 0; ii < SizeX; ii++)
112 {
113 for (int jj = 0; jj < SizeY; jj++)
114 {
115 m_heightmap[idx++] = (float)pTerrain[ii, jj];
116
117 }
118 }
119 }
120
121 public HeightmapTerrainData(float[] pHeightmap, int pX, int pY, int pZ)
122 {
123 m_heightmap = pHeightmap;
124 SizeX = pX;
125 SizeY = pY;
126 SizeZ = pZ;
127 }
128
129 // Just create an array of doubles. Presumes the caller implicitly knows the size.
130 public Array LegacyTerrainSerialization()
131 {
132 Array ret = null;
133 using (MemoryStream str = new MemoryStream(SizeX * SizeY * sizeof(double)))
134 {
135 using (BinaryWriter bw = new BinaryWriter(str))
136 {
137 // TODO: COMPATIBILITY - Add byte-order conversions
138 for (int ii = 0; ii < m_heightmap.Length; ii++)
139 {
140 double height = (double)m_heightmap[ii];
141 if (height == 0.0)
142 height = double.Epsilon;
143
144 bw.Write(height);
145 }
146 }
147 ret = str.ToArray();
148 }
149 return ret;
150 }
151 }
152}
diff --git a/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs
new file mode 100644
index 0000000..2e856bc
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs
@@ -0,0 +1,958 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/* Freely adapted from the Aurora version of the terrain compressor.
29 * Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/
30 */
31
32using System;
33using System.Reflection;
34
35using log4net;
36
37using OpenSim.Framework;
38using OpenSim.Region.Framework;
39using OpenSim.Region.Framework.Scenes;
40
41using OpenMetaverse;
42using OpenMetaverse.Packets;
43
44namespace OpenSim.Region.ClientStack.LindenUDP
45{
46 public static class OpenSimTerrainCompressor
47 {
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49 private static string LogHeader = "[TERRAIN COMPRESSOR]";
50
51 public const int END_OF_PATCHES = 97;
52
53 private const float OO_SQRT2 = 0.7071067811865475244008443621049f;
54 private const int STRIDE = 264;
55
56 private const int ZERO_CODE = 0x0;
57 private const int ZERO_EOB = 0x2;
58 private const int POSITIVE_VALUE = 0x6;
59 private const int NEGATIVE_VALUE = 0x7;
60
61 private static readonly float[] DequantizeTable16 =
62 new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
63
64 private static readonly float[] DequantizeTable32 =
65 new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
66
67 private static readonly float[] CosineTable16 = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
68 //private static readonly float[] CosineTable32 = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize];
69 private static readonly int[] CopyMatrix16 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
70 private static readonly int[] CopyMatrix32 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
71
72 private static readonly float[] QuantizeTable16 =
73 new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
74
75 static OpenSimTerrainCompressor()
76 {
77 // Initialize the decompression tables
78 BuildDequantizeTable16();
79 SetupCosines16();
80 BuildCopyMatrix16();
81 BuildQuantizeTable16();
82 }
83
84 // Unused: left for historical reference.
85 public static LayerDataPacket CreateLayerDataPacket(TerrainPatch[] patches, byte type, int pRegionSizeX,
86 int pRegionSizeY)
87 {
88 LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
89
90 TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
91 {Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
92
93 // Should be enough to fit even the most poorly packed data
94 byte[] data = new byte[patches.Length*Constants.TerrainPatchSize*Constants.TerrainPatchSize*2];
95 BitPack bitpack = new BitPack(data, 0);
96 bitpack.PackBits(header.Stride, 16);
97 bitpack.PackBits(header.PatchSize, 8);
98 bitpack.PackBits(type, 8);
99
100 foreach (TerrainPatch t in patches)
101 CreatePatch(bitpack, t.Data, t.X, t.Y, pRegionSizeX, pRegionSizeY);
102
103 bitpack.PackBits(END_OF_PATCHES, 8);
104
105 layer.LayerData.Data = new byte[bitpack.BytePos + 1];
106 Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
107
108 return layer;
109 }
110
111 // Legacy land packet generation gluing old land representation (heights) to compressed representation.
112 // This is an intermediate step in converting terrain into a variable sized heightmap. Some of the
113 // routines (like IClientAPI) only pass the float array of heights around. This entry
114 // converts that legacy representation into the more compact represenation used in
115 // TerrainChannel. Someday fix the plumbing between here and the scene.
116 public static LayerDataPacket CreateLandPacket(float[] heightmap, int patchX, int patchY, uint sizeX, uint sizeY)
117 {
118 int[] xPieces = new int[1];
119 int[] yPieces = new int[1];
120
121 short[] newmap = new short[heightmap.Length];
122 for (int ii = 0; ii < heightmap.Length; ii++)
123 newmap[ii] = TerrainChannel.ToCompressedHeight(heightmap[ii]);
124
125 xPieces[0] = patchX; // patch X dimension
126 yPieces[0] = patchY;
127
128 m_log.DebugFormat("{0} CreateLandPacket. patchX={1}, patchY={2}, sizeX={3}, sizeY={4}",
129 LogHeader, patchX, patchY, sizeX, sizeY);
130
131 return CreateLandPacket(newmap, xPieces, yPieces, (int)TerrainPatch.LayerType.Land, sizeX, sizeY);
132 }
133
134 /// <summary>
135 /// Creates a LayerData packet for compressed land data given a full
136 /// simulator heightmap and an array of indices of patches to compress
137 /// </summary>
138 /// <param name="heightmap">
139 /// A 256 * 256 array of floating point values
140 /// specifying the height at each meter in the simulator
141 /// </param>
142 /// <param name="x">
143 /// Array of indexes in the 16x16 grid of patches
144 /// for this simulator. For example if 1 and 17 are specified, patches
145 /// x=1,y=0 and x=1,y=1 are sent
146 /// </param>
147 /// <param name="y">
148 /// Array of indexes in the 16x16 grid of patches
149 /// for this simulator. For example if 1 and 17 are specified, patches
150 /// x=1,y=0 and x=1,y=1 are sent
151 /// </param>
152 /// <param name="type"></param>
153 /// <param name="pRegionSizeX"></param>
154 /// <param name="pRegionSizeY"></param>
155 /// <returns></returns>
156 public static LayerDataPacket CreateLandPacket(short[] heightmap, int[] x, int[] y, byte type,
157 uint pRegionSizeX, uint pRegionSizeY)
158 {
159 LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
160
161 TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
162 {Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
163
164 byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2];
165 BitPack bitpack = new BitPack(data, 0);
166 bitpack.PackBits(header.Stride, 16);
167 bitpack.PackBits(header.PatchSize, 8);
168 bitpack.PackBits(type, 8);
169
170 for (int i = 0; i < x.Length; i++)
171 CreatePatchFromHeightmap(bitpack, heightmap, x[i], y[i], pRegionSizeX, pRegionSizeY);
172
173 bitpack.PackBits(END_OF_PATCHES, 8);
174
175 layer.LayerData.Data = new byte[bitpack.BytePos + 1];
176 Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
177
178 return layer;
179 }
180
181 // Unused: left for historical reference.
182 public static void CreatePatch(BitPack output, float[] patchData, int x, int y, int pRegionSizeX, int pRegionSizeY)
183 {
184 TerrainPatch.Header header = PrescanPatch(patchData);
185 header.QuantWBits = 136;
186 if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
187 {
188 header.PatchIDs = (y & 0xFFFF);
189 header.PatchIDs += (x << 16);
190 }
191 else
192 {
193 header.PatchIDs = (y & 0x1F);
194 header.PatchIDs += (x << 5);
195 }
196
197 // NOTE: No idea what prequant and postquant should be or what they do
198
199 int wbits;
200 int[] patch = CompressPatch(patchData, header, 10, out wbits);
201 wbits = EncodePatchHeader(output, header, patch, Constants.RegionSize, Constants.RegionSize, wbits);
202 EncodePatch(output, patch, 0, wbits);
203 }
204
205 /// <summary>
206 /// Add a patch of terrain to a BitPacker
207 /// </summary>
208 /// <param name="output">BitPacker to write the patch to</param>
209 /// <param name="heightmap">
210 /// Heightmap of the simulator. Presumed to be an sizeX*sizeY array.
211 /// </param>
212 /// <param name="patchX">
213 /// X offset of the patch to create.
214 /// </param>
215 /// <param name="patchY">
216 /// Y offset of the patch to create.
217 /// </param>
218 /// <param name="pRegionSizeX"></param>
219 /// <param name="pRegionSizeY"></param>
220 public static void CreatePatchFromHeightmap(BitPack output, short[] heightmap, int patchX, int patchY,
221 uint pRegionSizeX, uint pRegionSizeY)
222 {
223 TerrainPatch.Header header = PrescanPatch(heightmap, patchX, patchY, pRegionSizeX, pRegionSizeY);
224 header.QuantWBits = 136;
225
226 // If larger than legacy region size, pack patch X and Y info differently.
227 if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
228 {
229 header.PatchIDs = (patchY & 0xFFFF);
230 header.PatchIDs += (patchX << 16);
231 }
232 else
233 {
234 header.PatchIDs = (patchY & 0x1F);
235 header.PatchIDs += (patchX << 5);
236 }
237
238 // NOTE: No idea what prequant and postquant should be or what they do
239 int wbits;
240 int[] patch = CompressPatch(heightmap, patchX, patchY, header, 10, pRegionSizeX, pRegionSizeY, out wbits);
241 wbits = EncodePatchHeader(output, header, patch, pRegionSizeX, pRegionSizeY, wbits);
242 EncodePatch(output, patch, 0, wbits);
243 }
244
245 private static TerrainPatch.Header PrescanPatch(float[] patch)
246 {
247 TerrainPatch.Header header = new TerrainPatch.Header();
248 float zmax = -99999999.0f;
249 float zmin = 99999999.0f;
250
251 for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
252 {
253 float val = patch[i];
254 if (val > zmax) zmax = val;
255 if (val < zmin) zmin = val;
256 }
257
258 header.DCOffset = zmin;
259 header.Range = (int) ((zmax - zmin) + 1.0f);
260
261 return header;
262 }
263
264 // Scan the height info we're returning and return a patch packet header for this patch.
265 // TODO. Why are patches ordered Y,X rather than X,Y?
266 private static TerrainPatch.Header PrescanPatch(short[] heightmap, int patchX, int patchY,
267 uint pRegionSizeX, uint pRegionSizeY)
268 {
269 TerrainPatch.Header header = new TerrainPatch.Header();
270 short zmax = -32767;
271 short zmin = 32767;
272
273 for (int j = patchY*16; j < (patchY + 1)*16; j++)
274 {
275 for (int i = patchX*16; i < (patchX + 1)*16; i++)
276 {
277 short val = heightmap[j*pRegionSizeX + i];
278 if (val > zmax) zmax = val;
279 if (val < zmin) zmin = val;
280 }
281 }
282
283 // Since the the min and max values are the shorts, rescale to be real values.
284 // TODO: all this logic should go into the class wrapping the short values.
285 header.DCOffset = TerrainChannel.FromCompressedHeight(zmin);
286 header.Range = (int)(TerrainChannel.FromCompressedHeight(zmax) - TerrainChannel.FromCompressedHeight(zmin) + 1.0f);
287
288 return header;
289 }
290
291 public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack)
292 {
293 TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)};
294
295 // Quantized word bits
296 if (header.QuantWBits == END_OF_PATCHES)
297 return header;
298
299 // DC offset
300 header.DCOffset = bitpack.UnpackFloat();
301
302 // Range
303 header.Range = bitpack.UnpackBits(16);
304
305 // Patch IDs (10 bits)
306 header.PatchIDs = bitpack.UnpackBits(10);
307
308 // Word bits
309 header.WordBits = (uint) ((header.QuantWBits & 0x0f) + 2);
310
311 return header;
312 }
313
314 private static int EncodePatchHeader(BitPack output, TerrainPatch.Header header, int[] patch, uint pRegionSizeX,
315 uint pRegionSizeY, int wbits)
316 {
317 /*
318 int temp;
319 int wbits = (header.QuantWBits & 0x0f) + 2;
320 uint maxWbits = (uint)wbits + 5;
321 uint minWbits = ((uint)wbits >> 1);
322 int wbitsMaxValue;
323 */
324 // goal is to determ minimum number of bits to use so all data fits
325 /*
326 wbits = (int)minWbits;
327 wbitsMaxValue = (1 << wbits);
328
329 for (int i = 0; i < patch.Length; i++)
330 {
331 temp = patch[i];
332 if (temp != 0)
333 {
334 // Get the absolute value
335 if (temp < 0) temp *= -1;
336
337 no coments..
338
339 for (int j = (int)maxWbits; j > (int)minWbits; j--)
340 {
341 if ((temp & (1 << j)) != 0)
342 {
343 if (j > wbits) wbits = j;
344 break;
345 }
346 }
347
348 while (temp > wbitsMaxValue)
349 {
350 wbits++;
351 if (wbits == maxWbits)
352 goto Done;
353 wbitsMaxValue = 1 << wbits;
354 }
355 }
356 }
357
358 Done:
359
360 // wbits += 1;
361 */
362 // better check
363 if (wbits > 17)
364 wbits = 16;
365 else if (wbits < 3)
366 wbits = 3;
367
368 header.QuantWBits &= 0xf0;
369
370 header.QuantWBits |= (wbits - 2);
371
372 output.PackBits(header.QuantWBits, 8);
373 output.PackFloat(header.DCOffset);
374 output.PackBits(header.Range, 16);
375 if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
376 output.PackBits(header.PatchIDs, 32);
377 else
378 output.PackBits(header.PatchIDs, 10);
379
380 return wbits;
381 }
382
383 private static void IDCTColumn16(float[] linein, float[] lineout, int column)
384 {
385 for (int n = 0; n < Constants.TerrainPatchSize; n++)
386 {
387 float total = OO_SQRT2*linein[column];
388
389 for (int u = 1; u < Constants.TerrainPatchSize; u++)
390 {
391 int usize = u*Constants.TerrainPatchSize;
392 total += linein[usize + column]*CosineTable16[usize + n];
393 }
394
395 lineout[Constants.TerrainPatchSize*n + column] = total;
396 }
397 }
398
399 private static void IDCTLine16(float[] linein, float[] lineout, int line)
400 {
401 const float oosob = 2.0f/Constants.TerrainPatchSize;
402 int lineSize = line*Constants.TerrainPatchSize;
403
404 for (int n = 0; n < Constants.TerrainPatchSize; n++)
405 {
406 float total = OO_SQRT2*linein[lineSize];
407
408 for (int u = 1; u < Constants.TerrainPatchSize; u++)
409 {
410 total += linein[lineSize + u]*CosineTable16[u*Constants.TerrainPatchSize + n];
411 }
412
413 lineout[lineSize + n] = total*oosob;
414 }
415 }
416
417/*
418 private static void DCTLine16(float[] linein, float[] lineout, int line)
419 {
420 float total = 0.0f;
421 int lineSize = line * Constants.TerrainPatchSize;
422
423 for (int n = 0; n < Constants.TerrainPatchSize; n++)
424 {
425 total += linein[lineSize + n];
426 }
427
428 lineout[lineSize] = OO_SQRT2 * total;
429
430 int uptr = 0;
431 for (int u = 1; u < Constants.TerrainPatchSize; u++)
432 {
433 total = 0.0f;
434 uptr += Constants.TerrainPatchSize;
435
436 for (int n = 0; n < Constants.TerrainPatchSize; n++)
437 {
438 total += linein[lineSize + n] * CosineTable16[uptr + n];
439 }
440
441 lineout[lineSize + u] = total;
442 }
443 }
444*/
445
446 private static void DCTLine16(float[] linein, float[] lineout, int line)
447 {
448 // outputs transpose data (lines exchanged with coluns )
449 // so to save a bit of cpu when doing coluns
450 float total = 0.0f;
451 int lineSize = line*Constants.TerrainPatchSize;
452
453 for (int n = 0; n < Constants.TerrainPatchSize; n++)
454 {
455 total += linein[lineSize + n];
456 }
457
458 lineout[line] = OO_SQRT2*total;
459
460 for (int u = Constants.TerrainPatchSize;
461 u < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
462 u += Constants.TerrainPatchSize)
463 {
464 total = 0.0f;
465 for (int ptrn = lineSize, ptru = u; ptrn < lineSize + Constants.TerrainPatchSize; ptrn++,ptru++)
466 {
467 total += linein[ptrn]*CosineTable16[ptru];
468 }
469
470 lineout[line + u] = total;
471 }
472 }
473
474
475 /*
476 private static void DCTColumn16(float[] linein, int[] lineout, int column)
477 {
478 float total = 0.0f;
479 // const float oosob = 2.0f / Constants.TerrainPatchSize;
480
481 for (int n = 0; n < Constants.TerrainPatchSize; n++)
482 {
483 total += linein[Constants.TerrainPatchSize * n + column];
484 }
485
486 // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
487 lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * QuantizeTable16[column]);
488
489 for (int uptr = Constants.TerrainPatchSize; uptr < Constants.TerrainPatchSize * Constants.TerrainPatchSize; uptr += Constants.TerrainPatchSize)
490 {
491 total = 0.0f;
492
493 for (int n = 0; n < Constants.TerrainPatchSize; n++)
494 {
495 total += linein[Constants.TerrainPatchSize * n + column] * CosineTable16[uptr + n];
496 }
497
498 // lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
499 lineout[CopyMatrix16[uptr + column]] = (int)(total * QuantizeTable16[uptr + column]);
500 }
501 }
502 */
503
504 private static void DCTColumn16(float[] linein, int[] lineout, int column)
505 {
506 // input columns are in fact stored in lines now
507
508 float total = 0.0f;
509// const float oosob = 2.0f / Constants.TerrainPatchSize;
510 int inlinesptr = Constants.TerrainPatchSize*column;
511
512 for (int n = 0; n < Constants.TerrainPatchSize; n++)
513 {
514 total += linein[inlinesptr + n];
515 }
516
517 // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
518 lineout[CopyMatrix16[column]] = (int) (OO_SQRT2*total*QuantizeTable16[column]);
519
520 for (int uptr = Constants.TerrainPatchSize;
521 uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
522 uptr += Constants.TerrainPatchSize)
523 {
524 total = 0.0f;
525
526 for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
527 {
528 total += linein[n]*CosineTable16[ptru];
529 }
530
531// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
532 lineout[CopyMatrix16[uptr + column]] = (int) (total*QuantizeTable16[uptr + column]);
533 }
534 }
535
536 private static int DCTColumn16Wbits(float[] linein, int[] lineout, int column, int wbits, int maxwbits)
537 {
538 // input columns are in fact stored in lines now
539
540 bool dowbits = wbits != maxwbits;
541 int wbitsMaxValue = 1 << wbits;
542
543 float total = 0.0f;
544 // const float oosob = 2.0f / Constants.TerrainPatchSize;
545 int inlinesptr = Constants.TerrainPatchSize*column;
546
547 for (int n = 0; n < Constants.TerrainPatchSize; n++)
548 {
549 total += linein[inlinesptr + n];
550 }
551
552 // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
553 int tmp = (int) (OO_SQRT2*total*QuantizeTable16[column]);
554 lineout[CopyMatrix16[column]] = tmp;
555
556 if (dowbits)
557 {
558 if (tmp < 0) tmp *= -1;
559 while (tmp > wbitsMaxValue)
560 {
561 wbits++;
562 wbitsMaxValue = 1 << wbits;
563 if (wbits == maxwbits)
564 {
565 dowbits = false;
566 break;
567 }
568 }
569 }
570
571 for (int uptr = Constants.TerrainPatchSize;
572 uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
573 uptr += Constants.TerrainPatchSize)
574 {
575 total = 0.0f;
576
577 for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
578 {
579 total += linein[n]*CosineTable16[ptru];
580 }
581
582 tmp = (int) (total*QuantizeTable16[uptr + column]);
583 lineout[CopyMatrix16[uptr + column]] = tmp;
584
585 if (dowbits)
586 {
587 if (tmp < 0) tmp *= -1;
588 while (tmp > wbitsMaxValue)
589 {
590 wbits++;
591 wbitsMaxValue = 1 << wbits;
592 if (wbits == maxwbits)
593 {
594 dowbits = false;
595 break;
596 }
597 }
598 }
599 }
600 return wbits;
601 }
602
603 public static void DecodePatch(int[] patches, BitPack bitpack, TerrainPatch.Header header, int size)
604 {
605 for (int n = 0; n < size*size; n++)
606 {
607 // ?
608 int temp = bitpack.UnpackBits(1);
609 if (temp != 0)
610 {
611 // Value or EOB
612 temp = bitpack.UnpackBits(1);
613 if (temp != 0)
614 {
615 // Value
616 temp = bitpack.UnpackBits(1);
617 if (temp != 0)
618 {
619 // Negative
620 temp = bitpack.UnpackBits((int) header.WordBits);
621 patches[n] = temp*-1;
622 }
623 else
624 {
625 // Positive
626 temp = bitpack.UnpackBits((int) header.WordBits);
627 patches[n] = temp;
628 }
629 }
630 else
631 {
632 // Set the rest to zero
633 // TODO: This might not be necessary
634 for (int o = n; o < size*size; o++)
635 {
636 patches[o] = 0;
637 }
638 break;
639 }
640 }
641 else
642 {
643 patches[n] = 0;
644 }
645 }
646 }
647
648 private static void EncodePatch(BitPack output, int[] patch, int postquant, int wbits)
649 {
650 int maxwbitssize = (1 << wbits) - 1;
651
652 if (postquant > Constants.TerrainPatchSize*Constants.TerrainPatchSize || postquant < 0)
653 {
654 Logger.Log("Postquant is outside the range of allowed values in EncodePatch()", Helpers.LogLevel.Error);
655 return;
656 }
657
658 if (postquant != 0) patch[Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant] = 0;
659
660 for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
661 {
662 int temp = patch[i];
663
664 if (temp == 0)
665 {
666 bool eob = true;
667
668 for (int j = i; j < Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant; j++)
669 {
670 if (patch[j] != 0)
671 {
672 eob = false;
673 break;
674 }
675 }
676
677 if (eob)
678 {
679 output.PackBits(ZERO_EOB, 2);
680 return;
681 }
682 output.PackBits(ZERO_CODE, 1);
683 }
684 else
685 {
686 if (temp < 0)
687 {
688 temp *= -1;
689
690 if (temp > maxwbitssize) temp = maxwbitssize;
691
692 output.PackBits(NEGATIVE_VALUE, 3);
693 output.PackBits(temp, wbits);
694 }
695 else
696 {
697 if (temp > maxwbitssize) temp = maxwbitssize;
698
699 output.PackBits(POSITIVE_VALUE, 3);
700 output.PackBits(temp, wbits);
701 }
702 }
703 }
704 }
705
706 public static float[] DecompressPatch(int[] patches, TerrainPatch.Header header, TerrainPatch.GroupHeader group)
707 {
708 float[] block = new float[group.PatchSize*group.PatchSize];
709 float[] output = new float[group.PatchSize*group.PatchSize];
710 int prequant = (header.QuantWBits >> 4) + 2;
711 int quantize = 1 << prequant;
712 float ooq = 1.0f/quantize;
713 float mult = ooq*header.Range;
714 float addval = mult*(1 << (prequant - 1)) + header.DCOffset;
715
716 if (group.PatchSize == Constants.TerrainPatchSize)
717 {
718 for (int n = 0; n < Constants.TerrainPatchSize*Constants.TerrainPatchSize; n++)
719 {
720 block[n] = patches[CopyMatrix16[n]]*DequantizeTable16[n];
721 }
722
723 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
724
725 for (int o = 0; o < Constants.TerrainPatchSize; o++)
726 IDCTColumn16(block, ftemp, o);
727 for (int o = 0; o < Constants.TerrainPatchSize; o++)
728 IDCTLine16(ftemp, block, o);
729 }
730 else
731 {
732 for (int n = 0; n < Constants.TerrainPatchSize*2*Constants.TerrainPatchSize*2; n++)
733 {
734 block[n] = patches[CopyMatrix32[n]]*DequantizeTable32[n];
735 }
736
737 Logger.Log("Implement IDCTPatchLarge", Helpers.LogLevel.Error);
738 }
739
740 for (int j = 0; j < block.Length; j++)
741 {
742 output[j] = block[j]*mult + addval;
743 }
744
745 return output;
746 }
747
748 private static int[] CompressPatch(float[] patchData, TerrainPatch.Header header, int prequant, out int wbits)
749 {
750 float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
751 int wordsize = (prequant - 2) & 0x0f;
752 float oozrange = 1.0f/header.Range;
753 float range = (1 << prequant);
754 float premult = oozrange*range;
755 float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
756
757 header.QuantWBits = wordsize;
758 header.QuantWBits |= wordsize << 4;
759
760 int k = 0;
761 for (int j = 0; j < Constants.TerrainPatchSize; j++)
762 {
763 for (int i = 0; i < Constants.TerrainPatchSize; i++)
764 block[k++] = patchData[j*Constants.TerrainPatchSize + i]*premult - sub;
765 }
766
767 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
768 int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
769
770
771 int maxWbits = prequant + 5;
772 wbits = (prequant >> 1);
773
774 for (int o = 0; o < Constants.TerrainPatchSize; o++)
775 DCTLine16(block, ftemp, o);
776 for (int o = 0; o < Constants.TerrainPatchSize; o++)
777 wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
778
779 return itemp;
780 }
781
782 private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant, out int wbits)
783 {
784 float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
785 float oozrange = 1.0f/header.Range;
786 float range = (1 << prequant);
787 float premult = oozrange*range;
788 float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
789 int wordsize = (prequant - 2) & 0x0f;
790
791 header.QuantWBits = wordsize;
792 header.QuantWBits |= wordsize << 4;
793
794 int k = 0;
795 for (int j = 0; j < Constants.TerrainPatchSize; j++)
796 {
797 for (int i = 0; i < Constants.TerrainPatchSize; i++)
798 block[k++] = patchData[j, i]*premult - sub;
799 }
800
801 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
802 int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
803
804 int maxWbits = prequant + 5;
805 wbits = (prequant >> 1);
806
807 for (int o = 0; o < Constants.TerrainPatchSize; o++)
808 DCTLine16(block, ftemp, o);
809 for (int o = 0; o < Constants.TerrainPatchSize; o++)
810 wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
811
812 return itemp;
813 }
814
815 private static int[] CompressPatch(short[] heightmap, int patchX, int patchY, TerrainPatch.Header header,
816 int prequant, uint pRegionSizeX, uint pRegionSizeY, out int wbits)
817 {
818 float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
819 int wordsize = prequant;
820 float oozrange = 1.0f/header.Range;
821 float range = (1 << prequant);
822 float premult = oozrange*range;
823 float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
824
825 header.QuantWBits = wordsize - 2;
826 header.QuantWBits |= (prequant - 2) << 4;
827
828 int k = 0;
829
830 premult /= Constants.TerrainCompression; // put here short to float factor
831
832 int jPatchLimit = patchY;
833 if (patchY >= (pRegionSizeY / Constants.TerrainPatchSize))
834 {
835 jPatchLimit = (int)(pRegionSizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
836 }
837 jPatchLimit = (jPatchLimit + 1) * Constants.TerrainPatchSize;
838
839 int iPatchLimit = patchX;
840 if (patchX >= (pRegionSizeX / Constants.TerrainPatchSize))
841 {
842 iPatchLimit = (int)(pRegionSizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
843 }
844 iPatchLimit = (iPatchLimit + 1) * Constants.TerrainPatchSize;
845
846 for (int j = patchY * Constants.TerrainPatchSize; j < jPatchLimit; j++)
847 {
848 for (int i = patchX * Constants.TerrainPatchSize; i < iPatchLimit; i++)
849 {
850 block[k++] = (heightmap[j*pRegionSizeX + i])*premult - sub;
851 }
852 }
853
854 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
855 int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
856
857 int maxWbits = prequant + 5;
858 wbits = (prequant >> 1);
859
860 for (int o = 0; o < Constants.TerrainPatchSize; o++)
861 DCTLine16(block, ftemp, o);
862 for (int o = 0; o < Constants.TerrainPatchSize; o++)
863 wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
864
865 return itemp;
866 }
867
868 #region Initialization
869
870 private static void BuildDequantizeTable16()
871 {
872 for (int j = 0; j < Constants.TerrainPatchSize; j++)
873 {
874 for (int i = 0; i < Constants.TerrainPatchSize; i++)
875 {
876 DequantizeTable16[j*Constants.TerrainPatchSize + i] = 1.0f + 2.0f*(i + j);
877 }
878 }
879 }
880
881 private static void BuildQuantizeTable16()
882 {
883 const float oosob = 2.0f/Constants.TerrainPatchSize;
884 for (int j = 0; j < Constants.TerrainPatchSize; j++)
885 {
886 for (int i = 0; i < Constants.TerrainPatchSize; i++)
887 {
888// QuantizeTable16[j * Constants.TerrainPatchSize + i] = 1.0f / (1.0f + 2.0f * ((float)i + (float)j));
889 QuantizeTable16[j*Constants.TerrainPatchSize + i] = oosob/(1.0f + 2.0f*(i + (float) j));
890 }
891 }
892 }
893
894 private static void SetupCosines16()
895 {
896 const float hposz = (float) Math.PI*0.5f/Constants.TerrainPatchSize;
897
898 for (int u = 0; u < Constants.TerrainPatchSize; u++)
899 {
900 for (int n = 0; n < Constants.TerrainPatchSize; n++)
901 {
902 CosineTable16[u*Constants.TerrainPatchSize + n] = (float) Math.Cos((2.0f*n + 1.0f)*u*hposz);
903 }
904 }
905 }
906
907 private static void BuildCopyMatrix16()
908 {
909 bool diag = false;
910 bool right = true;
911 int i = 0;
912 int j = 0;
913 int count = 0;
914
915 while (i < Constants.TerrainPatchSize && j < Constants.TerrainPatchSize)
916 {
917 CopyMatrix16[j*Constants.TerrainPatchSize + i] = count++;
918
919 if (!diag)
920 {
921 if (right)
922 {
923 if (i < Constants.TerrainPatchSize - 1) i++;
924 else j++;
925
926 right = false;
927 diag = true;
928 }
929 else
930 {
931 if (j < Constants.TerrainPatchSize - 1) j++;
932 else i++;
933
934 right = true;
935 diag = true;
936 }
937 }
938 else
939 {
940 if (right)
941 {
942 i++;
943 j--;
944 if (i == Constants.TerrainPatchSize - 1 || j == 0) diag = false;
945 }
946 else
947 {
948 i--;
949 j++;
950 if (j == Constants.TerrainPatchSize - 1 || i == 0) diag = false;
951 }
952 }
953 }
954 }
955
956 #endregion Initialization
957 }
958} \ No newline at end of file