varregion: plug in TerrainData class and modify TerrainModule and LLClientView to use same. This passes a terrain info class around rather than passing a one dimensional array thus allowing variable regions. Update the database storage for variable region sizes. This should be downward compatible (same format for 256x256 regions).

This commit is contained in:
Robert Adams
2013-10-02 16:59:37 -07:00
parent 25ae59b9eb
commit 7416809077
13 changed files with 281 additions and 310 deletions

View File

@@ -34,11 +34,13 @@ using System.Text;
using System.Threading;
using System.Timers;
using System.Xml;
using log4net;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.Messages.Linden;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Client;
using OpenSim.Framework.Monitoring;
@@ -48,7 +50,6 @@ using OpenSim.Services.Interfaces;
using Timer = System.Timers.Timer;
using AssetLandmark = OpenSim.Framework.AssetLandmark;
using RegionFlags = OpenMetaverse.RegionFlags;
using Nini.Config;
using System.IO;
using PermissionMask = OpenSim.Framework.PermissionMask;
@@ -307,6 +308,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[LLCLIENTVIEW]";
protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
/// <summary>
@@ -447,7 +449,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// ~LLClientView()
// {
// m_log.DebugFormat("[LLCLIENTVIEW]: Destructor called for {0}, circuit code {1}", Name, CircuitCode);
// m_log.DebugFormat("{0} Destructor called for {1}, circuit code {2}", LogHeader, Name, CircuitCode);
// }
/// <summary>
@@ -513,9 +515,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// there is some unidentified connection problem, not where we have issues due to deadlock
if (!IsActive && !force)
{
m_log.DebugFormat(
"[CLIENT]: Not attempting to close inactive client {0} in {1} since force flag is not set",
Name, m_scene.Name);
m_log.DebugFormat( "{0} Not attempting to close inactive client {1} in {2} since force flag is not set",
LogHeader, Name, m_scene.Name);
return;
}
@@ -1162,10 +1163,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="o"></param>
private void DoSendLayerData(object o)
{
float[] map = LLHeightFieldMoronize((float[])o);
float[] map = (float[])o;
try
{
// Send LayerData in typerwriter pattern
//for (int y = 0; y < 16; y++)
//{
// for (int x = 0; x < 16; x++)
@@ -1230,7 +1232,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// }
/// <summary>
/// Sends a specified patch to a client
/// Sends a terrain packet for the point specified.
/// This is a legacy call that has refarbed the terrain into a flat map of floats.
/// We just use the terrain from the region we know about.
/// </summary>
/// <param name="px">Patch coordinate (x) 0..15</param>
/// <param name="py">Patch coordinate (y) 0..15</param>
@@ -1239,10 +1243,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
try
{
int[] patches = new int[] { py * 16 + px };
float[] heightmap = (map.Length == 65536) ? map : LLHeightFieldMoronize(map);
LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
// For unknown reasons, after this point, patch numbers are swapped X for y.
// That means, that for <patchNumX, patchNumY, the array location is computed as map[patchNumY * 16 + patchNumX].
// TODO: someday straighten the below implementation to keep the X row order for patch numbers.
// Since this is passing only one patch, we just swap the patch numbers.
LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(m_scene.Heightmap.GetTerrainData(), px, py);
// When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience.
// To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain.
@@ -1260,14 +1265,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (m_justEditedTerrain)
{
layerpack.Header.Reliable = false;
OutPacket(layerpack,
ThrottleOutPacketType.Unknown );
OutPacket(layerpack, ThrottleOutPacketType.Unknown );
}
else
{
layerpack.Header.Reliable = true;
OutPacket(layerpack,
ThrottleOutPacketType.Land);
OutPacket(layerpack, ThrottleOutPacketType.Land);
}
}
catch (Exception e)
@@ -1276,38 +1279,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
/// <summary>
/// Munges heightfield into the LLUDP backed in restricted heightfield.
/// </summary>
/// <param name="map">float array in the base; Constants.RegionSize</param>
/// <returns>float array in the base 256</returns>
internal float[] LLHeightFieldMoronize(float[] map)
{
if (map.Length == 65536)
return map;
else
{
float[] returnmap = new float[65536];
if (map.Length < 65535)
{
// rebase the vector stride to 256
for (int i = 0; i < Constants.RegionSize; i++)
Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, (int)Constants.RegionSize);
}
else
{
for (int i = 0; i < 256; i++)
Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256);
}
//Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536);
return returnmap;
}
}
/// <summary>
/// Send the wind matrix to the client
/// </summary>
@@ -2780,8 +2751,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (req.AssetInf.Data == null)
{
m_log.ErrorFormat("Cannot send asset {0} ({1}), asset data is null",
req.AssetInf.ID, req.AssetInf.Metadata.ContentType);
m_log.ErrorFormat("{0} Cannot send asset {1} ({2}), asset data is null",
LogHeader, req.AssetInf.ID, req.AssetInf.Metadata.ContentType);
return;
}

View File

@@ -30,10 +30,14 @@ using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Net;
using log4net;
using Nini.Config;
using OpenMetaverse;
using Mono.Addins;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
using OpenSim.Region.CoreModules.World.Terrain.FileLoaders;
@@ -130,8 +134,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (m_scene.Heightmap == null)
{
m_channel = new TerrainChannel(m_InitialTerrain,
m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY, m_scene.RegionInfo.RegionSizeZ);
m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX,
(int)m_scene.RegionInfo.RegionSizeY,
(int)m_scene.RegionInfo.RegionSizeZ);
m_scene.Heightmap = m_channel;
UpdateRevertMap();
}
@@ -707,7 +712,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private void CheckForTerrainUpdates(bool respectEstateSettings)
{
bool shouldTaint = false;
float[] serialised = m_channel.GetFloatsSerialised();
float[] terrData = m_channel.GetFloatsSerialised();
int x;
for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize)
{
@@ -716,16 +721,16 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (m_channel.Tainted(x, y))
{
// if we should respect the estate settings then
// fixup and height deltas that don't respect them
// If we should respect the estate settings then
// fixup and height deltas that don't respect them.
// Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values.
if (respectEstateSettings && LimitChannelChanges(x, y))
{
// this has been vetoed, so update
// what we are going to send to the client
serialised = m_channel.GetFloatsSerialised();
// Terrain heights were modified. Refetch the terrain info.
terrData = m_channel.GetFloatsSerialised();
}
SendToClients(serialised, x, y);
SendToClients(terrData, x, y);
shouldTaint = true;
}
}
@@ -794,13 +799,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
/// <param name="x">The patch corner to send</param>
/// <param name="y">The patch corner to send</param>
private void SendToClients(float[] serialised, int x, int y)
private void SendToClients(float[] heightMap, int x, int y)
{
m_scene.ForEachClient(
delegate(IClientAPI controller)
{ controller.SendLayerData(
x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised);
}
{ controller.SendLayerData( x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); }
);
}

View File

@@ -136,24 +136,4 @@ namespace OpenSim.Region.Framework.Interfaces
void Shutdown();
}
// 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
}
}

View File

@@ -25,6 +25,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using OpenSim.Framework;
namespace OpenSim.Region.Framework.Interfaces
{
public interface ITerrainChannel
@@ -35,18 +37,20 @@ namespace OpenSim.Region.Framework.Interfaces
double this[int x, int y] { get; set; }
// Return the packaged terrain data for passing into lower levels of communication
TerrainData GetTerrainData();
/// <summary>
/// Squash the entire heightmap into a single dimensioned array
/// </summary>
/// <returns></returns>
float[] GetFloatsSerialised();
// Get version of map as a single dimensioned array and each value compressed
// into an int (compressedHeight = (int)(floatHeight * Constants.TerrainCompression);)
// This is done to make the map smaller as it can get pretty larger for variable sized regions.
short[] GetCompressedMap();
double[,] GetDoubles();
// Check if a location has been updated. Clears the taint flag as a side effect.
bool Tainted(int x, int y);
ITerrainChannel MakeCopy();
string SaveToXmlString();
void LoadFromXmlString(string data);

View File

@@ -1905,7 +1905,7 @@ namespace OpenSim.Region.Framework.Scenes
m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
m_log.InfoFormat("[TERRAIN]: No default terrain. Generating a new terrain {0}.", m_InitialTerrain);
Heightmap = new TerrainChannel(m_InitialTerrain, RegionInfo.RegionSizeX, RegionInfo.RegionSizeY, RegionInfo.RegionSizeZ);
Heightmap = new TerrainChannel(m_InitialTerrain, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID);
}

View File

@@ -25,13 +25,18 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Text;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using System;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.Serialization;
using log4net;
namespace OpenSim.Region.Framework.Scenes
{
@@ -40,18 +45,20 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public class TerrainChannel : ITerrainChannel
{
protected bool[,] m_taint;
protected short[] m_map;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[TERRAIN CHANNEL]";
public int Width { get; private set; } // X dimension
protected TerrainData m_terrainData;
public int Width { get { return m_terrainData.SizeX; } } // X dimension
// Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y
public int Height { get; private set; } // Y dimension
public int Altitude { get; private set; } // Y dimension
public int Height { get { return m_terrainData.SizeY; } } // Y dimension
public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension
// Default, not-often-used builder
public TerrainChannel()
{
InitializeStructures(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight, false);
m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
FlatLand();
// PinHeadIsland();
}
@@ -59,27 +66,23 @@ namespace OpenSim.Region.Framework.Scenes
// Create terrain of given size
public TerrainChannel(int pX, int pY)
{
InitializeStructures((uint)pX, (uint)pY, Constants.RegionHeight, true);
m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight);
}
// Create terrain of specified size and initialize with specified terrain.
// TODO: join this with the terrain initializers.
public TerrainChannel(String type, uint pX, uint pY, uint pZ)
public TerrainChannel(String type, int pX, int pY, int pZ)
{
InitializeStructures(pX, pY, pZ, false);
m_terrainData = new HeightmapTerrainData(pX, pY, pZ);
if (type.Equals("flat"))
FlatLand();
else
PinHeadIsland();
}
public TerrainChannel(double[,] pM, uint pH)
public TerrainChannel(double[,] pM, uint pAltitude)
{
InitializeStructures((uint)pM.GetLength(0), (uint)pM.GetLength(1), pH, false);
int idx = 0;
for (int ii = 0; ii < Height; ii++)
for (int jj = 0; jj < Width; jj++)
m_map[idx++] = ToCompressedHeight(pM[ii, jj]);
m_terrainData = new HeightmapTerrainData(pM);
}
#region ITerrainChannel Members
@@ -90,20 +93,23 @@ namespace OpenSim.Region.Framework.Scenes
return this.Copy();
}
// ITerrainChannel.GetCompressedMap()
public short[] GetCompressedMap()
// ITerrainChannel.GetTerrainData()
public TerrainData GetTerrainData()
{
return m_map;
return m_terrainData;
}
// ITerrainChannel.GetFloatsSerialized()
// NOTICE that the one dimensional form is ordered by Y!!
public float[] GetFloatsSerialised()
{
int points = Width * Height;
float[] heights = new float[points];
for (int ii = 0; ii < points; ii++)
heights[ii] = FromCompressedHeight(m_map[ii]);
int idx = 0;
for (int ii = 0; ii < Height; ii++)
for (int jj = 0; jj < Width; jj++)
heights[idx++] = m_terrainData[jj, ii];
return heights;
}
@@ -116,11 +122,11 @@ namespace OpenSim.Region.Framework.Scenes
double[,] heights = new double[w, l];
int idx = 0; // index into serialized array
for (int ii = 0; ii < l; ii++)
for (int ii = 0; ii < w; ii++)
{
for (int jj = 0; jj < w; jj++)
for (int jj = 0; jj < l; jj++)
{
heights[ii, jj] = (double)FromCompressedHeight(m_map[idx]);
heights[ii, jj] = (double)m_terrainData[ii, jj];
idx++;
}
}
@@ -131,31 +137,20 @@ namespace OpenSim.Region.Framework.Scenes
// ITerrainChannel.this[x,y]
public double this[int x, int y]
{
get { return m_map[x * Width + y]; }
get { return (double)m_terrainData[x, y]; }
set
{
// Will "fix" terrain hole problems. Although not fantastically.
if (Double.IsNaN(value) || Double.IsInfinity(value))
return;
int idx = x * Width + y;
if (m_map[idx] != value)
{
m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true;
m_map[idx] = ToCompressedHeight(value);
}
m_terrainData[x, y] = (float)value;
}
}
// ITerrainChannel.Tainted()
public bool Tainted(int x, int y)
{
if (m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize])
{
m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = false;
return true;
}
return false;
return m_terrainData.IsTaintedAt(x, y);
}
// ITerrainChannel.SaveToXmlString()
@@ -188,49 +183,25 @@ namespace OpenSim.Region.Framework.Scenes
#endregion
private void InitializeStructures(uint pX, uint pY, uint pZ, bool shouldInitializeHeightmap)
{
Width = (int)pX;
Height = (int)pY;
Altitude = (int)pZ;
m_map = new short[Width * Height];
m_taint = new bool[Width / Constants.TerrainPatchSize, Height / Constants.TerrainPatchSize];
ClearTaint();
if (shouldInitializeHeightmap)
{
FlatLand();
}
}
public void ClearTaint()
{
for (int ii = 0; ii < Width / Constants.TerrainPatchSize; ii++)
for (int jj = 0; jj < Height / Constants.TerrainPatchSize; jj++)
m_taint[ii, jj] = false;
}
/*
// To save space (especially for large regions), keep the height as a short integer
// that is coded as the float height times the compression factor (usually '100'
// to make for two decimal points).
public short ToCompressedHeight(double pHeight)
public static short ToCompressedHeight(double pHeight)
{
return (short)(pHeight * Constants.TerrainCompression);
}
public float FromCompressedHeight(short pHeight)
public static float FromCompressedHeight(short pHeight)
{
return ((float)pHeight) / Constants.TerrainCompression;
}
*/
public TerrainChannel Copy()
{
TerrainChannel copy = new TerrainChannel();
copy.m_map = (short[])m_map.Clone();
copy.m_taint = (bool[,])m_taint.Clone();
copy.Width = Width;
copy.Height = Height;
copy.Altitude = Altitude;
copy.m_terrainData = m_terrainData.Clone();
return copy;
}
@@ -289,6 +260,8 @@ namespace OpenSim.Region.Framework.Scenes
byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
int index = 0;
m_terrainData = new HeightmapTerrainData(Width, Height, Altitude);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
@@ -321,7 +294,7 @@ namespace OpenSim.Region.Framework.Scenes
// New terrain serialization format that includes the width and length.
private void ToXml2(XmlWriter xmlWriter)
{
TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_map);
TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.GetCompressedMap());
XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
serializer.Serialize(xmlWriter, package);
}
@@ -331,38 +304,32 @@ namespace OpenSim.Region.Framework.Scenes
{
XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
Width = package.SizeX;
Height = package.SizeY;
Altitude = package.SizeZ;
m_map = package.Map;
m_terrainData = new HeightmapTerrainData(package.Map, package.SizeX, package.SizeY, package.SizeZ);
}
// Fill the heightmap with the center bump terrain
private void PinHeadIsland()
{
int x;
for (x = 0; x < Width; x++)
for (int x = 0; x < Width; x++)
{
int y;
for (y = 0; y < Height; y++)
for (int y = 0; y < Height; y++)
{
int idx = x * (int)Width + y;
m_map[idx] = ToCompressedHeight(TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10);
short spherFacA = ToCompressedHeight(TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01);
short spherFacB = ToCompressedHeight(TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001);
if (m_map[idx] < spherFacA)
m_map[idx] = spherFacA;
if (m_map[idx] < spherFacB)
m_map[idx] = spherFacB;
m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d);
float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d);
if (m_terrainData[x, y]< spherFacA)
m_terrainData[x, y]= spherFacA;
if (m_terrainData[x, y]< spherFacB)
m_terrainData[x, y] = spherFacB;
}
}
}
private void FlatLand()
{
short flatHeight = ToCompressedHeight(21);
for (int ii = 0; ii < m_map.Length; ii++)
m_map[ii] = flatHeight;
for (int xx = 0; xx < Width; xx++)
for (int yy = 0; yy < Height; yy++)
m_terrainData[xx, yy] = 21;
}
}
}

View File

@@ -113,22 +113,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// 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)
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY)
{
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);
LogHeader, patchX, patchY, terrData.SizeX, terrData.SizeY);
return CreateLandPacket(newmap, xPieces, yPieces, (int)TerrainPatch.LayerType.Land, sizeX, sizeY);
return CreateLandPacket(terrData, xPieces, yPieces, (int)TerrainPatch.LayerType.Land);
}
/// <summary>
@@ -153,8 +148,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <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)
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] x, int[] y, byte type)
{
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
@@ -168,7 +162,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
bitpack.PackBits(type, 8);
for (int i = 0; i < x.Length; i++)
CreatePatchFromHeightmap(bitpack, heightmap, x[i], y[i], pRegionSizeX, pRegionSizeY);
CreatePatchFromHeightmap(bitpack, terrData, x[i], y[i]);
bitpack.PackBits(END_OF_PATCHES, 8);
@@ -217,14 +211,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </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)
public static void CreatePatchFromHeightmap(BitPack output, TerrainData terrData, int patchX, int patchY)
{
TerrainPatch.Header header = PrescanPatch(heightmap, patchX, patchY, pRegionSizeX, pRegionSizeY);
TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY);
header.QuantWBits = 136;
// If larger than legacy region size, pack patch X and Y info differently.
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
{
header.PatchIDs = (patchY & 0xFFFF);
header.PatchIDs += (patchX << 16);
@@ -237,8 +230,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// 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);
int[] patch = CompressPatch(terrData, patchX, patchY, header, 10, out wbits);
wbits = EncodePatchHeader(output, header, patch, (uint)terrData.SizeX, (uint)terrData.SizeY, wbits);
EncodePatch(output, patch, 0, wbits);
}
@@ -262,19 +255,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
// 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)
private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY)
{
TerrainPatch.Header header = new TerrainPatch.Header();
short zmax = -32767;
short zmin = 32767;
float zmax = -99999999.0f;
float zmin = 99999999.0f;
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];
// short val = heightmap[j*pRegionSizeX + i];
float val = terrData[j, i];
if (val > zmax) zmax = val;
if (val < zmin) zmin = val;
}
@@ -282,8 +274,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// 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);
header.DCOffset = zmin;
header.Range = (int)(zmax - zmin + 1.0f);
return header;
}
@@ -812,8 +804,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return itemp;
}
private static int[] CompressPatch(short[] heightmap, int patchX, int patchY, TerrainPatch.Header header,
int prequant, uint pRegionSizeX, uint pRegionSizeY, out int wbits)
private static int[] CompressPatch(TerrainData terrData, int patchX, int patchY, TerrainPatch.Header header,
int prequant, out int wbits)
{
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int wordsize = prequant;
@@ -827,19 +819,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
int k = 0;
premult /= Constants.TerrainCompression; // put here short to float factor
int jPatchLimit = patchY;
if (patchY >= (pRegionSizeY / Constants.TerrainPatchSize))
if (patchY >= (terrData.SizeY / Constants.TerrainPatchSize))
{
jPatchLimit = (int)(pRegionSizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
jPatchLimit = (int)(terrData.SizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
}
jPatchLimit = (jPatchLimit + 1) * Constants.TerrainPatchSize;
int iPatchLimit = patchX;
if (patchX >= (pRegionSizeX / Constants.TerrainPatchSize))
if (patchX >= (terrData.SizeX / Constants.TerrainPatchSize))
{
iPatchLimit = (int)(pRegionSizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
iPatchLimit = (int)(terrData.SizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize;
}
iPatchLimit = (iPatchLimit + 1) * Constants.TerrainPatchSize;
@@ -847,7 +837,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
for (int i = patchX * Constants.TerrainPatchSize; i < iPatchLimit; i++)
{
block[k++] = (heightmap[j*pRegionSizeX + i])*premult - sub;
// block[k++] = (heightmap[j*pRegionSizeX + i])*premult - sub;
block[k++] = terrData[j, i] - sub;
}
}