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; DoubleDictionary SceneObjects = new DoubleDictionary(); int CurrentLocalID = 0; public ObjectManager(Simian server) { Server = server; } public void Start() { Server.UDP.RegisterPacketCallback(PacketType.ObjectAdd, new PacketCallback(ObjectAddHandler)); 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.DeRezObject, new PacketCallback(DeRezObjectHandler)); Server.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, new PacketCallback(MultipleObjectUpdateHandler)); Server.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, new PacketCallback(RequestObjectPropertiesFamilyHandler)); Server.UDP.RegisterPacketCallback(PacketType.CompleteAgentMovement, new PacketCallback(CompleteAgentMovementHandler)); //HACK: show prims } public void Stop() { } //TODO: Add interest list instead of this hack void CompleteAgentMovementHandler(Packet packet, Agent agent) { CompleteAgentMovementPacket complete = (CompleteAgentMovementPacket)packet; SceneObjects.ForEach(delegate(SimulationObject obj) { ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(obj.Prim, String.Empty, obj.Prim.RegionHandle, 0, obj.Prim.Flags); Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.State); }); } 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 (SceneObjects.TryGetValue(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.LocalID = (uint)Interlocked.Increment(ref CurrentLocalID); 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); SceneObjects.Add(prim.LocalID, prim.ID, simObj); // Send an update out to the creator ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(prim, String.Empty, prim.RegionHandle, 0, prim.Flags | PrimFlags.CreateSelected | PrimFlags.ObjectYouOwner); Server.UDP.SendPacket(agent.AgentID, updateToOwner, PacketCategory.State); // Send an update out to everyone else ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(prim, String.Empty, prim.RegionHandle, 0, prim.Flags); lock (Server.Agents) { foreach (Agent recipient in Server.Agents.Values) { if (recipient != agent) Server.UDP.SendPacket(recipient.AgentID, updateToOthers, 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 (SceneObjects.TryGetValue(select.ObjectData[i].ObjectLocalID, out obj)) { 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]; } } Server.UDP.SendPacket(agent.AgentID, properties, PacketCategory.Transaction); } void ObjectDeselectHandler(Packet packet, Agent agent) { ObjectDeselectPacket deselect = (ObjectDeselectPacket)packet; // 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 /// 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; } } }