diff --git a/OpenMetaverse/TerrainCompressor.cs b/OpenMetaverse/TerrainCompressor.cs
index e7618b49..a6fc201a 100644
--- a/OpenMetaverse/TerrainCompressor.cs
+++ b/OpenMetaverse/TerrainCompressor.cs
@@ -174,6 +174,32 @@ namespace OpenMetaverse
return layer;
}
+ public static LayerDataPacket CreateLandPacket(float[,] patchData, int x, int y)
+ {
+ LayerDataPacket layer = new LayerDataPacket();
+ layer.LayerID.Type = (byte)TerrainPatch.LayerType.Land;
+
+ TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader();
+ header.Stride = STRIDE;
+ header.PatchSize = 16;
+ header.Type = TerrainPatch.LayerType.Land;
+
+ byte[] data = new byte[1536];
+ BitPack bitpack = new BitPack(data, 0);
+ bitpack.PackBits(header.Stride, 16);
+ bitpack.PackBits(header.PatchSize, 8);
+ bitpack.PackBits((int)header.Type, 8);
+
+ CreatePatch(bitpack, patchData, x, y);
+
+ 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;
+ }
+
public static void CreatePatch(BitPack output, float[] patchData, int x, int y)
{
if (patchData.Length != 16 * 16)
@@ -190,6 +216,22 @@ namespace OpenMetaverse
EncodePatch(output, patch, 0, wbits);
}
+ public static void CreatePatch(BitPack output, float[,] patchData, int x, int y)
+ {
+ if (patchData.Length != 16 * 16)
+ throw new ArgumentException("Patch data must be a 16x16 array");
+
+ TerrainPatch.Header header = PrescanPatch(patchData);
+ header.QuantWBits = 136;
+ header.PatchIDs = (y & 0x1F);
+ header.PatchIDs += (x << 5);
+
+ // NOTE: No idea what prequant and postquant should be or what they do
+ int[] patch = CompressPatch(patchData, header, 10);
+ int wbits = EncodePatchHeader(output, header, patch);
+ EncodePatch(output, patch, 0, wbits);
+ }
+
///
/// Add a patch of terrain to a BitPacker
///
@@ -241,6 +283,28 @@ namespace OpenMetaverse
return header;
}
+ private static TerrainPatch.Header PrescanPatch(float[,] patch)
+ {
+ TerrainPatch.Header header = new TerrainPatch.Header();
+ float zmax = -99999999.0f;
+ float zmin = 99999999.0f;
+
+ for (int j = 0; j < 16; j++)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ float val = patch[j, i];
+ if (val > zmax) zmax = val;
+ if (val < zmin) zmin = val;
+ }
+ }
+
+ header.DCOffset = zmin;
+ header.Range = (int)((zmax - zmin) + 1.0f);
+
+ return header;
+ }
+
private static TerrainPatch.Header PrescanPatch(float[] heightmap, int patchX, int patchY)
{
TerrainPatch.Header header = new TerrainPatch.Header();
@@ -605,6 +669,36 @@ namespace OpenMetaverse
return itemp;
}
+ private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant)
+ {
+ float[] block = new float[16 * 16];
+ int wordsize = prequant;
+ float oozrange = 1.0f / (float)header.Range;
+ float range = (float)(1 << prequant);
+ float premult = oozrange * range;
+ float sub = (float)(1 << (prequant - 1)) + header.DCOffset * premult;
+
+ header.QuantWBits = wordsize - 2;
+ header.QuantWBits |= (prequant - 2) << 4;
+
+ int k = 0;
+ for (int j = 0; j < 16; j++)
+ {
+ for (int i = 0; i < 16; i++)
+ block[k++] = patchData[j, i] * premult - sub;
+ }
+
+ float[] ftemp = new float[16 * 16];
+ int[] itemp = new int[16 * 16];
+
+ for (int o = 0; o < 16; o++)
+ DCTLine16(block, ftemp, o);
+ for (int o = 0; o < 16; o++)
+ DCTColumn16(ftemp, itemp, o);
+
+ return itemp;
+ }
+
private static int[] CompressPatch(float[] heightmap, int patchX, int patchY, TerrainPatch.Header header, int prequant)
{
float[] block = new float[16 * 16];
diff --git a/Programs/Simian/Extensions/AuthFreeForAll.cs b/Programs/Simian/Extensions/AuthFreeForAll.cs
index ec4c9256..d9d06495 100644
--- a/Programs/Simian/Extensions/AuthFreeForAll.cs
+++ b/Programs/Simian/Extensions/AuthFreeForAll.cs
@@ -34,9 +34,9 @@ namespace Simian.Extensions
agent.Avatar.ID = UUID.Random();
agent.Balance = 1000;
agent.CreationTime = Utils.DateTimeToUnixTime(DateTime.Now);
- agent.CurrentLookAt = Vector3.Zero;
+ agent.CurrentLookAt = Vector3.UnitZ;
agent.CurrentPosition = new Vector3(128f, 128f, 25f);
- agent.CurrentRegionHandle = Utils.UIntsToLong(Simian.REGION_X, Simian.REGION_Y);
+ agent.CurrentRegionHandle = Utils.UIntsToLong(256 * server.Scene.RegionX, 256 * server.Scene.RegionY);
agent.FirstName = firstName;
agent.GodLevel = 0;
agent.HomeLookAt = agent.CurrentLookAt;
diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs
index d4084107..97ffb7f2 100644
--- a/Programs/Simian/Extensions/AvatarManager.cs
+++ b/Programs/Simian/Extensions/AvatarManager.cs
@@ -94,7 +94,7 @@ namespace Simian.Extensions
public void TriggerSound(Agent agent, UUID soundID, float gain)
{
SoundTriggerPacket sound = new SoundTriggerPacket();
- sound.SoundData.Handle = server.RegionHandle;
+ sound.SoundData.Handle = server.Scene.RegionHandle;
sound.SoundData.ObjectID = agent.Avatar.ID;
sound.SoundData.ParentID = agent.Avatar.ID;
sound.SoundData.OwnerID = agent.Avatar.ID;
diff --git a/Programs/Simian/Extensions/ConnectionManagement.cs b/Programs/Simian/Extensions/ConnectionManagement.cs
index 1415170c..7f9526e6 100644
--- a/Programs/Simian/Extensions/ConnectionManagement.cs
+++ b/Programs/Simian/Extensions/ConnectionManagement.cs
@@ -55,7 +55,7 @@ namespace Simian.Extensions
handshake.RegionInfo.TerrainStartHeight01 = 40f;
handshake.RegionInfo.TerrainStartHeight10 = 0f;
handshake.RegionInfo.TerrainStartHeight11 = 40f;
- handshake.RegionInfo2.RegionID = UUID.Random();
+ handshake.RegionInfo2.RegionID = server.Scene.RegionID;
server.UDP.SendPacket(agent.Avatar.ID, handshake, PacketCategory.Transaction);
}
diff --git a/Programs/Simian/Extensions/Movement.cs b/Programs/Simian/Extensions/Movement.cs
index 9c8c285c..e8c7d74a 100644
--- a/Programs/Simian/Extensions/Movement.cs
+++ b/Programs/Simian/Extensions/Movement.cs
@@ -18,7 +18,7 @@ namespace Simian.Extensions
const float FALL_DELAY = 0.33f; //seconds before starting animation
const float FALL_FORGIVENESS = 0.25f; //fall buffer in meters
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
- const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec (no clue why this is so high)
+ const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
@@ -30,6 +30,7 @@ namespace Simian.Extensions
Simian server;
Timer updateTimer;
long lastTick;
+ TerrainPatch[,] heightmap = new TerrainPatch[16, 16];
public int LastTick
{
@@ -45,6 +46,8 @@ namespace Simian.Extensions
{
this.server = server;
+ server.Scene.OnTerrainUpdate += Scene_OnTerrainUpdate;
+
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, new PacketCallback(AgentUpdateHandler));
server.UDP.RegisterPacketCallback(PacketType.AgentHeightWidth, new PacketCallback(AgentHeightWidthHandler));
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, new PacketCallback(SetAlwaysRunHandler));
@@ -63,6 +66,13 @@ namespace Simian.Extensions
}
}
+ void Scene_OnTerrainUpdate(object sender, uint x, uint y, float[,] patchData)
+ {
+ TerrainPatch patch = new TerrainPatch(16, 16);
+ patch.Height = patchData;
+ heightmap[y, x] = patch;
+ }
+
void UpdateTimer_Elapsed(object sender)
{
int tick = Environment.TickCount;
@@ -119,6 +129,11 @@ namespace Simian.Extensions
// TODO: calculate to get rid of "bot squat"
float lowerLimit = newFloor + agent.Avatar.Scale.Z / 2;
+ // TODO: check our Z (minus height/2) compared to all the prim
+ // bounding boxes, and define a new lowerLimit as a hack for platforms
+
+
+
// Z acceleration resulting from gravity
float gravity = 0f;
@@ -351,7 +366,7 @@ namespace Simian.Extensions
agent.Flags = (PrimFlags)update.AgentData.Flags;
ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar,
- server.RegionHandle, agent.Flags);
+ server.Scene.RegionHandle, agent.Flags);
server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State);
}
@@ -373,23 +388,54 @@ namespace Simian.Extensions
if (y > 255) y = 255;
else if (y < 0) y = 0;
- float center = server.Scene.Heightmap[y * 256 + x];
- float distX = position.X - (int)position.X;
- float distY = position.Y - (int)position.Y;
+ int patchX = x / 16;
+ int patchY = y / 16;
- float nearestX;
- float nearestY;
+ if (heightmap[patchY, patchX] != null)
+ {
+ float center = heightmap[patchY, patchX].Height[y - (patchY * 16), x - (patchX * 16)];
- if (distX > 0) nearestX = server.Scene.Heightmap[y * 256 + x + (x < 255 ? 1 : 0)];
- else nearestX = server.Scene.Heightmap[y * 256 + x - (x > 0 ? 1 : 0)];
+ float distX = position.X - (int)position.X;
+ float distY = position.Y - (int)position.Y;
- if (distY > 0) nearestY = server.Scene.Heightmap[(y + (y < 255 ? 1 : 0)) * 256 + x];
- else nearestY = server.Scene.Heightmap[(y - (y > 0 ? 1 : 0)) * 256 + x];
+ float nearestX;
+ float nearestY;
- float lerpX = Utils.Lerp(center, nearestX, Math.Abs(distX));
- float lerpY = Utils.Lerp(center, nearestY, Math.Abs(distY));
+ if (distX > 0f)
+ {
+ int i = x < 255 ? 1 : 0;
+ int newPatchX = (x + i) / 16;
+ nearestX = heightmap[patchY, newPatchX].Height[y - (patchY * 16), (x + i) - (newPatchX * 16)];
+ }
+ else
+ {
+ int i = x > 0 ? 1 : 0;
+ int newPatchX = (x - i) / 16;
+ nearestX = heightmap[patchY, newPatchX].Height[y - (patchY * 16), (x - i) - (newPatchX * 16)];
+ }
- return ((lerpX + lerpY) / 2);
+ if (distY > 0f)
+ {
+ int i = y < 255 ? 1 : 0;
+ int newPatchY = (y + i) / 16;
+ nearestY = heightmap[newPatchY, patchX].Height[(y + i) - (newPatchY * 16), x - (patchX * 16)];
+ }
+ else
+ {
+ int i = y > 0 ? 1 : 0;
+ int newPatchY = (y - i) / 16;
+ nearestY = heightmap[newPatchY, patchX].Height[(y - i) - (newPatchY * 16), x - (patchX * 16)];
+ }
+
+ float lerpX = Utils.Lerp(center, nearestX, Math.Abs(distX));
+ float lerpY = Utils.Lerp(center, nearestY, Math.Abs(distY));
+
+ return ((lerpX + lerpY) / 2);
+ }
+ else
+ {
+ return 0f;
+ }
}
void AgentHeightWidthHandler(Packet packet, Agent agent)
diff --git a/Programs/Simian/Extensions/ObjectManager.cs b/Programs/Simian/Extensions/ObjectManager.cs
index 26705a77..58a5d7cb 100644
--- a/Programs/Simian/Extensions/ObjectManager.cs
+++ b/Programs/Simian/Extensions/ObjectManager.cs
@@ -160,7 +160,7 @@ namespace Simian.Extensions
prim.Properties.Permissions = Permissions.FullPermissions;
prim.Properties.SalePrice = 10;
- prim.RegionHandle = server.RegionHandle;
+ prim.RegionHandle = server.Scene.RegionHandle;
prim.Rotation = add.ObjectData.Rotation;
prim.Scale = scale;
prim.Textures = new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE);
@@ -335,12 +335,12 @@ namespace Simian.Extensions
ObjectUpdatePacket update = new ObjectUpdatePacket();
- update.RegionData.RegionHandle = server.RegionHandle;
+ update.RegionData.RegionHandle = server.Scene.RegionHandle;
update.RegionData.TimeDilation = UInt16.MaxValue;
update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1];
- update.ObjectData[0] = SimulationObject.BuildUpdateBlock(linkSet[i].Prim, server.RegionHandle, linkSet[i].Prim.Flags);
+ update.ObjectData[0] = SimulationObject.BuildUpdateBlock(linkSet[i].Prim, server.Scene.RegionHandle, linkSet[i].Prim.Flags);
if (linkSet[i].Prim.ParentID > 0)
{
@@ -404,7 +404,7 @@ namespace Simian.Extensions
ObjectUpdatePacket update = new ObjectUpdatePacket();
- update.RegionData.RegionHandle = server.RegionHandle;
+ update.RegionData.RegionHandle = server.Scene.RegionHandle;
update.RegionData.TimeDilation = UInt16.MaxValue;
update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[linkSet.Count];
@@ -412,7 +412,7 @@ namespace Simian.Extensions
for (int i = 0; i < linkSet.Count; i++)
{
update.ObjectData[i] = SimulationObject.BuildUpdateBlock(linkSet[i].Prim,
- server.RegionHandle, linkSet[i].Prim.Flags);
+ server.Scene.RegionHandle, linkSet[i].Prim.Flags);
update.ObjectData[i].ParentID = 0;
linkSet[i].LinkNumber = 0;
diff --git a/Programs/Simian/Extensions/Periscope.cs b/Programs/Simian/Extensions/Periscope.cs
index abc9eccd..8985146c 100644
--- a/Programs/Simian/Extensions/Periscope.cs
+++ b/Programs/Simian/Extensions/Periscope.cs
@@ -83,11 +83,13 @@ namespace Simian.Extensions
void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
{
+ ulong localRegionHandle = Utils.UIntsToLong(256 * server.Scene.RegionX, 256 * server.Scene.RegionY);
+
// Add the avatar to both the agents list and the scene objects
Agent agent = new Agent();
agent.Avatar.ID = avatar.ID;
agent.Avatar = avatar;
- agent.CurrentRegionHandle = server.RegionHandle;
+ agent.CurrentRegionHandle = localRegionHandle;
agent.FirstName = avatar.FirstName;
agent.LastName = avatar.LastName;
@@ -144,12 +146,16 @@ namespace Simian.Extensions
void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
{
- // TODO: When Simian gets a terrain editing interface, switch this over to
- // edit the scene heightmap instead of sending packets direct to clients
- int[] patches = new int[1];
- patches[0] = (y * 16) + x;
- LayerDataPacket layer = TerrainCompressor.CreateLandPacket(data, x, y);
- server.UDP.BroadcastPacket(layer, PacketCategory.Terrain);
+ float[,] patchData = new float[16, 16];
+ for (int py = 0; py < 16; py++)
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ patchData[py, px] = data[py * 16 + px];
+ }
+ }
+
+ server.Scene.SetTerrainPatch(this, (uint)x, (uint)y, patchData);
}
void Self_OnChat(string message, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
@@ -181,13 +187,12 @@ namespace Simian.Extensions
}
);
- // FIXME: Use the scene region handle when it has one
- ulong regionHandle = Utils.UIntsToLong(Simian.REGION_X, Simian.REGION_Y);
+ ulong localRegionHandle = Utils.UIntsToLong(256 * server.Scene.RegionX, 256 * server.Scene.RegionY);
server.Scene.ForEachAgent(
delegate(Agent agent)
{
- if (agent.Avatar.RegionHandle != regionHandle &&
+ if (agent.Avatar.RegionHandle != localRegionHandle &&
agent.Avatar.RegionHandle != client.Network.CurrentSim.Handle)
{
server.Scene.AgentRemove(this, agent.Avatar.ID);
@@ -240,6 +245,8 @@ namespace Simian.Extensions
RegionHandshakePacket handshake = (RegionHandshakePacket)packet;
handshake.RegionInfo.SimOwner = (MasterAgent != null ? MasterAgent.Avatar.ID : UUID.Zero);
+ handshake.RegionInfo.RegionFlags &= ~(uint)RegionFlags.NoFly;
+ handshake.RegionInfo2.RegionID = server.Scene.RegionID;
// TODO: Need more methods to manipulate the scene so we can apply these properties.
// Right now this only gets sent out to people who are logged in when the master avatar
diff --git a/Programs/Simian/Extensions/PeriscopeMovement.cs b/Programs/Simian/Extensions/PeriscopeMovement.cs
index 027009ff..7f5226f1 100644
--- a/Programs/Simian/Extensions/PeriscopeMovement.cs
+++ b/Programs/Simian/Extensions/PeriscopeMovement.cs
@@ -31,6 +31,7 @@ namespace Simian.Extensions
Periscope periscope;
Timer updateTimer;
long lastTick;
+ TerrainPatch[,] heightmap = new TerrainPatch[16, 16];
public int LastTick
{
@@ -43,6 +44,8 @@ namespace Simian.Extensions
this.server = server;
this.periscope = periscope;
+ server.Scene.OnTerrainUpdate += Scene_OnTerrainUpdate;
+
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
@@ -60,6 +63,13 @@ namespace Simian.Extensions
}
}
+ void Scene_OnTerrainUpdate(object sender, uint x, uint y, float[,] patchData)
+ {
+ TerrainPatch patch = new TerrainPatch(16, 16);
+ patch.Height = patchData;
+ heightmap[y, x] = patch;
+ }
+
void UpdateTimer_Elapsed(object sender)
{
int tick = Environment.TickCount;
@@ -326,7 +336,7 @@ namespace Simian.Extensions
}
ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar,
- server.RegionHandle, agent.Flags);
+ server.Scene.RegionHandle, agent.Flags);
server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State);
}
@@ -348,23 +358,54 @@ namespace Simian.Extensions
if (y > 255) y = 255;
else if (y < 0) y = 0;
- float center = server.Scene.Heightmap[y * 256 + x];
- float distX = position.X - (int)position.X;
- float distY = position.Y - (int)position.Y;
+ int patchX = x / 16;
+ int patchY = y / 16;
- float nearestX;
- float nearestY;
+ if (heightmap[patchY, patchX] != null)
+ {
+ float center = heightmap[patchY, patchX].Height[y - (patchY * 16), x - (patchX * 16)];
- if (distX > 0) nearestX = server.Scene.Heightmap[y * 256 + x + (x < 255 ? 1 : 0)];
- else nearestX = server.Scene.Heightmap[y * 256 + x - (x > 0 ? 1 : 0)];
+ float distX = position.X - (int)position.X;
+ float distY = position.Y - (int)position.Y;
- if (distY > 0) nearestY = server.Scene.Heightmap[(y + (y < 255 ? 1 : 0)) * 256 + x];
- else nearestY = server.Scene.Heightmap[(y - (y > 0 ? 1 : 0)) * 256 + x];
+ float nearestX;
+ float nearestY;
- float lerpX = Utils.Lerp(center, nearestX, Math.Abs(distX));
- float lerpY = Utils.Lerp(center, nearestY, Math.Abs(distY));
+ if (distX > 0f)
+ {
+ int i = x < 255 ? 1 : 0;
+ int newPatchX = (x + i) / 16;
+ nearestX = heightmap[patchY, newPatchX].Height[y - (patchY * 16), (x + i) - (newPatchX * 16)];
+ }
+ else
+ {
+ int i = x > 0 ? 1 : 0;
+ int newPatchX = (x - i) / 16;
+ nearestX = heightmap[patchY, newPatchX].Height[y - (patchY * 16), (x - i) - (newPatchX * 16)];
+ }
- return ((lerpX + lerpY) / 2);
+ if (distY > 0f)
+ {
+ int i = y < 255 ? 1 : 0;
+ int newPatchY = (y + i) / 16;
+ nearestY = heightmap[newPatchY, patchX].Height[(y + i) - (newPatchY * 16), x - (patchX * 16)];
+ }
+ else
+ {
+ int i = y > 0 ? 1 : 0;
+ int newPatchY = (y - i) / 16;
+ nearestY = heightmap[newPatchY, patchX].Height[(y - i) - (newPatchY * 16), x - (patchX * 16)];
+ }
+
+ float lerpX = Utils.Lerp(center, nearestX, Math.Abs(distX));
+ float lerpY = Utils.Lerp(center, nearestY, Math.Abs(distY));
+
+ return ((lerpX + lerpY) / 2);
+ }
+ else
+ {
+ return 0f;
+ }
}
}
}
diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs
index 948827a8..9f40a9f4 100644
--- a/Programs/Simian/Extensions/SceneManager.cs
+++ b/Programs/Simian/Extensions/SceneManager.cs
@@ -12,13 +12,17 @@ using OpenMetaverse.Packets;
namespace Simian.Extensions
{
+
+
public class SceneManager : IExtension, ISceneProvider
{
Simian server;
DoubleDictionary sceneObjects = new DoubleDictionary();
DoubleDictionary sceneAgents = new DoubleDictionary();
int currentLocalID = 1;
- float[] heightmap = new float[256 * 256];
+ ulong regionHandle;
+ UUID regionID = UUID.Random();
+ TerrainPatch[,] heightmap = new TerrainPatch[16, 16];
public event ObjectAddCallback OnObjectAdd;
public event ObjectRemoveCallback OnObjectRemove;
@@ -29,23 +33,21 @@ namespace Simian.Extensions
public event AgentAddCallback OnAgentAdd;
public event AgentRemoveCallback OnAgentRemove;
public event AgentAppearanceCallback OnAgentAppearance;
- public event TerrainUpdatedCallback OnTerrainUpdated;
+ public event TerrainUpdateCallback OnTerrainUpdate;
- public float[] Heightmap
- {
- get { return heightmap; }
- set
- {
- if (value.Length != (256 * 256))
- throw new ArgumentException("Heightmap must be 256x256");
- heightmap = value;
- }
- }
+ public uint RegionX { get { return 1000; } }
+ public uint RegionY { get { return 1000; } }
+ public ulong RegionHandle { get { return regionHandle; } }
+ public UUID RegionID { get { return regionID; } }
- public float WaterHeight { get { return 35f; } }
+ public float WaterHeight { get { return 20f; } }
+
+ public uint TerrainPatchWidth { get { return 16; } }
+ public uint TerrainPatchHeight { get { return 16; } }
public SceneManager()
{
+ regionHandle = Utils.UIntsToLong(RegionX * 256, RegionY * 256);
}
public void Start(Simian server)
@@ -60,6 +62,26 @@ namespace Simian.Extensions
{
}
+ public float[,] GetTerrainPatch(uint x, uint y)
+ {
+ float[,] copy = new float[16, 16];
+ Buffer.BlockCopy(heightmap[y, x].Height, 0, copy, 0, 16 * 16 * sizeof(float));
+ return copy;
+ }
+
+ public void SetTerrainPatch(object sender, uint x, uint y, float[,] patchData)
+ {
+ if (OnTerrainUpdate != null)
+ OnTerrainUpdate(sender, x, y, patchData);
+
+ float[,] copy = new float[16, 16];
+ Buffer.BlockCopy(patchData, 0, copy, 0, 16 * 16 * sizeof(float));
+ heightmap[y, x].Height = copy;
+
+ LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap[y, x].Height, (int)x, (int)y);
+ server.UDP.BroadcastPacket(layer, PacketCategory.Terrain);
+ }
+
public bool ObjectAdd(object sender, SimulationObject obj, PrimFlags creatorFlags)
{
// Check if the object already exists in the scene
@@ -81,13 +103,13 @@ namespace Simian.Extensions
if (sceneAgents.ContainsKey(obj.Prim.OwnerID))
{
// Send an update out to the creator
- ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle,
+ ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle,
obj.Prim.Flags | creatorFlags);
server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
}
// Send an update out to everyone else
- ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle,
+ ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle,
obj.Prim.Flags);
server.Scene.ForEachAgent(
delegate(Agent recipient)
@@ -258,12 +280,12 @@ namespace Simian.Extensions
sceneAgents.Add(agent.Avatar.LocalID, agent.Avatar.ID, agent);
// Send an update out to the agent
- ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(agent.Avatar, server.RegionHandle,
+ ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(agent.Avatar, regionHandle,
agent.Avatar.Flags | creatorFlags);
server.UDP.SendPacket(agent.Avatar.ID, updateToOwner, PacketCategory.State);
// Send an update out to everyone else
- ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(agent.Avatar, server.RegionHandle,
+ ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(agent.Avatar, regionHandle,
agent.Avatar.Flags);
server.Scene.ForEachAgent(
delegate(Agent recipient)
@@ -334,7 +356,7 @@ namespace Simian.Extensions
// Broadcast an object update for this avatar
// TODO: Is this necessary here?
ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar,
- server.RegionHandle, agent.Flags);
+ regionHandle, agent.Flags);
server.UDP.BroadcastPacket(update, PacketCategory.State);
// Update the avatar
@@ -389,7 +411,7 @@ namespace Simian.Extensions
void BroadcastObjectUpdate(SimulationObject obj)
{
ObjectUpdatePacket update =
- SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle, obj.Prim.Flags);
+ SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, obj.Prim.Flags);
server.UDP.BroadcastPacket(update, PacketCategory.State);
}
@@ -435,7 +457,7 @@ namespace Simian.Extensions
complete.AgentData.SessionID = agent.SessionID;
complete.Data.LookAt = Vector3.UnitX;
complete.Data.Position = avatar.Position;
- complete.Data.RegionHandle = server.RegionHandle;
+ complete.Data.RegionHandle = regionHandle;
complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now);
complete.SimData.ChannelVersion = Utils.StringToBytes("Simian");
@@ -491,50 +513,80 @@ namespace Simian.Extensions
void LoadTerrain(string mapFile)
{
+ byte[] rgbValues = new byte[256 * 256 * 3];
+
if (File.Exists(mapFile))
{
lock (heightmap)
{
Bitmap bmp = LoadTGAClass.LoadTGA(mapFile);
- Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
- BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
- IntPtr ptr = bmpData.Scan0;
- int bytes = bmpData.Stride * bmp.Height;
- byte[] rgbValues = new byte[bytes];
- Marshal.Copy(ptr, rgbValues, 0, bytes);
- bmp.UnlockBits(bmpData);
-
- for (int i = 1, pos = 0; i < heightmap.Length; i++, pos += 3)
- heightmap[i] = (float)rgbValues[pos];
-
- if (OnTerrainUpdated != null)
- OnTerrainUpdated(this);
+ if (bmp.Width == 256 && bmp.Height == 256)
+ {
+ Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
+ BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
+ Marshal.Copy(bmpData.Scan0, rgbValues, 0, rgbValues.Length);
+ bmp.UnlockBits(bmpData);
+ }
+ else
+ {
+ Logger.Log("Map file " + mapFile + " has the wrong dimensions or wrong pixel format (must be 256x256 RGB). Defaulting to 25m",
+ Helpers.LogLevel.Warning);
+ for (int i = 0; i < rgbValues.Length; i++)
+ rgbValues[i] = 25;
+ }
}
}
else
{
Logger.Log("Map file " + mapFile + " not found, defaulting to 25m", Helpers.LogLevel.Info);
+ for (int i = 0; i < rgbValues.Length; i++)
+ rgbValues[i] = 25;
+ }
- server.Scene.Heightmap = new float[65536];
- for (int i = 0; i < server.Scene.Heightmap.Length; i++)
- server.Scene.Heightmap[i] = 25f;
+ uint patchX = 0, patchY = 0, x = 0, y = 0;
+ for (int i = 0; i < rgbValues.Length; i += 3)
+ {
+ if (heightmap[patchY, patchX] == null)
+ heightmap[patchY, patchX] = new TerrainPatch(16, 16);
+
+ heightmap[patchY, patchX].Height[y, x] = (float)rgbValues[i];
+
+ ++x;
+ if (x > 15)
+ {
+ if (y == 15)
+ {
+ if (OnTerrainUpdate != null)
+ OnTerrainUpdate(this, patchX, patchY, heightmap[patchY, patchX].Height);
+ }
+
+ x = 0;
+ ++patchX;
+ }
+
+ if (patchX > 15)
+ {
+ patchX = 0;
+ ++y;
+ }
+
+ if (y > 15)
+ {
+ y = 0;
+ ++patchY;
+ }
}
}
void SendLayerData(Agent agent)
{
- lock (heightmap)
+ for (int y = 0; y < 16; y++)
{
- for (int y = 0; y < 16; y++)
+ for (int x = 0; x < 16; x++)
{
- for (int x = 0; x < 16; x++)
- {
- int[] patches = new int[1];
- patches[0] = (y * 16) + x;
- LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap, patches);
- server.UDP.SendPacket(agent.Avatar.ID, layer, PacketCategory.Terrain);
- }
+ LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap[y, x].Height, x, y);
+ server.UDP.SendPacket(agent.Avatar.ID, layer, PacketCategory.Terrain);
}
}
}
diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs
index 7f4fffb1..733d2f22 100644
--- a/Programs/Simian/Interfaces/ISceneProvider.cs
+++ b/Programs/Simian/Interfaces/ISceneProvider.cs
@@ -4,6 +4,16 @@ using OpenMetaverse;
namespace Simian
{
+ public class TerrainPatch
+ {
+ public float[,] Height;
+
+ public TerrainPatch(uint width, uint height)
+ {
+ Height = new float[height, width];
+ }
+ }
+
public delegate void ObjectAddCallback(object sender, SimulationObject obj, PrimFlags creatorFlags);
public delegate void ObjectRemoveCallback(object sender, SimulationObject obj);
public delegate void ObjectTransformCallback(object sender, SimulationObject obj, Vector3 position,
@@ -16,8 +26,7 @@ namespace Simian
public delegate void AgentRemoveCallback(object sender, Agent agent);
public delegate void AgentAppearanceCallback(object sender, Agent agent, Primitive.TextureEntry textures,
byte[] visualParams);
- // TODO: Convert terrain to a patch-based system
- public delegate void TerrainUpdatedCallback(object sender);
+ public delegate void TerrainUpdateCallback(object sender, uint x, uint y, float[,] patchData);
public interface ISceneProvider
{
@@ -29,13 +38,21 @@ namespace Simian
event AgentAddCallback OnAgentAdd;
event AgentRemoveCallback OnAgentRemove;
event AgentAppearanceCallback OnAgentAppearance;
- event TerrainUpdatedCallback OnTerrainUpdated;
+ event TerrainUpdateCallback OnTerrainUpdate;
+
+ uint RegionX { get; }
+ uint RegionY { get; }
+ ulong RegionHandle { get; }
+ UUID RegionID { get; }
- // TODO: Convert to a patch-based system, and expose terrain editing
- // through functions instead of a property
- float[] Heightmap { get; set; }
float WaterHeight { get; }
+ uint TerrainPatchWidth { get; }
+ uint TerrainPatchHeight { get; }
+
+ float[,] GetTerrainPatch(uint x, uint y);
+ void SetTerrainPatch(object sender, uint x, uint y, float[,] patchData);
+
bool ObjectAdd(object sender, SimulationObject obj, PrimFlags creatorFlags);
bool ObjectRemove(object sender, uint localID);
bool ObjectRemove(object sender, UUID id);
diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs
index 18457cdb..67dcf3e4 100644
--- a/Programs/Simian/Simian.cs
+++ b/Programs/Simian/Simian.cs
@@ -18,17 +18,12 @@ namespace Simian
{
public const string CONFIG_FILE = "Simian.ini";
- // TODO: Don't hard-code these
- public const uint REGION_X = 256000;
- public const uint REGION_Y = 256000;
-
public int UDPPort = 9000;
public int HttpPort = 8002;
public string DataDir = "SimianData/";
public WebServer HttpServer;
public IniConfigSource ConfigFile;
- public ulong RegionHandle;
// Interfaces
public IAuthenticationProvider Authentication;
@@ -68,8 +63,6 @@ namespace Simian
InitHttpServer(HttpPort, true);
- RegionHandle = Utils.UIntsToLong(REGION_X, REGION_Y);
-
try
{
// Create a list of references for .cs extensions that are compiled at runtime