using System; using System.Collections.Generic; using OpenMetaverse; using OpenMetaverse.Packets; using OpenMetaverse.Rendering; namespace Simian { public class SimulationObject { /// Reference to the primitive object this class wraps public Primitive Prim; /// Link number, if this object is part of a linkset public int LinkNumber; /// True when an avatar grabs this object. Stops movement and /// rotation public bool Frozen; protected Simian Server; protected SimpleMesh[] Meshes = new SimpleMesh[4]; protected SimpleMesh[] WorldTransformedMeshes = new SimpleMesh[4]; public SimulationObject(SimulationObject obj) { Prim = new Primitive(obj.Prim); Server = obj.Server; LinkNumber = obj.LinkNumber; Frozen = obj.Frozen; // Skip everything else because it can be lazily reconstructed } public SimulationObject(Primitive prim, Simian server) { Prim = prim; Server = server; } public SimpleMesh GetMesh(DetailLevel lod) { int i = (int)lod; if (Meshes[i] != null) { return Meshes[i]; } else { Primitive prim = (Primitive)Prim; SimpleMesh mesh = Server.Mesher.GenerateSimpleMesh(prim, lod); Meshes[i] = mesh; return mesh; } } public SimpleMesh GetWorldMesh(DetailLevel lod, SimulationObject parent) { int i = (int)lod; if (WorldTransformedMeshes[i] != null) { return WorldTransformedMeshes[i]; } else { // Get the untransformed mesh SimpleMesh mesh = GetMesh(lod); // Copy to our new mesh SimpleMesh transformedMesh = new SimpleMesh(); transformedMesh.Indices = new List(mesh.Indices); transformedMesh.Path.Open = mesh.Path.Open; transformedMesh.Path.Points = new List(mesh.Path.Points); transformedMesh.Prim = mesh.Prim; transformedMesh.Profile.Concave = mesh.Profile.Concave; transformedMesh.Profile.Faces = new List(mesh.Profile.Faces); transformedMesh.Profile.MaxX = mesh.Profile.MaxX; transformedMesh.Profile.MinX = mesh.Profile.MinX; transformedMesh.Profile.Open = mesh.Profile.Open; transformedMesh.Profile.Positions = new List(mesh.Profile.Positions); transformedMesh.Profile.TotalOutsidePoints = mesh.Profile.TotalOutsidePoints; transformedMesh.Vertices = new List(mesh.Vertices); // Construct a matrix to transform to world space Matrix4 transform = Matrix4.Identity; if (parent != null) { // Apply parent rotation and translation first transform *= Matrix4.CreateFromQuaternion(parent.Prim.Rotation); transform *= Matrix4.CreateTranslation(parent.Prim.Position); } transform *= Matrix4.CreateScale(this.Prim.Scale); transform *= Matrix4.CreateFromQuaternion(this.Prim.Rotation); transform *= Matrix4.CreateTranslation(this.Prim.Position); // Transform the mesh for (int j = 0; j < transformedMesh.Vertices.Count; j++) { Vertex vertex = transformedMesh.Vertices[j]; vertex.Position *= transform; transformedMesh.Vertices[j] = vertex; } WorldTransformedMeshes[i] = transformedMesh; return transformedMesh; } } public static ObjectUpdatePacket BuildFullUpdate(Primitive obj, ulong regionHandle, byte state, PrimFlags flags) { 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, state, flags); return update; } public static byte[] BuildObjectData(Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity) { 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, byte state, PrimFlags flags) { byte[] objectData = BuildObjectData(obj.Position, obj.Rotation, obj.Velocity, obj.Acceleration, obj.AngularVelocity); ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); update.ClickAction = (byte)obj.ClickAction; update.CRC = 0; update.ExtraParams = new byte[0]; //FIXME: Need a serializer for ExtraParams update.Flags = (byte)flags; update.FullID = obj.ID; update.Gain = obj.SoundGain; update.ID = obj.LocalID; update.JointAxisOrAnchor = obj.JointAxisOrAnchor; update.JointPivot = obj.JointPivot; update.JointType = (byte)obj.Joint; update.Material = (byte)obj.PrimData.Material; update.MediaURL = Utils.StringToBytes(obj.MediaURL); update.NameValue = Utils.StringToBytes(NameValue.NameValuesToString(obj.NameValues)); update.ObjectData = objectData; update.OwnerID = obj.Properties.OwnerID; update.ParentID = obj.ParentID; update.PathBegin = Primitive.PackBeginCut(obj.PrimData.PathBegin); update.PathCurve = (byte)obj.PrimData.PathCurve; update.PathEnd = Primitive.PackEndCut(obj.PrimData.PathEnd); update.PathRadiusOffset = Primitive.PackPathTwist(obj.PrimData.PathRadiusOffset); update.PathRevolutions = Primitive.PackPathRevolutions(obj.PrimData.PathRevolutions); update.PathScaleX = Primitive.PackPathScale(obj.PrimData.PathScaleX); update.PathScaleY = Primitive.PackPathScale(obj.PrimData.PathScaleY); update.PathShearX = (byte)Primitive.PackPathShear(obj.PrimData.PathShearX); update.PathShearY = (byte)Primitive.PackPathShear(obj.PrimData.PathShearY); update.PathSkew = Primitive.PackPathTwist(obj.PrimData.PathSkew); update.PathTaperX = Primitive.PackPathTaper(obj.PrimData.PathTaperX); update.PathTaperY = Primitive.PackPathTaper(obj.PrimData.PathTaperY); update.PathTwist = Primitive.PackPathTwist(obj.PrimData.PathTwist); update.PathTwistBegin = Primitive.PackPathTwist(obj.PrimData.PathTwistBegin); update.PCode = (byte)obj.PrimData.PCode; update.ProfileBegin = Primitive.PackBeginCut(obj.PrimData.ProfileBegin); update.ProfileCurve = (byte)obj.PrimData.ProfileCurve; update.ProfileEnd = Primitive.PackEndCut(obj.PrimData.ProfileEnd); update.ProfileHollow = Primitive.PackProfileHollow(obj.PrimData.ProfileHollow); update.PSBlock = new byte[0]; // FIXME: update.TextColor = obj.TextColor.GetBytes(true); update.TextureAnim = obj.TextureAnim.GetBytes(); update.TextureEntry = obj.Textures == null ? new byte[0] : obj.Textures.ToBytes(); update.Radius = obj.SoundRadius; update.Scale = obj.Scale; update.Sound = obj.Sound; update.State = state; update.Text = Utils.StringToBytes(obj.Text); update.UpdateFlags = (uint)flags; update.Data = new byte[0]; // FIXME: return update; } } }