* Created an LLUDP folder to hold extensions that are purely LLUDP packet handlers. This is not a complete abstraction away from transport protocols, but it's a start
* Moved physics code from Movement.cs into PhysicsSimple.cs, and moved the physics loop into a thread in SceneManager
* Simian.ini cleanup


git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2490 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
John Hurliman
2009-03-17 22:33:22 +00:00
parent 3c5d40402f
commit 241b480320
22 changed files with 997 additions and 877 deletions

View File

@@ -0,0 +1,70 @@
using System;
using System.Net;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace Simian
{
public static class CapsMessages
{
public static OSDMap TeleportFinish(UUID agentID, int locationID, ulong regionHandle, Uri seedCap, SimAccess simAccess,
IPAddress simIP, int simPort, TeleportFlags teleportFlags)
{
OSDMap info = new OSDMap(8);
info.Add("AgentID", OSD.FromUUID(agentID));
info.Add("LocationID", OSD.FromInteger(locationID)); // Unused by the client
info.Add("RegionHandle", OSD.FromULong(regionHandle));
info.Add("SeedCapability", OSD.FromUri(seedCap));
info.Add("SimAccess", OSD.FromInteger((byte)simAccess));
info.Add("SimIP", OSD.FromBinary(simIP.GetAddressBytes()));
info.Add("SimPort", OSD.FromInteger(simPort));
info.Add("TeleportFlags", OSD.FromUInteger((uint)teleportFlags));
OSDArray infoArray = new OSDArray(1);
infoArray.Add(info);
OSDMap teleport = new OSDMap(1);
teleport.Add("Info", infoArray);
return teleport;
}
public static OSDMap EnableSimulator(ulong regionHandle, IPAddress ip, int port)
{
OSDMap llsdSimInfo = new OSDMap(3);
llsdSimInfo.Add("Handle", OSD.FromULong(regionHandle));
llsdSimInfo.Add("IP", OSD.FromBinary(ip.GetAddressBytes()));
llsdSimInfo.Add("Port", OSD.FromInteger(port));
OSDArray arr = new OSDArray(1);
arr.Add(llsdSimInfo);
OSDMap llsdBody = new OSDMap(1);
llsdBody.Add("SimulatorInfo", arr);
return llsdBody;
}
public static OSDMap EnableClient(UUID agentID, UUID sessionID, UUID secureSessionID, int circuitCode, string firstName, string lastName, Uri callbackUri)
{
OSDMap map = new OSDMap(7);
map["agent_id"] = OSD.FromUUID(agentID);
map["session_id"] = OSD.FromUUID(sessionID);
map["secure_session_id"] = OSD.FromUUID(secureSessionID);
map["circuit_code"] = OSD.FromInteger(circuitCode);
map["first_name"] = OSD.FromString(firstName);
map["last_name"] = OSD.FromString(lastName);
map["callback_uri"] = OSD.FromUri(callbackUri);
return map;
}
public static OSDMap EnableClientComplete(UUID agentID)
{
OSDMap map = new OSDMap(1);
map["agent_id"] = OSD.FromUUID(agentID);
return map;
}
}
}

View File

@@ -32,6 +32,8 @@ namespace Simian
public class SceneManager : ISceneProvider
{
const int TARGET_FRAMES_PER_SECOND = 45;
// Interfaces. Although no other classes will access these interfaces directly
// (getters are used instead), they must be marked public so ExtensionLoader
// can automatically assign them
@@ -100,6 +102,7 @@ namespace Simian
public uint TerrainPatchHeight { get { return 16; } }
public uint TerrainPatchCountWidth { get { return 16; } }
public uint TerrainPatchCountHeight { get { return 16; } }
public float TimeDilation { get { return TimeDilation; } }
Simian server;
// Contains all scene objects, including prims and avatars
@@ -130,6 +133,8 @@ namespace Simian
/// is established for a client in this dictionary, an enable_client_complete message will be
/// sent to the associated URI</summary>
Dictionary<UUID, Uri> enableClientCompleteCallbacks = new Dictionary<UUID, Uri>();
float timeDilation;
bool running;
public SceneManager()
{
@@ -137,6 +142,8 @@ namespace Simian
public bool Start(Simian server, RegionInfo regionInfo, X509Certificate2 regionCert, string defaultTerrainFile, int staticObjects, int physicalObjects)
{
running = true;
this.server = server;
this.regionName = regionInfo.Name;
this.endpoint = regionInfo.IPAndPort;
@@ -193,11 +200,16 @@ namespace Simian
server.Grid.OnRegionUpdate += Grid_OnRegionUpdate;
udp.OnAgentConnection += udp_OnAgentConnection;
udp.RegisterPacketCallback(PacketType.CompleteAgentMovement, CompleteAgentMovementHandler);
udp.RegisterPacketCallback(PacketType.ChatFromViewer, ChatHandler);
// Load the default terrain for this sim
if (!String.IsNullOrEmpty(defaultTerrainFile))
LoadTerrain(Simian.DATA_DIR + defaultTerrainFile);
// Start the physics thread
Thread physicsThread = new Thread(new ThreadStart(PhysicsThread));
physicsThread.Name = "Physics";
physicsThread.Start();
Logger.Log(String.Format("Region {0} online at ({1},{2}) listening on {3}", regionName, regionX, regionY, endpoint),
Helpers.LogLevel.Info);
@@ -208,18 +220,12 @@ namespace Simian
return true;
}
void ChatHandler(Packet packet, Agent agent)
{
ChatFromViewerPacket chat = (ChatFromViewerPacket)packet;
string message = Utils.BytesToString(chat.ChatData.Message);
if (message == "lol")
SendEvent(agent, "lol", new OSDMap());
}
public void Stop()
{
Logger.Log("Stopping region " + regionName, Helpers.LogLevel.Info);
running = false;
// Remove all of the agents from the scene. This will shutdown UDP connections and event queues to
// each of the agents as well
lock (sceneAgents)
@@ -990,14 +996,8 @@ namespace Simian
// Create a callback for enable_client_complete
Uri callbackUri = server.Capabilities.CreateCapability(EnableClientCompleteCapHandler, false, null);
OSDMap map = new OSDMap();
map["agent_id"] = OSD.FromUUID(agent.ID);
map["session_id"] = OSD.FromUUID(agent.SessionID);
map["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID);
map["circuit_code"] = OSD.FromInteger((int)agent.CircuitCode);
map["first_name"] = OSD.FromString(agent.Info.FirstName);
map["last_name"] = OSD.FromString(agent.Info.LastName);
map["callback_uri"] = OSD.FromUri(callbackUri);
OSDMap enableClient = CapsMessages.EnableClient(agent.ID, agent.SessionID, agent.SecureSessionID,
(int)agent.CircuitCode, agent.Info.FirstName, agent.Info.LastName, callbackUri);
AutoResetEvent waitEvent = new AutoResetEvent(false);
@@ -1014,24 +1014,15 @@ namespace Simian
if (success)
{
// Send the EnableSimulator capability to clients
OSDMap llsdSimInfo = new OSDMap(3);
RegionInfo neighbor = neighbors[i];
OSDMap enableSimulator = CapsMessages.EnableSimulator(neighbor.Handle, neighbor.IPAndPort.Address, neighbor.IPAndPort.Port);
llsdSimInfo.Add("Handle", OSD.FromULong(neighbors[i].Handle));
llsdSimInfo.Add("IP", OSD.FromBinary(neighbors[i].IPAndPort.Address.GetAddressBytes()));
llsdSimInfo.Add("Port", OSD.FromInteger(neighbors[i].IPAndPort.Port));
OSDArray arr = new OSDArray(1);
arr.Add(llsdSimInfo);
OSDMap llsdBody = new OSDMap(1);
llsdBody.Add("SimulatorInfo", arr);
SendEvent(agent, "EnableSimulator", llsdBody);
SendEvent(agent, "EnableSimulator", enableSimulator);
}
}
waitEvent.Set();
};
request.StartRequest(map);
request.StartRequest(enableClient);
if (!waitEvent.WaitOne(30 * 1000, false))
Logger.Log("enable_client request timed out", Helpers.LogLevel.Warning);
@@ -1229,8 +1220,7 @@ namespace Simian
Logger.Log("Sending enable_client_complete callback to " + callbackUri.ToString(), Helpers.LogLevel.Info);
OSDMap map = new OSDMap(1);
map["agent_id"] = OSD.FromUUID(agent.ID);
OSDMap enableClientComplete = CapsMessages.EnableClientComplete(agent.ID);
AutoResetEvent waitEvent = new AutoResetEvent(false);
@@ -1251,7 +1241,7 @@ namespace Simian
}
waitEvent.Set();
};
request.StartRequest(map);
request.StartRequest(enableClientComplete);
if (!waitEvent.WaitOne(30 * 1000, false))
Logger.Log("enable_client_complete request timed out", Helpers.LogLevel.Warning);
@@ -1285,6 +1275,40 @@ namespace Simian
#endregion Callback Handlers
void PhysicsThread()
{
const float TARGET_FRAME_TIME = 1f / (float)TARGET_FRAMES_PER_SECOND;
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
float elapsedTime = 0f;
int sleepMS;
while (running)
{
stopwatch.Start();
physics.Update(elapsedTime);
// Measure the duration of this frame
stopwatch.Stop();
elapsedTime = (float)stopwatch.Elapsed.TotalSeconds;
stopwatch.Reset();
// Calculate time dilation and decide if we need to sleep to limit FPS
if (elapsedTime < TARGET_FRAME_TIME)
{
timeDilation = (1f / elapsedTime) / (float)TARGET_FRAMES_PER_SECOND;
sleepMS = (int)((TARGET_FRAME_TIME - elapsedTime) * 1000f);
Thread.Sleep(sleepMS);
elapsedTime = TARGET_FRAME_TIME;
}
else
{
timeDilation = 1f;
}
}
}
void LoadTerrain(string mapFile)
{
byte[] rgbValues = new byte[256 * 256 * 3];
@@ -1366,7 +1390,7 @@ namespace Simian
// Send an update out to the creator
ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket();
updateToOwner.RegionData.RegionHandle = regionHandle;
updateToOwner.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue);
updateToOwner.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1];
updateToOwner.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim,
obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner, obj.CRC);
@@ -1629,7 +1653,7 @@ namespace Simian
// Send an update out to the creator
ObjectUpdateCompressedPacket updateToOwner = new ObjectUpdateCompressedPacket();
updateToOwner.RegionData.RegionHandle = regionHandle;
updateToOwner.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue);
updateToOwner.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[1];
updateToOwner.ObjectData[0] = new ObjectUpdateCompressedPacket.ObjectDataBlock();
updateToOwner.ObjectData[0].UpdateFlags = (uint)(obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner);
@@ -1707,7 +1731,7 @@ namespace Simian
ImprovedTerseObjectUpdatePacket update = new ImprovedTerseObjectUpdatePacket();
update.RegionData.RegionHandle = RegionHandle;
update.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
update.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue);
update.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
update.ObjectData[0] = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock();
update.ObjectData[0].Data = data;

View File

@@ -6,5 +6,8 @@ namespace Simian
public interface IParcelProvider
{
void SendParcelOverlay(Agent agent);
void UpdateParcel(Parcel parcel);
int GetParcelID(int x, int y);
bool TryGetParcel(int parcelID, out Parcel parcel);
}
}

View File

@@ -5,7 +5,11 @@ namespace Simian
{
public interface IPhysicsProvider
{
float TimeDilation { get; }
/// <summary>
/// Runs a single physics frame
/// </summary>
/// <param name="elapsedTime">The time since Update was called last</param>
void Update(float elapsedTime);
Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj);
bool TryGetObjectMass(UUID objectID, out float mass);

View File

@@ -87,6 +87,8 @@ namespace Simian
#endregion Scene related classes
#region Scene delegates
public delegate void ObjectAddOrUpdateCallback(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags updateFlags);
public delegate void ObjectRemoveCallback(object sender, SimulationObject obj);
public delegate void ObjectSetRotationAxisCallback(object sender, SimulationObject obj, Vector3 rotationAxis);
@@ -105,6 +107,8 @@ namespace Simian
public delegate void TerrainUpdateCallback(object sender, uint x, uint y, float[,] patchData);
public delegate void WindUpdateCallback(object sender, uint x, uint y, Vector2 windSpeed);
#endregion Scene delegates
public interface ISceneProvider
{
event ObjectAddOrUpdateCallback OnObjectAddOrUpdate;
@@ -144,13 +148,12 @@ namespace Simian
Vector3 DefaultLookAt { get; }
Vector3 DefaultPosition { get; }
IPEndPoint IPAndPort { get; set; }
float WaterHeight { get; }
uint TerrainPatchWidth { get; }
uint TerrainPatchHeight { get; }
uint TerrainPatchCountWidth { get; }
uint TerrainPatchCountHeight { get; }
float TimeDilation { get; }
bool Start(Simian server, RegionInfo regionInfo, X509Certificate2 regionCert, string defaultTerrainFile, int staticObjects, int physicalObjects);
void Stop();

View File

@@ -6,12 +6,12 @@ using OpenMetaverse.Packets;
namespace Simian
{
public class TransferManager : IExtension<ISceneProvider>
public class LLAssets : IExtension<ISceneProvider>
{
ISceneProvider scene;
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
public TransferManager()
public LLAssets()
{
}

View File

@@ -5,11 +5,11 @@ using OpenMetaverse.Packets;
namespace Simian
{
public class ConnectionManagement : IExtension<ISceneProvider>
public class LLConnections : IExtension<ISceneProvider>
{
ISceneProvider scene;
public ConnectionManagement()
public LLConnections()
{
}
@@ -114,7 +114,7 @@ namespace Simian
{
ObjectUpdatePacket update = new ObjectUpdatePacket();
update.RegionData.RegionHandle = scene.RegionHandle;
update.RegionData.TimeDilation = (ushort)(scene.Physics.TimeDilation * (float)UInt16.MaxValue);
update.RegionData.TimeDilation = (ushort)(scene.TimeDilation * (float)UInt16.MaxValue);
update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1];
update.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC);

View File

@@ -241,7 +241,7 @@ namespace Simian
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
// FIXME: Add XML config support for HyperGrid destinations
// FIXME: Add .ini config support for HyperGrid destinations
/*else if (request.Info.RegionHandle == Utils.UIntsToLong((scene.RegionX + 1) * 256, scene.RegionY * 256))
{
// Special case: adjacent simulator is the HyperGrid portal
@@ -296,21 +296,8 @@ namespace Simian
Logger.Log(String.Format("HyperGrid teleporting to {0} ({1}, {2}) @ {3}",
link.RegionName, x, y, destination), Helpers.LogLevel.Info);
OSDMap info = new OSDMap();
info.Add("AgentID", OSD.FromUUID(agent.ID));
info.Add("LocationID", OSD.FromInteger(4)); // Unused by the client
info.Add("RegionHandle", OSD.FromULong(link.RegionHandle));
info.Add("SeedCapability", OSD.FromUri(seedCap));
info.Add("SimAccess", OSD.FromInteger((byte)SimAccess.Min));
info.Add("SimIP", OSD.FromBinary(entry.AddressList[0].GetAddressBytes()));
info.Add("SimPort", OSD.FromInteger(link.UDPPort));
info.Add("TeleportFlags", OSD.FromUInteger((uint)TeleportFlags.ViaLocation));
OSDArray infoArray = new OSDArray(1);
infoArray.Add(info);
OSDMap teleport = new OSDMap();
teleport.Add("Info", infoArray);
OSDMap teleport = CapsMessages.TeleportFinish(agent.ID, 4, link.RegionHandle, seedCap, (byte)SimAccess.Min,
entry.AddressList[0], link.UDPPort, TeleportFlags.ViaLocation);
scene.SendEvent(agent, "TeleportFinish", teleport);
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.Rendering;
namespace Simian
{
public class LLMovement : IExtension<ISceneProvider>
{
//static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
//static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f);
ISceneProvider scene;
public LLMovement()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
return true;
}
public void Stop()
{
}
void AgentRequestSitHandler(Packet packet, Agent agent)
{
AgentRequestSitPacket request = (AgentRequestSitPacket)packet;
SimulationObject obj;
if (scene.TryGetObject(request.TargetObject.TargetID, out obj))
{
agent.RequestedSitTarget = request.TargetObject.TargetID;
agent.RequestedSitOffset = request.TargetObject.Offset;
AvatarSitResponsePacket response = new AvatarSitResponsePacket();
response.SitObject.ID = request.TargetObject.TargetID;
response.SitTransform.AutoPilot = true;
response.SitTransform.CameraAtOffset = Vector3.Zero;
response.SitTransform.CameraEyeOffset = Vector3.Zero;
response.SitTransform.ForceMouselook = false;
response.SitTransform.SitPosition = request.TargetObject.Offset;
response.SitTransform.SitRotation = obj.SitRotation;
scene.UDP.SendPacket(agent.ID, response, PacketCategory.State);
}
else
{
//TODO: send error
}
}
void AgentSitHandler(Packet packet, Agent agent)
{
AgentSitPacket sit = (AgentSitPacket)packet;
if (agent.RequestedSitTarget != UUID.Zero)
{
SimulationObject obj;
SimulationObject avObj;
if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj))
{
agent.Avatar.Prim.Flags &= ~PrimFlags.Physics;
agent.Avatar.Prim.ParentID = obj.Prim.LocalID;
agent.Avatar.Prim.Position = new Vector3(
obj.Prim.Scale.X * 0.5f,
obj.Prim.Scale.Z * 0.5f,
agent.Avatar.Prim.Scale.Z * 0.33f);
scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
scene.Avatars.SetDefaultAnimation(agent, Animations.SIT);
scene.Avatars.SendAnimations(agent);
}
else
{
//TODO: send error
}
agent.RequestedSitTarget = UUID.Zero;
agent.RequestedSitOffset = Vector3.Zero;
}
}
void AgentUpdateHandler(Packet packet, Agent agent)
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
SimulationObject obj;
if (scene.TryGetObject(agent.ID, out obj))
{
if (agent.Avatar.Prim.ParentID == 0)
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
agent.State = (AgentState)update.AgentData.State;
agent.HideTitle = update.AgentData.Flags != 0;
// Check for standing up
SimulationObject parent;
if (scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) &&
agent.Avatar.Prim.ParentID > 0 &&
(agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP)
{
agent.Avatar.Prim.Position = parent.Prim.Position
+ Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation))
+ Vector3.UnitZ;
agent.Avatar.Prim.ParentID = 0;
scene.Avatars.SetDefaultAnimation(agent, Animations.STAND);
scene.Avatars.SendAnimations(agent);
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
}
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation);
}
}
void SetAlwaysRunHandler(Packet packet, Agent agent)
{
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
agent.Running = run.AgentData.AlwaysRun;
}
}
}

View File

@@ -8,11 +8,11 @@ using OpenMetaverse.Packets;
namespace Simian
{
public class ObjectManager : IExtension<ISceneProvider>
public class LLObjects : IExtension<ISceneProvider>
{
ISceneProvider scene;
public ObjectManager()
public LLObjects()
{
}

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class LLParcels : IExtension<ISceneProvider>
{
ISceneProvider scene;
public LLParcels()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler);
return true;
}
public void Stop()
{
}
void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result,
Agent agent)
{
Parcel parcel;
if (scene.Parcels.TryGetParcel(parcelID, out parcel))
{
ParcelPropertiesPacket properties = new ParcelPropertiesPacket();
properties.AgeVerificationBlock.RegionDenyAgeUnverified = false;
properties.ParcelData.AABBMax = parcel.AABBMax;
properties.ParcelData.AABBMin = parcel.AABBMin;
properties.ParcelData.Area = parcel.Area;
properties.ParcelData.AuctionID = parcel.AuctionID;
properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID;
properties.ParcelData.Bitmap = parcel.Bitmap;
properties.ParcelData.Category = (byte)parcel.Category;
properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate);
properties.ParcelData.ClaimPrice = parcel.ClaimPrice;
properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc);
properties.ParcelData.GroupID = parcel.GroupID;
properties.ParcelData.GroupPrims = parcel.GroupPrims;
properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned;
properties.ParcelData.LandingType = (byte)parcel.Landing;
properties.ParcelData.LocalID = parcel.LocalID;
properties.ParcelData.MaxPrims = parcel.MaxPrims;
properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale;
properties.ParcelData.MediaID = parcel.Media.MediaID;
properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL);
properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL);
properties.ParcelData.Name = Utils.StringToBytes(parcel.Name);
properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime;
properties.ParcelData.OtherCount = parcel.OtherCount;
properties.ParcelData.OtherPrims = parcel.OtherPrims;
properties.ParcelData.OwnerID = parcel.OwnerID;
properties.ParcelData.OwnerPrims = parcel.OwnerPrims;
properties.ParcelData.ParcelFlags = (uint)parcel.Flags;
properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus;
properties.ParcelData.PassHours = parcel.PassHours;
properties.ParcelData.PassPrice = parcel.PassPrice;
properties.ParcelData.PublicCount = parcel.PublicCount;
properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous;
properties.ParcelData.RegionDenyIdentified = false; // Deprecated
properties.ParcelData.RegionDenyTransacted = false; // Deprecated
properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride;
properties.ParcelData.RentPrice = parcel.RentPrice;
properties.ParcelData.RequestResult = (int)result;
properties.ParcelData.SalePrice = parcel.SalePrice;
properties.ParcelData.SelectedPrims = 0; // TODO:
properties.ParcelData.SelfCount = parcel.SelfCount;
properties.ParcelData.SequenceID = sequenceID;
properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims;
properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims;
properties.ParcelData.SnapSelection = snapSelection;
properties.ParcelData.SnapshotID = parcel.SnapshotID;
properties.ParcelData.Status = (byte)parcel.Status;
properties.ParcelData.TotalPrims = parcel.TotalPrims;
properties.ParcelData.UserLocation = parcel.UserLocation;
properties.ParcelData.UserLookAt = parcel.UserLookAt;
// HACK: Make everyone think they are the owner of this parcel
properties.ParcelData.OwnerID = agent.ID;
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
}
else
{
Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning);
}
}
void ParcelPropertiesRequestHandler(Packet packet, Agent agent)
{
ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet;
// TODO: Replace with HashSet when we switch to .NET 3.5
List<int> parcels = new List<int>();
// Convert the boundaries to integers
int north = (int)Math.Round(request.ParcelData.North) / 4;
int east = (int)Math.Round(request.ParcelData.East) / 4;
int south = (int)Math.Round(request.ParcelData.South) / 4;
int west = (int)Math.Round(request.ParcelData.West) / 4;
// Find all of the parcels within the given boundaries
int xLen = east - west;
int yLen = north - south;
for (int x = 0; x < xLen; x++)
{
for (int y = 0; y < yLen; y++)
{
if (west + x < 64 && south + y < 64)
{
int currentParcelID = scene.Parcels.GetParcelID(west + x, south + y);
if (!parcels.Contains(currentParcelID))
parcels.Add(currentParcelID);
}
}
}
ParcelResult result = ParcelResult.NoData;
if (parcels.Count == 1)
result = ParcelResult.Single;
else if (parcels.Count > 1)
result = ParcelResult.Multiple;
for (int i = 0; i < parcels.Count; i++)
SendParcelProperties(parcels[i], request.ParcelData.SequenceID, request.ParcelData.SnapSelection, result, agent);
}
void ParcelPropertiesUpdateHandler(Packet packet, Agent agent)
{
ParcelPropertiesUpdatePacket update = (ParcelPropertiesUpdatePacket)packet;
Parcel parcel;
if (scene.Parcels.TryGetParcel(update.ParcelData.LocalID, out parcel))
{
parcel.AuthBuyerID = update.ParcelData.AuthBuyerID;
parcel.Category = (Parcel.ParcelCategory)update.ParcelData.Category;
parcel.Desc = Utils.BytesToString(update.ParcelData.Desc);
parcel.Flags = (Parcel.ParcelFlags)update.ParcelData.ParcelFlags;
parcel.GroupID = update.ParcelData.GroupID;
parcel.Landing = (Parcel.LandingType)update.ParcelData.LandingType;
parcel.Media.MediaAutoScale = update.ParcelData.MediaAutoScale;
parcel.Media.MediaID = update.ParcelData.MediaID;
parcel.Media.MediaURL = Utils.BytesToString(update.ParcelData.MediaURL);
parcel.MusicURL = Utils.BytesToString(update.ParcelData.MusicURL);
parcel.Name = Utils.BytesToString(update.ParcelData.Name);
parcel.PassHours = update.ParcelData.PassHours;
parcel.PassPrice = update.ParcelData.PassPrice;
parcel.SalePrice = update.ParcelData.SalePrice;
parcel.SnapshotID = update.ParcelData.SnapshotID;
parcel.UserLocation = update.ParcelData.UserLocation;
parcel.UserLookAt = update.ParcelData.UserLookAt;
scene.Parcels.UpdateParcel(parcel);
if (update.ParcelData.Flags != 0)
SendParcelProperties(parcel.LocalID, 0, false, ParcelResult.Single, agent);
}
else
{
Logger.Log("Got a ParcelPropertiesUpdate for an unknown parcel " + update.ParcelData.LocalID,
Helpers.LogLevel.Warning);
}
}
}
}

View File

@@ -102,12 +102,12 @@ namespace Simian
}
}
public class ImageDelivery : IExtension<ISceneProvider>
public class LLTextures : IExtension<ISceneProvider>
{
ISceneProvider scene;
Dictionary<UUID, ImageDownload> CurrentDownloads = new Dictionary<UUID, ImageDownload>();
public ImageDelivery()
public LLTextures()
{
}

View File

@@ -1,518 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.Rendering;
namespace Simian
{
public class Movement : IExtension<ISceneProvider>
{
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
const float GRAVITY = 9.8f; //meters/sec
const float WALK_SPEED = 3f; //meters/sec
const float RUN_SPEED = 5f; //meters/sec
const float FLY_SPEED = 10f; //meters/sec
const float FALL_DELAY = 0.33f; //seconds before starting animation
const float FALL_FORGIVENESS = .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
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
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f);
const float SQRT_TWO = 1.41421356f;
ISceneProvider scene;
Timer updateTimer;
long lastTick;
public int LastTick
{
get { return (int) Interlocked.Read(ref lastTick); }
set { Interlocked.Exchange(ref lastTick, value); }
}
public Movement()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
LastTick = Environment.TickCount;
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
return true;
}
public void Stop()
{
if (updateTimer != null)
{
updateTimer.Dispose();
updateTimer = null;
}
}
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
{
bool forceMeshing = false;
bool forceTransform = false;
if ((update & UpdateFlags.Scale) != 0 ||
(update & UpdateFlags.Position) != 0 ||
(update & UpdateFlags.Rotation) != 0)
{
forceTransform = true;
}
if ((update & UpdateFlags.PrimData) != 0)
{
forceMeshing = true;
}
// TODO: This doesn't update children prims when their parents move
obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform);
}
void UpdateTimer_Elapsed(object sender)
{
int tick = Environment.TickCount;
float seconds = (float)((tick - LastTick) / 1000f);
LastTick = tick;
scene.ForEachAgent(
delegate(Agent agent)
{
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
return;
bool animsChanged = false;
// Create forward and left vectors from the current avatar rotation
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
// Check control flags
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
// direction in which the avatar is trying to move
Vector3 move = Vector3.Zero;
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
if (heldUp) { move.Z += 1; }
if (heldDown) { move.Z -= 1; }
// is the avatar trying to move?
bool moving = move != Vector3.Zero;
bool jumping = agent.TickJump != 0;
// 2-dimensional speed multipler
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
if ((heldForward || heldBack) && (heldLeft || heldRight))
speed /= SQRT_TWO;
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
agentPosition += (move * speed);
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
if (!flying && newFloor != oldFloor)
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
//HACK: distance from avatar center to the bottom of its feet
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
float lowerLimit = newFloor + distanceFromFloor;
//"bridge" physics
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
{
//start ray at our feet
Vector3 rayStart = new Vector3(
agent.Avatar.Prim.Position.X,
agent.Avatar.Prim.Position.Y,
agent.Avatar.Prim.Position.Z - distanceFromFloor
);
//end ray at 0.01m below our feet
Vector3 rayEnd = new Vector3(
rayStart.X,
rayStart.Y,
rayStart.Z - 0.01f
);
scene.ForEachObject(delegate(SimulationObject obj)
{
//HACK: check nearby objects (what did you expect, octree?)
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
{
Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
if (collision != rayEnd) //we collided!
{
//check if we are any higher than before
float height = collision.Z + distanceFromFloor;
if (height > lowerLimit) lowerLimit = height;
}
}
});
}
// Z acceleration resulting from gravity
float gravity = 0f;
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
if (flying)
{
agent.TickFall = 0;
agent.TickJump = 0;
//velocity falloff while flying
agent.Avatar.Prim.Velocity.X *= 0.66f;
agent.Avatar.Prim.Velocity.Y *= 0.66f;
agent.Avatar.Prim.Velocity.Z *= 0.33f;
if (agent.Avatar.Prim.Position.Z == lowerLimit)
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
if (move.X != 0 || move.Y != 0)
{ //flying horizontally
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
animsChanged = true;
}
else if (move.Z > 0)
{ //flying straight up
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.Z < 0)
{ //flying straight down
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
animsChanged = true;
}
else
{ //hovering in the air
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
{ //falling, floating, or landing from a jump
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
{ //above water
//override controls while drifting
move = Vector3.Zero;
//keep most of our horizontal inertia
agent.Avatar.Prim.Velocity.X *= 0.975f;
agent.Avatar.Prim.Velocity.Y *= 0.975f;
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
{ //just started falling
agent.TickFall = Environment.TickCount;
}
else
{
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
if (!jumping)
{ //falling
if (fallElapsed > FALL_DELAY)
{ //falling long enough to trigger the animation
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
}
}
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
{ //at the water line
gravity = 0f;
agent.Avatar.Prim.Velocity *= 0.5f;
agent.Avatar.Prim.Velocity.Z = 0f;
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
if (move.Z > 0)
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.X != 0 || move.Y != 0)
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
animsChanged = true;
}
else
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else
{ //underwater
gravity = 0f; //buoyant
agent.Avatar.Prim.Velocity *= 0.5f * seconds;
agent.Avatar.Prim.Velocity.Z += 0.75f * seconds;
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
else
{ //on the ground
agent.TickFall = 0;
//friction
agent.Avatar.Prim.Acceleration *= 0.2f;
agent.Avatar.Prim.Velocity *= 0.2f;
agent.Avatar.Prim.Position.Z = lowerLimit;
if (move.Z > 0)
{ //jumping
if (!jumping)
{ //begin prejump
move.Z = 0; //override Z control
if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
animsChanged = true;
agent.TickJump = Environment.TickCount;
}
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
{ //start actual jump
if (agent.TickJump == -1)
{
//already jumping! end current jump
agent.TickJump = 0;
return;
}
if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
animsChanged = true;
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
agent.TickJump = -1; //flag that we are currently jumping
}
else move.Z = 0; //override Z control
}
else
{ //not jumping
agent.TickJump = 0;
if (move.X != 0 || move.Y != 0)
{ //not walking
if (move.Z < 0)
{ //crouchwalking
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
animsChanged = true;
}
else if (agent.Running)
{ //running
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
animsChanged = true;
}
else
{ //walking
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
animsChanged = true;
}
}
else
{ //walking
if (move.Z < 0)
{ //crouching
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
animsChanged = true;
}
else
{ //standing
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
animsChanged = true;
}
}
}
}
if (animsChanged)
scene.Avatars.SendAnimations(agent);
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
// static acceleration when any control is held, otherwise none
if (moving)
{
agent.Avatar.Prim.Acceleration = move * speed;
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
agent.Avatar.Prim.Acceleration.Z = -maxVel;
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
agent.Avatar.Prim.Acceleration.Z = maxVel;
}
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
agent.Avatar.Prim.Velocity.Z = -maxVel;
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
agent.Avatar.Prim.Velocity.Z = maxVel;
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
}
);
}
void AgentRequestSitHandler(Packet packet, Agent agent)
{
AgentRequestSitPacket request = (AgentRequestSitPacket)packet;
SimulationObject obj;
if (scene.TryGetObject(request.TargetObject.TargetID, out obj))
{
agent.RequestedSitTarget = request.TargetObject.TargetID;
agent.RequestedSitOffset = request.TargetObject.Offset;
AvatarSitResponsePacket response = new AvatarSitResponsePacket();
response.SitObject.ID = request.TargetObject.TargetID;
response.SitTransform.AutoPilot = true;
response.SitTransform.CameraAtOffset = Vector3.Zero;
response.SitTransform.CameraEyeOffset = Vector3.Zero;
response.SitTransform.ForceMouselook = false;
response.SitTransform.SitPosition = request.TargetObject.Offset;
response.SitTransform.SitRotation = obj.SitRotation;
scene.UDP.SendPacket(agent.ID, response, PacketCategory.State);
}
else
{
//TODO: send error
}
}
void AgentSitHandler(Packet packet, Agent agent)
{
AgentSitPacket sit = (AgentSitPacket)packet;
if (agent.RequestedSitTarget != UUID.Zero)
{
SimulationObject obj;
SimulationObject avObj;
if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj))
{
agent.Avatar.Prim.Flags &= ~PrimFlags.Physics;
agent.Avatar.Prim.ParentID = obj.Prim.LocalID;
agent.Avatar.Prim.Position = new Vector3(
obj.Prim.Scale.X * 0.5f,
obj.Prim.Scale.Z * 0.5f,
agent.Avatar.Prim.Scale.Z * 0.33f);
scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
scene.Avatars.SetDefaultAnimation(agent, Animations.SIT);
scene.Avatars.SendAnimations(agent);
}
else
{
//TODO: send error
}
agent.RequestedSitTarget = UUID.Zero;
agent.RequestedSitOffset = Vector3.Zero;
}
}
void AgentUpdateHandler(Packet packet, Agent agent)
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
SimulationObject obj;
if (scene.TryGetObject(agent.ID, out obj))
{
if (agent.Avatar.Prim.ParentID == 0)
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
agent.State = (AgentState)update.AgentData.State;
agent.HideTitle = update.AgentData.Flags != 0;
// Check for standing up
SimulationObject parent;
if (scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) &&
agent.Avatar.Prim.ParentID > 0 &&
(agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP)
{
agent.Avatar.Prim.Position = parent.Prim.Position
+ Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation))
+ Vector3.UnitZ;
agent.Avatar.Prim.ParentID = 0;
scene.Avatars.SetDefaultAnimation(agent, Animations.STAND);
scene.Avatars.SendAnimations(agent);
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
}
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation);
}
}
void SetAlwaysRunHandler(Packet packet, Agent agent)
{
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
agent.Running = run.AgentData.AlwaysRun;
}
}
}

View File

@@ -48,8 +48,6 @@ namespace Simian
// Add the default parcel to the list
parcels[parcel.LocalID] = parcel;
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler);
return true;
}
@@ -124,6 +122,21 @@ namespace Simian
}
}
public void UpdateParcel(Parcel parcel)
{
lock (parcels) parcels[parcel.LocalID] = parcel;
}
public int GetParcelID(int x, int y)
{
return parcelOverlay[y * 64 + x];
}
public bool TryGetParcel(int parcelID, out Parcel parcel)
{
return parcels.TryGetValue(parcelID, out parcel);
}
void UpdateParcelSize(ref Parcel parcel)
{
int minX = 64;
@@ -156,152 +169,5 @@ namespace Simian
parcel.AABBMax.Y = maxY;
parcel.Area = area;
}
void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result,
Agent agent)
{
Parcel parcel;
if (parcels.TryGetValue(parcelID, out parcel))
{
ParcelPropertiesPacket properties = new ParcelPropertiesPacket();
properties.AgeVerificationBlock.RegionDenyAgeUnverified = false;
properties.ParcelData.AABBMax = parcel.AABBMax;
properties.ParcelData.AABBMin = parcel.AABBMin;
properties.ParcelData.Area = parcel.Area;
properties.ParcelData.AuctionID = parcel.AuctionID;
properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID;
properties.ParcelData.Bitmap = parcel.Bitmap;
properties.ParcelData.Category = (byte)parcel.Category;
properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate);
properties.ParcelData.ClaimPrice = parcel.ClaimPrice;
properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc);
properties.ParcelData.GroupID = parcel.GroupID;
properties.ParcelData.GroupPrims = parcel.GroupPrims;
properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned;
properties.ParcelData.LandingType = (byte)parcel.Landing;
properties.ParcelData.LocalID = parcel.LocalID;
properties.ParcelData.MaxPrims = parcel.MaxPrims;
properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale;
properties.ParcelData.MediaID = parcel.Media.MediaID;
properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL);
properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL);
properties.ParcelData.Name = Utils.StringToBytes(parcel.Name);
properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime;
properties.ParcelData.OtherCount = parcel.OtherCount;
properties.ParcelData.OtherPrims = parcel.OtherPrims;
properties.ParcelData.OwnerID = parcel.OwnerID;
properties.ParcelData.OwnerPrims = parcel.OwnerPrims;
properties.ParcelData.ParcelFlags = (uint)parcel.Flags;
properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus;
properties.ParcelData.PassHours = parcel.PassHours;
properties.ParcelData.PassPrice = parcel.PassPrice;
properties.ParcelData.PublicCount = parcel.PublicCount;
properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous;
properties.ParcelData.RegionDenyIdentified = false; // Deprecated
properties.ParcelData.RegionDenyTransacted = false; // Deprecated
properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride;
properties.ParcelData.RentPrice = parcel.RentPrice;
properties.ParcelData.RequestResult = (int)result;
properties.ParcelData.SalePrice = parcel.SalePrice;
properties.ParcelData.SelectedPrims = 0; // TODO:
properties.ParcelData.SelfCount = parcel.SelfCount;
properties.ParcelData.SequenceID = sequenceID;
properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims;
properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims;
properties.ParcelData.SnapSelection = snapSelection;
properties.ParcelData.SnapshotID = parcel.SnapshotID;
properties.ParcelData.Status = (byte)parcel.Status;
properties.ParcelData.TotalPrims = parcel.TotalPrims;
properties.ParcelData.UserLocation = parcel.UserLocation;
properties.ParcelData.UserLookAt = parcel.UserLookAt;
// HACK: Make everyone think they are the owner of this parcel
properties.ParcelData.OwnerID = agent.ID;
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
}
else
{
Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning);
}
}
void ParcelPropertiesRequestHandler(Packet packet, Agent agent)
{
ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet;
// TODO: Replace with HashSet when we switch to .NET 3.5
List<int> parcels = new List<int>();
// Convert the boundaries to integers
int north = (int)Math.Round(request.ParcelData.North) / 4;
int east = (int)Math.Round(request.ParcelData.East) / 4;
int south = (int)Math.Round(request.ParcelData.South) / 4;
int west = (int)Math.Round(request.ParcelData.West) / 4;
// Find all of the parcels within the given boundaries
int xLen = east - west;
int yLen = north - south;
for (int x = 0; x < xLen; x++)
{
for (int y = 0; y < yLen; y++)
{
if (west + x < 64 && south + y < 64)
{
int currentParcelID = parcelOverlay[(south + y) * 64 + (west + x)];
if (!parcels.Contains(currentParcelID))
parcels.Add(currentParcelID);
}
}
}
ParcelResult result = ParcelResult.NoData;
if (parcels.Count == 1)
result = ParcelResult.Single;
else if (parcels.Count > 1)
result = ParcelResult.Multiple;
for (int i = 0; i < parcels.Count; i++)
SendParcelProperties(parcels[i], request.ParcelData.SequenceID, request.ParcelData.SnapSelection, result, agent);
}
void ParcelPropertiesUpdateHandler(Packet packet, Agent agent)
{
ParcelPropertiesUpdatePacket update = (ParcelPropertiesUpdatePacket)packet;
Parcel parcel;
if (parcels.TryGetValue(update.ParcelData.LocalID, out parcel))
{
parcel.AuthBuyerID = update.ParcelData.AuthBuyerID;
parcel.Category = (Parcel.ParcelCategory)update.ParcelData.Category;
parcel.Desc = Utils.BytesToString(update.ParcelData.Desc);
parcel.Flags = (Parcel.ParcelFlags)update.ParcelData.ParcelFlags;
parcel.GroupID = update.ParcelData.GroupID;
parcel.Landing = (Parcel.LandingType)update.ParcelData.LandingType;
parcel.Media.MediaAutoScale = update.ParcelData.MediaAutoScale;
parcel.Media.MediaID = update.ParcelData.MediaID;
parcel.Media.MediaURL = Utils.BytesToString(update.ParcelData.MediaURL);
parcel.MusicURL = Utils.BytesToString(update.ParcelData.MusicURL);
parcel.Name = Utils.BytesToString(update.ParcelData.Name);
parcel.PassHours = update.ParcelData.PassHours;
parcel.PassPrice = update.ParcelData.PassPrice;
parcel.SalePrice = update.ParcelData.SalePrice;
parcel.SnapshotID = update.ParcelData.SnapshotID;
parcel.UserLocation = update.ParcelData.UserLocation;
parcel.UserLookAt = update.ParcelData.UserLookAt;
lock (parcels)
parcels[parcel.LocalID] = parcel;
if (update.ParcelData.Flags != 0)
SendParcelProperties(parcel.LocalID, 0, false, ParcelResult.Single, agent);
}
else
{
Logger.Log("Got a ParcelPropertiesUpdate for an unknown parcel " + update.ParcelData.LocalID,
Helpers.LogLevel.Warning);
}
}
}
}

View File

@@ -10,8 +10,6 @@ namespace Simian
{
public class PeriscopeTransferManager
{
public const string UPLOAD_DIR = "uploadedAssets";
ISceneProvider scene;
GridClient client;
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();

View File

@@ -7,12 +7,25 @@ namespace Simian
{
public class PhysicsSimple : IExtension<ISceneProvider>, IPhysicsProvider
{
ISceneProvider scene;
// Run our own frames per second limiter on top of the limiting done by ISceneProvider
const int FRAMES_PER_SECOND = 10;
public float TimeDilation
{
get { return 1.0f; }
}
const float GRAVITY = 9.8f; //meters/sec
const float WALK_SPEED = 3f; //meters/sec
const float RUN_SPEED = 5f; //meters/sec
const float FLY_SPEED = 10f; //meters/sec
const float FALL_DELAY = 0.33f; //seconds before starting animation
const float FALL_FORGIVENESS = .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
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
const float SQRT_TWO = 1.41421356f;
ISceneProvider scene;
float elapsedSinceUpdate;
public PhysicsSimple()
{
@@ -21,6 +34,8 @@ namespace Simian
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
return true;
}
@@ -28,6 +43,335 @@ namespace Simian
{
}
public void Update(float elapsedTime)
{
if (elapsedSinceUpdate >= 1f / (float)FRAMES_PER_SECOND)
{
elapsedTime = elapsedSinceUpdate;
elapsedSinceUpdate = 0f;
}
else
{
elapsedSinceUpdate += elapsedTime;
return;
}
scene.ForEachAgent(
delegate(Agent agent)
{
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
return;
bool animsChanged = false;
// Create forward and left vectors from the current avatar rotation
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
// Check control flags
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
// direction in which the avatar is trying to move
Vector3 move = Vector3.Zero;
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
if (heldUp) { move.Z += 1; }
if (heldDown) { move.Z -= 1; }
// is the avatar trying to move?
bool moving = move != Vector3.Zero;
bool jumping = agent.TickJump != 0;
// 2-dimensional speed multipler
float speed = elapsedTime * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
if ((heldForward || heldBack) && (heldLeft || heldRight))
speed /= SQRT_TWO;
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
agentPosition += (move * speed);
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
if (!flying && newFloor != oldFloor)
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
//HACK: distance from avatar center to the bottom of its feet
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
float lowerLimit = newFloor + distanceFromFloor;
//"bridge" physics
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
{
//start ray at our feet
Vector3 rayStart = new Vector3(
agent.Avatar.Prim.Position.X,
agent.Avatar.Prim.Position.Y,
agent.Avatar.Prim.Position.Z - distanceFromFloor
);
//end ray at 0.01m below our feet
Vector3 rayEnd = new Vector3(
rayStart.X,
rayStart.Y,
rayStart.Z - 0.01f
);
scene.ForEachObject(delegate(SimulationObject obj)
{
//HACK: check nearby objects (what did you expect, octree?)
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
{
Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
if (collision != rayEnd) //we collided!
{
//check if we are any higher than before
float height = collision.Z + distanceFromFloor;
if (height > lowerLimit) lowerLimit = height;
}
}
});
}
// Z acceleration resulting from gravity
float gravity = 0f;
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
if (flying)
{
agent.TickFall = 0;
agent.TickJump = 0;
//velocity falloff while flying
agent.Avatar.Prim.Velocity.X *= 0.66f;
agent.Avatar.Prim.Velocity.Y *= 0.66f;
agent.Avatar.Prim.Velocity.Z *= 0.33f;
if (agent.Avatar.Prim.Position.Z == lowerLimit)
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
if (move.X != 0 || move.Y != 0)
{ //flying horizontally
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
animsChanged = true;
}
else if (move.Z > 0)
{ //flying straight up
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.Z < 0)
{ //flying straight down
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
animsChanged = true;
}
else
{ //hovering in the air
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
{ //falling, floating, or landing from a jump
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
{ //above water
//override controls while drifting
move = Vector3.Zero;
//keep most of our horizontal inertia
agent.Avatar.Prim.Velocity.X *= 0.975f;
agent.Avatar.Prim.Velocity.Y *= 0.975f;
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
{ //just started falling
agent.TickFall = Environment.TickCount;
}
else
{
gravity = GRAVITY * fallElapsed * elapsedTime; //normal gravity
if (!jumping)
{ //falling
if (fallElapsed > FALL_DELAY)
{ //falling long enough to trigger the animation
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
}
}
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
{ //at the water line
gravity = 0f;
agent.Avatar.Prim.Velocity *= 0.5f;
agent.Avatar.Prim.Velocity.Z = 0f;
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
if (move.Z > 0)
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.X != 0 || move.Y != 0)
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
animsChanged = true;
}
else
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else
{ //underwater
gravity = 0f; //buoyant
agent.Avatar.Prim.Velocity *= 0.5f * elapsedTime;
agent.Avatar.Prim.Velocity.Z += 0.75f * elapsedTime;
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
else
{ //on the ground
agent.TickFall = 0;
//friction
agent.Avatar.Prim.Acceleration *= 0.2f;
agent.Avatar.Prim.Velocity *= 0.2f;
agent.Avatar.Prim.Position.Z = lowerLimit;
if (move.Z > 0)
{ //jumping
if (!jumping)
{ //begin prejump
move.Z = 0; //override Z control
if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
animsChanged = true;
agent.TickJump = Environment.TickCount;
}
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
{ //start actual jump
if (agent.TickJump == -1)
{
//already jumping! end current jump
agent.TickJump = 0;
return;
}
if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
animsChanged = true;
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * elapsedTime;
agent.TickJump = -1; //flag that we are currently jumping
}
else move.Z = 0; //override Z control
}
else
{ //not jumping
agent.TickJump = 0;
if (move.X != 0 || move.Y != 0)
{ //not walking
if (move.Z < 0)
{ //crouchwalking
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
animsChanged = true;
}
else if (agent.Running)
{ //running
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
animsChanged = true;
}
else
{ //walking
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
animsChanged = true;
}
}
else
{ //walking
if (move.Z < 0)
{ //crouching
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
animsChanged = true;
}
else
{ //standing
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
animsChanged = true;
}
}
}
}
if (animsChanged)
scene.Avatars.SendAnimations(agent);
float maxVel = AVATAR_TERMINAL_VELOCITY * elapsedTime;
// static acceleration when any control is held, otherwise none
if (moving)
{
agent.Avatar.Prim.Acceleration = move * speed;
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
agent.Avatar.Prim.Acceleration.Z = -maxVel;
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
agent.Avatar.Prim.Acceleration.Z = maxVel;
}
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
agent.Avatar.Prim.Velocity.Z = -maxVel;
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
agent.Avatar.Prim.Velocity.Z = maxVel;
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
}
);
}
public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj)
{
Vector3 closestPoint = rayEnd;
@@ -80,6 +424,28 @@ namespace Simian
}
}
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
{
// Recompute meshes for
bool forceMeshing = false;
bool forceTransform = false;
if ((update & UpdateFlags.Scale) != 0 ||
(update & UpdateFlags.Position) != 0 ||
(update & UpdateFlags.Rotation) != 0)
{
forceTransform = true;
}
if ((update & UpdateFlags.PrimData) != 0)
{
forceMeshing = true;
}
// TODO: This doesn't update children prims when their parents move
obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform);
}
/// <summary>
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
/// </summary>