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