diff --git a/Programs/Simian/Extensions/AssetManager.cs b/Programs/Simian/Extensions/AssetManager.cs index 80e2e86a..32450d0e 100644 --- a/Programs/Simian/Extensions/AssetManager.cs +++ b/Programs/Simian/Extensions/AssetManager.cs @@ -89,7 +89,7 @@ namespace Simian.Extensions public byte[] EncodePrimAsset(List linkset) { // FIXME: - return new byte[0]; + return Utils.EmptyBytes; } public bool TryDecodePrimAsset(byte[] primAssetData, out List linkset) diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs index 589449fa..92ae4d87 100644 --- a/Programs/Simian/Extensions/AvatarManager.cs +++ b/Programs/Simian/Extensions/AvatarManager.cs @@ -327,7 +327,7 @@ namespace Simian.Extensions response.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); response.WearableData[i].TextureIndex = cached.WearableData[i].TextureIndex; response.WearableData[i].TextureID = UUID.Zero; - response.WearableData[i].HostName = new byte[0]; + response.WearableData[i].HostName = Utils.EmptyBytes; } response.Header.Zerocoded = true; @@ -372,8 +372,8 @@ namespace Simian.Extensions } else { - reply.UUIDNameBlock[i].FirstName = new byte[0]; - reply.UUIDNameBlock[i].LastName = new byte[0]; + reply.UUIDNameBlock[i].FirstName = Utils.EmptyBytes; + reply.UUIDNameBlock[i].LastName = Utils.EmptyBytes; } } diff --git a/Programs/Simian/Extensions/FriendManager.cs b/Programs/Simian/Extensions/FriendManager.cs index 93324665..e23b6e8c 100644 --- a/Programs/Simian/Extensions/FriendManager.cs +++ b/Programs/Simian/Extensions/FriendManager.cs @@ -46,7 +46,7 @@ namespace Simian.Extensions sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online; sendIM.MessageBlock.ID = agent.ID; sendIM.MessageBlock.Message = im.MessageBlock.Message; - sendIM.MessageBlock.BinaryBucket = new byte[0]; + sendIM.MessageBlock.BinaryBucket = Utils.EmptyBytes; sendIM.MessageBlock.Timestamp = 0; sendIM.MessageBlock.Position = agent.Avatar.GetSimulatorPosition(); diff --git a/Programs/Simian/Extensions/InventoryManager.cs b/Programs/Simian/Extensions/InventoryManager.cs index 4e476dd4..1dc07021 100644 --- a/Programs/Simian/Extensions/InventoryManager.cs +++ b/Programs/Simian/Extensions/InventoryManager.cs @@ -386,8 +386,8 @@ namespace Simian.Extensions Logger.Log("FetchInventory called for an unknown item " + itemID.ToString(), Helpers.LogLevel.Warning); - blocks[i].Name = new byte[0]; - blocks[i].Description = new byte[0]; + blocks[i].Name = Utils.EmptyBytes; + blocks[i].Description = Utils.EmptyBytes; } } diff --git a/Programs/Simian/Extensions/Messaging.cs b/Programs/Simian/Extensions/Messaging.cs index 6d17337d..999b65f5 100644 --- a/Programs/Simian/Extensions/Messaging.cs +++ b/Programs/Simian/Extensions/Messaging.cs @@ -57,7 +57,7 @@ namespace Simian.Extensions sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online; sendIM.MessageBlock.ID = agent.ID; sendIM.MessageBlock.Message = im.MessageBlock.Message; - sendIM.MessageBlock.BinaryBucket = new byte[0]; + sendIM.MessageBlock.BinaryBucket = Utils.EmptyBytes; sendIM.MessageBlock.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); sendIM.MessageBlock.Position = agent.Avatar.GetSimulatorPosition(); diff --git a/Programs/Simian/Extensions/Movement.cs b/Programs/Simian/Extensions/Movement.cs index 44485892..c5e22fa9 100644 --- a/Programs/Simian/Extensions/Movement.cs +++ b/Programs/Simian/Extensions/Movement.cs @@ -445,7 +445,7 @@ namespace Simian.Extensions } ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar.Prim, - server.Scene.RegionHandle, agent.Avatar.Prim.Flags); + server.Scene.RegionHandle, agent.Avatar.Prim.Flags, 0); server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State); } diff --git a/Programs/Simian/Extensions/ObjectManager.cs b/Programs/Simian/Extensions/ObjectManager.cs index 6011e69c..48235c36 100644 --- a/Programs/Simian/Extensions/ObjectManager.cs +++ b/Programs/Simian/Extensions/ObjectManager.cs @@ -68,7 +68,7 @@ namespace Simian.Extensions if (server.Scene.TryGetObject(add.ObjectData.RayTargetID, out obj)) { // Test for a collision with the specified object - position = ObjectCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd, obj); + position = server.Physics.ObjectCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd, obj); } } @@ -240,50 +240,25 @@ namespace Simian.Extensions { //Logger.DebugLog("Selecting object " + obj.Prim.LocalID); - if (obj.Prim.Properties != null) - { - properties.ObjectData[0].BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask; - properties.ObjectData[0].CreationDate = Utils.DateTimeToUnixTime(obj.Prim.Properties.CreationDate); - properties.ObjectData[0].CreatorID = obj.Prim.Properties.CreatorID; - properties.ObjectData[0].Description = Utils.StringToBytes(obj.Prim.Properties.Description); - properties.ObjectData[0].EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask; - properties.ObjectData[0].GroupID = obj.Prim.Properties.GroupID; - properties.ObjectData[0].GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask; - properties.ObjectData[0].LastOwnerID = obj.Prim.Properties.LastOwnerID; - properties.ObjectData[0].Name = Utils.StringToBytes(obj.Prim.Properties.Name); - properties.ObjectData[0].NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask; - properties.ObjectData[0].ObjectID = obj.Prim.ID; - properties.ObjectData[0].OwnerID = obj.Prim.Properties.OwnerID; - properties.ObjectData[0].OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask; - properties.ObjectData[0].OwnershipCost = obj.Prim.Properties.OwnershipCost; - properties.ObjectData[0].SalePrice = obj.Prim.Properties.SalePrice; - properties.ObjectData[0].SaleType = (byte)obj.Prim.Properties.SaleType; - properties.ObjectData[0].SitName = new byte[0]; // FIXME: Finish these - properties.ObjectData[0].TextureID = new byte[0]; - properties.ObjectData[0].TouchName = new byte[0]; - } - else - { - properties.ObjectData[0].BaseMask = (uint)PermissionMask.All; - properties.ObjectData[0].CreationDate = Utils.DateTimeToUnixTime(DateTime.Now); - properties.ObjectData[0].CreatorID = agent.ID; - properties.ObjectData[0].Description = Utils.StringToBytes(String.Empty); - properties.ObjectData[0].EveryoneMask = (uint)PermissionMask.All; - properties.ObjectData[0].GroupID = UUID.Zero; - properties.ObjectData[0].GroupMask = (uint)PermissionMask.All; - properties.ObjectData[0].LastOwnerID = UUID.Zero; - properties.ObjectData[0].Name = Utils.StringToBytes(String.Empty); - properties.ObjectData[0].NextOwnerMask = (uint)PermissionMask.All; - properties.ObjectData[0].ObjectID = obj.Prim.ID; - properties.ObjectData[0].OwnerID = agent.ID; - properties.ObjectData[0].OwnerMask = (uint)PermissionMask.All; - properties.ObjectData[0].OwnershipCost = 0; - properties.ObjectData[0].SalePrice = 0; - properties.ObjectData[0].SaleType = (byte)SaleType.Not; - properties.ObjectData[0].SitName = new byte[0]; - properties.ObjectData[0].TextureID = new byte[0]; - properties.ObjectData[0].TouchName = new byte[0]; - } + properties.ObjectData[0].BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask; + properties.ObjectData[0].CreationDate = Utils.DateTimeToUnixTime(obj.Prim.Properties.CreationDate); + properties.ObjectData[0].CreatorID = obj.Prim.Properties.CreatorID; + properties.ObjectData[0].Description = Utils.StringToBytes(obj.Prim.Properties.Description); + properties.ObjectData[0].EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask; + properties.ObjectData[0].GroupID = obj.Prim.Properties.GroupID; + properties.ObjectData[0].GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask; + properties.ObjectData[0].LastOwnerID = obj.Prim.Properties.LastOwnerID; + properties.ObjectData[0].Name = Utils.StringToBytes(obj.Prim.Properties.Name); + properties.ObjectData[0].NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask; + properties.ObjectData[0].ObjectID = obj.Prim.ID; + properties.ObjectData[0].OwnerID = obj.Prim.Properties.OwnerID; + properties.ObjectData[0].OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask; + properties.ObjectData[0].OwnershipCost = obj.Prim.Properties.OwnershipCost; + properties.ObjectData[0].SalePrice = obj.Prim.Properties.SalePrice; + properties.ObjectData[0].SaleType = (byte)obj.Prim.Properties.SaleType; + properties.ObjectData[0].SitName = Utils.StringToBytes(obj.Prim.Properties.SitName); + properties.ObjectData[0].TextureID = Utils.EmptyBytes; // FIXME: What is this? + properties.ObjectData[0].TouchName = Utils.StringToBytes(obj.Prim.Properties.TouchName); server.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction); } @@ -292,11 +267,11 @@ namespace Simian.Extensions Logger.Log("ObjectSelect sent for missing object " + select.ObjectData[i].ObjectLocalID, Helpers.LogLevel.Warning); - properties.ObjectData[0].Description = new byte[0]; - properties.ObjectData[0].Name = new byte[0]; - properties.ObjectData[0].SitName = new byte[0]; - properties.ObjectData[0].TextureID = new byte[0]; - properties.ObjectData[0].TouchName = new byte[0]; + properties.ObjectData[0].Description = Utils.EmptyBytes; + properties.ObjectData[0].Name = Utils.EmptyBytes; + properties.ObjectData[0].SitName = Utils.EmptyBytes; + properties.ObjectData[0].TextureID = Utils.EmptyBytes; + properties.ObjectData[0].TouchName = Utils.EmptyBytes; KillObjectPacket kill = new KillObjectPacket(); kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; @@ -597,7 +572,6 @@ namespace Simian.Extensions DeRezDestination destination = (DeRezDestination)derez.AgentBlock.Destination; // TODO: Check permissions - for (int i = 0; i < derez.ObjectData.Length; i++) { uint localID = derez.ObjectData[i].ObjectLocalID; @@ -728,7 +702,6 @@ namespace Simian.Extensions else { // Ghosted prim, send a kill message to this agent - // FIXME: Handle children KillObjectPacket kill = new KillObjectPacket(); kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); @@ -747,46 +720,22 @@ namespace Simian.Extensions if (server.Scene.TryGetObject(request.ObjectData.ObjectID, out obj)) { ObjectPropertiesFamilyPacket props = new ObjectPropertiesFamilyPacket(); - - if (obj.Prim.Properties != null) - { - props.ObjectData.BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask; - props.ObjectData.Category = (uint)obj.Prim.Properties.Category; - props.ObjectData.Description = Utils.StringToBytes(obj.Prim.Properties.Description); - props.ObjectData.EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask; - props.ObjectData.GroupID = obj.Prim.Properties.GroupID; - props.ObjectData.GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask; - props.ObjectData.LastOwnerID = obj.Prim.Properties.LastOwnerID; - props.ObjectData.Name = Utils.StringToBytes(obj.Prim.Properties.Name); - props.ObjectData.NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask; - props.ObjectData.ObjectID = obj.Prim.ID; - props.ObjectData.OwnerID = obj.Prim.Properties.OwnerID; - props.ObjectData.OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask; - props.ObjectData.OwnershipCost = obj.Prim.Properties.OwnershipCost; - props.ObjectData.RequestFlags = (uint)type; - props.ObjectData.SalePrice = obj.Prim.Properties.SalePrice; - props.ObjectData.SaleType = (byte)obj.Prim.Properties.SaleType; - } - else - { - // Make up some default properties for this prim - props.ObjectData.BaseMask = (uint)PermissionMask.All; - props.ObjectData.Category = (uint)ObjectCategory.None; - props.ObjectData.Description = Utils.StringToBytes(String.Empty); - props.ObjectData.EveryoneMask = (uint)PermissionMask.All; - props.ObjectData.GroupID = UUID.Zero; - props.ObjectData.GroupMask = (uint)PermissionMask.All; - props.ObjectData.LastOwnerID = UUID.Zero; - props.ObjectData.Name = Utils.StringToBytes(String.Empty); - props.ObjectData.NextOwnerMask = (uint)PermissionMask.All; - props.ObjectData.ObjectID = obj.Prim.ID; - props.ObjectData.OwnerID = agent.ID; - props.ObjectData.OwnerMask = (uint)PermissionMask.All; - props.ObjectData.OwnershipCost = 0; - props.ObjectData.RequestFlags = (uint)ReportType.None; - props.ObjectData.SalePrice = 0; - props.ObjectData.SaleType = (byte)SaleType.Not; - } + props.ObjectData.BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask; + props.ObjectData.Category = (uint)obj.Prim.Properties.Category; + props.ObjectData.Description = Utils.StringToBytes(obj.Prim.Properties.Description); + props.ObjectData.EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask; + props.ObjectData.GroupID = obj.Prim.Properties.GroupID; + props.ObjectData.GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask; + props.ObjectData.LastOwnerID = obj.Prim.Properties.LastOwnerID; + props.ObjectData.Name = Utils.StringToBytes(obj.Prim.Properties.Name); + props.ObjectData.NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask; + props.ObjectData.ObjectID = obj.Prim.ID; + props.ObjectData.OwnerID = obj.Prim.Properties.OwnerID; + props.ObjectData.OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask; + props.ObjectData.OwnershipCost = obj.Prim.Properties.OwnershipCost; + props.ObjectData.RequestFlags = (uint)type; + props.ObjectData.SalePrice = obj.Prim.Properties.SalePrice; + props.ObjectData.SaleType = (byte)obj.Prim.Properties.SaleType; server.UDP.SendPacket(agent.ID, props, PacketCategory.Transaction); } @@ -803,113 +752,5 @@ namespace Simian.Extensions Logger.DebugLog("Full scene collision test was requested, ignoring"); return rayEnd; } - - Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj) - { - const float OO_THREE = 1f / 3f; - - Vector3 closestPoint = rayEnd; - - if (rayStart == rayEnd) - { - Logger.DebugLog("RayStart is equal to RayEnd, returning given location"); - return closestPoint; - } - - Vector3 direction = Vector3.Normalize(rayEnd - rayStart); - - // Get the mesh that has been transformed into world-space - SimpleMesh mesh = null; - if (obj.Prim.ParentID != 0) - { - SimulationObject parent; - if (server.Scene.TryGetObject(obj.Prim.ParentID, out parent)) - mesh = obj.GetWorldMesh(DetailLevel.Low, parent); - } - else - { - mesh = obj.GetWorldMesh(DetailLevel.Low, null); - } - - if (mesh != null) - { - // Iterate through all of the triangles in the mesh, doing a ray-triangle intersection - - float closestDistance = Single.MaxValue; - for (int i = 0; i < mesh.Indices.Count; i += 3) - { - Vector3 point0 = mesh.Vertices[mesh.Indices[i + 0]].Position; - Vector3 point1 = mesh.Vertices[mesh.Indices[i + 1]].Position; - Vector3 point2 = mesh.Vertices[mesh.Indices[i + 2]].Position; - - if (RayTriangleIntersection(rayStart, direction, point0, point1, point2)) - { - // HACK: Find the barycenter of this triangle. Would be better to have - // RayTriangleIntersection return the exact collision point - Vector3 center = (point0 + point1 + point2) * OO_THREE; - - Logger.DebugLog("Collision hit with triangle at " + center); - - if ((center - rayStart).Length() < closestDistance) - closestPoint = center; - } - } - } - - return closestPoint; - } - - /// - /// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf - /// - /// Origin point of the ray - /// Unit vector representing the direction of the ray - /// Position of the first triangle corner - /// Position of the second triangle corner - /// Position of the third triangle corner - /// True if the ray passes through the triangle, otherwise false - bool RayTriangleIntersection(Vector3 origin, Vector3 direction, Vector3 vert0, Vector3 vert1, Vector3 vert2) - { - const float EPSILON = 0.00001f; - - Vector3 edge1, edge2, pvec; - float determinant, invDeterminant; - - // Find vectors for two edges sharing vert0 - edge1 = vert1 - vert0; - edge2 = vert2 - vert0; - - // Begin calculating the determinant - pvec = Vector3.Cross(direction, edge2); - - // If the determinant is near zero, ray lies in plane of triangle - determinant = Vector3.Dot(edge1, pvec); - - if (determinant > -EPSILON && determinant < EPSILON) - return false; - - invDeterminant = 1f / determinant; - - // Calculate distance from vert0 to ray origin - Vector3 tvec = origin - vert0; - - // Calculate U parameter and test bounds - float u = Vector3.Dot(tvec, pvec) * invDeterminant; - if (u < 0.0f || u > 1.0f) - return false; - - // Prepare to test V parameter - Vector3 qvec = Vector3.Cross(tvec, edge1); - - // Calculate V parameter and test bounds - float v = Vector3.Dot(direction, qvec) * invDeterminant; - if (v < 0.0f || u + v > 1.0f) - return false; - - //t = Vector3.Dot(edge2, qvec) * invDeterminant; - - return true; - } - } } diff --git a/Programs/Simian/Extensions/PeriscopeTransferManager.cs b/Programs/Simian/Extensions/PeriscopeTransferManager.cs index 237e7841..f78a44f0 100644 --- a/Programs/Simian/Extensions/PeriscopeTransferManager.cs +++ b/Programs/Simian/Extensions/PeriscopeTransferManager.cs @@ -84,7 +84,7 @@ namespace Simian.Extensions RequestXferPacket xfer = new RequestXferPacket(); xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile; xfer.XferID.FilePath = 0; - xfer.XferID.Filename = new byte[0]; + xfer.XferID.Filename = Utils.EmptyBytes; xfer.XferID.ID = request.AssetBlock.TransactionID.GetULong(); xfer.XferID.UseBigPackets = false; xfer.XferID.VFileID = asset.AssetID; diff --git a/Programs/Simian/Extensions/PhysicsSimple.cs b/Programs/Simian/Extensions/PhysicsSimple.cs new file mode 100644 index 00000000..af525c7f --- /dev/null +++ b/Programs/Simian/Extensions/PhysicsSimple.cs @@ -0,0 +1,407 @@ +using System; +using ExtensionLoader; +using OpenMetaverse; +using OpenMetaverse.Rendering; + +namespace Simian.Extensions +{ + public class PhysicsSimple : IExtension, IPhysicsProvider + { + Simian server; + + public PhysicsSimple() + { + } + + public void Start(Simian server) + { + this.server = server; + } + + public void Stop() + { + } + + public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj) + { + const float OO_THREE = 1f / 3f; + + Vector3 closestPoint = rayEnd; + + if (rayStart == rayEnd) + { + Logger.DebugLog("RayStart is equal to RayEnd, returning given location"); + return closestPoint; + } + + Vector3 direction = Vector3.Normalize(rayEnd - rayStart); + + // Get the mesh that has been transformed into world-space + SimpleMesh mesh = null; + if (obj.Prim.ParentID != 0) + { + SimulationObject parent; + if (server.Scene.TryGetObject(obj.Prim.ParentID, out parent)) + mesh = obj.GetWorldMesh(DetailLevel.Low, parent); + } + else + { + mesh = obj.GetWorldMesh(DetailLevel.Low, null); + } + + if (mesh != null) + { + // Iterate through all of the triangles in the mesh, doing a ray-triangle intersection + + float closestDistance = Single.MaxValue; + for (int i = 0; i < mesh.Indices.Count; i += 3) + { + Vector3 point0 = mesh.Vertices[mesh.Indices[i + 0]].Position; + Vector3 point1 = mesh.Vertices[mesh.Indices[i + 1]].Position; + Vector3 point2 = mesh.Vertices[mesh.Indices[i + 2]].Position; + + if (RayTriangleIntersection(rayStart, direction, point0, point1, point2)) + { + // HACK: Find the barycenter of this triangle. Would be better to have + // RayTriangleIntersection return the exact collision point + Vector3 center = (point0 + point1 + point2) * OO_THREE; + + Logger.DebugLog("Collision hit with triangle at " + center); + + if ((center - rayStart).Length() < closestDistance) + closestPoint = center; + } + } + } + + return closestPoint; + } + + public bool TryGetObjectMass(UUID objectID, out float mass) + { + SimulationObject obj; + if (server.Scene.TryGetObject(objectID, out obj)) + { + mass = CalculateMass(obj.Prim); + return true; + } + else + { + mass = 0f; + return false; + } + } + + /// + /// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf + /// + /// Origin point of the ray + /// Unit vector representing the direction of the ray + /// Position of the first triangle corner + /// Position of the second triangle corner + /// Position of the third triangle corner + /// True if the ray passes through the triangle, otherwise false + bool RayTriangleIntersection(Vector3 origin, Vector3 direction, Vector3 vert0, Vector3 vert1, Vector3 vert2) + { + const float EPSILON = 0.00001f; + + Vector3 edge1, edge2, pvec; + float determinant, invDeterminant; + + // Find vectors for two edges sharing vert0 + edge1 = vert1 - vert0; + edge2 = vert2 - vert0; + + // Begin calculating the determinant + pvec = Vector3.Cross(direction, edge2); + + // If the determinant is near zero, ray lies in plane of triangle + determinant = Vector3.Dot(edge1, pvec); + + if (determinant > -EPSILON && determinant < EPSILON) + return false; + + invDeterminant = 1f / determinant; + + // Calculate distance from vert0 to ray origin + Vector3 tvec = origin - vert0; + + // Calculate U parameter and test bounds + float u = Vector3.Dot(tvec, pvec) * invDeterminant; + if (u < 0.0f || u > 1.0f) + return false; + + // Prepare to test V parameter + Vector3 qvec = Vector3.Cross(tvec, edge1); + + // Calculate V parameter and test bounds + float v = Vector3.Dot(direction, qvec) * invDeterminant; + if (v < 0.0f || u + v > 1.0f) + return false; + + //t = Vector3.Dot(edge2, qvec) * invDeterminant; + + return true; + } + + /// + /// Adapted from code written by Teravus for OpenSim + /// + /// Primitive to calculate the mass of + /// Estimated mass of the given primitive + static float CalculateMass(Primitive prim) + { + const float PRIM_DENSITY = 10.000006836f; // Aluminum g/cm3 + + float volume = 0f; + float returnMass = 0f; + + // TODO: Use the prim material in mass calculations once our physics + // engine supports different materials + + switch (prim.PrimData.ProfileCurve) + { + case ProfileCurve.Square: + // Profile Volume + + volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z; + + // If the user has 'hollowed out' + if (prim.PrimData.ProfileHollow > 0.0f) + { + float hollowAmount = prim.PrimData.ProfileHollow; + + // calculate the hollow volume by it's shape compared to the prim shape + float hollowVolume = 0; + switch (prim.PrimData.ProfileHole) + { + case HoleType.Square: + case HoleType.Same: + // Cube Hollow volume calculation + float hollowsizex = prim.Scale.X * hollowAmount; + float hollowsizey = prim.Scale.Y * hollowAmount; + float hollowsizez = prim.Scale.Z * hollowAmount; + hollowVolume = hollowsizex * hollowsizey * hollowsizez; + break; + + case HoleType.Circle: + // Hollow shape is a perfect cyllinder in respect to the cube's scale + // Cyllinder hollow volume calculation + float hRadius = prim.Scale.X * 0.5f; + float hLength = prim.Scale.Z; + + // pi * r2 * h + hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount); + break; + + case HoleType.Triangle: + // Equilateral Triangular Prism volume hollow calculation + // Triangle is an Equilateral Triangular Prism with aLength = to _size.Y + + float aLength = prim.Scale.Y; + // 1/2 abh + hollowVolume = (float)((0.5 * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount); + break; + + default: + hollowVolume = 0; + break; + } + volume = volume - hollowVolume; + } + + break; + case ProfileCurve.Circle: + if (prim.PrimData.PathCurve == PathCurve.Line) + { + // Cylinder + float volume1 = (float)(Math.PI * Math.Pow(prim.Scale.X / 2, 2) * prim.Scale.Z); + float volume2 = (float)(Math.PI * Math.Pow(prim.Scale.Y / 2, 2) * prim.Scale.Z); + + // Approximating the cylinder's irregularity. + if (volume1 > volume2) + { + volume = (float)volume1 - (volume1 - volume2); + } + else if (volume2 > volume1) + { + volume = (float)volume2 - (volume2 - volume1); + } + else + { + // Regular cylinder + volume = volume1; + } + } + else + { + // We don't know what the shape is yet, so use default + volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z; + } + + // If the user has 'hollowed out' + if (prim.PrimData.ProfileHollow > 0.0f) + { + float hollowAmount = prim.PrimData.ProfileHollow; + + // calculate the hollow volume by it's shape compared to the prim shape + float hollowVolume = 0f; + switch (prim.PrimData.ProfileHole) + { + case HoleType.Circle: + case HoleType.Same: + // Hollow shape is a perfect cyllinder in respect to the cube's scale + // Cyllinder hollow volume calculation + float hRadius = prim.Scale.X * 0.5f; + float hLength = prim.Scale.Z; + + // pi * r2 * h + hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount); + break; + + case HoleType.Square: + // Cube Hollow volume calculation + float hollowsizex = prim.Scale.X * hollowAmount; + float hollowsizey = prim.Scale.Y * hollowAmount; + float hollowsizez = prim.Scale.Z * hollowAmount; + hollowVolume = hollowsizex * hollowsizey * hollowsizez; + break; + + case HoleType.Triangle: + // Equilateral Triangular Prism volume hollow calculation + // Triangle is an Equilateral Triangular Prism with aLength = to _size.Y + + float aLength = prim.Scale.Y; + // 1/2 abh + hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount; + break; + + default: + hollowVolume = 0; + break; + } + volume = volume - hollowVolume; + } + break; + + case ProfileCurve.HalfCircle: + if (prim.PrimData.PathCurve == PathCurve.Circle) + { + if (prim.Scale.X == prim.Scale.Y && prim.Scale.Y == prim.Scale.Z) + { + // regular sphere + // v = 4/3 * pi * r^3 + float sradius3 = (float)Math.Pow((prim.Scale.X * 0.5f), 3); + volume = (4f / 3f) * (float)Math.PI * sradius3; + } + else + { + // we treat this as a box currently + volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z; + } + } + else + { + // We don't know what the shape is yet, so use default + volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z; + } + break; + + case ProfileCurve.EqualTriangle: + float xA = -0.25f * prim.Scale.X; + float yA = -0.45f * prim.Scale.Y; + + float xB = 0.5f * prim.Scale.X; + float yB = 0; + + float xC = -0.25f * prim.Scale.X; + float yC = 0.45f * prim.Scale.Y; + + volume = (float)((Math.Abs((xB * yA - xA * yB) + (xC * yB - xB * yC) + (xA * yC - xC * yA)) / 2) * prim.Scale.Z); + + // If the user has 'hollowed out' + // ProfileHollow is one of those 0 to 50000 values :P + // we like percentages better.. so turning into a percentage + if (prim.PrimData.ProfileHollow > 0.0f) + { + float hollowAmount = prim.PrimData.ProfileHollow; + + // calculate the hollow volume by it's shape compared to the prim shape + float hollowVolume = 0f; + + switch (prim.PrimData.ProfileHole) + { + case HoleType.Triangle: + case HoleType.Same: + // Equilateral Triangular Prism volume hollow calculation + // Triangle is an Equilateral Triangular Prism with aLength = to _size.Y + + float aLength = prim.Scale.Y; + // 1/2 abh + hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount; + break; + + case HoleType.Square: + // Cube Hollow volume calculation + float hollowsizex = prim.Scale.X * hollowAmount; + float hollowsizey = prim.Scale.Y * hollowAmount; + float hollowsizez = prim.Scale.Z * hollowAmount; + hollowVolume = hollowsizex * hollowsizey * hollowsizez; + break; + + case HoleType.Circle: + // Hollow shape is a perfect cyllinder in respect to the cube's scale + // Cyllinder hollow volume calculation + float hRadius = prim.Scale.X * 0.5f; + float hLength = prim.Scale.Z; + + // pi * r2 * h + hollowVolume = ((float)((Math.PI * Math.Pow(hRadius, 2) * hLength) / 2) * hollowAmount); + break; + + default: + hollowVolume = 0; + break; + } + volume = volume - hollowVolume; + } + break; + + default: + // we don't have all of the volume formulas yet so + // use the common volume formula for all + volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z; + break; + } + + // Calculate Path cut effect on volume + // Not exact, in the triangle hollow example + // They should never be zero or less then zero.. + // we'll ignore it if it's less then zero + + if (prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd > 0.0f) + { + float pathCutAmount = prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd; + + // Check the return amount for sanity + if (pathCutAmount >= 0.99f) + pathCutAmount = 0.99f; + + volume = volume - (volume * pathCutAmount); + } + + // Mass = density * volume + if (prim.PrimData.PathTaperX != 1f) + volume *= (prim.PrimData.PathTaperX / 3f) + 0.001f; + if (prim.PrimData.PathTaperY != 1f) + volume *= (prim.PrimData.PathTaperY / 3f) + 0.001f; + + returnMass = PRIM_DENSITY * volume; + + if (returnMass <= 0f) + returnMass = 0.0001f; //ckrinke: Mass must be greater then zero. + + return returnMass; + } + } +} diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index 7923131f..6580a9e1 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -270,6 +270,9 @@ namespace Simian.Extensions obj.Prim.Textures = new Primitive.TextureEntry(new UUID("89556747-24cb-43ed-920b-47caed15465f")); // Plywood } + // Reset the prim CRC + obj.CRC = 0; + // Add the object to the scene dictionary sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj); @@ -277,13 +280,13 @@ namespace Simian.Extensions { // Send an update out to the creator ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, - obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner); + obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner, obj.CRC); server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State); } // Send an update out to everyone else ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, - obj.Prim.Flags); + obj.Prim.Flags, obj.CRC); server.Scene.ForEachAgent( delegate(Agent recipient) { @@ -398,6 +401,9 @@ namespace Simian.Extensions obj.Prim.Acceleration = acceleration; obj.Prim.AngularVelocity = angularVelocity; + // Reset the prim CRC + obj.CRC = 0; + // Inform clients BroadcastObjectUpdate(obj.Prim); } @@ -416,6 +422,9 @@ namespace Simian.Extensions // Update the object obj.Prim.Flags = flags; + // Reset the prim CRC + obj.CRC = 0; + // Inform clients BroadcastObjectUpdate(obj.Prim); } @@ -434,6 +443,9 @@ namespace Simian.Extensions // Update the object obj.Prim.PrimData = data; + // Reset the prim CRC + obj.CRC = 0; + // Inform clients BroadcastObjectUpdate(obj.Prim); } @@ -453,6 +465,9 @@ namespace Simian.Extensions obj.Prim.Textures = textureEntry; obj.Prim.MediaURL = mediaURL; + // Reset the prim CRC + obj.CRC = 0; + // Inform clients BroadcastObjectUpdate(obj.Prim); } @@ -950,7 +965,7 @@ namespace Simian.Extensions sceneObjects.ForEach(delegate(SimulationObject obj) { ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(obj.Prim, - obj.Prim.RegionHandle, obj.Prim.Flags); + obj.Prim.RegionHandle, obj.Prim.Flags, obj.CRC); server.UDP.SendPacket(agent.ID, update, PacketCategory.State); }); diff --git a/Programs/Simian/Extensions/ScriptApi.cs b/Programs/Simian/Extensions/ScriptApi.cs index 7f69ef65..22840bc3 100644 --- a/Programs/Simian/Extensions/ScriptApi.cs +++ b/Programs/Simian/Extensions/ScriptApi.cs @@ -1994,7 +1994,7 @@ namespace Simian.Extensions server.Messages.SendInstantMessage(this, hostObject.Prim.ID, hostObject.Prim.Properties.Name, toID, InstantMessageDialog.MessageFromObject, false, hostObject.Prim.ID, false, hostObject.GetSimulatorPosition(), - 0, UUID.Zero, DateTime.Now, message, new byte[0]); + 0, UUID.Zero, DateTime.Now, message, Utils.EmptyBytes); ScriptSleep(2000); } @@ -4833,7 +4833,7 @@ namespace Simian.Extensions int c = 0; for (int i = 0; i < src1.Length; i++) { - ret += src1[i] ^ src2[c]; + ret += (char)(src1[i] ^ src2[c]); c++; if (c >= src2.Length) @@ -5691,7 +5691,17 @@ namespace Simian.Extensions if (remain < 1) return; LSL_Rotation q = rules.GetQuaternionItem(idx++); - llSetRot(q); + // try to let this work as in SL... + if (parent == prim) + { + // special case: If we are root, rotate the parent to the new rotation + SetRot(parent, Rot2Quaternion(q)); + } + else + { + // We are a child. The rotation values will be set to the one of root modified by rot, as in SL. Don't ask. + SetRot(hostObject, parent.Prim.Rotation * Rot2Quaternion(q)); + } break; case ScriptTypes.PRIM_TYPE: if (remain < 3) return; diff --git a/Programs/Simian/Extensions/TransferManager.cs b/Programs/Simian/Extensions/TransferManager.cs index 8dc041b9..35b5a112 100644 --- a/Programs/Simian/Extensions/TransferManager.cs +++ b/Programs/Simian/Extensions/TransferManager.cs @@ -79,7 +79,7 @@ namespace Simian.Extensions RequestXferPacket xfer = new RequestXferPacket(); xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile; xfer.XferID.FilePath = 0; - xfer.XferID.Filename = new byte[0]; + xfer.XferID.Filename = Utils.EmptyBytes; xfer.XferID.ID = request.AssetBlock.TransactionID.GetULong(); xfer.XferID.UseBigPackets = false; xfer.XferID.VFileID = asset.AssetID; diff --git a/Programs/Simian/Interfaces/IPhysicsProvider.cs b/Programs/Simian/Interfaces/IPhysicsProvider.cs new file mode 100644 index 00000000..175e2fd7 --- /dev/null +++ b/Programs/Simian/Interfaces/IPhysicsProvider.cs @@ -0,0 +1,11 @@ +using System; +using OpenMetaverse; + +namespace Simian +{ + public interface IPhysicsProvider + { + Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj); + bool TryGetObjectMass(UUID objectID, out float mass); + } +} diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs index 70b5ed6f..95a5ffa8 100644 --- a/Programs/Simian/Simian.cs +++ b/Programs/Simian/Simian.cs @@ -44,6 +44,7 @@ namespace Simian public ICapabilitiesProvider Capabilities; public IScriptEngine ScriptEngine; public IMessagingProvider Messages; + public IPhysicsProvider Physics; // Persistent extensions public List PersistentExtensions = new List(); diff --git a/Programs/Simian/SimulationObject.cs b/Programs/Simian/SimulationObject.cs index c15fde38..849a44e0 100644 --- a/Programs/Simian/SimulationObject.cs +++ b/Programs/Simian/SimulationObject.cs @@ -36,6 +36,36 @@ namespace Simian protected SimpleMesh[] Meshes; protected SimpleMesh[] WorldTransformedMeshes; + uint? crc; + + public uint CRC + { + get + { + if (crc.HasValue) + return crc.Value; + + int len = 0; + byte[] bytes = new byte[1024]; + ObjectUpdatePacket.ObjectDataBlock block = BuildUpdateBlock(Prim, PrimFlags.None, 0); + block.ToBytes(bytes, ref len); + --len; + + CRC32 crc32 = new CRC32(); + crc32.Update(bytes, 0, len); + + crc = crc32.CRC; + return crc.Value; + } + set + { + if (value == 0) + crc = null; + else + crc = value; + } + } + public SimulationObject(SimulationObject obj) { Prim = new Primitive(obj.Prim); @@ -201,42 +231,29 @@ namespace Simian } } - public static ObjectUpdatePacket BuildFullUpdate(Primitive obj, ulong regionHandle, PrimFlags flags) + public static ObjectUpdatePacket BuildFullUpdate(Primitive obj, ulong regionHandle, PrimFlags flags, uint crc) { ObjectUpdatePacket update = new ObjectUpdatePacket(); update.RegionData.RegionHandle = regionHandle; update.RegionData.TimeDilation = UInt16.MaxValue; update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - update.ObjectData[0] = BuildUpdateBlock(obj, regionHandle, flags); + update.ObjectData[0] = BuildUpdateBlock(obj, flags, crc); return update; } - public static byte[] BuildObjectData(Vector3 position, Quaternion rotation, Vector3 velocity, - Vector3 acceleration, Vector3 angularVelocity) + public static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlock(Primitive obj, PrimFlags flags, uint crc) { byte[] objectData = new byte[60]; - int pos = 0; - position.GetBytes().CopyTo(objectData, pos); - pos += 12; - velocity.GetBytes().CopyTo(objectData, pos); - pos += 12; - acceleration.GetBytes().CopyTo(objectData, pos); - pos += 12; - rotation.GetBytes().CopyTo(objectData, pos); - pos += 12; - angularVelocity.GetBytes().CopyTo(objectData, pos); - return objectData; - } - - public static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlock(Primitive obj, ulong regionHandle, PrimFlags flags) - { - byte[] objectData = BuildObjectData(obj.Position, obj.Rotation, obj.Velocity, - obj.Acceleration, obj.AngularVelocity); + obj.Position.ToBytes(objectData, 0); + obj.Velocity.ToBytes(objectData, 12); + obj.Acceleration.ToBytes(objectData, 24); + obj.Rotation.ToBytes(objectData, 36); + obj.AngularVelocity.ToBytes(objectData, 48); ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); update.ClickAction = (byte)obj.ClickAction; - update.CRC = 0; + update.CRC = crc; update.ExtraParams = obj.GetExtraParamsBytes(); update.Flags = (byte)flags; update.FullID = obj.ID; @@ -273,14 +290,14 @@ namespace Simian update.PSBlock = obj.ParticleSys.GetBytes(); update.TextColor = obj.TextColor.GetBytes(true); update.TextureAnim = obj.TextureAnim.GetBytes(); - update.TextureEntry = obj.Textures == null ? new byte[0] : obj.Textures.ToBytes(); + update.TextureEntry = obj.Textures == null ? Utils.EmptyBytes : obj.Textures.ToBytes(); update.Radius = obj.SoundRadius; update.Scale = obj.Scale; update.Sound = obj.Sound; update.State = obj.PrimData.State; update.Text = Utils.StringToBytes(obj.Text); update.UpdateFlags = (uint)flags; - update.Data = obj.GenericData == null ? new byte[0] : obj.GenericData; + update.Data = obj.GenericData == null ? Utils.EmptyBytes : obj.GenericData; return update; }