aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs273
-rw-r--r--OpenSim/Region/Framework/Scenes/TerrainCompressor.cs948
2 files changed, 1112 insertions, 109 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 849fec3..4c0fba5 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -312,6 +312,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
312 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; 312 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
313 313
314 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 314 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
315 private static string LogHeader = "[LLCLIENTVIEW]";
315 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients 316 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
316 317
317 /// <summary> 318 /// <summary>
@@ -690,12 +691,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
690 /// <returns>true if the handler was added. This is currently always the case.</returns> 691 /// <returns>true if the handler was added. This is currently always the case.</returns>
691 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync) 692 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync)
692 { 693 {
694 return AddLocalPacketHandler(packetType, handler, doAsync, false);
695 }
696
697 /// <summary>
698 /// Add a handler for the given packet type.
699 /// </summary>
700 /// <param name="packetType"></param>
701 /// <param name="handler"></param>
702 /// <param name="doAsync">
703 /// If true, when the packet is received handle it on a different thread. Whether this is given direct to
704 /// a threadpool thread or placed in a queue depends on the inEngine parameter.
705 /// </param>
706 /// <param name="inEngine">
707 /// If async is false then this parameter is ignored.
708 /// If async is true and inEngine is false, then the packet is sent directly to a
709 /// threadpool thread.
710 /// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine.
711 /// This may result in slower handling but reduces the risk of overloading the simulator when there are many
712 /// simultaneous async requests.
713 /// </param>
714 /// <returns>true if the handler was added. This is currently always the case.</returns>
715 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine)
716 {
693 bool result = false; 717 bool result = false;
694 lock (m_packetHandlers) 718 lock (m_packetHandlers)
695 { 719 {
696 if (!m_packetHandlers.ContainsKey(packetType)) 720 if (!m_packetHandlers.ContainsKey(packetType))
697 { 721 {
698 m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync }); 722 m_packetHandlers.Add(
723 packetType, new PacketProcessor() { method = handler, Async = doAsync });
699 result = true; 724 result = true;
700 } 725 }
701 } 726 }
@@ -1174,11 +1199,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1174 1199
1175 /// <summary> 1200 /// <summary>
1176 /// Send the region heightmap to the client 1201 /// Send the region heightmap to the client
1202 /// This method is only called when not doing intellegent terrain patch sending and
1203 /// is only called when the scene presence is initially created and sends all of the
1204 /// region's patches to the client.
1177 /// </summary> 1205 /// </summary>
1178 /// <param name="map">heightmap</param> 1206 /// <param name="map">heightmap</param>
1179 public virtual void SendLayerData(float[] map) 1207 public virtual void SendLayerData(float[] map)
1180 { 1208 {
1181 Util.FireAndForget(DoSendLayerData, map); 1209 Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData());
1182 1210
1183 // Send it sync, and async. It's not that much data 1211 // Send it sync, and async. It's not that much data
1184 // and it improves user experience just so much! 1212 // and it improves user experience just so much!
@@ -1191,15 +1219,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1191 /// <param name="o"></param> 1219 /// <param name="o"></param>
1192 private void DoSendLayerData(object o) 1220 private void DoSendLayerData(object o)
1193 { 1221 {
1194 float[] map = LLHeightFieldMoronize((float[])o); 1222 TerrainData map = (TerrainData)o;
1195 1223
1196 try 1224 try
1197 { 1225 {
1226 // Send LayerData in typerwriter pattern
1198 for (int y = 0; y < 16; y++) 1227 for (int y = 0; y < 16; y++)
1199 { 1228 {
1200 for (int x = 0; x < 16; x+=4) 1229 for (int x = 0; x < 16; x++)
1201 { 1230 {
1202 SendLayerPacket(x, y, map); 1231 SendLayerData(x, y, map);
1203 } 1232 }
1204 } 1233 }
1205 } 1234 }
@@ -1209,77 +1238,95 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1209 } 1238 }
1210 } 1239 }
1211 1240
1212 /// <summary> 1241 // Legacy form of invocation that passes around a bare data array.
1213 /// Sends a set of four patches (x, x+1, ..., x+3) to the client 1242 // Just ignore what was passed and use the real terrain info that is part of the scene.
1214 /// </summary> 1243 // As a HORRIBLE kludge in an attempt to not change the definition of IClientAPI,
1215 /// <param name="map">heightmap</param> 1244 // there is a special form for specifying multiple terrain patches to send.
1216 /// <param name="px">X coordinate for patches 0..12</param> 1245 // The form is to pass 'px' as negative the number of patches to send and to
1217 /// <param name="py">Y coordinate for patches 0..15</param> 1246 // pass the float array as pairs of patch X and Y coordinates. So, passing 'px'
1218 private void SendLayerPacket(int x, int y, float[] map) 1247 // as -2 and map= [3, 5, 8, 4] would mean to send two terrain heightmap patches
1248 // and the patches to send are <3,5> and <8,4>.
1249 public void SendLayerData(int px, int py, float[] map)
1219 { 1250 {
1220 int[] patches = new int[4]; 1251 if (px >= 0)
1221 patches[0] = x + 0 + y * 16; 1252 {
1222 patches[1] = x + 1 + y * 16; 1253 SendLayerData(px, py, m_scene.Heightmap.GetTerrainData());
1223 patches[2] = x + 2 + y * 16; 1254 }
1224 patches[3] = x + 3 + y * 16; 1255 else
1256 {
1257 int numPatches = -px;
1258 int[] xPatches = new int[numPatches];
1259 int[] yPatches = new int[numPatches];
1260 for (int pp = 0; pp < numPatches; pp++)
1261 {
1262 xPatches[pp] = (int)map[pp * 2];
1263 yPatches[pp] = (int)map[pp * 2 + 1];
1264 }
1225 1265
1226 float[] heightmap = (map.Length == 65536) ? 1266 // DebugSendingPatches("SendLayerData", xPatches, yPatches);
1227 map :
1228 LLHeightFieldMoronize(map);
1229 1267
1230 try 1268 SendLayerData(xPatches, yPatches, m_scene.Heightmap.GetTerrainData());
1231 {
1232 Packet layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
1233 OutPacket(layerpack, ThrottleOutPacketType.Land);
1234 } 1269 }
1235 catch 1270 }
1271
1272 private void DebugSendingPatches(string pWho, int[] pX, int[] pY)
1273 {
1274 if (m_log.IsDebugEnabled)
1236 { 1275 {
1237 for (int px = x ; px < x + 4 ; px++) 1276 int numPatches = pX.Length;
1238 SendLayerData(px, y, map); 1277 string Xs = "";
1278 string Ys = "";
1279 for (int pp = 0; pp < numPatches; pp++)
1280 {
1281 Xs += String.Format("{0}", (int)pX[pp]) + ",";
1282 Ys += String.Format("{0}", (int)pY[pp]) + ",";
1283 }
1284 m_log.DebugFormat("{0} {1}: numPatches={2}, X={3}, Y={4}", LogHeader, pWho, numPatches, Xs, Ys);
1239 } 1285 }
1240 } 1286 }
1241 1287
1242 /// <summary> 1288 /// <summary>
1243 /// Sends a specified patch to a client 1289 /// Sends a terrain packet for the point specified.
1290 /// This is a legacy call that has refarbed the terrain into a flat map of floats.
1291 /// We just use the terrain from the region we know about.
1244 /// </summary> 1292 /// </summary>
1245 /// <param name="px">Patch coordinate (x) 0..15</param> 1293 /// <param name="px">Patch coordinate (x) 0..15</param>
1246 /// <param name="py">Patch coordinate (y) 0..15</param> 1294 /// <param name="py">Patch coordinate (y) 0..15</param>
1247 /// <param name="map">heightmap</param> 1295 /// <param name="map">heightmap</param>
1248 public void SendLayerData(int px, int py, float[] map) 1296 public void SendLayerData(int px, int py, TerrainData terrData)
1297 {
1298 int[] xPatches = new[] { px };
1299 int[] yPatches = new[] { py };
1300 SendLayerData(xPatches, yPatches, terrData);
1301 }
1302
1303 private void SendLayerData(int[] px, int[] py, TerrainData terrData)
1249 { 1304 {
1250 try 1305 try
1251 { 1306 {
1252 int[] patches = new int[] { py * 16 + px }; 1307 /* test code using the terrain compressor in libOpenMetaverse
1253 float[] heightmap = (map.Length == 65536) ? 1308 int[] patchInd = new int[1];
1254 map : 1309 patchInd[0] = px + (py * Constants.TerrainPatchSize);
1255 LLHeightFieldMoronize(map); 1310 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(terrData.GetFloatsSerialized(), patchInd);
1256 1311 */
1257 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); 1312 // Many, many patches could have been passed to us. Since the patches will be compressed
1258 1313 // into variable sized blocks, we cannot pre-compute how many will fit into one
1259 // When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience. 1314 // packet. While some fancy packing algorithm is possible, 4 seems to always fit.
1260 // To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain. 1315 int PatchesAssumedToFit = 4;
1261 // We also make them unreliable because it's extremely likely that multiple packets will be sent for a terrain patch area 1316 for (int pcnt = 0; pcnt < px.Length; pcnt += PatchesAssumedToFit)
1262 // invalidating previous packets for that area. 1317 {
1263 1318 int remaining = Math.Min(px.Length - pcnt, PatchesAssumedToFit);
1264 // It's possible for an editing user to flood themselves with edited packets but the majority of use cases are such that only a 1319 int[] xPatches = new int[remaining];
1265 // tiny percentage of users will be editing the terrain. Other, non-editing users will see the edits much slower. 1320 int[] yPatches = new int[remaining];
1266 1321 for (int ii = 0; ii < remaining; ii++)
1267 // One last note on this topic, by the time users are going to be editing the terrain, it's extremely likely that the sim will 1322 {
1268 // have rezzed already and therefore this is not likely going to cause any additional issues with lost packets, objects or terrain 1323 xPatches[ii] = px[pcnt + ii];
1269 // patches. 1324 yPatches[ii] = py[pcnt + ii];
1325 }
1326 LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(terrData, xPatches, yPatches);
1327 // DebugSendingPatches("SendLayerDataInternal", xPatches, yPatches);
1270 1328
1271 // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we only have one cache miss. 1329 SendTheLayerPacket(layerpack);
1272 if (m_justEditedTerrain)
1273 {
1274 layerpack.Header.Reliable = false;
1275 OutPacket(layerpack,
1276 ThrottleOutPacketType.Unknown );
1277 }
1278 else
1279 {
1280 layerpack.Header.Reliable = true;
1281 OutPacket(layerpack,
1282 ThrottleOutPacketType.Task);
1283 } 1330 }
1284 } 1331 }
1285 catch (Exception e) 1332 catch (Exception e)
@@ -1288,36 +1335,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1288 } 1335 }
1289 } 1336 }
1290 1337
1291 /// <summary> 1338 // When a user edits the terrain, so much data is sent, the data queues up fast and presents a
1292 /// Munges heightfield into the LLUDP backed in restricted heightfield. 1339 // sub optimal editing experience. To alleviate this issue, when the user edits the terrain, we
1293 /// </summary> 1340 // start skipping the queues until they're done editing the terrain. We also make them
1294 /// <param name="map">float array in the base; Constants.RegionSize</param> 1341 // unreliable because it's extremely likely that multiple packets will be sent for a terrain patch
1295 /// <returns>float array in the base 256</returns> 1342 // area invalidating previous packets for that area.
1296 internal float[] LLHeightFieldMoronize(float[] map) 1343
1344 // It's possible for an editing user to flood themselves with edited packets but the majority
1345 // of use cases are such that only a tiny percentage of users will be editing the terrain.
1346 // Other, non-editing users will see the edits much slower.
1347
1348 // One last note on this topic, by the time users are going to be editing the terrain, it's
1349 // extremely likely that the sim will have rezzed already and therefore this is not likely going
1350 // to cause any additional issues with lost packets, objects or terrain patches.
1351
1352 // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we
1353 // only have one cache miss.
1354 private void SendTheLayerPacket(LayerDataPacket layerpack)
1297 { 1355 {
1298 if (map.Length == 65536) 1356 if (m_justEditedTerrain)
1299 return map; 1357 {
1358 layerpack.Header.Reliable = false;
1359 OutPacket(layerpack, ThrottleOutPacketType.Unknown );
1360 }
1300 else 1361 else
1301 { 1362 {
1302 float[] returnmap = new float[65536]; 1363 layerpack.Header.Reliable = true;
1303 1364 OutPacket(layerpack, ThrottleOutPacketType.Land);
1304 if (map.Length < 65535)
1305 {
1306 // rebase the vector stride to 256
1307 for (int i = 0; i < Constants.RegionSize; i++)
1308 Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, (int)Constants.RegionSize);
1309 }
1310 else
1311 {
1312 for (int i = 0; i < 256; i++)
1313 Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256);
1314 }
1315
1316 //Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536);
1317
1318 return returnmap;
1319 } 1365 }
1320
1321 } 1366 }
1322 1367
1323 /// <summary> 1368 /// <summary>
@@ -1346,21 +1391,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1346 { 1391 {
1347 Vector2[] windSpeeds = (Vector2[])o; 1392 Vector2[] windSpeeds = (Vector2[])o;
1348 TerrainPatch[] patches = new TerrainPatch[2]; 1393 TerrainPatch[] patches = new TerrainPatch[2];
1349 patches[0] = new TerrainPatch(); 1394 patches[0] = new TerrainPatch { Data = new float[16 * 16] };
1350 patches[0].Data = new float[16 * 16]; 1395 patches[1] = new TerrainPatch { Data = new float[16 * 16] };
1351 patches[1] = new TerrainPatch();
1352 patches[1].Data = new float[16 * 16];
1353 1396
1354 for (int y = 0; y < 16; y++) 1397 for (int x = 0; x < 16 * 16; x++)
1355 { 1398 {
1356 for (int x = 0; x < 16; x++) 1399 patches[0].Data[x] = windSpeeds[x].X;
1357 { 1400 patches[1].Data[x] = windSpeeds[x].Y;
1358 patches[0].Data[y * 16 + x] = windSpeeds[y * 16 + x].X;
1359 patches[1].Data[y * 16 + x] = windSpeeds[y * 16 + x].Y;
1360 }
1361 } 1401 }
1362 1402
1363 LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Wind); 1403 byte layerType = (byte)TerrainPatch.LayerType.Wind;
1404 if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
1405 layerType = (byte)TerrainPatch.LayerType.WindExtended;
1406
1407 // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType);
1408 LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType,
1409 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY);
1364 layerpack.Header.Zerocoded = true; 1410 layerpack.Header.Zerocoded = true;
1365 OutPacket(layerpack, ThrottleOutPacketType.Wind); 1411 OutPacket(layerpack, ThrottleOutPacketType.Wind);
1366 } 1412 }
@@ -1384,7 +1430,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1384 } 1430 }
1385 } 1431 }
1386 1432
1387 LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Cloud); 1433 byte layerType = (byte)TerrainPatch.LayerType.Cloud;
1434 if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
1435 layerType = (byte)TerrainPatch.LayerType.CloudExtended;
1436
1437 // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType);
1438 LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType,
1439 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY);
1388 layerpack.Header.Zerocoded = true; 1440 layerpack.Header.Zerocoded = true;
1389 OutPacket(layerpack, ThrottleOutPacketType.Cloud); 1441 OutPacket(layerpack, ThrottleOutPacketType.Cloud);
1390 } 1442 }
@@ -1489,10 +1541,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1489 mapReply.Data[i].Access = mapBlocks2[i].Access; 1541 mapReply.Data[i].Access = mapBlocks2[i].Access;
1490 mapReply.Data[i].Agents = mapBlocks2[i].Agents; 1542 mapReply.Data[i].Agents = mapBlocks2[i].Agents;
1491 1543
1492 // TODO: hookup varregion sim size here
1493 mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock(); 1544 mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock();
1494 mapReply.Size[i].SizeX = 256; 1545 mapReply.Size[i].SizeX = mapBlocks2[i].SizeX;
1495 mapReply.Size[i].SizeY = 256; 1546 mapReply.Size[i].SizeY = mapBlocks2[i].SizeY;
1496 } 1547 }
1497 OutPacket(mapReply, ThrottleOutPacketType.Land); 1548 OutPacket(mapReply, ThrottleOutPacketType.Land);
1498 } 1549 }
@@ -1657,15 +1708,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1657 1708
1658 public void SendKillObject(List<uint> localIDs) 1709 public void SendKillObject(List<uint> localIDs)
1659 { 1710 {
1660// foreach (uint id in localIDs)
1661// m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle);
1662
1663 // remove pending entities
1664 lock (m_entityProps.SyncRoot)
1665 m_entityProps.Remove(localIDs);
1666 lock (m_entityUpdates.SyncRoot)
1667 m_entityUpdates.Remove(localIDs);
1668
1669 KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject); 1711 KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject);
1670 // TODO: don't create new blocks if recycling an old packet 1712 // TODO: don't create new blocks if recycling an old packet
1671 kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count]; 1713 kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count];
@@ -9087,6 +9129,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9087 TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest; 9129 TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest;
9088 if (handlerTeleportLocationRequest != null) 9130 if (handlerTeleportLocationRequest != null)
9089 { 9131 {
9132 // Adjust teleport location to base of a larger region if requested to teleport to a sub-region
9133 uint locX, locY;
9134 Util.RegionHandleToWorldLoc(tpLocReq.Info.RegionHandle, out locX, out locY);
9135 if ((locX >= m_scene.RegionInfo.WorldLocX)
9136 && (locX < (m_scene.RegionInfo.WorldLocX + m_scene.RegionInfo.RegionSizeX))
9137 && (locY >= m_scene.RegionInfo.WorldLocY)
9138 && (locY < (m_scene.RegionInfo.WorldLocY + m_scene.RegionInfo.RegionSizeY)) )
9139 {
9140 tpLocReq.Info.RegionHandle = m_scene.RegionInfo.RegionHandle;
9141 tpLocReq.Info.Position.X += locX - m_scene.RegionInfo.WorldLocX;
9142 tpLocReq.Info.Position.Y += locY - m_scene.RegionInfo.WorldLocY;
9143 }
9144
9090 handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position, 9145 handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position,
9091 tpLocReq.Info.LookAt, 16); 9146 tpLocReq.Info.LookAt, 16);
9092 } 9147 }
diff --git a/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs
new file mode 100644
index 0000000..fc8f8cd
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs
@@ -0,0 +1,948 @@
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
50#pragma warning disable 414
51 private static string LogHeader = "[TERRAIN COMPRESSOR]";
52#pragma warning restore 414
53
54 public const int END_OF_PATCHES = 97;
55
56 private const float OO_SQRT2 = 0.7071067811865475244008443621049f;
57 private const int STRIDE = 264;
58
59 private const int ZERO_CODE = 0x0;
60 private const int ZERO_EOB = 0x2;
61 private const int POSITIVE_VALUE = 0x6;
62 private const int NEGATIVE_VALUE = 0x7;
63
64 private static readonly float[] DequantizeTable16 =
65 new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
66
67 private static readonly float[] DequantizeTable32 =
68 new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
69
70 private static readonly float[] CosineTable16 = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
71 //private static readonly float[] CosineTable32 = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize];
72 private static readonly int[] CopyMatrix16 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
73 private static readonly int[] CopyMatrix32 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
74
75 private static readonly float[] QuantizeTable16 =
76 new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
77
78 static OpenSimTerrainCompressor()
79 {
80 // Initialize the decompression tables
81 BuildDequantizeTable16();
82 SetupCosines16();
83 BuildCopyMatrix16();
84 BuildQuantizeTable16();
85 }
86
87 // Used to send cloud and wind patches
88 public static LayerDataPacket CreateLayerDataPacket(TerrainPatch[] patches, byte type, int pRegionSizeX,
89 int pRegionSizeY)
90 {
91 LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
92
93 TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
94 {Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
95
96 // Should be enough to fit even the most poorly packed data
97 byte[] data = new byte[patches.Length*Constants.TerrainPatchSize*Constants.TerrainPatchSize*2];
98 BitPack bitpack = new BitPack(data, 0);
99 bitpack.PackBits(header.Stride, 16);
100 bitpack.PackBits(header.PatchSize, 8);
101 bitpack.PackBits(type, 8);
102
103 foreach (TerrainPatch t in patches)
104 CreatePatch(bitpack, t.Data, t.X, t.Y, pRegionSizeX, pRegionSizeY);
105
106 bitpack.PackBits(END_OF_PATCHES, 8);
107
108 layer.LayerData.Data = new byte[bitpack.BytePos + 1];
109 Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
110
111 return layer;
112 }
113
114 // Create a land packet for a single patch.
115 public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY)
116 {
117 int[] xPieces = new int[1];
118 int[] yPieces = new int[1];
119 xPieces[0] = patchX; // patch X dimension
120 yPieces[0] = patchY;
121
122 return CreateLandPacket(terrData, xPieces, yPieces);
123 }
124
125 public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] xPieces, int[] yPieces)
126 {
127 byte landPacketType = (byte)TerrainPatch.LayerType.Land;
128 if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
129 {
130 landPacketType = (byte)TerrainPatch.LayerType.LandExtended;
131 }
132
133 return CreateLandPacket(terrData, xPieces, yPieces, landPacketType);
134 }
135
136 /// <summary>
137 /// Creates a LayerData packet for compressed land data given a full
138 /// simulator heightmap and an array of indices of patches to compress
139 /// </summary>
140 /// <param name="terrData">
141 /// Terrain data that can result in a meter square heightmap.
142 /// </param>
143 /// <param name="x">
144 /// Array of indexes in the grid of patches
145 /// for this simulator.
146 /// If creating a packet for multiple patches, there will be entries in
147 /// both the X and Y arrays for each of the patches.
148 /// For example if patches 1 and 17 are to be sent,
149 /// x[] = {1,1} and y[] = {0,1} which specifies the patches at
150 /// indexes <1,0> and <1,1> (presuming the terrain size is 16x16 patches).
151 /// </param>
152 /// <param name="y">
153 /// Array of indexes in the grid of patches.
154 /// </param>
155 /// <param name="type"></param>
156 /// <returns></returns>
157 public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] x, int[] y, byte type)
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, terrData, x[i], y[i]);
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, TerrainData terrData, int patchX, int patchY)
221 {
222 TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY);
223 header.QuantWBits = 136;
224
225 // If larger than legacy region size, pack patch X and Y info differently.
226 if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
227 {
228 header.PatchIDs = (patchY & 0xFFFF);
229 header.PatchIDs += (patchX << 16);
230 }
231 else
232 {
233 header.PatchIDs = (patchY & 0x1F);
234 header.PatchIDs += (patchX << 5);
235 }
236
237 // m_log.DebugFormat("{0} CreatePatchFromHeightmap. patchX={1}, patchY={2}, DCOffset={3}, range={4}",
238 // LogHeader, patchX, patchY, header.DCOffset, header.Range);
239
240 // NOTE: No idea what prequant and postquant should be or what they do
241 int wbits;
242 int[] patch = CompressPatch(terrData, patchX, patchY, header, 10, out wbits);
243 wbits = EncodePatchHeader(output, header, patch, (uint)terrData.SizeX, (uint)terrData.SizeY, wbits);
244 EncodePatch(output, patch, 0, wbits);
245 }
246
247 private static TerrainPatch.Header PrescanPatch(float[] patch)
248 {
249 TerrainPatch.Header header = new TerrainPatch.Header();
250 float zmax = -99999999.0f;
251 float zmin = 99999999.0f;
252
253 for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
254 {
255 float val = patch[i];
256 if (val > zmax) zmax = val;
257 if (val < zmin) zmin = val;
258 }
259
260 header.DCOffset = zmin;
261 header.Range = (int) ((zmax - zmin) + 1.0f);
262
263 return header;
264 }
265
266 // Scan the height info we're returning and return a patch packet header for this patch.
267 private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY)
268 {
269 TerrainPatch.Header header = new TerrainPatch.Header();
270 float zmax = -99999999.0f;
271 float zmin = 99999999.0f;
272
273 for (int j = patchY*Constants.TerrainPatchSize; j < (patchY + 1)*Constants.TerrainPatchSize; j++)
274 {
275 for (int i = patchX*Constants.TerrainPatchSize; i < (patchX + 1)*Constants.TerrainPatchSize; i++)
276 {
277 float val = terrData[i, j];
278 if (val > zmax) zmax = val;
279 if (val < zmin) zmin = val;
280 }
281 }
282
283 header.DCOffset = zmin;
284 header.Range = (int)((zmax - zmin) + 1.0f);
285
286 return header;
287 }
288
289 public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack)
290 {
291 TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)};
292
293 // Quantized word bits
294 if (header.QuantWBits == END_OF_PATCHES)
295 return header;
296
297 // DC offset
298 header.DCOffset = bitpack.UnpackFloat();
299
300 // Range
301 header.Range = bitpack.UnpackBits(16);
302
303 // Patch IDs (10 bits)
304 header.PatchIDs = bitpack.UnpackBits(10);
305
306 // Word bits
307 header.WordBits = (uint) ((header.QuantWBits & 0x0f) + 2);
308
309 return header;
310 }
311
312 private static int EncodePatchHeader(BitPack output, TerrainPatch.Header header, int[] patch, uint pRegionSizeX,
313 uint pRegionSizeY, int wbits)
314 {
315 /*
316 int temp;
317 int wbits = (header.QuantWBits & 0x0f) + 2;
318 uint maxWbits = (uint)wbits + 5;
319 uint minWbits = ((uint)wbits >> 1);
320 int wbitsMaxValue;
321 */
322 // goal is to determ minimum number of bits to use so all data fits
323 /*
324 wbits = (int)minWbits;
325 wbitsMaxValue = (1 << wbits);
326
327 for (int i = 0; i < patch.Length; i++)
328 {
329 temp = patch[i];
330 if (temp != 0)
331 {
332 // Get the absolute value
333 if (temp < 0) temp *= -1;
334
335 no coments..
336
337 for (int j = (int)maxWbits; j > (int)minWbits; j--)
338 {
339 if ((temp & (1 << j)) != 0)
340 {
341 if (j > wbits) wbits = j;
342 break;
343 }
344 }
345
346 while (temp > wbitsMaxValue)
347 {
348 wbits++;
349 if (wbits == maxWbits)
350 goto Done;
351 wbitsMaxValue = 1 << wbits;
352 }
353 }
354 }
355
356 Done:
357
358 // wbits += 1;
359 */
360 // better check
361 if (wbits > 17)
362 wbits = 16;
363 else if (wbits < 3)
364 wbits = 3;
365
366 header.QuantWBits &= 0xf0;
367
368 header.QuantWBits |= (wbits - 2);
369
370 output.PackBits(header.QuantWBits, 8);
371 output.PackFloat(header.DCOffset);
372 output.PackBits(header.Range, 16);
373 if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
374 output.PackBits(header.PatchIDs, 32);
375 else
376 output.PackBits(header.PatchIDs, 10);
377
378 return wbits;
379 }
380
381 private static void IDCTColumn16(float[] linein, float[] lineout, int column)
382 {
383 for (int n = 0; n < Constants.TerrainPatchSize; n++)
384 {
385 float total = OO_SQRT2*linein[column];
386
387 for (int u = 1; u < Constants.TerrainPatchSize; u++)
388 {
389 int usize = u*Constants.TerrainPatchSize;
390 total += linein[usize + column]*CosineTable16[usize + n];
391 }
392
393 lineout[Constants.TerrainPatchSize*n + column] = total;
394 }
395 }
396
397 private static void IDCTLine16(float[] linein, float[] lineout, int line)
398 {
399 const float oosob = 2.0f/Constants.TerrainPatchSize;
400 int lineSize = line*Constants.TerrainPatchSize;
401
402 for (int n = 0; n < Constants.TerrainPatchSize; n++)
403 {
404 float total = OO_SQRT2*linein[lineSize];
405
406 for (int u = 1; u < Constants.TerrainPatchSize; u++)
407 {
408 total += linein[lineSize + u]*CosineTable16[u*Constants.TerrainPatchSize + n];
409 }
410
411 lineout[lineSize + n] = total*oosob;
412 }
413 }
414
415/*
416 private static void DCTLine16(float[] linein, float[] lineout, int line)
417 {
418 float total = 0.0f;
419 int lineSize = line * Constants.TerrainPatchSize;
420
421 for (int n = 0; n < Constants.TerrainPatchSize; n++)
422 {
423 total += linein[lineSize + n];
424 }
425
426 lineout[lineSize] = OO_SQRT2 * total;
427
428 int uptr = 0;
429 for (int u = 1; u < Constants.TerrainPatchSize; u++)
430 {
431 total = 0.0f;
432 uptr += Constants.TerrainPatchSize;
433
434 for (int n = 0; n < Constants.TerrainPatchSize; n++)
435 {
436 total += linein[lineSize + n] * CosineTable16[uptr + n];
437 }
438
439 lineout[lineSize + u] = total;
440 }
441 }
442*/
443
444 private static void DCTLine16(float[] linein, float[] lineout, int line)
445 {
446 // outputs transpose data (lines exchanged with coluns )
447 // so to save a bit of cpu when doing coluns
448 float total = 0.0f;
449 int lineSize = line*Constants.TerrainPatchSize;
450
451 for (int n = 0; n < Constants.TerrainPatchSize; n++)
452 {
453 total += linein[lineSize + n];
454 }
455
456 lineout[line] = OO_SQRT2*total;
457
458 for (int u = Constants.TerrainPatchSize;
459 u < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
460 u += Constants.TerrainPatchSize)
461 {
462 total = 0.0f;
463 for (int ptrn = lineSize, ptru = u; ptrn < lineSize + Constants.TerrainPatchSize; ptrn++,ptru++)
464 {
465 total += linein[ptrn]*CosineTable16[ptru];
466 }
467
468 lineout[line + u] = total;
469 }
470 }
471
472
473 /*
474 private static void DCTColumn16(float[] linein, int[] lineout, int column)
475 {
476 float total = 0.0f;
477 // const float oosob = 2.0f / Constants.TerrainPatchSize;
478
479 for (int n = 0; n < Constants.TerrainPatchSize; n++)
480 {
481 total += linein[Constants.TerrainPatchSize * n + column];
482 }
483
484 // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
485 lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * QuantizeTable16[column]);
486
487 for (int uptr = Constants.TerrainPatchSize; uptr < Constants.TerrainPatchSize * Constants.TerrainPatchSize; uptr += Constants.TerrainPatchSize)
488 {
489 total = 0.0f;
490
491 for (int n = 0; n < Constants.TerrainPatchSize; n++)
492 {
493 total += linein[Constants.TerrainPatchSize * n + column] * CosineTable16[uptr + n];
494 }
495
496 // lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
497 lineout[CopyMatrix16[uptr + column]] = (int)(total * QuantizeTable16[uptr + column]);
498 }
499 }
500
501 private static void DCTColumn16(float[] linein, int[] lineout, int column)
502 {
503 // input columns are in fact stored in lines now
504
505 float total = 0.0f;
506// const float oosob = 2.0f / Constants.TerrainPatchSize;
507 int inlinesptr = Constants.TerrainPatchSize*column;
508
509 for (int n = 0; n < Constants.TerrainPatchSize; n++)
510 {
511 total += linein[inlinesptr + n];
512 }
513
514 // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
515 lineout[CopyMatrix16[column]] = (int) (OO_SQRT2*total*QuantizeTable16[column]);
516
517 for (int uptr = Constants.TerrainPatchSize;
518 uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
519 uptr += Constants.TerrainPatchSize)
520 {
521 total = 0.0f;
522
523 for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
524 {
525 total += linein[n]*CosineTable16[ptru];
526 }
527
528// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
529 lineout[CopyMatrix16[uptr + column]] = (int) (total*QuantizeTable16[uptr + column]);
530 }
531 }
532 */
533
534 private static int DCTColumn16Wbits(float[] linein, int[] lineout, int column, int wbits, int maxwbits)
535 {
536 // input columns are in fact stored in lines now
537
538 bool dowbits = wbits != maxwbits;
539 int wbitsMaxValue = 1 << wbits;
540
541 float total = 0.0f;
542 // const float oosob = 2.0f / Constants.TerrainPatchSize;
543 int inlinesptr = Constants.TerrainPatchSize*column;
544
545 for (int n = 0; n < Constants.TerrainPatchSize; n++)
546 {
547 total += linein[inlinesptr + n];
548 }
549
550 // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
551 int tmp = (int) (OO_SQRT2*total*QuantizeTable16[column]);
552 lineout[CopyMatrix16[column]] = tmp;
553
554 if (dowbits)
555 {
556 if (tmp < 0) tmp *= -1;
557 while (tmp > wbitsMaxValue)
558 {
559 wbits++;
560 wbitsMaxValue = 1 << wbits;
561 if (wbits == maxwbits)
562 {
563 dowbits = false;
564 break;
565 }
566 }
567 }
568
569 for (int uptr = Constants.TerrainPatchSize;
570 uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
571 uptr += Constants.TerrainPatchSize)
572 {
573 total = 0.0f;
574
575 for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
576 {
577 total += linein[n]*CosineTable16[ptru];
578 }
579
580 tmp = (int) (total*QuantizeTable16[uptr + column]);
581 lineout[CopyMatrix16[uptr + column]] = tmp;
582
583 if (dowbits)
584 {
585 if (tmp < 0) tmp *= -1;
586 while (tmp > wbitsMaxValue)
587 {
588 wbits++;
589 wbitsMaxValue = 1 << wbits;
590 if (wbits == maxwbits)
591 {
592 dowbits = false;
593 break;
594 }
595 }
596 }
597 }
598 return wbits;
599 }
600
601 public static void DecodePatch(int[] patches, BitPack bitpack, TerrainPatch.Header header, int size)
602 {
603 for (int n = 0; n < size*size; n++)
604 {
605 // ?
606 int temp = bitpack.UnpackBits(1);
607 if (temp != 0)
608 {
609 // Value or EOB
610 temp = bitpack.UnpackBits(1);
611 if (temp != 0)
612 {
613 // Value
614 temp = bitpack.UnpackBits(1);
615 if (temp != 0)
616 {
617 // Negative
618 temp = bitpack.UnpackBits((int) header.WordBits);
619 patches[n] = temp*-1;
620 }
621 else
622 {
623 // Positive
624 temp = bitpack.UnpackBits((int) header.WordBits);
625 patches[n] = temp;
626 }
627 }
628 else
629 {
630 // Set the rest to zero
631 // TODO: This might not be necessary
632 for (int o = n; o < size*size; o++)
633 {
634 patches[o] = 0;
635 }
636 break;
637 }
638 }
639 else
640 {
641 patches[n] = 0;
642 }
643 }
644 }
645
646 private static void EncodePatch(BitPack output, int[] patch, int postquant, int wbits)
647 {
648 int maxwbitssize = (1 << wbits) - 1;
649
650 if (postquant > Constants.TerrainPatchSize*Constants.TerrainPatchSize || postquant < 0)
651 {
652 Logger.Log("Postquant is outside the range of allowed values in EncodePatch()", Helpers.LogLevel.Error);
653 return;
654 }
655
656 if (postquant != 0) patch[Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant] = 0;
657
658 for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
659 {
660 int temp = patch[i];
661
662 if (temp == 0)
663 {
664 bool eob = true;
665
666 for (int j = i; j < Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant; j++)
667 {
668 if (patch[j] != 0)
669 {
670 eob = false;
671 break;
672 }
673 }
674
675 if (eob)
676 {
677 output.PackBits(ZERO_EOB, 2);
678 return;
679 }
680 output.PackBits(ZERO_CODE, 1);
681 }
682 else
683 {
684 if (temp < 0)
685 {
686 temp *= -1;
687
688 if (temp > maxwbitssize) temp = maxwbitssize;
689
690 output.PackBits(NEGATIVE_VALUE, 3);
691 output.PackBits(temp, wbits);
692 }
693 else
694 {
695 if (temp > maxwbitssize) temp = maxwbitssize;
696
697 output.PackBits(POSITIVE_VALUE, 3);
698 output.PackBits(temp, wbits);
699 }
700 }
701 }
702 }
703
704 public static float[] DecompressPatch(int[] patches, TerrainPatch.Header header, TerrainPatch.GroupHeader group)
705 {
706 float[] block = new float[group.PatchSize*group.PatchSize];
707 float[] output = new float[group.PatchSize*group.PatchSize];
708 int prequant = (header.QuantWBits >> 4) + 2;
709 int quantize = 1 << prequant;
710 float ooq = 1.0f/quantize;
711 float mult = ooq*header.Range;
712 float addval = mult*(1 << (prequant - 1)) + header.DCOffset;
713
714 if (group.PatchSize == Constants.TerrainPatchSize)
715 {
716 for (int n = 0; n < Constants.TerrainPatchSize*Constants.TerrainPatchSize; n++)
717 {
718 block[n] = patches[CopyMatrix16[n]]*DequantizeTable16[n];
719 }
720
721 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
722
723 for (int o = 0; o < Constants.TerrainPatchSize; o++)
724 IDCTColumn16(block, ftemp, o);
725 for (int o = 0; o < Constants.TerrainPatchSize; o++)
726 IDCTLine16(ftemp, block, o);
727 }
728 else
729 {
730 for (int n = 0; n < Constants.TerrainPatchSize*2*Constants.TerrainPatchSize*2; n++)
731 {
732 block[n] = patches[CopyMatrix32[n]]*DequantizeTable32[n];
733 }
734
735 Logger.Log("Implement IDCTPatchLarge", Helpers.LogLevel.Error);
736 }
737
738 for (int j = 0; j < block.Length; j++)
739 {
740 output[j] = block[j]*mult + addval;
741 }
742
743 return output;
744 }
745
746 private static int[] CompressPatch(float[] patchData, TerrainPatch.Header header, int prequant, out int wbits)
747 {
748 float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
749 int wordsize = (prequant - 2) & 0x0f;
750 float oozrange = 1.0f/header.Range;
751 float range = (1 << prequant);
752 float premult = oozrange*range;
753 float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
754
755 header.QuantWBits = wordsize;
756 header.QuantWBits |= wordsize << 4;
757
758 int k = 0;
759 for (int j = 0; j < Constants.TerrainPatchSize; j++)
760 {
761 for (int i = 0; i < Constants.TerrainPatchSize; i++)
762 block[k++] = patchData[j*Constants.TerrainPatchSize + i]*premult - sub;
763 }
764
765 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
766 int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
767
768
769 int maxWbits = prequant + 5;
770 wbits = (prequant >> 1);
771
772 for (int o = 0; o < Constants.TerrainPatchSize; o++)
773 DCTLine16(block, ftemp, o);
774 for (int o = 0; o < Constants.TerrainPatchSize; o++)
775 wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
776
777 return itemp;
778 }
779
780 private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant, out int wbits)
781 {
782 float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
783 float oozrange = 1.0f/header.Range;
784 float range = (1 << prequant);
785 float premult = oozrange*range;
786 float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
787 int wordsize = (prequant - 2) & 0x0f;
788
789 header.QuantWBits = wordsize;
790 header.QuantWBits |= wordsize << 4;
791
792 int k = 0;
793 for (int j = 0; j < Constants.TerrainPatchSize; j++)
794 {
795 for (int i = 0; i < Constants.TerrainPatchSize; i++)
796 block[k++] = patchData[j, i]*premult - sub;
797 }
798
799 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
800 int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
801
802 int maxWbits = prequant + 5;
803 wbits = (prequant >> 1);
804
805 for (int o = 0; o < Constants.TerrainPatchSize; o++)
806 DCTLine16(block, ftemp, o);
807 for (int o = 0; o < Constants.TerrainPatchSize; o++)
808 wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
809
810 return itemp;
811 }
812
813 private static int[] CompressPatch(TerrainData terrData, int patchX, int patchY, TerrainPatch.Header header,
814 int prequant, out int wbits)
815 {
816 float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
817 int wordsize = prequant;
818 float oozrange = 1.0f/header.Range;
819 float range = (1 << prequant);
820 float premult = oozrange*range;
821 float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
822
823 header.QuantWBits = wordsize - 2;
824 header.QuantWBits |= (prequant - 2) << 4;
825
826 int k = 0;
827
828 int yPatchLimit = patchY >= (terrData.SizeY / Constants.TerrainPatchSize) ?
829 (terrData.SizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchY;
830 yPatchLimit = (yPatchLimit + 1) * Constants.TerrainPatchSize;
831
832 int xPatchLimit = patchX >= (terrData.SizeX / Constants.TerrainPatchSize) ?
833 (terrData.SizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchX;
834 xPatchLimit = (xPatchLimit + 1) * Constants.TerrainPatchSize;
835
836 for (int yy = patchY * Constants.TerrainPatchSize; yy < yPatchLimit; yy++)
837 {
838 for (int xx = patchX * Constants.TerrainPatchSize; xx < xPatchLimit; xx++)
839 {
840 block[k++] = terrData[xx, yy] * premult - sub;
841 }
842 }
843
844 float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
845 int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
846
847 int maxWbits = prequant + 5;
848 wbits = (prequant >> 1);
849
850 for (int o = 0; o < Constants.TerrainPatchSize; o++)
851 DCTLine16(block, ftemp, o);
852 for (int o = 0; o < Constants.TerrainPatchSize; o++)
853 wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
854
855 return itemp;
856 }
857
858 #region Initialization
859
860 private static void BuildDequantizeTable16()
861 {
862 for (int j = 0; j < Constants.TerrainPatchSize; j++)
863 {
864 for (int i = 0; i < Constants.TerrainPatchSize; i++)
865 {
866 DequantizeTable16[j*Constants.TerrainPatchSize + i] = 1.0f + 2.0f*(i + j);
867 }
868 }
869 }
870
871 private static void BuildQuantizeTable16()
872 {
873 const float oosob = 2.0f/Constants.TerrainPatchSize;
874 for (int j = 0; j < Constants.TerrainPatchSize; j++)
875 {
876 for (int i = 0; i < Constants.TerrainPatchSize; i++)
877 {
878// QuantizeTable16[j * Constants.TerrainPatchSize + i] = 1.0f / (1.0f + 2.0f * ((float)i + (float)j));
879 QuantizeTable16[j*Constants.TerrainPatchSize + i] = oosob/(1.0f + 2.0f*(i + (float) j));
880 }
881 }
882 }
883
884 private static void SetupCosines16()
885 {
886 const float hposz = (float) Math.PI*0.5f/Constants.TerrainPatchSize;
887
888 for (int u = 0; u < Constants.TerrainPatchSize; u++)
889 {
890 for (int n = 0; n < Constants.TerrainPatchSize; n++)
891 {
892 CosineTable16[u*Constants.TerrainPatchSize + n] = (float) Math.Cos((2.0f*n + 1.0f)*u*hposz);
893 }
894 }
895 }
896
897 private static void BuildCopyMatrix16()
898 {
899 bool diag = false;
900 bool right = true;
901 int i = 0;
902 int j = 0;
903 int count = 0;
904
905 while (i < Constants.TerrainPatchSize && j < Constants.TerrainPatchSize)
906 {
907 CopyMatrix16[j*Constants.TerrainPatchSize + i] = count++;
908
909 if (!diag)
910 {
911 if (right)
912 {
913 if (i < Constants.TerrainPatchSize - 1) i++;
914 else j++;
915
916 right = false;
917 diag = true;
918 }
919 else
920 {
921 if (j < Constants.TerrainPatchSize - 1) j++;
922 else i++;
923
924 right = true;
925 diag = true;
926 }
927 }
928 else
929 {
930 if (right)
931 {
932 i++;
933 j--;
934 if (i == Constants.TerrainPatchSize - 1 || j == 0) diag = false;
935 }
936 else
937 {
938 i--;
939 j++;
940 if (j == Constants.TerrainPatchSize - 1 || i == 0) diag = false;
941 }
942 }
943 }
944 }
945
946 #endregion Initialization
947 }
948}