Merge branch 'mbworkvar2' into ubitvar

This commit is contained in:
UbitUmarov
2015-08-19 08:48:50 +01:00
76 changed files with 3561 additions and 1898 deletions

View File

@@ -2221,14 +2221,9 @@ namespace OpenSim.Region.Framework.Scenes
itemID = UUID.Zero;
if (grp != null)
{
Vector3 inventoryStoredPosition = new Vector3
(((grp.AbsolutePosition.X > (int)Constants.RegionSize)
? 250
: grp.AbsolutePosition.X)
,
(grp.AbsolutePosition.X > (int)Constants.RegionSize)
? 250
: grp.AbsolutePosition.X,
Vector3 inventoryStoredPosition = new Vector3(
Math.Min(grp.AbsolutePosition.X, RegionInfo.RegionSizeX - 6),
Math.Min(grp.AbsolutePosition.Y, RegionInfo.RegionSizeY - 6),
grp.AbsolutePosition.Z);
Vector3 originalPosition = grp.AbsolutePosition;

View File

@@ -6,7 +6,7 @@
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
@@ -103,7 +103,29 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// If false then physical objects are disabled, though collisions will continue as normal.
/// </summary>
public bool PhysicsEnabled { get; set; }
public bool PhysicsEnabled
{
get
{
return m_physicsEnabled;
}
set
{
m_physicsEnabled = value;
if (PhysicsScene != null)
{
IPhysicsParameters physScene = PhysicsScene as IPhysicsParameters;
if (physScene != null)
physScene.SetPhysicsParameter(
"Active", m_physicsEnabled.ToString(), PhysParameterEntry.APPLY_TO_NONE);
}
}
}
private bool m_physicsEnabled;
/// <summary>
/// If false then scripts are not enabled on the smiulator
@@ -160,11 +182,6 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public SimStatsReporter StatsReporter { get; private set; }
public List<Border> NorthBorders = new List<Border>();
public List<Border> EastBorders = new List<Border>();
public List<Border> SouthBorders = new List<Border>();
public List<Border> WestBorders = new List<Border>();
/// <summary>
/// Controls whether physics can be applied to prims. Even if false, prims still have entries in a
/// PhysicsScene in order to perform collision detection
@@ -204,15 +221,16 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public int m_linksetCapacity = 0;
public bool m_clampPrimSize;
public bool m_trustBinaries;
public bool m_allowScriptCrossings = true;
/// <summary>
/// Max prims an Physical object will hold
/// </summary>
///
public int m_linksetPhysCapacity = 0;
public bool m_clampPrimSize;
public bool m_trustBinaries;
public bool m_allowScriptCrossings;
public bool m_useFlySlow;
public bool m_useTrashOnDelete = true;
@@ -364,7 +382,6 @@ namespace OpenSim.Region.Framework.Scenes
// TODO: Possibly stop other classes being able to manipulate this directly.
private SceneGraph m_sceneGraph;
private volatile int m_bordersLocked;
private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing
private volatile bool m_backingup;
private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>();
@@ -446,18 +463,6 @@ namespace OpenSim.Region.Framework.Scenes
set { m_splitRegionID = value; }
}
public bool BordersLocked
{
get { return m_bordersLocked == 1; }
set
{
if (value == true)
m_bordersLocked = 1;
else
m_bordersLocked = 0;
}
}
public new float TimeDilation
{
get { return m_sceneGraph.PhysicsScene.TimeDilation; }
@@ -1075,28 +1080,6 @@ namespace OpenSim.Region.Framework.Scenes
PeriodicBackup = true;
UseBackup = true;
BordersLocked = true;
Border northBorder = new Border();
northBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, RegionInfo.RegionSizeY); //<---
northBorder.CrossDirection = Cardinals.N;
NorthBorders.Add(northBorder);
Border southBorder = new Border();
southBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue,0); //--->
southBorder.CrossDirection = Cardinals.S;
SouthBorders.Add(southBorder);
Border eastBorder = new Border();
eastBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, RegionInfo.RegionSizeX); //<---
eastBorder.CrossDirection = Cardinals.E;
EastBorders.Add(eastBorder);
Border westBorder = new Border();
westBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue,0); //--->
westBorder.CrossDirection = Cardinals.W;
WestBorders.Add(westBorder);
BordersLocked = false;
m_eventManager = new EventManager();
m_permissions = new ScenePermissions(this);
@@ -1975,7 +1958,7 @@ namespace OpenSim.Region.Framework.Scenes
{
try
{
double[,] map = SimulationDataService.LoadTerrain(RegionInfo.RegionID);
TerrainData map = SimulationDataService.LoadTerrain(RegionInfo.RegionID, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
if (map == null)
{
// This should be in the Terrain module, but it isn't because
@@ -1986,7 +1969,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);
Heightmap = new TerrainChannel(m_InitialTerrain, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID);
}
@@ -2611,185 +2594,35 @@ namespace OpenSim.Region.Framework.Scenes
EntityTransferModule.Cross(grp, attemptedPosition, silent);
}
public Border GetCrossedBorder(Vector3 position, Cardinals gridline)
// Simple test to see if a position is in the current region.
// This test is mostly used to see if a region crossing is necessary.
// Assuming the position is relative to the region so anything outside its bounds.
// Return 'true' if position inside region.
public bool PositionIsInCurrentRegion(Vector3 pos)
{
if (BordersLocked)
bool ret = false;
int xx = (int)Math.Floor(pos.X);
int yy = (int)Math.Floor(pos.Y);
if (xx < 0 || yy < 0)
return false;
IRegionCombinerModule regionCombinerModule = RequestModuleInterface<IRegionCombinerModule>();
if (regionCombinerModule == null)
{
switch (gridline)
{
case Cardinals.N:
lock (NorthBorders)
{
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
case Cardinals.S:
lock (SouthBorders)
{
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
case Cardinals.E:
lock (EastBorders)
{
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
case Cardinals.W:
lock (WestBorders)
{
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
}
// Regular region. Just check for region size
if (xx < RegionInfo.RegionSizeX && yy < RegionInfo.RegionSizeY )
ret = true;
}
else
{
switch (gridline)
{
case Cardinals.N:
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return b;
}
break;
case Cardinals.S:
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return b;
}
break;
case Cardinals.E:
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return b;
}
break;
case Cardinals.W:
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return b;
}
break;
}
// We're in a mega-region so see if we are still in that larger region
ret = regionCombinerModule.PositionIsInMegaregion(this.RegionInfo.RegionID, xx, yy);
}
return null;
return ret;
}
public bool TestBorderCross(Vector3 position, Cardinals border)
{
if (BordersLocked)
{
switch (border)
{
case Cardinals.N:
lock (NorthBorders)
{
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
case Cardinals.E:
lock (EastBorders)
{
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
case Cardinals.S:
lock (SouthBorders)
{
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
case Cardinals.W:
lock (WestBorders)
{
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
}
}
else
{
switch (border)
{
case Cardinals.N:
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return true;
}
break;
case Cardinals.E:
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return true;
}
break;
case Cardinals.S:
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return true;
}
break;
case Cardinals.W:
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return true;
}
break;
}
}
return false;
}
/// <summary>
/// Called when objects or attachments cross the border, or teleport, between regions.
/// </summary>
@@ -4116,60 +3949,11 @@ namespace OpenSim.Region.Framework.Scenes
{
// CleanDroppedAttachments();
if (TestBorderCross(acd.startpos, Cardinals.E))
{
Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.E);
acd.startpos.X = crossedBorder.BorderLine.Z - 1;
}
if (TestBorderCross(acd.startpos, Cardinals.N))
{
Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.N);
acd.startpos.Y = crossedBorder.BorderLine.Z - 1;
}
//Mitigate http://opensimulator.org/mantis/view.php?id=3522
// Check if start position is outside of region
// If it is, check the Z start position also.. if not, leave it alone.
if (BordersLocked)
{
lock (EastBorders)
{
if (acd.startpos.X > EastBorders[0].BorderLine.Z)
{
m_log.Warn("FIX AGENT POSITION");
acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
}
lock (NorthBorders)
{
if (acd.startpos.Y > NorthBorders[0].BorderLine.Z)
{
m_log.Warn("FIX Agent POSITION");
acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
}
} else
{
if (acd.startpos.X > EastBorders[0].BorderLine.Z)
{
m_log.Warn("FIX AGENT POSITION");
acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
if (acd.startpos.Y > NorthBorders[0].BorderLine.Z)
{
m_log.Warn("FIX Agent POSITION");
acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
}
// Make sure avatar position is in the region (why it wouldn't be is a mystery but do sanity checking)
if (acd.startpos.X < 0) acd.startpos.X = 1f;
if (acd.startpos.X >= RegionInfo.RegionSizeX) acd.startpos.X = RegionInfo.RegionSizeX - 1f;
if (acd.startpos.Y < 0) acd.startpos.Y = 1f;
if (acd.startpos.Y >= RegionInfo.RegionSizeY) acd.startpos.Y = RegionInfo.RegionSizeY - 1f;
// m_log.DebugFormat(
// "[SCENE]: Found telehub object {0} for new user connection {1} to {2}",
@@ -4883,44 +4667,6 @@ namespace OpenSim.Region.Framework.Scenes
ScenePresence sp = GetScenePresence(remoteClient.AgentId);
if (sp != null)
{
uint regionX = RegionInfo.RegionLocX;
uint regionY = RegionInfo.RegionLocY;
Utils.LongToUInts(regionHandle, out regionX, out regionY);
int shiftx = (int) regionX - (int) RegionInfo.RegionLocX * (int)Constants.RegionSize;
int shifty = (int) regionY - (int) RegionInfo.RegionLocY * (int)Constants.RegionSize;
position.X += shiftx;
position.Y += shifty;
bool result = false;
if (TestBorderCross(position,Cardinals.N))
result = true;
if (TestBorderCross(position, Cardinals.S))
result = true;
if (TestBorderCross(position, Cardinals.E))
result = true;
if (TestBorderCross(position, Cardinals.W))
result = true;
// bordercross if position is outside of region
if (!result)
{
regionHandle = RegionInfo.RegionHandle;
}
else
{
// not in this region, undo the shift!
position.X -= shiftx;
position.Y -= shifty;
}
if (EntityTransferModule != null)
{
EntityTransferModule.Teleport(sp, regionHandle, position, lookAt, teleportFlags);

View File

@@ -52,6 +52,7 @@ namespace OpenSim.Region.Framework.Scenes
public class SceneCommunicationService //one instance per region
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[SCENE COMMUNICATION SERVICE]";
protected RegionInfo m_regionInfo;
protected Scene m_scene;
@@ -100,7 +101,7 @@ namespace OpenSim.Region.Framework.Scenes
{
m_log.WarnFormat(
"[SCENE COMMUNICATION SERVICE]: Region {0} failed to inform neighbour at {1}-{2} that it is up.",
m_scene.Name, x / Constants.RegionSize, y / Constants.RegionSize);
m_scene.Name, Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y));
}
}
@@ -166,7 +167,7 @@ namespace OpenSim.Region.Framework.Scenes
// we only want to send one update to each simulator; the simulator will
// hand it off to the regions where a child agent exists, this does assume
// that the region position is cached or performance will degrade
Utils.LongToUInts(regionHandle, out x, out y);
Util.RegionHandleToWorldLoc(regionHandle, out x, out y);
GridRegion dest = m_scene.GridService.GetRegionByPosition(UUID.Zero, (int)x, (int)y);
if (dest == null)
continue;
@@ -206,7 +207,7 @@ namespace OpenSim.Region.Framework.Scenes
//m_commsProvider.InterRegion.TellRegionToCloseChildConnection(regionHandle, agentID);
uint x = 0, y = 0;
Utils.LongToUInts(regionHandle, out x, out y);
Util.RegionHandleToWorldLoc(regionHandle, out x, out y);
GridRegion destination = m_scene.GridService.GetRegionByPosition(m_regionInfo.ScopeID, (int)x, (int)y);
@@ -226,6 +227,8 @@ namespace OpenSim.Region.Framework.Scenes
{
foreach (ulong handle in regionslst)
{
// We must take a copy here since handle acts like a reference when used in an iterator.
// This leads to race conditions if directly passed to SendCloseChildAgent with more than one neighbour region.
ulong handleCopy = handle;
Util.FireAndForget((o) => { SendCloseChildAgent(agentID, handleCopy, auth_code); });
}

View File

@@ -412,7 +412,7 @@ namespace OpenSim.Region.Framework.Scenes
{
get
{
Vector3 minScale = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
Vector3 minScale = new Vector3(Constants.MaximumRegionSize, Constants.MaximumRegionSize, Constants.MaximumRegionSize);
Vector3 maxScale = Vector3.Zero;
Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);
@@ -529,12 +529,10 @@ namespace OpenSim.Region.Framework.Scenes
set
{
Vector3 val = value;
if (Scene != null && !IsAttachmentCheckFull()
&& !Scene.LoadingPrims &&
(Scene.TestBorderCross(val, Cardinals.E) ||
Scene.TestBorderCross(val, Cardinals.W) ||
Scene.TestBorderCross(val, Cardinals.N) ||
Scene.TestBorderCross(val, Cardinals.S))
if (Scene != null
&& Scene.PositionIsInCurrentRegion(val)
&& !IsAttachmentCheckFull()
&& !Scene.LoadingPrims
)
{
if (!inTransit)

View File

@@ -2979,10 +2979,7 @@ namespace OpenSim.Region.Framework.Scenes
{
Vector3 newpos = new Vector3(pa.Position.GetBytes(), 0);
if (ParentGroup.Scene.TestBorderCross(newpos, Cardinals.N)
|| ParentGroup.Scene.TestBorderCross(newpos, Cardinals.S)
|| ParentGroup.Scene.TestBorderCross(newpos, Cardinals.E)
|| ParentGroup.Scene.TestBorderCross(newpos, Cardinals.W))
if (!ParentGroup.Scene.PositionIsInCurrentRegion(newpos))
{
ParentGroup.AbsolutePosition = newpos;
return;

View File

@@ -842,9 +842,8 @@ namespace OpenSim.Region.Framework.Scenes
foreach (ulong handle in seeds.Keys)
{
uint x, y;
Utils.LongToUInts(handle, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(handle, out x, out y);
if (Util.IsOutsideView(DrawDistance, x, Scene.RegionInfo.RegionLocX, y, Scene.RegionInfo.RegionLocY))
{
old.Add(handle);
@@ -866,9 +865,7 @@ namespace OpenSim.Region.Framework.Scenes
foreach (KeyValuePair<ulong, string> kvp in KnownRegions)
{
uint x, y;
Utils.LongToUInts(kvp.Key, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(kvp.Key, out x, out y);
m_log.Info(" >> "+x+", "+y+": "+kvp.Value);
}
}
@@ -1170,18 +1167,6 @@ namespace OpenSim.Region.Framework.Scenes
if (ParentID == 0)
{
if (m_scene.TestBorderCross(pos, Cardinals.E))
{
Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.E);
pos.X = crossedBorder.BorderLine.Z - 1;
}
if (m_scene.TestBorderCross(pos, Cardinals.N))
{
Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.N);
pos.Y = crossedBorder.BorderLine.Z - 1;
}
CheckAndAdjustLandingPoint(ref pos);
if (pos.X < 0f || pos.Y < 0f || pos.Z < 0f)
@@ -1201,7 +1186,7 @@ namespace OpenSim.Region.Framework.Scenes
float posZLimit = 0;
if (pos.X < Constants.RegionSize && pos.Y < Constants.RegionSize)
if (pos.X < m_scene.RegionInfo.RegionSizeX && pos.Y < m_scene.RegionInfo.RegionSizeY)
posZLimit = (float)m_scene.Heightmap[(int)pos.X, (int)pos.Y];
float newPosZ = posZLimit + localAVHeight / 2;
@@ -2612,7 +2597,7 @@ namespace OpenSim.Region.Framework.Scenes
if (regionCombinerModule != null)
regionSize = regionCombinerModule.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
else
regionSize = new Vector2(Constants.RegionSize);
regionSize = new Vector2(m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
if (pos.X < 0 || pos.X >= regionSize.X
|| pos.Y < 0 || pos.Y >= regionSize.Y
@@ -2630,8 +2615,8 @@ namespace OpenSim.Region.Framework.Scenes
// }
// Get terrain height for sub-region in a megaregion if necessary
int X = (int)((m_scene.RegionInfo.RegionLocX * Constants.RegionSize) + pos.X);
int Y = (int)((m_scene.RegionInfo.RegionLocY * Constants.RegionSize) + pos.Y);
int X = (int)((m_scene.RegionInfo.WorldLocX) + pos.X);
int Y = (int)((m_scene.RegionInfo.WorldLocY) + pos.Y);
GridRegion target_region = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y);
// If X and Y is NaN, target_region will be null
if (target_region == null)
@@ -2642,7 +2627,7 @@ namespace OpenSim.Region.Framework.Scenes
if (!SceneManager.Instance.TryGetScene(target_regionID, out targetScene))
targetScene = m_scene;
float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % Constants.RegionSize), (int)(pos.Y % Constants.RegionSize)];
float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % regionSize.X), (int)(pos.Y % regionSize.Y)];
// dont try to land underground
terrainHeight += Appearance.AvatarHeight / 2;
pos.Z = Math.Max(terrainHeight, pos.Z);
@@ -3872,32 +3857,28 @@ namespace OpenSim.Region.Framework.Scenes
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Testing border check for projected position {0} of {1} in {2}",
// pos2, Name, Scene.Name);
if( Scene.TestBorderCross(pos2, Cardinals.E) ||
Scene.TestBorderCross(pos2, Cardinals.W) ||
Scene.TestBorderCross(pos2, Cardinals.N) ||
Scene.TestBorderCross(pos2, Cardinals.S)
)
if (Scene.PositionIsInCurrentRegion(pos2))
return;
if (!CrossToNewRegion() && m_requestedSitTargetUUID == UUID.Zero)
{
if (!CrossToNewRegion() && m_requestedSitTargetUUID == UUID.Zero)
{
// we don't have entity transfer module
Vector3 pos = AbsolutePosition;
float px = pos.X;
if (px < 0)
pos.X += Velocity.X * 2;
else if (px > m_scene.RegionInfo.RegionSizeX)
pos.X -= Velocity.X * 2;
// we don't have entity transfer module
Vector3 pos = AbsolutePosition;
float px = pos.X;
if (px < 0)
pos.X += Velocity.X * 2;
else if (px > m_scene.RegionInfo.RegionSizeX)
pos.X -= Velocity.X * 2;
float py = pos.Y;
if (py < 0)
pos.Y += Velocity.Y * 2;
else if (py > m_scene.RegionInfo.RegionSizeY)
pos.Y -= Velocity.Y * 2;
float py = pos.Y;
if (py < 0)
pos.Y += Velocity.Y * 2;
else if (py > m_scene.RegionInfo.RegionSizeY)
pos.Y -= Velocity.Y * 2;
Velocity = Vector3.Zero;
AbsolutePosition = pos;
}
Velocity = Vector3.Zero;
AbsolutePosition = pos;
}
}
@@ -3962,7 +3943,7 @@ namespace OpenSim.Region.Framework.Scenes
// Put the child agent back at the center
AbsolutePosition
= new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f), 70);
= new Vector3(((float)m_scene.RegionInfo.RegionSizeX * 0.5f), ((float)m_scene.RegionInfo.RegionSizeY * 0.5f), 70);
Animator.ResetAnimations();
}
@@ -3989,9 +3970,7 @@ namespace OpenSim.Region.Framework.Scenes
if (handle != Scene.RegionInfo.RegionHandle)
{
uint x, y;
Utils.LongToUInts(handle, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(handle, out x, out y);
// m_log.Debug("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX)));
// m_log.Debug("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY)));

View File

@@ -25,13 +25,20 @@
* 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 OpenMetaverse;
using log4net;
namespace OpenSim.Region.Framework.Scenes
{
@@ -40,140 +47,136 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public class TerrainChannel : ITerrainChannel
{
private readonly bool[,] taint;
private double[,] map;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[TERRAIN CHANNEL]";
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 { return m_terrainData.SizeY; } } // Y dimension
public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension
// Default, not-often-used builder
public TerrainChannel()
{
map = new double[Constants.RegionSize, Constants.RegionSize];
taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16];
PinHeadIsland();
m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
FlatLand();
// PinHeadIsland();
}
public TerrainChannel(String type)
// Create terrain of given size
public TerrainChannel(int pX, int pY)
{
map = new double[Constants.RegionSize, Constants.RegionSize];
taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16];
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, int pX, int pY, int pZ)
{
m_terrainData = new HeightmapTerrainData(pX, pY, pZ);
if (type.Equals("flat"))
FlatLand();
else
PinHeadIsland();
}
public TerrainChannel(double[,] import)
// Create channel passed a heightmap and expected dimensions of the region.
// The heightmap might not fit the passed size so accomodations must be made.
public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude)
{
map = import;
taint = new bool[import.GetLength(0),import.GetLength(1)];
int hmSizeX = pM.GetLength(0);
int hmSizeY = pM.GetLength(1);
m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude);
for (int xx = 0; xx < pSizeX; xx++)
for (int yy = 0; yy < pSizeY; yy++)
if (xx > hmSizeX || yy > hmSizeY)
m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight;
else
m_terrainData[xx, yy] = (float)pM[xx, yy];
}
public TerrainChannel(bool createMap)
public TerrainChannel(TerrainData pTerrData)
{
if (createMap)
{
map = new double[Constants.RegionSize,Constants.RegionSize];
taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16];
}
}
public TerrainChannel(int w, int h)
{
map = new double[w,h];
taint = new bool[w / 16,h / 16];
m_terrainData = pTerrData;
}
#region ITerrainChannel Members
public int Width
{
get { return map.GetLength(0); }
}
public int Height
{
get { return map.GetLength(1); }
}
// ITerrainChannel.MakeCopy()
public ITerrainChannel MakeCopy()
{
TerrainChannel copy = new TerrainChannel(false);
copy.map = (double[,]) map.Clone();
return copy;
return this.Copy();
}
// ITerrainChannel.GetTerrainData()
public TerrainData GetTerrainData()
{
return m_terrainData;
}
// ITerrainChannel.GetFloatsSerialized()
// This one dimensional version is ordered so height = map[y*sizeX+x];
// DEPRECATED: don't use this function as it does not retain the dimensions of the terrain
// and the caller will probably do the wrong thing if the terrain is not the legacy 256x256.
public float[] GetFloatsSerialised()
{
// Move the member variables into local variables, calling
// member variables 256*256 times gets expensive
int w = Width;
int h = Height;
float[] heights = new float[w * h];
return m_terrainData.GetFloatsSerialized();
}
// ITerrainChannel.GetDoubles()
public double[,] GetDoubles()
{
double[,] heights = new double[Width, Height];
int i, j; // map coordinates
int idx = 0; // index into serialized array
for (i = 0; i < h; i++)
for (int ii = 0; ii < Width; ii++)
{
for (j = 0; j < w; j++)
for (int jj = 0; jj < Height; jj++)
{
heights[idx++] = (float)map[j, i];
heights[ii, jj] = (double)m_terrainData[ii, jj];
idx++;
}
}
return heights;
}
public double[,] GetDoubles()
{
return map;
}
// ITerrainChannel.this[x,y]
public double this[int x, int y]
{
get
{
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x >= (int)Constants.RegionSize) x = (int)Constants.RegionSize - 1;
if (y >= (int)Constants.RegionSize) y = (int)Constants.RegionSize - 1;
return map[x, y];
get {
if (x < 0 || x >= Width || y < 0 || y >= Height)
return 0;
return (double)m_terrainData[x, y];
}
set
{
// Will "fix" terrain hole problems. Although not fantastically.
if (Double.IsNaN(value) || Double.IsInfinity(value))
return;
if (map[x, y] != value)
{
taint[x / 16, y / 16] = true;
map[x, y] = value;
}
m_terrainData[x, y] = (float)value;
}
}
// ITerrainChannel.GetHieghtAtXYZ(x, y, z)
public float GetHeightAtXYZ(float x, float y, float z)
{
if (x < 0 || x >= Width || y < 0 || y >= Height)
return 0;
return m_terrainData[(int)x, (int)y];
}
// ITerrainChannel.Tainted()
public bool Tainted(int x, int y)
{
if (taint[x / 16, y / 16])
{
taint[x / 16, y / 16] = false;
return true;
}
return false;
}
#endregion
public TerrainChannel Copy()
{
TerrainChannel copy = new TerrainChannel(false);
copy.map = (double[,]) map.Clone();
return copy;
return m_terrainData.IsTaintedAt(x, y);
}
// ITerrainChannel.SaveToXmlString()
public string SaveToXmlString()
{
XmlWriterSettings settings = new XmlWriterSettings();
@@ -189,13 +192,7 @@ namespace OpenSim.Region.Framework.Scenes
}
}
private void WriteXml(XmlWriter writer)
{
writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
ToXml(writer);
writer.WriteEndElement();
}
// ITerrainChannel.LoadFromXmlString()
public void LoadFromXmlString(string data)
{
StringReader sr = new StringReader(data);
@@ -207,12 +204,124 @@ namespace OpenSim.Region.Framework.Scenes
sr.Close();
}
private void ReadXml(XmlReader reader)
// ITerrainChannel.Merge
public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement)
{
reader.ReadStartElement("TerrainMap");
FromXml(reader);
m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader,
newTerrain.Width, newTerrain.Height,
displacement, radianRotation, rotationDisplacement,
m_terrainData.SizeX, m_terrainData.SizeY);
for (int xx = 0; xx < newTerrain.Width; xx++)
{
for (int yy = 0; yy < newTerrain.Height; yy++)
{
int dispX = (int)displacement.X;
int dispY = (int)displacement.Y;
float newHeight = (float)newTerrain[xx, yy] + displacement.Z;
if (radianRotation == 0)
{
// If no rotation, place the new height in the specified location
dispX += xx;
dispY += yy;
if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
{
m_terrainData[dispX, dispY] = newHeight;
}
}
else
{
// If rotating, we have to smooth the result because the conversion
// to ints will mean heightmap entries will not get changed
// First compute the rotation location for the new height.
dispX += (int)(rotationDisplacement.X
+ ((float)xx - rotationDisplacement.X) * Math.Cos(radianRotation)
- ((float)yy - rotationDisplacement.Y) * Math.Sin(radianRotation) );
dispY += (int)(rotationDisplacement.Y
+ ((float)xx - rotationDisplacement.X) * Math.Sin(radianRotation)
+ ((float)yy - rotationDisplacement.Y) * Math.Cos(radianRotation) );
if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
{
float oldHeight = m_terrainData[dispX, dispY];
// Smooth the heights around this location if the old height is far from this one
for (int sxx = dispX - 2; sxx < dispX + 2; sxx++)
{
for (int syy = dispY - 2; syy < dispY + 2; syy++)
{
if (sxx >= 0 && sxx < m_terrainData.SizeX && syy >= 0 && syy < m_terrainData.SizeY)
{
if (sxx == dispX && syy == dispY)
{
// Set height for the exact rotated point
m_terrainData[dispX, dispY] = newHeight;
}
else
{
if (Math.Abs(m_terrainData[sxx, syy] - newHeight) > 1f)
{
// If the adjacent height is far off, force it to this height
m_terrainData[sxx, syy] = newHeight;
}
}
}
}
}
}
if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
{
m_terrainData[dispX, dispY] = (float)newTerrain[xx, yy];
}
}
}
}
}
#endregion
public TerrainChannel Copy()
{
TerrainChannel copy = new TerrainChannel();
copy.m_terrainData = m_terrainData.Clone();
return copy;
}
private void WriteXml(XmlWriter writer)
{
if (Width == Constants.RegionSize && Height == Constants.RegionSize)
{
// Downward compatibility for legacy region terrain maps.
// If region is exactly legacy size, return the old format XML.
writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
ToXml(writer);
writer.WriteEndElement();
}
else
{
// New format XML that includes width and length.
writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty);
ToXml2(writer);
writer.WriteEndElement();
}
}
private void ReadXml(XmlReader reader)
{
// Check the first element. If legacy element, use the legacy reader.
if (reader.IsStartElement("TerrainMap"))
{
reader.ReadStartElement("TerrainMap");
FromXml(reader);
}
else
{
reader.ReadStartElement("TerrainMap2");
FromXml2(reader);
}
}
// Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
private void ToXml(XmlWriter xmlWriter)
{
float[] mapData = GetFloatsSerialised();
@@ -226,12 +335,15 @@ namespace OpenSim.Region.Framework.Scenes
serializer.Serialize(xmlWriter, buffer);
}
// Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
private void FromXml(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
int index = 0;
m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
@@ -244,35 +356,63 @@ namespace OpenSim.Region.Framework.Scenes
}
}
private class TerrainChannelXMLPackage
{
public int Version;
public int SizeX;
public int SizeY;
public int SizeZ;
public float CompressionFactor;
public int[] Map;
public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, int[] pMap)
{
Version = 1;
SizeX = pX;
SizeY = pY;
SizeZ = pZ;
CompressionFactor = pCompressionFactor;
Map = pMap;
}
}
// New terrain serialization format that includes the width and length.
private void ToXml2(XmlWriter xmlWriter)
{
TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor,
m_terrainData.GetCompressedMap());
XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
serializer.Serialize(xmlWriter, package);
}
// New terrain serialization format that includes the width and length.
private void FromXml2(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ);
}
// Fill the heightmap with the center bump terrain
private void PinHeadIsland()
{
int x;
for (x = 0; x < Constants.RegionSize; x++)
for (int x = 0; x < Width; x++)
{
int y;
for (y = 0; y < Constants.RegionSize; y++)
for (int y = 0; y < Height; y++)
{
map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01;
double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001;
if (map[x, y] < spherFacA)
map[x, y] = spherFacA;
if (map[x, y] < spherFacB)
map[x, y] = 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()
{
int x;
for (x = 0; x < Constants.RegionSize; x++)
{
int y;
for (y = 0; y < Constants.RegionSize; y++)
map[x, y] = 21;
}
m_terrainData.ClearLand();
}
}
}

View File

@@ -0,0 +1,948 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Freely adapted from the Aurora version of the terrain compressor.
* Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/
*/
using System;
using System.Reflection;
using log4net;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public static class OpenSimTerrainCompressor
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
#pragma warning disable 414
private static string LogHeader = "[TERRAIN COMPRESSOR]";
#pragma warning restore 414
public const int END_OF_PATCHES = 97;
private const float OO_SQRT2 = 0.7071067811865475244008443621049f;
private const int STRIDE = 264;
private const int ZERO_CODE = 0x0;
private const int ZERO_EOB = 0x2;
private const int POSITIVE_VALUE = 0x6;
private const int NEGATIVE_VALUE = 0x7;
private static readonly float[] DequantizeTable16 =
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly float[] DequantizeTable32 =
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly float[] CosineTable16 = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
//private static readonly float[] CosineTable32 = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize];
private static readonly int[] CopyMatrix16 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly int[] CopyMatrix32 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly float[] QuantizeTable16 =
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
static OpenSimTerrainCompressor()
{
// Initialize the decompression tables
BuildDequantizeTable16();
SetupCosines16();
BuildCopyMatrix16();
BuildQuantizeTable16();
}
// Used to send cloud and wind patches
public static LayerDataPacket CreateLayerDataPacket(TerrainPatch[] patches, byte type, int pRegionSizeX,
int pRegionSizeY)
{
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
{Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
// Should be enough to fit even the most poorly packed data
byte[] data = new byte[patches.Length*Constants.TerrainPatchSize*Constants.TerrainPatchSize*2];
BitPack bitpack = new BitPack(data, 0);
bitpack.PackBits(header.Stride, 16);
bitpack.PackBits(header.PatchSize, 8);
bitpack.PackBits(type, 8);
foreach (TerrainPatch t in patches)
CreatePatch(bitpack, t.Data, t.X, t.Y, pRegionSizeX, pRegionSizeY);
bitpack.PackBits(END_OF_PATCHES, 8);
layer.LayerData.Data = new byte[bitpack.BytePos + 1];
Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
return layer;
}
// Create a land packet for a single patch.
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY)
{
int[] xPieces = new int[1];
int[] yPieces = new int[1];
xPieces[0] = patchX; // patch X dimension
yPieces[0] = patchY;
return CreateLandPacket(terrData, xPieces, yPieces);
}
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] xPieces, int[] yPieces)
{
byte landPacketType = (byte)TerrainPatch.LayerType.Land;
if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
{
landPacketType = (byte)TerrainPatch.LayerType.LandExtended;
}
return CreateLandPacket(terrData, xPieces, yPieces, landPacketType);
}
/// <summary>
/// Creates a LayerData packet for compressed land data given a full
/// simulator heightmap and an array of indices of patches to compress
/// </summary>
/// <param name="terrData">
/// Terrain data that can result in a meter square heightmap.
/// </param>
/// <param name="x">
/// Array of indexes in the grid of patches
/// for this simulator.
/// If creating a packet for multiple patches, there will be entries in
/// both the X and Y arrays for each of the patches.
/// For example if patches 1 and 17 are to be sent,
/// x[] = {1,1} and y[] = {0,1} which specifies the patches at
/// indexes <1,0> and <1,1> (presuming the terrain size is 16x16 patches).
/// </param>
/// <param name="y">
/// Array of indexes in the grid of patches.
/// </param>
/// <param name="type"></param>
/// <returns></returns>
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] x, int[] y, byte type)
{
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
{Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2];
BitPack bitpack = new BitPack(data, 0);
bitpack.PackBits(header.Stride, 16);
bitpack.PackBits(header.PatchSize, 8);
bitpack.PackBits(type, 8);
for (int i = 0; i < x.Length; i++)
CreatePatchFromHeightmap(bitpack, terrData, x[i], y[i]);
bitpack.PackBits(END_OF_PATCHES, 8);
layer.LayerData.Data = new byte[bitpack.BytePos + 1];
Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
return layer;
}
// Unused: left for historical reference.
public static void CreatePatch(BitPack output, float[] patchData, int x, int y, int pRegionSizeX, int pRegionSizeY)
{
TerrainPatch.Header header = PrescanPatch(patchData);
header.QuantWBits = 136;
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
{
header.PatchIDs = (y & 0xFFFF);
header.PatchIDs += (x << 16);
}
else
{
header.PatchIDs = (y & 0x1F);
header.PatchIDs += (x << 5);
}
// NOTE: No idea what prequant and postquant should be or what they do
int wbits;
int[] patch = CompressPatch(patchData, header, 10, out wbits);
wbits = EncodePatchHeader(output, header, patch, Constants.RegionSize, Constants.RegionSize, wbits);
EncodePatch(output, patch, 0, wbits);
}
/// <summary>
/// Add a patch of terrain to a BitPacker
/// </summary>
/// <param name="output">BitPacker to write the patch to</param>
/// <param name="heightmap">
/// Heightmap of the simulator. Presumed to be an sizeX*sizeY array.
/// </param>
/// <param name="patchX">
/// X offset of the patch to create.
/// </param>
/// <param name="patchY">
/// Y offset of the patch to create.
/// </param>
/// <param name="pRegionSizeX"></param>
/// <param name="pRegionSizeY"></param>
public static void CreatePatchFromHeightmap(BitPack output, TerrainData terrData, int patchX, int patchY)
{
TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY);
header.QuantWBits = 136;
// If larger than legacy region size, pack patch X and Y info differently.
if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
{
header.PatchIDs = (patchY & 0xFFFF);
header.PatchIDs += (patchX << 16);
}
else
{
header.PatchIDs = (patchY & 0x1F);
header.PatchIDs += (patchX << 5);
}
// m_log.DebugFormat("{0} CreatePatchFromHeightmap. patchX={1}, patchY={2}, DCOffset={3}, range={4}",
// LogHeader, patchX, patchY, header.DCOffset, header.Range);
// NOTE: No idea what prequant and postquant should be or what they do
int 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);
}
private static TerrainPatch.Header PrescanPatch(float[] patch)
{
TerrainPatch.Header header = new TerrainPatch.Header();
float zmax = -99999999.0f;
float zmin = 99999999.0f;
for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
{
float val = patch[i];
if (val > zmax) zmax = val;
if (val < zmin) zmin = val;
}
header.DCOffset = zmin;
header.Range = (int) ((zmax - zmin) + 1.0f);
return header;
}
// Scan the height info we're returning and return a patch packet header for this patch.
private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY)
{
TerrainPatch.Header header = new TerrainPatch.Header();
float zmax = -99999999.0f;
float zmin = 99999999.0f;
for (int j = patchY*Constants.TerrainPatchSize; j < (patchY + 1)*Constants.TerrainPatchSize; j++)
{
for (int i = patchX*Constants.TerrainPatchSize; i < (patchX + 1)*Constants.TerrainPatchSize; i++)
{
float val = terrData[i, j];
if (val > zmax) zmax = val;
if (val < zmin) zmin = val;
}
}
header.DCOffset = zmin;
header.Range = (int)((zmax - zmin) + 1.0f);
return header;
}
public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack)
{
TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)};
// Quantized word bits
if (header.QuantWBits == END_OF_PATCHES)
return header;
// DC offset
header.DCOffset = bitpack.UnpackFloat();
// Range
header.Range = bitpack.UnpackBits(16);
// Patch IDs (10 bits)
header.PatchIDs = bitpack.UnpackBits(10);
// Word bits
header.WordBits = (uint) ((header.QuantWBits & 0x0f) + 2);
return header;
}
private static int EncodePatchHeader(BitPack output, TerrainPatch.Header header, int[] patch, uint pRegionSizeX,
uint pRegionSizeY, int wbits)
{
/*
int temp;
int wbits = (header.QuantWBits & 0x0f) + 2;
uint maxWbits = (uint)wbits + 5;
uint minWbits = ((uint)wbits >> 1);
int wbitsMaxValue;
*/
// goal is to determ minimum number of bits to use so all data fits
/*
wbits = (int)minWbits;
wbitsMaxValue = (1 << wbits);
for (int i = 0; i < patch.Length; i++)
{
temp = patch[i];
if (temp != 0)
{
// Get the absolute value
if (temp < 0) temp *= -1;
no coments..
for (int j = (int)maxWbits; j > (int)minWbits; j--)
{
if ((temp & (1 << j)) != 0)
{
if (j > wbits) wbits = j;
break;
}
}
while (temp > wbitsMaxValue)
{
wbits++;
if (wbits == maxWbits)
goto Done;
wbitsMaxValue = 1 << wbits;
}
}
}
Done:
// wbits += 1;
*/
// better check
if (wbits > 17)
wbits = 16;
else if (wbits < 3)
wbits = 3;
header.QuantWBits &= 0xf0;
header.QuantWBits |= (wbits - 2);
output.PackBits(header.QuantWBits, 8);
output.PackFloat(header.DCOffset);
output.PackBits(header.Range, 16);
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
output.PackBits(header.PatchIDs, 32);
else
output.PackBits(header.PatchIDs, 10);
return wbits;
}
private static void IDCTColumn16(float[] linein, float[] lineout, int column)
{
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
float total = OO_SQRT2*linein[column];
for (int u = 1; u < Constants.TerrainPatchSize; u++)
{
int usize = u*Constants.TerrainPatchSize;
total += linein[usize + column]*CosineTable16[usize + n];
}
lineout[Constants.TerrainPatchSize*n + column] = total;
}
}
private static void IDCTLine16(float[] linein, float[] lineout, int line)
{
const float oosob = 2.0f/Constants.TerrainPatchSize;
int lineSize = line*Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
float total = OO_SQRT2*linein[lineSize];
for (int u = 1; u < Constants.TerrainPatchSize; u++)
{
total += linein[lineSize + u]*CosineTable16[u*Constants.TerrainPatchSize + n];
}
lineout[lineSize + n] = total*oosob;
}
}
/*
private static void DCTLine16(float[] linein, float[] lineout, int line)
{
float total = 0.0f;
int lineSize = line * Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[lineSize + n];
}
lineout[lineSize] = OO_SQRT2 * total;
int uptr = 0;
for (int u = 1; u < Constants.TerrainPatchSize; u++)
{
total = 0.0f;
uptr += Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[lineSize + n] * CosineTable16[uptr + n];
}
lineout[lineSize + u] = total;
}
}
*/
private static void DCTLine16(float[] linein, float[] lineout, int line)
{
// outputs transpose data (lines exchanged with coluns )
// so to save a bit of cpu when doing coluns
float total = 0.0f;
int lineSize = line*Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[lineSize + n];
}
lineout[line] = OO_SQRT2*total;
for (int u = Constants.TerrainPatchSize;
u < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
u += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int ptrn = lineSize, ptru = u; ptrn < lineSize + Constants.TerrainPatchSize; ptrn++,ptru++)
{
total += linein[ptrn]*CosineTable16[ptru];
}
lineout[line + u] = total;
}
}
/*
private static void DCTColumn16(float[] linein, int[] lineout, int column)
{
float total = 0.0f;
// const float oosob = 2.0f / Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[Constants.TerrainPatchSize * n + column];
}
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * QuantizeTable16[column]);
for (int uptr = Constants.TerrainPatchSize; uptr < Constants.TerrainPatchSize * Constants.TerrainPatchSize; uptr += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[Constants.TerrainPatchSize * n + column] * CosineTable16[uptr + n];
}
// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
lineout[CopyMatrix16[uptr + column]] = (int)(total * QuantizeTable16[uptr + column]);
}
}
private static void DCTColumn16(float[] linein, int[] lineout, int column)
{
// input columns are in fact stored in lines now
float total = 0.0f;
// const float oosob = 2.0f / Constants.TerrainPatchSize;
int inlinesptr = Constants.TerrainPatchSize*column;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[inlinesptr + n];
}
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
lineout[CopyMatrix16[column]] = (int) (OO_SQRT2*total*QuantizeTable16[column]);
for (int uptr = Constants.TerrainPatchSize;
uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
uptr += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
{
total += linein[n]*CosineTable16[ptru];
}
// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
lineout[CopyMatrix16[uptr + column]] = (int) (total*QuantizeTable16[uptr + column]);
}
}
*/
private static int DCTColumn16Wbits(float[] linein, int[] lineout, int column, int wbits, int maxwbits)
{
// input columns are in fact stored in lines now
bool dowbits = wbits != maxwbits;
int wbitsMaxValue = 1 << wbits;
float total = 0.0f;
// const float oosob = 2.0f / Constants.TerrainPatchSize;
int inlinesptr = Constants.TerrainPatchSize*column;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[inlinesptr + n];
}
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
int tmp = (int) (OO_SQRT2*total*QuantizeTable16[column]);
lineout[CopyMatrix16[column]] = tmp;
if (dowbits)
{
if (tmp < 0) tmp *= -1;
while (tmp > wbitsMaxValue)
{
wbits++;
wbitsMaxValue = 1 << wbits;
if (wbits == maxwbits)
{
dowbits = false;
break;
}
}
}
for (int uptr = Constants.TerrainPatchSize;
uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
uptr += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
{
total += linein[n]*CosineTable16[ptru];
}
tmp = (int) (total*QuantizeTable16[uptr + column]);
lineout[CopyMatrix16[uptr + column]] = tmp;
if (dowbits)
{
if (tmp < 0) tmp *= -1;
while (tmp > wbitsMaxValue)
{
wbits++;
wbitsMaxValue = 1 << wbits;
if (wbits == maxwbits)
{
dowbits = false;
break;
}
}
}
}
return wbits;
}
public static void DecodePatch(int[] patches, BitPack bitpack, TerrainPatch.Header header, int size)
{
for (int n = 0; n < size*size; n++)
{
// ?
int temp = bitpack.UnpackBits(1);
if (temp != 0)
{
// Value or EOB
temp = bitpack.UnpackBits(1);
if (temp != 0)
{
// Value
temp = bitpack.UnpackBits(1);
if (temp != 0)
{
// Negative
temp = bitpack.UnpackBits((int) header.WordBits);
patches[n] = temp*-1;
}
else
{
// Positive
temp = bitpack.UnpackBits((int) header.WordBits);
patches[n] = temp;
}
}
else
{
// Set the rest to zero
// TODO: This might not be necessary
for (int o = n; o < size*size; o++)
{
patches[o] = 0;
}
break;
}
}
else
{
patches[n] = 0;
}
}
}
private static void EncodePatch(BitPack output, int[] patch, int postquant, int wbits)
{
int maxwbitssize = (1 << wbits) - 1;
if (postquant > Constants.TerrainPatchSize*Constants.TerrainPatchSize || postquant < 0)
{
Logger.Log("Postquant is outside the range of allowed values in EncodePatch()", Helpers.LogLevel.Error);
return;
}
if (postquant != 0) patch[Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant] = 0;
for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
{
int temp = patch[i];
if (temp == 0)
{
bool eob = true;
for (int j = i; j < Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant; j++)
{
if (patch[j] != 0)
{
eob = false;
break;
}
}
if (eob)
{
output.PackBits(ZERO_EOB, 2);
return;
}
output.PackBits(ZERO_CODE, 1);
}
else
{
if (temp < 0)
{
temp *= -1;
if (temp > maxwbitssize) temp = maxwbitssize;
output.PackBits(NEGATIVE_VALUE, 3);
output.PackBits(temp, wbits);
}
else
{
if (temp > maxwbitssize) temp = maxwbitssize;
output.PackBits(POSITIVE_VALUE, 3);
output.PackBits(temp, wbits);
}
}
}
}
public static float[] DecompressPatch(int[] patches, TerrainPatch.Header header, TerrainPatch.GroupHeader group)
{
float[] block = new float[group.PatchSize*group.PatchSize];
float[] output = new float[group.PatchSize*group.PatchSize];
int prequant = (header.QuantWBits >> 4) + 2;
int quantize = 1 << prequant;
float ooq = 1.0f/quantize;
float mult = ooq*header.Range;
float addval = mult*(1 << (prequant - 1)) + header.DCOffset;
if (group.PatchSize == Constants.TerrainPatchSize)
{
for (int n = 0; n < Constants.TerrainPatchSize*Constants.TerrainPatchSize; n++)
{
block[n] = patches[CopyMatrix16[n]]*DequantizeTable16[n];
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
for (int o = 0; o < Constants.TerrainPatchSize; o++)
IDCTColumn16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
IDCTLine16(ftemp, block, o);
}
else
{
for (int n = 0; n < Constants.TerrainPatchSize*2*Constants.TerrainPatchSize*2; n++)
{
block[n] = patches[CopyMatrix32[n]]*DequantizeTable32[n];
}
Logger.Log("Implement IDCTPatchLarge", Helpers.LogLevel.Error);
}
for (int j = 0; j < block.Length; j++)
{
output[j] = block[j]*mult + addval;
}
return output;
}
private static int[] CompressPatch(float[] patchData, TerrainPatch.Header header, int prequant, out int wbits)
{
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int wordsize = (prequant - 2) & 0x0f;
float oozrange = 1.0f/header.Range;
float range = (1 << prequant);
float premult = oozrange*range;
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
header.QuantWBits = wordsize;
header.QuantWBits |= wordsize << 4;
int k = 0;
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
block[k++] = patchData[j*Constants.TerrainPatchSize + i]*premult - sub;
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int maxWbits = prequant + 5;
wbits = (prequant >> 1);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
DCTLine16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
return itemp;
}
private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant, out int wbits)
{
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
float oozrange = 1.0f/header.Range;
float range = (1 << prequant);
float premult = oozrange*range;
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
int wordsize = (prequant - 2) & 0x0f;
header.QuantWBits = wordsize;
header.QuantWBits |= wordsize << 4;
int k = 0;
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
block[k++] = patchData[j, i]*premult - sub;
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int maxWbits = prequant + 5;
wbits = (prequant >> 1);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
DCTLine16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
return itemp;
}
private static int[] CompressPatch(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;
float oozrange = 1.0f/header.Range;
float range = (1 << prequant);
float premult = oozrange*range;
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
header.QuantWBits = wordsize - 2;
header.QuantWBits |= (prequant - 2) << 4;
int k = 0;
int yPatchLimit = patchY >= (terrData.SizeY / Constants.TerrainPatchSize) ?
(terrData.SizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchY;
yPatchLimit = (yPatchLimit + 1) * Constants.TerrainPatchSize;
int xPatchLimit = patchX >= (terrData.SizeX / Constants.TerrainPatchSize) ?
(terrData.SizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchX;
xPatchLimit = (xPatchLimit + 1) * Constants.TerrainPatchSize;
for (int yy = patchY * Constants.TerrainPatchSize; yy < yPatchLimit; yy++)
{
for (int xx = patchX * Constants.TerrainPatchSize; xx < xPatchLimit; xx++)
{
block[k++] = terrData[xx, yy] * premult - sub;
}
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int maxWbits = prequant + 5;
wbits = (prequant >> 1);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
DCTLine16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
return itemp;
}
#region Initialization
private static void BuildDequantizeTable16()
{
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
{
DequantizeTable16[j*Constants.TerrainPatchSize + i] = 1.0f + 2.0f*(i + j);
}
}
}
private static void BuildQuantizeTable16()
{
const float oosob = 2.0f/Constants.TerrainPatchSize;
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
{
// QuantizeTable16[j * Constants.TerrainPatchSize + i] = 1.0f / (1.0f + 2.0f * ((float)i + (float)j));
QuantizeTable16[j*Constants.TerrainPatchSize + i] = oosob/(1.0f + 2.0f*(i + (float) j));
}
}
}
private static void SetupCosines16()
{
const float hposz = (float) Math.PI*0.5f/Constants.TerrainPatchSize;
for (int u = 0; u < Constants.TerrainPatchSize; u++)
{
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
CosineTable16[u*Constants.TerrainPatchSize + n] = (float) Math.Cos((2.0f*n + 1.0f)*u*hposz);
}
}
}
private static void BuildCopyMatrix16()
{
bool diag = false;
bool right = true;
int i = 0;
int j = 0;
int count = 0;
while (i < Constants.TerrainPatchSize && j < Constants.TerrainPatchSize)
{
CopyMatrix16[j*Constants.TerrainPatchSize + i] = count++;
if (!diag)
{
if (right)
{
if (i < Constants.TerrainPatchSize - 1) i++;
else j++;
right = false;
diag = true;
}
else
{
if (j < Constants.TerrainPatchSize - 1) j++;
else i++;
right = true;
diag = true;
}
}
else
{
if (right)
{
i++;
j--;
if (i == Constants.TerrainPatchSize - 1 || j == 0) diag = false;
}
else
{
i--;
j++;
if (j == Constants.TerrainPatchSize - 1 || i == 0) diag = false;
}
}
}
}
#endregion Initialization
}
}