using System; using System.Collections.Generic; using System.Threading; using OpenMetaverse; using OpenMetaverse.Rendering; using OpenMetaverse.Packets; namespace Simian.Extensions { public class ObjectManager : ISimianExtension { Simian Server; public ObjectManager(Simian server) { Server = server; } public void Start() { Server.UDP.RegisterPacketCallback(PacketType.ObjectAdd, new PacketCallback(ObjectAddHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectDuplicate, new PacketCallback(ObjectDuplicateHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectSelect, new PacketCallback(ObjectSelectHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectDeselect, new PacketCallback(ObjectDeselectHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectLink, new PacketCallback(ObjectLinkHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectDelink, new PacketCallback(ObjectDelinkHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectShape, new PacketCallback(ObjectShapeHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectFlagUpdate, new PacketCallback(ObjectFlagUpdateHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectExtraParams, new PacketCallback(ObjectExtraParamsHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectImage, new PacketCallback(ObjectImageHandler)); Server.UDP.RegisterPacketCallback(PacketType.DeRezObject, new PacketCallback(DeRezObjectHandler)); Server.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, new PacketCallback(MultipleObjectUpdateHandler)); Server.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, new PacketCallback(RequestObjectPropertiesFamilyHandler)); } public void Stop() { } void ObjectAddHandler(Packet packet, Agent agent) { ObjectAddPacket add = (ObjectAddPacket)packet; Vector3 position = Vector3.Zero; Vector3 scale = add.ObjectData.Scale; PCode pcode = (PCode)add.ObjectData.PCode; PrimFlags flags = (PrimFlags)add.ObjectData.AddFlags; bool bypassRaycast = (add.ObjectData.BypassRaycast == 1); bool rayEndIsIntersection = (add.ObjectData.RayEndIsIntersection == 1); #region Position Calculation if (rayEndIsIntersection) { // HACK: Blindly trust where the client tells us to place position = add.ObjectData.RayEnd; } else { if (add.ObjectData.RayTargetID != UUID.Zero) { SimulationObject obj; 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); } } if (position == Vector3.Zero) { // Test for a collision with the entire scene position = FullSceneCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd); } } // Position lies on the face of another surface, either terrain of an object. // Back up along the ray so we are not colliding with the mesh. // HACK: This is really cheesy and should be done by a collision system Vector3 rayDir = Vector3.Normalize(add.ObjectData.RayEnd - add.ObjectData.RayStart); position -= rayDir * scale; #endregion Position Calculation #region Foliage Handling // Set all foliage to phantom if (pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree) { flags |= PrimFlags.Phantom; if (pcode != PCode.Grass) { // Resize based on the foliage type Tree tree = (Tree)add.ObjectData.State; switch (tree) { case Tree.Cypress1: case Tree.Cypress2: scale = new Vector3(4f, 4f, 10f); break; default: scale = new Vector3(4f, 4f, 4f); break; } } } #endregion Foliage Handling // Create an object Primitive prim = new Primitive(); prim.Flags = PrimFlags.ObjectModify | PrimFlags.ObjectCopy | PrimFlags.ObjectAnyOwner | PrimFlags.ObjectMove | PrimFlags.ObjectTransfer | PrimFlags.ObjectOwnerModify; // TODO: Security check prim.GroupID = add.AgentData.GroupID; prim.ID = UUID.Random(); prim.MediaURL = String.Empty; prim.OwnerID = agent.AgentID; prim.Position = position; prim.PrimData.Material = (Material)add.ObjectData.Material; prim.PrimData.PathCurve = (PathCurve)add.ObjectData.PathCurve; prim.PrimData.ProfileCurve = (ProfileCurve)add.ObjectData.ProfileCurve; prim.PrimData.PathBegin = Primitive.UnpackBeginCut(add.ObjectData.PathBegin); prim.PrimData.PathEnd = Primitive.UnpackEndCut(add.ObjectData.PathEnd); prim.PrimData.PathScaleX = Primitive.UnpackPathScale(add.ObjectData.PathScaleX); prim.PrimData.PathScaleY = Primitive.UnpackPathScale(add.ObjectData.PathScaleY); prim.PrimData.PathShearX = Primitive.UnpackPathShear((sbyte)add.ObjectData.PathShearX); prim.PrimData.PathShearY = Primitive.UnpackPathShear((sbyte)add.ObjectData.PathShearY); prim.PrimData.PathTwist = Primitive.UnpackPathTwist(add.ObjectData.PathTwist); prim.PrimData.PathTwistBegin = Primitive.UnpackPathTwist(add.ObjectData.PathTwistBegin); prim.PrimData.PathRadiusOffset = Primitive.UnpackPathTwist(add.ObjectData.PathRadiusOffset); prim.PrimData.PathTaperX = Primitive.UnpackPathTaper(add.ObjectData.PathTaperX); prim.PrimData.PathTaperY = Primitive.UnpackPathTaper(add.ObjectData.PathTaperY); prim.PrimData.PathRevolutions = Primitive.UnpackPathRevolutions(add.ObjectData.PathRevolutions); prim.PrimData.PathSkew = Primitive.UnpackPathTwist(add.ObjectData.PathSkew); prim.PrimData.ProfileBegin = Primitive.UnpackBeginCut(add.ObjectData.ProfileBegin); prim.PrimData.ProfileEnd = Primitive.UnpackEndCut(add.ObjectData.ProfileEnd); prim.PrimData.ProfileHollow = Primitive.UnpackProfileHollow(add.ObjectData.ProfileHollow); prim.PrimData.PCode = pcode; prim.Properties.CreationDate = DateTime.Now; prim.Properties.CreatorID = agent.AgentID; prim.Properties.Description = String.Empty; prim.Properties.GroupID = add.AgentData.GroupID; prim.Properties.LastOwnerID = agent.AgentID; prim.Properties.Name = "New Object"; prim.Properties.ObjectID = prim.ID; prim.Properties.OwnerID = prim.OwnerID; prim.Properties.Permissions = Permissions.FullPermissions; prim.Properties.SalePrice = 10; prim.RegionHandle = Server.RegionHandle; prim.Rotation = add.ObjectData.Rotation; prim.Scale = scale; prim.Textures = new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE); prim.TextColor = Color4.Black; // Add this prim to the object database SimulationObject simObj = new SimulationObject(prim, Server); Server.Scene.ObjectAdd(this, agent, simObj, flags); } void ObjectDuplicateHandler(Packet packet, Agent agent) { ObjectDuplicatePacket duplicate = (ObjectDuplicatePacket)packet; PrimFlags flags = (PrimFlags)duplicate.SharedData.DuplicateFlags; Vector3 offset = duplicate.SharedData.Offset; for (int i = 0; i < duplicate.ObjectData.Length; i++) { uint dupeID = duplicate.ObjectData[i].ObjectLocalID; SimulationObject obj; if (Server.Scene.TryGetObject(dupeID, out obj)) { SimulationObject newObj = new SimulationObject(obj); newObj.Prim.Position += offset; newObj.Prim.ID = UUID.Random(); Server.Scene.ObjectAdd(this, agent, newObj, flags); } else { Logger.Log("ObjectDuplicate sent for missing object " + dupeID, Helpers.LogLevel.Warning); KillObjectPacket kill = new KillObjectPacket(); kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); kill.ObjectData[0].ID = dupeID; Server.UDP.SendPacket(agent.AgentID, kill, PacketCategory.State); } } } void ObjectSelectHandler(Packet packet, Agent agent) { ObjectSelectPacket select = (ObjectSelectPacket)packet; ObjectPropertiesPacket properties = new ObjectPropertiesPacket(); properties.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[select.ObjectData.Length]; for (int i = 0; i < select.ObjectData.Length; i++) { properties.ObjectData[i] = new ObjectPropertiesPacket.ObjectDataBlock(); SimulationObject obj; if (Server.Scene.TryGetObject(select.ObjectData[i].ObjectLocalID, out obj)) { //Logger.DebugLog("Selecting object " + obj.Prim.LocalID); properties.ObjectData[i].BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask; properties.ObjectData[i].CreationDate = Utils.DateTimeToUnixTime(obj.Prim.Properties.CreationDate); properties.ObjectData[i].CreatorID = obj.Prim.Properties.CreatorID; properties.ObjectData[i].Description = Utils.StringToBytes(obj.Prim.Properties.Description); properties.ObjectData[i].EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask; properties.ObjectData[i].GroupID = obj.Prim.Properties.GroupID; properties.ObjectData[i].GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask; properties.ObjectData[i].LastOwnerID = obj.Prim.Properties.LastOwnerID; properties.ObjectData[i].Name = Utils.StringToBytes(obj.Prim.Properties.Name); properties.ObjectData[i].NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask; properties.ObjectData[i].ObjectID = obj.Prim.ID; properties.ObjectData[i].OwnerID = obj.Prim.Properties.OwnerID; properties.ObjectData[i].OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask; properties.ObjectData[i].OwnershipCost = obj.Prim.Properties.OwnershipCost; properties.ObjectData[i].SalePrice = obj.Prim.Properties.SalePrice; properties.ObjectData[i].SaleType = (byte)obj.Prim.Properties.SaleType; properties.ObjectData[i].SitName = new byte[0]; properties.ObjectData[i].TextureID = new byte[0]; properties.ObjectData[i].TouchName = new byte[0]; } else { Logger.Log("ObjectSelect sent for missing object " + select.ObjectData[i].ObjectLocalID, Helpers.LogLevel.Warning); properties.ObjectData[i].Description = new byte[0]; properties.ObjectData[i].Name = new byte[0]; properties.ObjectData[i].SitName = new byte[0]; properties.ObjectData[i].TextureID = new byte[0]; properties.ObjectData[i].TouchName = new byte[0]; KillObjectPacket kill = new KillObjectPacket(); kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); kill.ObjectData[0].ID = select.ObjectData[i].ObjectLocalID; Server.UDP.SendPacket(agent.AgentID, kill, PacketCategory.State); } } Server.UDP.SendPacket(agent.AgentID, properties, PacketCategory.Transaction); } void ObjectDeselectHandler(Packet packet, Agent agent) { ObjectDeselectPacket deselect = (ObjectDeselectPacket)packet; for (int i = 0; i < deselect.ObjectData.Length; i++) { uint localID = deselect.ObjectData[i].ObjectLocalID; SimulationObject obj; if (Server.Scene.TryGetObject(localID, out obj)) { //Logger.DebugLog("Deselecting object " + obj.Prim.LocalID); } } // TODO: Do we need this at all? } void ObjectLinkHandler(Packet packet, Agent agent) { ObjectLinkPacket link = (ObjectLinkPacket)packet; List linkSet = new List(); for (int i = 0; i < link.ObjectData.Length; i++) { SimulationObject obj; if (!Server.Scene.TryGetObject(link.ObjectData[i].ObjectLocalID, out obj)) { //TODO: send an error message return; } else if (obj.Prim.OwnerID != agent.AgentID) { //TODO: send an error message return; } else { linkSet.Add(obj); } } ObjectUpdatePacket update = new ObjectUpdatePacket(); update.RegionData.RegionHandle = Server.RegionHandle; update.RegionData.TimeDilation = UInt16.MaxValue; update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[linkSet.Count]; for (int i = 0; i < linkSet.Count; i++) { linkSet[i].LinkNumber = i + 1; update.ObjectData[i] = SimulationObject.BuildUpdateBlock(linkSet[i].Prim, Server.RegionHandle, linkSet[i].Prim.PrimData.State, linkSet[i].Prim.Flags); if (linkSet[i].Prim.ParentID > 0) { SimulationObject parent; if (Server.Scene.TryGetObject(linkSet[i].Prim.ParentID, out parent)) { //re-add old root orientation linkSet[i].Prim.Position += parent.Prim.Position; linkSet[i].Prim.Rotation *= parent.Prim.Rotation; } } if (i > 0) { //subtract root prim orientation linkSet[i].Prim.Position -= linkSet[0].Prim.Position; linkSet[i].Prim.Rotation /= linkSet[0].Prim.Rotation; //set parent ID update.ObjectData[i].ParentID = linkSet[0].Prim.LocalID; } else { update.ObjectData[i].ParentID = 0; } update.ObjectData[i].ObjectData = SimulationObject.BuildObjectData( linkSet[i].Prim.Position, linkSet[i].Prim.Rotation, Vector3.Zero, Vector3.Zero, Vector3.Zero); } Server.UDP.BroadcastPacket(update, PacketCategory.State); } void ObjectDelinkHandler(Packet packet, Agent agent) { ObjectDelinkPacket delink = (ObjectDelinkPacket)packet; List linkSet = new List(); for (int i = 0; i < delink.ObjectData.Length; i++) { SimulationObject obj; if (!Server.Scene.TryGetObject(delink.ObjectData[i].ObjectLocalID, out obj)) { //TODO: send an error message return; } else if (obj.Prim.OwnerID != agent.AgentID) { //TODO: send an error message return; } else { linkSet.Add(obj); } } ObjectUpdatePacket update = new ObjectUpdatePacket(); update.RegionData.RegionHandle = Server.RegionHandle; update.RegionData.TimeDilation = UInt16.MaxValue; update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[linkSet.Count]; for (int i = 0; i < linkSet.Count; i++) { update.ObjectData[i] = SimulationObject.BuildUpdateBlock(linkSet[i].Prim, Server.RegionHandle, 0, linkSet[i].Prim.Flags); update.ObjectData[i].ParentID = 0; linkSet[i].LinkNumber = 0; //add root prim orientation to child prims if (i > 0) { linkSet[i].Prim.Position += linkSet[0].Prim.Position; linkSet[i].Prim.Rotation *= linkSet[0].Prim.Rotation; } update.ObjectData[i].ObjectData = SimulationObject.BuildObjectData( linkSet[i].Prim.Position, linkSet[i].Prim.Rotation, Vector3.Zero, Vector3.Zero, Vector3.Zero); } Server.UDP.BroadcastPacket(update, PacketCategory.State); } void ObjectShapeHandler(Packet packet, Agent agent) { ObjectShapePacket shape = (ObjectShapePacket)packet; for (int i = 0; i < shape.ObjectData.Length; i++) { ObjectShapePacket.ObjectDataBlock block = shape.ObjectData[i]; SimulationObject obj; if (Server.Scene.TryGetObject(block.ObjectLocalID, out obj)) { Primitive.ConstructionData data = obj.Prim.PrimData; data.PathBegin = Primitive.UnpackBeginCut(block.PathBegin); data.PathCurve = (PathCurve)block.PathCurve; data.PathEnd = Primitive.UnpackEndCut(block.PathEnd); data.PathRadiusOffset = Primitive.UnpackPathTwist(block.PathRadiusOffset); data.PathRevolutions = Primitive.UnpackPathRevolutions(block.PathRevolutions); data.PathScaleX = Primitive.UnpackPathScale(block.PathScaleX); data.PathScaleY = Primitive.UnpackPathScale(block.PathScaleY); data.PathShearX = Primitive.UnpackPathShear((sbyte)block.PathShearX); data.PathShearY = Primitive.UnpackPathShear((sbyte)block.PathShearY); data.PathSkew = Primitive.UnpackPathTwist(block.PathSkew); data.PathTaperX = Primitive.UnpackPathTaper(block.PathTaperX); data.PathTaperY = Primitive.UnpackPathTaper(block.PathTaperY); data.PathTwist = Primitive.UnpackPathTwist(block.PathTwist); data.PathTwistBegin = Primitive.UnpackPathTwist(block.PathTwistBegin); data.ProfileBegin = Primitive.UnpackBeginCut(block.ProfileBegin); data.profileCurve = block.ProfileCurve; data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd); data.ProfileHollow = Primitive.UnpackProfileHollow(block.ProfileHollow); Server.Scene.ObjectModify(this, obj, data); } else { Logger.Log("Got an ObjectShape packet for unknown object " + block.ObjectLocalID, Helpers.LogLevel.Warning); } } } void ObjectFlagUpdateHandler(Packet packet, Agent agent) { ObjectFlagUpdatePacket update = (ObjectFlagUpdatePacket)packet; SimulationObject obj; if (Server.Scene.TryGetObject(update.AgentData.ObjectLocalID, out obj)) { PrimFlags flags = obj.Prim.Flags; if (update.AgentData.CastsShadows) flags |= PrimFlags.CastShadows; else flags &= ~PrimFlags.CastShadows; if (update.AgentData.IsPhantom) flags |= PrimFlags.Phantom; else flags &= ~PrimFlags.Phantom; if (update.AgentData.IsTemporary) flags |= PrimFlags.Temporary; else flags &= ~PrimFlags.Temporary; if (update.AgentData.UsePhysics) flags |= PrimFlags.Physics; else flags &= ~PrimFlags.Physics; Server.Scene.ObjectFlags(this, obj, flags); } else { Logger.Log("Got an ObjectFlagUpdate packet for unknown object " + update.AgentData.ObjectLocalID, Helpers.LogLevel.Warning); } } void ObjectExtraParamsHandler(Packet packet, Agent agent) { ObjectExtraParamsPacket extra = (ObjectExtraParamsPacket)packet; for (int i = 0; i < extra.ObjectData.Length; i++) { ObjectExtraParamsPacket.ObjectDataBlock block = extra.ObjectData[i]; SimulationObject obj; if (Server.Scene.TryGetObject(block.ObjectLocalID, out obj)) { ExtraParamType type = (ExtraParamType)block.ParamType; } } } void ObjectImageHandler(Packet packet, Agent agent) { } void DeRezObjectHandler(Packet packet, Agent agent) { DeRezObjectPacket derez = (DeRezObjectPacket)packet; DeRezDestination destination = (DeRezDestination)derez.AgentBlock.Destination; // TODO: Check permissions for (int i = 0; i < derez.ObjectData.Length; i++) { uint localID = derez.ObjectData[i].ObjectLocalID; SimulationObject obj; if (Server.Scene.TryGetObject(localID, out obj)) { switch (destination) { case DeRezDestination.AgentInventorySave: Logger.Log("DeRezObject: Got an AgentInventorySave, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.AgentInventoryCopy: Logger.Log("DeRezObject: Got an AgentInventorySave, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.TaskInventory: Logger.Log("DeRezObject: Got a TaskInventory, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.Attachment: Logger.Log("DeRezObject: Got an Attachment, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.AgentInventoryTake: Logger.Log("DeRezObject: Got an AgentInventoryTake, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.ForceToGodInventory: Logger.Log("DeRezObject: Got a ForceToGodInventory, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.TrashFolder: InventoryObject invObj; if (agent.Inventory.TryGetValue(derez.AgentBlock.DestinationID, out invObj) && invObj is InventoryFolder) { // FIXME: Handle children InventoryFolder trash = (InventoryFolder)invObj; Server.Inventory.CreateItem(agent, obj.Prim.Properties.Name, obj.Prim.Properties.Description, InventoryType.Object, AssetType.Object, obj.Prim.ID, trash.ID, PermissionMask.All, PermissionMask.All, agent.AgentID, obj.Prim.Properties.CreatorID, derez.AgentBlock.TransactionID, 0); Server.Scene.ObjectRemove(this, obj); Logger.DebugLog(String.Format("Derezzed prim {0} to agent inventory trash", obj.Prim.LocalID)); } else { Logger.Log("DeRezObject: Got a TrashFolder with an invalid trash folder: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); } break; case DeRezDestination.AttachmentToInventory: Logger.Log("DeRezObject: Got an AttachmentToInventory, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.AttachmentExists: Logger.Log("DeRezObject: Got an AttachmentExists, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.ReturnToOwner: Logger.Log("DeRezObject: Got a ReturnToOwner, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; case DeRezDestination.ReturnToLastOwner: Logger.Log("DeRezObject: Got a ReturnToLastOwner, DestID: " + derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning); break; } } } } void MultipleObjectUpdateHandler(Packet packet, Agent agent) { MultipleObjectUpdatePacket update = (MultipleObjectUpdatePacket)packet; for (int i = 0; i < update.ObjectData.Length; i++) { MultipleObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[i]; SimulationObject obj; if (Server.Scene.TryGetObject(block.ObjectLocalID, out obj)) { UpdateType type = (UpdateType)block.Type; bool linked = ((type & UpdateType.Linked) != 0); int pos = 0; Vector3 position = obj.Prim.Position; Quaternion rotation = obj.Prim.Rotation; Vector3 scale = obj.Prim.Scale; if ((type & UpdateType.Position) != 0) { position = new Vector3(block.Data, pos); pos += 12; } if ((type & UpdateType.Rotation) != 0) { rotation = new Quaternion(block.Data, pos, true); pos += 12; } if ((type & UpdateType.Scale) != 0) { scale = new Vector3(block.Data, pos); pos += 12; // FIXME: Use this in linksets bool uniform = ((type & UpdateType.Uniform) != 0); } // Although the object has already been modified, we need // to inform the scene manager of the changes so they are // sent to clients and propagated to other extensions Server.Scene.ObjectTransform(this, obj, position, rotation, obj.Prim.Velocity, obj.Prim.Acceleration, obj.Prim.AngularVelocity, scale); } 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(); kill.ObjectData[0].ID = block.ObjectLocalID; Server.UDP.SendPacket(agent.AgentID, kill, PacketCategory.State); } } } void RequestObjectPropertiesFamilyHandler(Packet packet, Agent agent) { RequestObjectPropertiesFamilyPacket request = (RequestObjectPropertiesFamilyPacket)packet; ReportType type = (ReportType)request.ObjectData.RequestFlags; SimulationObject obj; if (Server.Scene.TryGetObject(request.ObjectData.ObjectID, out obj)) { ObjectPropertiesFamilyPacket props = new ObjectPropertiesFamilyPacket(); 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.AgentID, props, PacketCategory.Transaction); } else { Logger.Log("RequestObjectPropertiesFamily sent for unknown object " + request.ObjectData.ObjectID.ToString(), Helpers.LogLevel.Warning); } } Vector3 FullSceneCollisionTest(Vector3 rayStart, Vector3 rayEnd) { // HACK: For now 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); Ray ray = new Ray(rayStart, direction); // 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; } } }