diff --git a/OpenMetaverse/Messages/LindenMessages.cs b/OpenMetaverse/Messages/LindenMessages.cs index 4ac54e3f..3b8305f9 100644 --- a/OpenMetaverse/Messages/LindenMessages.cs +++ b/OpenMetaverse/Messages/LindenMessages.cs @@ -4112,6 +4112,57 @@ namespace OpenMetaverse.Messages.Linden } } + /// + /// Event Queue message describing physics engine attributes of a list of objects + /// Sim sends these when object is selected + /// + public class ObjectPhysicsPropertiesMessage : IMessage + { + /// Array with the list of physics properties + public Primitive.PhysicsProperties[] ObjectPhysicsProperties; + + /// + /// Serializes the message + /// + /// Serialized OSD + public OSDMap Serialize() + { + OSDMap ret = new OSDMap(); + OSDArray array = new OSDArray(); + + for (int i = 0; i < ObjectPhysicsProperties.Length; i++) + { + array.Add(ObjectPhysicsProperties[i].GetOSD()); + } + + ret["ObjectData"] = array; + return ret; + + } + + /// + /// Deseializes the message + /// + /// Incoming data to deserialize + public void Deserialize(OSDMap map) + { + OSDArray array = map["ObjectData"] as OSDArray; + if (array != null) + { + ObjectPhysicsProperties = new Primitive.PhysicsProperties[array.Count]; + + for (int i = 0; i < array.Count; i++) + { + ObjectPhysicsProperties[i] = Primitive.PhysicsProperties.FromOSD(array[i]); + } + } + else + { + ObjectPhysicsProperties = new Primitive.PhysicsProperties[0]; + } + } + } + #endregion Object Messages #region Object Media Messages diff --git a/OpenMetaverse/Messages/MessageEventDecoder.cs b/OpenMetaverse/Messages/MessageEventDecoder.cs index add5eea8..4847ac60 100644 --- a/OpenMetaverse/Messages/MessageEventDecoder.cs +++ b/OpenMetaverse/Messages/MessageEventDecoder.cs @@ -96,6 +96,7 @@ namespace OpenMetaverse.Messages case "SetDisplayNameReply": message = new SetDisplayNameReplyMessage(); break; case "DisplayNameUpdate": message = new DisplayNameUpdateMessage(); break; //case "ProductInfoRequest": message = new ProductInfoRequestMessage(); break; + case "ObjectPhysicsProperties": message = new ObjectPhysicsPropertiesMessage(); break; // Capabilities TODO: // DispatchRegionInfo @@ -127,7 +128,7 @@ namespace OpenMetaverse.Messages } catch (Exception e) { - Logger.Log("Exception while tring to Deserialize " + eventName + ":" + e.Message + ": " + e.StackTrace, Helpers.LogLevel.Error); + Logger.Log("Exception while trying to Deserialize " + eventName + ":" + e.Message + ": " + e.StackTrace, Helpers.LogLevel.Error); } return null; diff --git a/OpenMetaverse/ObjectManager.cs b/OpenMetaverse/ObjectManager.cs index cf26d92a..7ce314f6 100644 --- a/OpenMetaverse/ObjectManager.cs +++ b/OpenMetaverse/ObjectManager.cs @@ -30,6 +30,7 @@ using System.Threading; using OpenMetaverse.Packets; using OpenMetaverse.Http; using OpenMetaverse.StructuredData; +using OpenMetaverse.Interfaces; using OpenMetaverse.Messages.Linden; namespace OpenMetaverse @@ -398,6 +399,32 @@ namespace OpenMetaverse /// Array indexed on prim face of media entry data public delegate void ObjectMediaCallback(bool success, string version, MediaEntry[] faceMedia); + /// The event subscribers, null of no subscribers + private EventHandler m_PhysicsProperties; + + ///Raises the PhysicsProperties Event + /// A PhysicsPropertiesEventArgs object containing + /// the data sent from the simulator + protected virtual void OnPhysicsProperties(PhysicsPropertiesEventArgs e) + { + EventHandler handler = m_PhysicsProperties; + if (handler != null) + handler(this, e); + } + + /// Thread sync lock object + private readonly object m_PhysicsPropertiesLock = new object(); + + /// Raised when the simulator sends us data containing + /// additional information + /// + /// + public event EventHandler PhysicsProperties + { + add { lock (m_PhysicsPropertiesLock) { m_PhysicsProperties += value; } } + remove { lock (m_PhysicsPropertiesLock) { m_PhysicsProperties -= value; } } + } + #endregion Delegates /// Reference to the GridClient object @@ -422,6 +449,7 @@ namespace OpenMetaverse Client.Network.RegisterCallback(PacketType.ObjectPropertiesFamily, ObjectPropertiesFamilyHandler); Client.Network.RegisterCallback(PacketType.ObjectProperties, ObjectPropertiesHandler); Client.Network.RegisterCallback(PacketType.PayPriceReply, PayPriceReplyHandler); + Client.Network.RegisterEventCallback("ObjectPhysicsProperties", ObjectPhysicsPropertiesHandler); } #region Internal event handlers @@ -1976,12 +2004,12 @@ namespace OpenMetaverse prim.TreeSpecies = (Tree)block.Data[0]; else Logger.Log("Got a foliage update with an invalid TreeSpecies field", Helpers.LogLevel.Warning); - // prim.ScratchPad = Utils.EmptyBytes; - // break; - //default: - // prim.ScratchPad = new byte[block.Data.Length]; - // if (block.Data.Length > 0) - // Buffer.BlockCopy(block.Data, 0, prim.ScratchPad, 0, prim.ScratchPad.Length); + // prim.ScratchPad = Utils.EmptyBytes; + // break; + //default: + // prim.ScratchPad = new byte[block.Data.Length]; + // if (block.Data.Length > 0) + // Buffer.BlockCopy(block.Data, 0, prim.ScratchPad, 0, prim.ScratchPad.Length); break; } prim.ScratchPad = Utils.EmptyBytes; @@ -2770,6 +2798,39 @@ namespace OpenMetaverse } } + /// + /// + /// + /// + /// + /// + protected void ObjectPhysicsPropertiesHandler(string capsKey, IMessage message, Simulator simulator) + { + ObjectPhysicsPropertiesMessage msg = (ObjectPhysicsPropertiesMessage)message; + if (m_PhysicsProperties != null) + { + for (int i = 0; i < msg.ObjectPhysicsProperties.Length; i++) + { + OnPhysicsProperties(new PhysicsPropertiesEventArgs(simulator, msg.ObjectPhysicsProperties[i])); + } + } + + if (Client.Settings.OBJECT_TRACKING) + { + for (int i = 0; i < msg.ObjectPhysicsProperties.Length; i++) + { + lock (simulator.ObjectsPrimitives.Dictionary) + { + if (simulator.ObjectsPrimitives.Dictionary.ContainsKey(msg.ObjectPhysicsProperties[i].LocalID)) + { + simulator.ObjectsPrimitives.Dictionary[msg.ObjectPhysicsProperties[i].LocalID].PhysicsProps = msg.ObjectPhysicsProperties[i]; + } + } + } + } + + } + #endregion Packet Handlers #region Utility Functions @@ -3559,5 +3620,27 @@ namespace OpenMetaverse } } + /// + /// Set when simulator sends us infomation on primitive's physical properties + /// + public class PhysicsPropertiesEventArgs : EventArgs + { + /// Simulator where the message originated + public Simulator Simulator; + /// Updated physical properties + public Primitive.PhysicsProperties PhysicsProperties; + + /// + /// Constructor + /// + /// Simulator where the message originated + /// Updated physical properties + public PhysicsPropertiesEventArgs(Simulator sim, Primitive.PhysicsProperties props) + { + Simulator = sim; + PhysicsProperties = props; + } + } + #endregion } diff --git a/OpenMetaverse/Primitives/Primitive.cs b/OpenMetaverse/Primitives/Primitive.cs index d80bcfc4..a1eef6b3 100644 --- a/OpenMetaverse/Primitives/Primitive.cs +++ b/OpenMetaverse/Primitives/Primitive.cs @@ -1,1278 +1,1338 @@ -/* - * Copyright (c) 2006-2008, openmetaverse.org - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.org nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using OpenMetaverse.StructuredData; - -namespace OpenMetaverse -{ - public partial class Primitive : IEquatable - { - // Used for packing and unpacking parameters - protected const float CUT_QUANTA = 0.00002f; - protected const float SCALE_QUANTA = 0.01f; - protected const float SHEAR_QUANTA = 0.01f; - protected const float TAPER_QUANTA = 0.01f; - protected const float REV_QUANTA = 0.015f; - protected const float HOLLOW_QUANTA = 0.00002f; - - #region Subclasses - - /// - /// Parameters used to construct a visual representation of a primitive - /// - public struct ConstructionData - { - private const byte PROFILE_MASK = 0x0F; - private const byte HOLE_MASK = 0xF0; - - /// - public byte profileCurve; - /// - public PathCurve PathCurve; - /// - public float PathEnd; - /// - public float PathRadiusOffset; - /// - public float PathSkew; - /// - public float PathScaleX; - /// - public float PathScaleY; - /// - public float PathShearX; - /// - public float PathShearY; - /// - public float PathTaperX; - /// - public float PathTaperY; - /// - public float PathBegin; - /// - public float PathTwist; - /// - public float PathTwistBegin; - /// - public float PathRevolutions; - /// - public float ProfileBegin; - /// - public float ProfileEnd; - /// - public float ProfileHollow; - - /// - public Material Material; - /// - public byte State; - /// - public PCode PCode; - - #region Properties - - /// Attachment point to an avatar - public AttachmentPoint AttachmentPoint - { - get { return (AttachmentPoint)Utils.SwapWords(State); } - set { State = (byte)Utils.SwapWords((byte)value); } - } - - /// - public ProfileCurve ProfileCurve - { - get { return (ProfileCurve)(profileCurve & PROFILE_MASK); } - set - { - profileCurve &= HOLE_MASK; - profileCurve |= (byte)value; - } - } - - /// - public HoleType ProfileHole - { - get { return (HoleType)(profileCurve & HOLE_MASK); } - set - { - profileCurve &= PROFILE_MASK; - profileCurve |= (byte)value; - } - } - - /// - public Vector2 PathBeginScale - { - get - { - Vector2 begin = new Vector2(1f, 1f); - if (PathScaleX > 1f) - begin.X = 2f - PathScaleX; - if (PathScaleY > 1f) - begin.Y = 2f - PathScaleY; - return begin; - } - } - - /// - public Vector2 PathEndScale - { - get - { - Vector2 end = new Vector2(1f, 1f); - if (PathScaleX < 1f) - end.X = PathScaleX; - if (PathScaleY < 1f) - end.Y = PathScaleY; - return end; - } - } - - #endregion Properties - } - - /// - /// Information on the flexible properties of a primitive - /// - public class FlexibleData - { - /// - public int Softness; - /// - public float Gravity; - /// - public float Drag; - /// - public float Wind; - /// - public float Tension; - /// - public Vector3 Force; - - /// - /// Default constructor - /// - public FlexibleData() - { - } - - /// - /// - /// - /// - /// - public FlexibleData(byte[] data, int pos) - { - if (data.Length >= 5) - { - Softness = ((data[pos] & 0x80) >> 6) | ((data[pos + 1] & 0x80) >> 7); - - Tension = (float)(data[pos++] & 0x7F) / 10.0f; - Drag = (float)(data[pos++] & 0x7F) / 10.0f; - Gravity = (float)(data[pos++] / 10.0f) - 10.0f; - Wind = (float)data[pos++] / 10.0f; - Force = new Vector3(data, pos); - } - else - { - Softness = 0; - - Tension = 0.0f; - Drag = 0.0f; - Gravity = 0.0f; - Wind = 0.0f; - Force = Vector3.Zero; - } - } - - /// - /// - /// - /// - public byte[] GetBytes() - { - byte[] data = new byte[16]; - int i = 0; - - // Softness is packed in the upper bits of tension and drag - data[i] = (byte)((Softness & 2) << 6); - data[i + 1] = (byte)((Softness & 1) << 7); - - data[i++] |= (byte)((byte)(Tension * 10.01f) & 0x7F); - data[i++] |= (byte)((byte)(Drag * 10.01f) & 0x7F); - data[i++] = (byte)((Gravity + 10.0f) * 10.01f); - data[i++] = (byte)(Wind * 10.01f); - - Force.GetBytes().CopyTo(data, i); - - return data; - } - - /// - /// - /// - /// - public OSD GetOSD() - { - OSDMap map = new OSDMap(); - - map["simulate_lod"] = OSD.FromInteger(Softness); - map["gravity"] = OSD.FromReal(Gravity); - map["air_friction"] = OSD.FromReal(Drag); - map["wind_sensitivity"] = OSD.FromReal(Wind); - map["tension"] = OSD.FromReal(Tension); - map["user_force"] = OSD.FromVector3(Force); - - return map; - } - - public static FlexibleData FromOSD(OSD osd) - { - FlexibleData flex = new FlexibleData(); - - if (osd.Type == OSDType.Map) - { - OSDMap map = (OSDMap)osd; - - flex.Softness = map["simulate_lod"].AsInteger(); - flex.Gravity = (float)map["gravity"].AsReal(); - flex.Drag = (float)map["air_friction"].AsReal(); - flex.Wind = (float)map["wind_sensitivity"].AsReal(); - flex.Tension = (float)map["tension"].AsReal(); - flex.Force = ((OSDArray)map["user_force"]).AsVector3(); - } - - return flex; - } - - public override int GetHashCode() - { - return - Softness.GetHashCode() ^ - Gravity.GetHashCode() ^ - Drag.GetHashCode() ^ - Wind.GetHashCode() ^ - Tension.GetHashCode() ^ - Force.GetHashCode(); - } - } - - /// - /// Information on the light properties of a primitive - /// - public class LightData - { - /// - public Color4 Color; - /// - public float Intensity; - /// - public float Radius; - /// - public float Cutoff; - /// - public float Falloff; - - /// - /// Default constructor - /// - public LightData() - { - } - - /// - /// - /// - /// - /// - public LightData(byte[] data, int pos) - { - if (data.Length - pos >= 16) - { - Color = new Color4(data, pos, false); - Radius = Utils.BytesToFloat(data, pos + 4); - Cutoff = Utils.BytesToFloat(data, pos + 8); - Falloff = Utils.BytesToFloat(data, pos + 12); - - // Alpha in color is actually intensity - Intensity = Color.A; - Color.A = 1f; - } - else - { - Color = Color4.Black; - Radius = 0f; - Cutoff = 0f; - Falloff = 0f; - Intensity = 0f; - } - } - - /// - /// - /// - /// - public byte[] GetBytes() - { - byte[] data = new byte[16]; - - // Alpha channel in color is intensity - Color4 tmpColor = Color; - tmpColor.A = Intensity; - tmpColor.GetBytes().CopyTo(data, 0); - Utils.FloatToBytes(Radius).CopyTo(data, 4); - Utils.FloatToBytes(Cutoff).CopyTo(data, 8); - Utils.FloatToBytes(Falloff).CopyTo(data, 12); - - return data; - } - - public OSD GetOSD() - { - OSDMap map = new OSDMap(); - - map["color"] = OSD.FromColor4(Color); - map["intensity"] = OSD.FromReal(Intensity); - map["radius"] = OSD.FromReal(Radius); - map["cutoff"] = OSD.FromReal(Cutoff); - map["falloff"] = OSD.FromReal(Falloff); - - return map; - } - - public static LightData FromOSD(OSD osd) - { - LightData light = new LightData(); - - if (osd.Type == OSDType.Map) - { - OSDMap map = (OSDMap)osd; - - light.Color = ((OSDArray)map["color"]).AsColor4(); - light.Intensity = (float)map["intensity"].AsReal(); - light.Radius = (float)map["radius"].AsReal(); - light.Cutoff = (float)map["cutoff"].AsReal(); - light.Falloff = (float)map["falloff"].AsReal(); - } - - return light; - } - - public override int GetHashCode() - { - return - Color.GetHashCode() ^ - Intensity.GetHashCode() ^ - Radius.GetHashCode() ^ - Cutoff.GetHashCode() ^ - Falloff.GetHashCode(); - } - - /// - /// - /// - /// - public override string ToString() - { - return String.Format("Color: {0} Intensity: {1} Radius: {2} Cutoff: {3} Falloff: {4}", - Color, Intensity, Radius, Cutoff, Falloff); - } - } - - /// - /// Information on the sculpt properties of a sculpted primitive - /// - public class SculptData - { - public UUID SculptTexture; - private byte type; - - public SculptType Type - { - get { return (SculptType)(type & 7); } - set { type = (byte)value; } - } - - /// - /// Render inside out (inverts the normals). - /// - public bool Invert - { - get { return ((type & (byte)SculptType.Invert) != 0); } - } - - /// - /// Render an X axis mirror of the sculpty. - /// - public bool Mirror - { - get { return ((type & (byte)SculptType.Mirror) != 0); } - } - - /// - /// Default constructor - /// - public SculptData() - { - } - - /// - /// - /// - /// - /// - public SculptData(byte[] data, int pos) - { - if (data.Length >= 17) - { - SculptTexture = new UUID(data, pos); - type = data[pos + 16]; - } - else - { - SculptTexture = UUID.Zero; - type = (byte)SculptType.None; - } - } - - public byte[] GetBytes() - { - byte[] data = new byte[17]; - - SculptTexture.GetBytes().CopyTo(data, 0); - data[16] = type; - - return data; - } - - public OSD GetOSD() - { - OSDMap map = new OSDMap(); - - map["texture"] = OSD.FromUUID(SculptTexture); - map["type"] = OSD.FromInteger(type); - - return map; - } - - public static SculptData FromOSD(OSD osd) - { - SculptData sculpt = new SculptData(); - - if (osd.Type == OSDType.Map) - { - OSDMap map = (OSDMap)osd; - - sculpt.SculptTexture = map["texture"].AsUUID(); - sculpt.type = (byte)map["type"].AsInteger(); - } - - return sculpt; - } - - public override int GetHashCode() - { - return SculptTexture.GetHashCode() ^ type.GetHashCode(); - } - } - - /// - /// Extended properties to describe an object - /// - public class ObjectProperties - { - /// - public UUID ObjectID; - /// - public UUID CreatorID; - /// - public UUID OwnerID; - /// - public UUID GroupID; - /// - public DateTime CreationDate; - /// - public Permissions Permissions; - /// - public int OwnershipCost; - /// - public SaleType SaleType; - /// - public int SalePrice; - /// - public byte AggregatePerms; - /// - public byte AggregatePermTextures; - /// - public byte AggregatePermTexturesOwner; - /// - public ObjectCategory Category; - /// - public short InventorySerial; - /// - public UUID ItemID; - /// - public UUID FolderID; - /// - public UUID FromTaskID; - /// - public UUID LastOwnerID; - /// - public string Name; - /// - public string Description; - /// - public string TouchName; - /// - public string SitName; - /// - public UUID[] TextureIDs; - - /// - /// Default constructor - /// - public ObjectProperties() - { - Name = String.Empty; - Description = String.Empty; - TouchName = String.Empty; - SitName = String.Empty; - } - - /// - /// Set the properties that are set in an ObjectPropertiesFamily packet - /// - /// that has - /// been partially filled by an ObjectPropertiesFamily packet - public void SetFamilyProperties(ObjectProperties props) - { - ObjectID = props.ObjectID; - OwnerID = props.OwnerID; - GroupID = props.GroupID; - Permissions = props.Permissions; - OwnershipCost = props.OwnershipCost; - SaleType = props.SaleType; - SalePrice = props.SalePrice; - Category = props.Category; - LastOwnerID = props.LastOwnerID; - Name = props.Name; - Description = props.Description; - } - - public byte[] GetTextureIDBytes() - { - if (TextureIDs == null || TextureIDs.Length == 0) - return Utils.EmptyBytes; - - byte[] bytes = new byte[16 * TextureIDs.Length]; - for (int i = 0; i < TextureIDs.Length; i++) - TextureIDs[i].ToBytes(bytes, 16 * i); - - return bytes; - } - } - - #endregion Subclasses - - #region Public Members - - /// - public UUID ID; - /// - public UUID GroupID; - /// - public uint LocalID; - /// - public uint ParentID; - /// - public ulong RegionHandle; - /// - public PrimFlags Flags; - /// Foliage type for this primitive. Only applicable if this - /// primitive is foliage - public Tree TreeSpecies; - /// Unknown - public byte[] ScratchPad; - /// - public Vector3 Position; - /// - public Vector3 Scale; - /// - public Quaternion Rotation = Quaternion.Identity; - /// - public Vector3 Velocity; - /// - public Vector3 AngularVelocity; - /// - public Vector3 Acceleration; - /// - public Vector4 CollisionPlane; - /// - public FlexibleData Flexible; - /// - public LightData Light; - /// - public SculptData Sculpt; - /// - public ClickAction ClickAction; - /// - public UUID Sound; - /// Identifies the owner if audio or a particle system is - /// active - public UUID OwnerID; - /// - public SoundFlags SoundFlags; - /// - public float SoundGain; - /// - public float SoundRadius; - /// - public string Text; - /// - public Color4 TextColor; - /// - public string MediaURL; - /// - public JointType Joint; - /// - public Vector3 JointPivot; - /// - public Vector3 JointAxisOrAnchor; - /// - public NameValue[] NameValues; - /// - public ConstructionData PrimData; - /// - public ObjectProperties Properties; - - #endregion Public Members - - #region Properties - - /// Uses basic heuristics to estimate the primitive shape - public PrimType Type - { - get - { - if (Sculpt != null && Sculpt.Type != SculptType.None) - return PrimType.Sculpt; - - bool linearPath = (PrimData.PathCurve == PathCurve.Line || PrimData.PathCurve == PathCurve.Flexible); - float scaleY = PrimData.PathScaleY; - - if (linearPath) - { - switch (PrimData.ProfileCurve) - { - case ProfileCurve.Circle: - return PrimType.Cylinder; - case ProfileCurve.Square: - return PrimType.Box; - case ProfileCurve.IsoTriangle: - case ProfileCurve.EqualTriangle: - case ProfileCurve.RightTriangle: - return PrimType.Prism; - case ProfileCurve.HalfCircle: - default: - return PrimType.Unknown; - } - } - else - { - switch (PrimData.PathCurve) - { - case PathCurve.Flexible: - return PrimType.Unknown; - case PathCurve.Circle: - switch (PrimData.ProfileCurve) - { - case ProfileCurve.Circle: - if (scaleY > 0.75f) - return PrimType.Sphere; - else - return PrimType.Torus; - case ProfileCurve.HalfCircle: - return PrimType.Sphere; - case ProfileCurve.EqualTriangle: - return PrimType.Ring; - case ProfileCurve.Square: - if (scaleY <= 0.75f) - return PrimType.Tube; - else - return PrimType.Unknown; - default: - return PrimType.Unknown; - } - case PathCurve.Circle2: - if (PrimData.ProfileCurve == ProfileCurve.Circle) - return PrimType.Sphere; - else - return PrimType.Unknown; - default: - return PrimType.Unknown; - } - } - } - } - - #endregion Properties - - #region Constructors - - /// - /// Default constructor - /// - public Primitive() - { - // Default a few null property values to String.Empty - Text = String.Empty; - MediaURL = String.Empty; - } - - public Primitive(Primitive prim) - { - ID = prim.ID; - GroupID = prim.GroupID; - LocalID = prim.LocalID; - ParentID = prim.ParentID; - RegionHandle = prim.RegionHandle; - Flags = prim.Flags; - TreeSpecies = prim.TreeSpecies; - if (prim.ScratchPad != null) - { - ScratchPad = new byte[prim.ScratchPad.Length]; - Buffer.BlockCopy(prim.ScratchPad, 0, ScratchPad, 0, ScratchPad.Length); - } - else - ScratchPad = Utils.EmptyBytes; - Position = prim.Position; - Scale = prim.Scale; - Rotation = prim.Rotation; - Velocity = prim.Velocity; - AngularVelocity = prim.AngularVelocity; - Acceleration = prim.Acceleration; - CollisionPlane = prim.CollisionPlane; - Flexible = prim.Flexible; - Light = prim.Light; - Sculpt = prim.Sculpt; - ClickAction = prim.ClickAction; - Sound = prim.Sound; - OwnerID = prim.OwnerID; - SoundFlags = prim.SoundFlags; - SoundGain = prim.SoundGain; - SoundRadius = prim.SoundRadius; - Text = prim.Text; - TextColor = prim.TextColor; - MediaURL = prim.MediaURL; - Joint = prim.Joint; - JointPivot = prim.JointPivot; - JointAxisOrAnchor = prim.JointAxisOrAnchor; - if (prim.NameValues != null) - { - if (NameValues == null || NameValues.Length != prim.NameValues.Length) - NameValues = new NameValue[prim.NameValues.Length]; - Array.Copy(prim.NameValues, NameValues, prim.NameValues.Length); - } - else - NameValues = null; - PrimData = prim.PrimData; - Properties = prim.Properties; - // FIXME: Get a real copy constructor for TextureEntry instead of serializing to bytes and back - if (prim.Textures != null) - { - byte[] textureBytes = prim.Textures.GetBytes(); - Textures = new TextureEntry(textureBytes, 0, textureBytes.Length); - } - else - { - Textures = null; - } - TextureAnim = prim.TextureAnim; - ParticleSys = prim.ParticleSys; - } - - #endregion Constructors - - #region Public Methods - - public virtual OSD GetOSD() - { - OSDMap path = new OSDMap(14); - path["begin"] = OSD.FromReal(PrimData.PathBegin); - path["curve"] = OSD.FromInteger((int)PrimData.PathCurve); - path["end"] = OSD.FromReal(PrimData.PathEnd); - path["radius_offset"] = OSD.FromReal(PrimData.PathRadiusOffset); - path["revolutions"] = OSD.FromReal(PrimData.PathRevolutions); - path["scale_x"] = OSD.FromReal(PrimData.PathScaleX); - path["scale_y"] = OSD.FromReal(PrimData.PathScaleY); - path["shear_x"] = OSD.FromReal(PrimData.PathShearX); - path["shear_y"] = OSD.FromReal(PrimData.PathShearY); - path["skew"] = OSD.FromReal(PrimData.PathSkew); - path["taper_x"] = OSD.FromReal(PrimData.PathTaperX); - path["taper_y"] = OSD.FromReal(PrimData.PathTaperY); - path["twist"] = OSD.FromReal(PrimData.PathTwist); - path["twist_begin"] = OSD.FromReal(PrimData.PathTwistBegin); - - OSDMap profile = new OSDMap(4); - profile["begin"] = OSD.FromReal(PrimData.ProfileBegin); - profile["curve"] = OSD.FromInteger((int)PrimData.ProfileCurve); - profile["hole"] = OSD.FromInteger((int)PrimData.ProfileHole); - profile["end"] = OSD.FromReal(PrimData.ProfileEnd); - profile["hollow"] = OSD.FromReal(PrimData.ProfileHollow); - - OSDMap volume = new OSDMap(2); - volume["path"] = path; - volume["profile"] = profile; - - OSDMap prim = new OSDMap(20); - if (Properties != null) - { - prim["name"] = OSD.FromString(Properties.Name); - prim["description"] = OSD.FromString(Properties.Description); - } - else - { - prim["name"] = OSD.FromString("Object"); - prim["description"] = OSD.FromString(String.Empty); - } - - prim["phantom"] = OSD.FromBoolean(((Flags & PrimFlags.Phantom) != 0)); - prim["physical"] = OSD.FromBoolean(((Flags & PrimFlags.Physics) != 0)); - prim["position"] = OSD.FromVector3(Position); - prim["rotation"] = OSD.FromQuaternion(Rotation); - prim["scale"] = OSD.FromVector3(Scale); - prim["pcode"] = OSD.FromInteger((int)PrimData.PCode); - prim["material"] = OSD.FromInteger((int)PrimData.Material); - prim["shadows"] = OSD.FromBoolean(((Flags & PrimFlags.CastShadows) != 0)); - prim["state"] = OSD.FromInteger(PrimData.State); - - prim["id"] = OSD.FromUUID(ID); - prim["localid"] = OSD.FromUInteger(LocalID); - prim["parentid"] = OSD.FromUInteger(ParentID); - - prim["volume"] = volume; - - if (Textures != null) - prim["textures"] = Textures.GetOSD(); - - if (Light != null) - prim["light"] = Light.GetOSD(); - - if (Flexible != null) - prim["flex"] = Flexible.GetOSD(); - - if (Sculpt != null) - prim["sculpt"] = Sculpt.GetOSD(); - - return prim; - } - - public static Primitive FromOSD(OSD osd) - { - Primitive prim = new Primitive(); - Primitive.ConstructionData data; - - OSDMap map = (OSDMap)osd; - OSDMap volume = (OSDMap)map["volume"]; - OSDMap path = (OSDMap)volume["path"]; - OSDMap profile = (OSDMap)volume["profile"]; - - #region Path/Profile - - data.profileCurve = (byte)0; - data.Material = (Material)map["material"].AsInteger(); - data.PCode = (PCode)map["pcode"].AsInteger(); - data.State = (byte)map["state"].AsInteger(); - - data.PathBegin = (float)path["begin"].AsReal(); - data.PathCurve = (PathCurve)path["curve"].AsInteger(); - data.PathEnd = (float)path["end"].AsReal(); - data.PathRadiusOffset = (float)path["radius_offset"].AsReal(); - data.PathRevolutions = (float)path["revolutions"].AsReal(); - data.PathScaleX = (float)path["scale_x"].AsReal(); - data.PathScaleY = (float)path["scale_y"].AsReal(); - data.PathShearX = (float)path["shear_x"].AsReal(); - data.PathShearY = (float)path["shear_y"].AsReal(); - data.PathSkew = (float)path["skew"].AsReal(); - data.PathTaperX = (float)path["taper_x"].AsReal(); - data.PathTaperY = (float)path["taper_y"].AsReal(); - data.PathTwist = (float)path["twist"].AsReal(); - data.PathTwistBegin = (float)path["twist_begin"].AsReal(); - - data.ProfileBegin = (float)profile["begin"].AsReal(); - data.ProfileEnd = (float)profile["end"].AsReal(); - data.ProfileHollow = (float)profile["hollow"].AsReal(); - data.ProfileCurve = (ProfileCurve)profile["curve"].AsInteger(); - data.ProfileHole = (HoleType)profile["hole"].AsInteger(); - - #endregion Path/Profile - - prim.PrimData = data; - - if (map["phantom"].AsBoolean()) - prim.Flags |= PrimFlags.Phantom; - - if (map["physical"].AsBoolean()) - prim.Flags |= PrimFlags.Physics; - - if (map["shadows"].AsBoolean()) - prim.Flags |= PrimFlags.CastShadows; - - prim.ID = map["id"].AsUUID(); - prim.LocalID = map["localid"].AsUInteger(); - prim.ParentID = map["parentid"].AsUInteger(); - prim.Position = ((OSDArray)map["position"]).AsVector3(); - prim.Rotation = ((OSDArray)map["rotation"]).AsQuaternion(); - prim.Scale = ((OSDArray)map["scale"]).AsVector3(); - - if (map["flex"]) - prim.Flexible = FlexibleData.FromOSD(map["flex"]); - - if (map["light"]) - prim.Light = LightData.FromOSD(map["light"]); - - if (map["sculpt"]) - prim.Sculpt = SculptData.FromOSD(map["sculpt"]); - - prim.Textures = TextureEntry.FromOSD(map["textures"]); - prim.Properties = new ObjectProperties(); - - if (!string.IsNullOrEmpty(map["name"].AsString())) - { - prim.Properties.Name = map["name"].AsString(); - } - - if (!string.IsNullOrEmpty(map["description"].AsString())) - { - prim.Properties.Description = map["description"].AsString(); - } - - return prim; - } - - public int SetExtraParamsFromBytes(byte[] data, int pos) - { - int i = pos; - int totalLength = 1; - - if (data.Length == 0 || pos >= data.Length) - return 0; - - byte extraParamCount = data[i++]; - - for (int k = 0; k < extraParamCount; k++) - { - ExtraParamType type = (ExtraParamType)Utils.BytesToUInt16(data, i); - i += 2; - - uint paramLength = Utils.BytesToUInt(data, i); - i += 4; - - if (type == ExtraParamType.Flexible) - Flexible = new FlexibleData(data, i); - else if (type == ExtraParamType.Light) - Light = new LightData(data, i); - else if (type == ExtraParamType.Sculpt) - Sculpt = new SculptData(data, i); - - i += (int)paramLength; - totalLength += (int)paramLength + 6; - } - - return totalLength; - } - - public byte[] GetExtraParamsBytes() - { - byte[] flexible = null; - byte[] light = null; - byte[] sculpt = null; - byte[] buffer = null; - int size = 1; - int pos = 0; - byte count = 0; - - if (Flexible != null) - { - flexible = Flexible.GetBytes(); - size += flexible.Length + 6; - ++count; - } - if (Light != null) - { - light = Light.GetBytes(); - size += light.Length + 6; - ++count; - } - if (Sculpt != null) - { - sculpt = Sculpt.GetBytes(); - size += sculpt.Length + 6; - ++count; - } - - buffer = new byte[size]; - buffer[0] = count; - ++pos; - - if (flexible != null) - { - Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Flexible), 0, buffer, pos, 2); - pos += 2; - - Buffer.BlockCopy(Utils.UIntToBytes((uint)flexible.Length), 0, buffer, pos, 4); - pos += 4; - - Buffer.BlockCopy(flexible, 0, buffer, pos, flexible.Length); - pos += flexible.Length; - } - if (light != null) - { - Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Light), 0, buffer, pos, 2); - pos += 2; - - Buffer.BlockCopy(Utils.UIntToBytes((uint)light.Length), 0, buffer, pos, 4); - pos += 4; - - Buffer.BlockCopy(light, 0, buffer, pos, light.Length); - pos += light.Length; - } - if (sculpt != null) - { - Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Sculpt), 0, buffer, pos, 2); - pos += 2; - - Buffer.BlockCopy(Utils.UIntToBytes((uint)sculpt.Length), 0, buffer, pos, 4); - pos += 4; - - Buffer.BlockCopy(sculpt, 0, buffer, pos, sculpt.Length); - pos += sculpt.Length; - } - - return buffer; - } - - #endregion Public Methods - - #region Overrides - - public override bool Equals(object obj) - { - return (obj is Primitive) ? this == (Primitive)obj : false; - } - - public bool Equals(Primitive other) - { - return this == other; - } - - public override string ToString() - { - switch (PrimData.PCode) - { - case PCode.Prim: - return String.Format("{0} ({1})", Type, ID); - default: - return String.Format("{0} ({1})", PrimData.PCode, ID); - } - } - - public override int GetHashCode() - { - return - Position.GetHashCode() ^ - Velocity.GetHashCode() ^ - Acceleration.GetHashCode() ^ - Rotation.GetHashCode() ^ - AngularVelocity.GetHashCode() ^ - ClickAction.GetHashCode() ^ - (Flexible != null ? Flexible.GetHashCode() : 0) ^ - (Light != null ? Light.GetHashCode() : 0) ^ - (Sculpt != null ? Sculpt.GetHashCode() : 0) ^ - Flags.GetHashCode() ^ - PrimData.Material.GetHashCode() ^ - MediaURL.GetHashCode() ^ - //TODO: NameValues? - (Properties != null ? Properties.OwnerID.GetHashCode() : 0) ^ - ParentID.GetHashCode() ^ - PrimData.PathBegin.GetHashCode() ^ - PrimData.PathCurve.GetHashCode() ^ - PrimData.PathEnd.GetHashCode() ^ - PrimData.PathRadiusOffset.GetHashCode() ^ - PrimData.PathRevolutions.GetHashCode() ^ - PrimData.PathScaleX.GetHashCode() ^ - PrimData.PathScaleY.GetHashCode() ^ - PrimData.PathShearX.GetHashCode() ^ - PrimData.PathShearY.GetHashCode() ^ - PrimData.PathSkew.GetHashCode() ^ - PrimData.PathTaperX.GetHashCode() ^ - PrimData.PathTaperY.GetHashCode() ^ - PrimData.PathTwist.GetHashCode() ^ - PrimData.PathTwistBegin.GetHashCode() ^ - PrimData.PCode.GetHashCode() ^ - PrimData.ProfileBegin.GetHashCode() ^ - PrimData.ProfileCurve.GetHashCode() ^ - PrimData.ProfileEnd.GetHashCode() ^ - PrimData.ProfileHollow.GetHashCode() ^ - ParticleSys.GetHashCode() ^ - TextColor.GetHashCode() ^ - TextureAnim.GetHashCode() ^ - (Textures != null ? Textures.GetHashCode() : 0) ^ - SoundRadius.GetHashCode() ^ - Scale.GetHashCode() ^ - Sound.GetHashCode() ^ - PrimData.State.GetHashCode() ^ - Text.GetHashCode() ^ - TreeSpecies.GetHashCode(); - } - - #endregion Overrides - - #region Operators - - public static bool operator ==(Primitive lhs, Primitive rhs) - { - if ((Object)lhs == null || (Object)rhs == null) - { - return (Object)rhs == (Object)lhs; - } - return (lhs.ID == rhs.ID); - } - - public static bool operator !=(Primitive lhs, Primitive rhs) - { - if ((Object)lhs == null || (Object)rhs == null) - { - return (Object)rhs != (Object)lhs; - } - return !(lhs.ID == rhs.ID); - } - - #endregion Operators - - #region Parameter Packing Methods - - public static ushort PackBeginCut(float beginCut) - { - return (ushort)Math.Round(beginCut / CUT_QUANTA); - } - - public static ushort PackEndCut(float endCut) - { - return (ushort)(50000 - (ushort)Math.Round(endCut / CUT_QUANTA)); - } - - public static byte PackPathScale(float pathScale) - { - return (byte)(200 - (byte)Math.Round(pathScale / SCALE_QUANTA)); - } - - public static sbyte PackPathShear(float pathShear) - { - return (sbyte)Math.Round(pathShear / SHEAR_QUANTA); - } - - /// - /// Packs PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew - /// parameters in to signed eight bit values - /// - /// Floating point parameter to pack - /// Signed eight bit value containing the packed parameter - public static sbyte PackPathTwist(float pathTwist) - { - return (sbyte)Math.Round(pathTwist / SCALE_QUANTA); - } - - public static sbyte PackPathTaper(float pathTaper) - { - return (sbyte)Math.Round(pathTaper / TAPER_QUANTA); - } - - public static byte PackPathRevolutions(float pathRevolutions) - { - return (byte)Math.Round((pathRevolutions - 1f) / REV_QUANTA); - } - - public static ushort PackProfileHollow(float profileHollow) - { - return (ushort)Math.Round(profileHollow / HOLLOW_QUANTA); - } - - #endregion Parameter Packing Methods - - #region Parameter Unpacking Methods - - public static float UnpackBeginCut(ushort beginCut) - { - return (float)beginCut * CUT_QUANTA; - } - - public static float UnpackEndCut(ushort endCut) - { - return (float)(50000 - endCut) * CUT_QUANTA; - } - - public static float UnpackPathScale(byte pathScale) - { - return (float)(200 - pathScale) * SCALE_QUANTA; - } - - public static float UnpackPathShear(sbyte pathShear) - { - return (float)pathShear * SHEAR_QUANTA; - } - - /// - /// Unpacks PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew - /// parameters from signed eight bit integers to floating point values - /// - /// Signed eight bit value to unpack - /// Unpacked floating point value - public static float UnpackPathTwist(sbyte pathTwist) - { - return (float)pathTwist * SCALE_QUANTA; - } - - public static float UnpackPathTaper(sbyte pathTaper) - { - return (float)pathTaper * TAPER_QUANTA; - } - - public static float UnpackPathRevolutions(byte pathRevolutions) - { - return (float)pathRevolutions * REV_QUANTA + 1f; - } - - public static float UnpackProfileHollow(ushort profileHollow) - { - return (float)profileHollow * HOLLOW_QUANTA; - } - - #endregion Parameter Unpacking Methods - } -} +/* + * Copyright (c) 2006-2008, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using OpenMetaverse.StructuredData; + +namespace OpenMetaverse +{ + public partial class Primitive : IEquatable + { + // Used for packing and unpacking parameters + protected const float CUT_QUANTA = 0.00002f; + protected const float SCALE_QUANTA = 0.01f; + protected const float SHEAR_QUANTA = 0.01f; + protected const float TAPER_QUANTA = 0.01f; + protected const float REV_QUANTA = 0.015f; + protected const float HOLLOW_QUANTA = 0.00002f; + + #region Subclasses + + /// + /// Parameters used to construct a visual representation of a primitive + /// + public struct ConstructionData + { + private const byte PROFILE_MASK = 0x0F; + private const byte HOLE_MASK = 0xF0; + + /// + public byte profileCurve; + /// + public PathCurve PathCurve; + /// + public float PathEnd; + /// + public float PathRadiusOffset; + /// + public float PathSkew; + /// + public float PathScaleX; + /// + public float PathScaleY; + /// + public float PathShearX; + /// + public float PathShearY; + /// + public float PathTaperX; + /// + public float PathTaperY; + /// + public float PathBegin; + /// + public float PathTwist; + /// + public float PathTwistBegin; + /// + public float PathRevolutions; + /// + public float ProfileBegin; + /// + public float ProfileEnd; + /// + public float ProfileHollow; + + /// + public Material Material; + /// + public byte State; + /// + public PCode PCode; + + #region Properties + + /// Attachment point to an avatar + public AttachmentPoint AttachmentPoint + { + get { return (AttachmentPoint)Utils.SwapWords(State); } + set { State = (byte)Utils.SwapWords((byte)value); } + } + + /// + public ProfileCurve ProfileCurve + { + get { return (ProfileCurve)(profileCurve & PROFILE_MASK); } + set + { + profileCurve &= HOLE_MASK; + profileCurve |= (byte)value; + } + } + + /// + public HoleType ProfileHole + { + get { return (HoleType)(profileCurve & HOLE_MASK); } + set + { + profileCurve &= PROFILE_MASK; + profileCurve |= (byte)value; + } + } + + /// + public Vector2 PathBeginScale + { + get + { + Vector2 begin = new Vector2(1f, 1f); + if (PathScaleX > 1f) + begin.X = 2f - PathScaleX; + if (PathScaleY > 1f) + begin.Y = 2f - PathScaleY; + return begin; + } + } + + /// + public Vector2 PathEndScale + { + get + { + Vector2 end = new Vector2(1f, 1f); + if (PathScaleX < 1f) + end.X = PathScaleX; + if (PathScaleY < 1f) + end.Y = PathScaleY; + return end; + } + } + + #endregion Properties + } + + /// + /// Information on the flexible properties of a primitive + /// + public class FlexibleData + { + /// + public int Softness; + /// + public float Gravity; + /// + public float Drag; + /// + public float Wind; + /// + public float Tension; + /// + public Vector3 Force; + + /// + /// Default constructor + /// + public FlexibleData() + { + } + + /// + /// + /// + /// + /// + public FlexibleData(byte[] data, int pos) + { + if (data.Length >= 5) + { + Softness = ((data[pos] & 0x80) >> 6) | ((data[pos + 1] & 0x80) >> 7); + + Tension = (float)(data[pos++] & 0x7F) / 10.0f; + Drag = (float)(data[pos++] & 0x7F) / 10.0f; + Gravity = (float)(data[pos++] / 10.0f) - 10.0f; + Wind = (float)data[pos++] / 10.0f; + Force = new Vector3(data, pos); + } + else + { + Softness = 0; + + Tension = 0.0f; + Drag = 0.0f; + Gravity = 0.0f; + Wind = 0.0f; + Force = Vector3.Zero; + } + } + + /// + /// + /// + /// + public byte[] GetBytes() + { + byte[] data = new byte[16]; + int i = 0; + + // Softness is packed in the upper bits of tension and drag + data[i] = (byte)((Softness & 2) << 6); + data[i + 1] = (byte)((Softness & 1) << 7); + + data[i++] |= (byte)((byte)(Tension * 10.01f) & 0x7F); + data[i++] |= (byte)((byte)(Drag * 10.01f) & 0x7F); + data[i++] = (byte)((Gravity + 10.0f) * 10.01f); + data[i++] = (byte)(Wind * 10.01f); + + Force.GetBytes().CopyTo(data, i); + + return data; + } + + /// + /// + /// + /// + public OSD GetOSD() + { + OSDMap map = new OSDMap(); + + map["simulate_lod"] = OSD.FromInteger(Softness); + map["gravity"] = OSD.FromReal(Gravity); + map["air_friction"] = OSD.FromReal(Drag); + map["wind_sensitivity"] = OSD.FromReal(Wind); + map["tension"] = OSD.FromReal(Tension); + map["user_force"] = OSD.FromVector3(Force); + + return map; + } + + public static FlexibleData FromOSD(OSD osd) + { + FlexibleData flex = new FlexibleData(); + + if (osd.Type == OSDType.Map) + { + OSDMap map = (OSDMap)osd; + + flex.Softness = map["simulate_lod"].AsInteger(); + flex.Gravity = (float)map["gravity"].AsReal(); + flex.Drag = (float)map["air_friction"].AsReal(); + flex.Wind = (float)map["wind_sensitivity"].AsReal(); + flex.Tension = (float)map["tension"].AsReal(); + flex.Force = ((OSDArray)map["user_force"]).AsVector3(); + } + + return flex; + } + + public override int GetHashCode() + { + return + Softness.GetHashCode() ^ + Gravity.GetHashCode() ^ + Drag.GetHashCode() ^ + Wind.GetHashCode() ^ + Tension.GetHashCode() ^ + Force.GetHashCode(); + } + } + + /// + /// Information on the light properties of a primitive + /// + public class LightData + { + /// + public Color4 Color; + /// + public float Intensity; + /// + public float Radius; + /// + public float Cutoff; + /// + public float Falloff; + + /// + /// Default constructor + /// + public LightData() + { + } + + /// + /// + /// + /// + /// + public LightData(byte[] data, int pos) + { + if (data.Length - pos >= 16) + { + Color = new Color4(data, pos, false); + Radius = Utils.BytesToFloat(data, pos + 4); + Cutoff = Utils.BytesToFloat(data, pos + 8); + Falloff = Utils.BytesToFloat(data, pos + 12); + + // Alpha in color is actually intensity + Intensity = Color.A; + Color.A = 1f; + } + else + { + Color = Color4.Black; + Radius = 0f; + Cutoff = 0f; + Falloff = 0f; + Intensity = 0f; + } + } + + /// + /// + /// + /// + public byte[] GetBytes() + { + byte[] data = new byte[16]; + + // Alpha channel in color is intensity + Color4 tmpColor = Color; + tmpColor.A = Intensity; + tmpColor.GetBytes().CopyTo(data, 0); + Utils.FloatToBytes(Radius).CopyTo(data, 4); + Utils.FloatToBytes(Cutoff).CopyTo(data, 8); + Utils.FloatToBytes(Falloff).CopyTo(data, 12); + + return data; + } + + public OSD GetOSD() + { + OSDMap map = new OSDMap(); + + map["color"] = OSD.FromColor4(Color); + map["intensity"] = OSD.FromReal(Intensity); + map["radius"] = OSD.FromReal(Radius); + map["cutoff"] = OSD.FromReal(Cutoff); + map["falloff"] = OSD.FromReal(Falloff); + + return map; + } + + public static LightData FromOSD(OSD osd) + { + LightData light = new LightData(); + + if (osd.Type == OSDType.Map) + { + OSDMap map = (OSDMap)osd; + + light.Color = ((OSDArray)map["color"]).AsColor4(); + light.Intensity = (float)map["intensity"].AsReal(); + light.Radius = (float)map["radius"].AsReal(); + light.Cutoff = (float)map["cutoff"].AsReal(); + light.Falloff = (float)map["falloff"].AsReal(); + } + + return light; + } + + public override int GetHashCode() + { + return + Color.GetHashCode() ^ + Intensity.GetHashCode() ^ + Radius.GetHashCode() ^ + Cutoff.GetHashCode() ^ + Falloff.GetHashCode(); + } + + /// + /// + /// + /// + public override string ToString() + { + return String.Format("Color: {0} Intensity: {1} Radius: {2} Cutoff: {3} Falloff: {4}", + Color, Intensity, Radius, Cutoff, Falloff); + } + } + + /// + /// Information on the sculpt properties of a sculpted primitive + /// + public class SculptData + { + public UUID SculptTexture; + private byte type; + + public SculptType Type + { + get { return (SculptType)(type & 7); } + set { type = (byte)value; } + } + + /// + /// Render inside out (inverts the normals). + /// + public bool Invert + { + get { return ((type & (byte)SculptType.Invert) != 0); } + } + + /// + /// Render an X axis mirror of the sculpty. + /// + public bool Mirror + { + get { return ((type & (byte)SculptType.Mirror) != 0); } + } + + /// + /// Default constructor + /// + public SculptData() + { + } + + /// + /// + /// + /// + /// + public SculptData(byte[] data, int pos) + { + if (data.Length >= 17) + { + SculptTexture = new UUID(data, pos); + type = data[pos + 16]; + } + else + { + SculptTexture = UUID.Zero; + type = (byte)SculptType.None; + } + } + + public byte[] GetBytes() + { + byte[] data = new byte[17]; + + SculptTexture.GetBytes().CopyTo(data, 0); + data[16] = type; + + return data; + } + + public OSD GetOSD() + { + OSDMap map = new OSDMap(); + + map["texture"] = OSD.FromUUID(SculptTexture); + map["type"] = OSD.FromInteger(type); + + return map; + } + + public static SculptData FromOSD(OSD osd) + { + SculptData sculpt = new SculptData(); + + if (osd.Type == OSDType.Map) + { + OSDMap map = (OSDMap)osd; + + sculpt.SculptTexture = map["texture"].AsUUID(); + sculpt.type = (byte)map["type"].AsInteger(); + } + + return sculpt; + } + + public override int GetHashCode() + { + return SculptTexture.GetHashCode() ^ type.GetHashCode(); + } + } + + /// + /// Extended properties to describe an object + /// + public class ObjectProperties + { + /// + public UUID ObjectID; + /// + public UUID CreatorID; + /// + public UUID OwnerID; + /// + public UUID GroupID; + /// + public DateTime CreationDate; + /// + public Permissions Permissions; + /// + public int OwnershipCost; + /// + public SaleType SaleType; + /// + public int SalePrice; + /// + public byte AggregatePerms; + /// + public byte AggregatePermTextures; + /// + public byte AggregatePermTexturesOwner; + /// + public ObjectCategory Category; + /// + public short InventorySerial; + /// + public UUID ItemID; + /// + public UUID FolderID; + /// + public UUID FromTaskID; + /// + public UUID LastOwnerID; + /// + public string Name; + /// + public string Description; + /// + public string TouchName; + /// + public string SitName; + /// + public UUID[] TextureIDs; + + /// + /// Default constructor + /// + public ObjectProperties() + { + Name = String.Empty; + Description = String.Empty; + TouchName = String.Empty; + SitName = String.Empty; + } + + /// + /// Set the properties that are set in an ObjectPropertiesFamily packet + /// + /// that has + /// been partially filled by an ObjectPropertiesFamily packet + public void SetFamilyProperties(ObjectProperties props) + { + ObjectID = props.ObjectID; + OwnerID = props.OwnerID; + GroupID = props.GroupID; + Permissions = props.Permissions; + OwnershipCost = props.OwnershipCost; + SaleType = props.SaleType; + SalePrice = props.SalePrice; + Category = props.Category; + LastOwnerID = props.LastOwnerID; + Name = props.Name; + Description = props.Description; + } + + public byte[] GetTextureIDBytes() + { + if (TextureIDs == null || TextureIDs.Length == 0) + return Utils.EmptyBytes; + + byte[] bytes = new byte[16 * TextureIDs.Length]; + for (int i = 0; i < TextureIDs.Length; i++) + TextureIDs[i].ToBytes(bytes, 16 * i); + + return bytes; + } + } + + /// + /// Describes physics attributes of the prim + /// + public class PhysicsProperties + { + /// Primitive's local ID + public uint LocalID; + /// Density (1000 for normal density) + public float Density; + /// Friction + public float Friction; + /// Gravity multiplier (1 for normal gravity) + public float GravityMultiplier; + /// Type of physics representation of this primitive in the simulator + public PhysicsShapeType PhysicsShapeType; + /// Restitution + public float Restitution; + + /// + /// Creates PhysicsProperties from OSD + /// + /// OSDMap with incoming data + /// Deserialized PhysicsProperties object + public static PhysicsProperties FromOSD(OSD osd) + { + PhysicsProperties ret = new PhysicsProperties(); + + if (osd is OSDMap) + { + OSDMap map = (OSDMap)osd; + ret.LocalID = map["LocalID"]; + ret.Density = map["Density"]; + ret.Friction = map["Friction"]; + ret.GravityMultiplier = map["GravityMultiplier"]; + ret.Restitution = map["Restitution"]; + ret.PhysicsShapeType = (PhysicsShapeType)map["PhysicsShapeType"].AsInteger(); + } + + return ret; + } + + /// + /// Serializes PhysicsProperties to OSD + /// + /// OSDMap with serialized PhysicsProperties data + public OSD GetOSD() + { + OSDMap map = new OSDMap(6); + map["LocalID"] = LocalID; + map["Density"] = Density; + map["Friction"] = Friction; + map["GravityMultiplier"] = GravityMultiplier; + map["Restitution"] = Restitution; + map["PhysicsShapeType"] = (int)PhysicsShapeType; + return map; + } + } + + #endregion Subclasses + + #region Public Members + + /// + public UUID ID; + /// + public UUID GroupID; + /// + public uint LocalID; + /// + public uint ParentID; + /// + public ulong RegionHandle; + /// + public PrimFlags Flags; + /// Foliage type for this primitive. Only applicable if this + /// primitive is foliage + public Tree TreeSpecies; + /// Unknown + public byte[] ScratchPad; + /// + public Vector3 Position; + /// + public Vector3 Scale; + /// + public Quaternion Rotation = Quaternion.Identity; + /// + public Vector3 Velocity; + /// + public Vector3 AngularVelocity; + /// + public Vector3 Acceleration; + /// + public Vector4 CollisionPlane; + /// + public FlexibleData Flexible; + /// + public LightData Light; + /// + public SculptData Sculpt; + /// + public ClickAction ClickAction; + /// + public UUID Sound; + /// Identifies the owner if audio or a particle system is + /// active + public UUID OwnerID; + /// + public SoundFlags SoundFlags; + /// + public float SoundGain; + /// + public float SoundRadius; + /// + public string Text; + /// + public Color4 TextColor; + /// + public string MediaURL; + /// + public JointType Joint; + /// + public Vector3 JointPivot; + /// + public Vector3 JointAxisOrAnchor; + /// + public NameValue[] NameValues; + /// + public ConstructionData PrimData; + /// + public ObjectProperties Properties; + /// Objects physics engine propertis + public PhysicsProperties PhysicsProps; + + #endregion Public Members + + #region Properties + + /// Uses basic heuristics to estimate the primitive shape + public PrimType Type + { + get + { + if (Sculpt != null && Sculpt.Type != SculptType.None) + return PrimType.Sculpt; + + bool linearPath = (PrimData.PathCurve == PathCurve.Line || PrimData.PathCurve == PathCurve.Flexible); + float scaleY = PrimData.PathScaleY; + + if (linearPath) + { + switch (PrimData.ProfileCurve) + { + case ProfileCurve.Circle: + return PrimType.Cylinder; + case ProfileCurve.Square: + return PrimType.Box; + case ProfileCurve.IsoTriangle: + case ProfileCurve.EqualTriangle: + case ProfileCurve.RightTriangle: + return PrimType.Prism; + case ProfileCurve.HalfCircle: + default: + return PrimType.Unknown; + } + } + else + { + switch (PrimData.PathCurve) + { + case PathCurve.Flexible: + return PrimType.Unknown; + case PathCurve.Circle: + switch (PrimData.ProfileCurve) + { + case ProfileCurve.Circle: + if (scaleY > 0.75f) + return PrimType.Sphere; + else + return PrimType.Torus; + case ProfileCurve.HalfCircle: + return PrimType.Sphere; + case ProfileCurve.EqualTriangle: + return PrimType.Ring; + case ProfileCurve.Square: + if (scaleY <= 0.75f) + return PrimType.Tube; + else + return PrimType.Unknown; + default: + return PrimType.Unknown; + } + case PathCurve.Circle2: + if (PrimData.ProfileCurve == ProfileCurve.Circle) + return PrimType.Sphere; + else + return PrimType.Unknown; + default: + return PrimType.Unknown; + } + } + } + } + + #endregion Properties + + #region Constructors + + /// + /// Default constructor + /// + public Primitive() + { + // Default a few null property values to String.Empty + Text = String.Empty; + MediaURL = String.Empty; + } + + public Primitive(Primitive prim) + { + ID = prim.ID; + GroupID = prim.GroupID; + LocalID = prim.LocalID; + ParentID = prim.ParentID; + RegionHandle = prim.RegionHandle; + Flags = prim.Flags; + TreeSpecies = prim.TreeSpecies; + if (prim.ScratchPad != null) + { + ScratchPad = new byte[prim.ScratchPad.Length]; + Buffer.BlockCopy(prim.ScratchPad, 0, ScratchPad, 0, ScratchPad.Length); + } + else + ScratchPad = Utils.EmptyBytes; + Position = prim.Position; + Scale = prim.Scale; + Rotation = prim.Rotation; + Velocity = prim.Velocity; + AngularVelocity = prim.AngularVelocity; + Acceleration = prim.Acceleration; + CollisionPlane = prim.CollisionPlane; + Flexible = prim.Flexible; + Light = prim.Light; + Sculpt = prim.Sculpt; + ClickAction = prim.ClickAction; + Sound = prim.Sound; + OwnerID = prim.OwnerID; + SoundFlags = prim.SoundFlags; + SoundGain = prim.SoundGain; + SoundRadius = prim.SoundRadius; + Text = prim.Text; + TextColor = prim.TextColor; + MediaURL = prim.MediaURL; + Joint = prim.Joint; + JointPivot = prim.JointPivot; + JointAxisOrAnchor = prim.JointAxisOrAnchor; + if (prim.NameValues != null) + { + if (NameValues == null || NameValues.Length != prim.NameValues.Length) + NameValues = new NameValue[prim.NameValues.Length]; + Array.Copy(prim.NameValues, NameValues, prim.NameValues.Length); + } + else + NameValues = null; + PrimData = prim.PrimData; + Properties = prim.Properties; + // FIXME: Get a real copy constructor for TextureEntry instead of serializing to bytes and back + if (prim.Textures != null) + { + byte[] textureBytes = prim.Textures.GetBytes(); + Textures = new TextureEntry(textureBytes, 0, textureBytes.Length); + } + else + { + Textures = null; + } + TextureAnim = prim.TextureAnim; + ParticleSys = prim.ParticleSys; + } + + #endregion Constructors + + #region Public Methods + + public virtual OSD GetOSD() + { + OSDMap path = new OSDMap(14); + path["begin"] = OSD.FromReal(PrimData.PathBegin); + path["curve"] = OSD.FromInteger((int)PrimData.PathCurve); + path["end"] = OSD.FromReal(PrimData.PathEnd); + path["radius_offset"] = OSD.FromReal(PrimData.PathRadiusOffset); + path["revolutions"] = OSD.FromReal(PrimData.PathRevolutions); + path["scale_x"] = OSD.FromReal(PrimData.PathScaleX); + path["scale_y"] = OSD.FromReal(PrimData.PathScaleY); + path["shear_x"] = OSD.FromReal(PrimData.PathShearX); + path["shear_y"] = OSD.FromReal(PrimData.PathShearY); + path["skew"] = OSD.FromReal(PrimData.PathSkew); + path["taper_x"] = OSD.FromReal(PrimData.PathTaperX); + path["taper_y"] = OSD.FromReal(PrimData.PathTaperY); + path["twist"] = OSD.FromReal(PrimData.PathTwist); + path["twist_begin"] = OSD.FromReal(PrimData.PathTwistBegin); + + OSDMap profile = new OSDMap(4); + profile["begin"] = OSD.FromReal(PrimData.ProfileBegin); + profile["curve"] = OSD.FromInteger((int)PrimData.ProfileCurve); + profile["hole"] = OSD.FromInteger((int)PrimData.ProfileHole); + profile["end"] = OSD.FromReal(PrimData.ProfileEnd); + profile["hollow"] = OSD.FromReal(PrimData.ProfileHollow); + + OSDMap volume = new OSDMap(2); + volume["path"] = path; + volume["profile"] = profile; + + OSDMap prim = new OSDMap(20); + if (Properties != null) + { + prim["name"] = OSD.FromString(Properties.Name); + prim["description"] = OSD.FromString(Properties.Description); + } + else + { + prim["name"] = OSD.FromString("Object"); + prim["description"] = OSD.FromString(String.Empty); + } + + prim["phantom"] = OSD.FromBoolean(((Flags & PrimFlags.Phantom) != 0)); + prim["physical"] = OSD.FromBoolean(((Flags & PrimFlags.Physics) != 0)); + prim["position"] = OSD.FromVector3(Position); + prim["rotation"] = OSD.FromQuaternion(Rotation); + prim["scale"] = OSD.FromVector3(Scale); + prim["pcode"] = OSD.FromInteger((int)PrimData.PCode); + prim["material"] = OSD.FromInteger((int)PrimData.Material); + prim["shadows"] = OSD.FromBoolean(((Flags & PrimFlags.CastShadows) != 0)); + prim["state"] = OSD.FromInteger(PrimData.State); + + prim["id"] = OSD.FromUUID(ID); + prim["localid"] = OSD.FromUInteger(LocalID); + prim["parentid"] = OSD.FromUInteger(ParentID); + + prim["volume"] = volume; + + if (Textures != null) + prim["textures"] = Textures.GetOSD(); + + if (Light != null) + prim["light"] = Light.GetOSD(); + + if (Flexible != null) + prim["flex"] = Flexible.GetOSD(); + + if (Sculpt != null) + prim["sculpt"] = Sculpt.GetOSD(); + + return prim; + } + + public static Primitive FromOSD(OSD osd) + { + Primitive prim = new Primitive(); + Primitive.ConstructionData data; + + OSDMap map = (OSDMap)osd; + OSDMap volume = (OSDMap)map["volume"]; + OSDMap path = (OSDMap)volume["path"]; + OSDMap profile = (OSDMap)volume["profile"]; + + #region Path/Profile + + data.profileCurve = (byte)0; + data.Material = (Material)map["material"].AsInteger(); + data.PCode = (PCode)map["pcode"].AsInteger(); + data.State = (byte)map["state"].AsInteger(); + + data.PathBegin = (float)path["begin"].AsReal(); + data.PathCurve = (PathCurve)path["curve"].AsInteger(); + data.PathEnd = (float)path["end"].AsReal(); + data.PathRadiusOffset = (float)path["radius_offset"].AsReal(); + data.PathRevolutions = (float)path["revolutions"].AsReal(); + data.PathScaleX = (float)path["scale_x"].AsReal(); + data.PathScaleY = (float)path["scale_y"].AsReal(); + data.PathShearX = (float)path["shear_x"].AsReal(); + data.PathShearY = (float)path["shear_y"].AsReal(); + data.PathSkew = (float)path["skew"].AsReal(); + data.PathTaperX = (float)path["taper_x"].AsReal(); + data.PathTaperY = (float)path["taper_y"].AsReal(); + data.PathTwist = (float)path["twist"].AsReal(); + data.PathTwistBegin = (float)path["twist_begin"].AsReal(); + + data.ProfileBegin = (float)profile["begin"].AsReal(); + data.ProfileEnd = (float)profile["end"].AsReal(); + data.ProfileHollow = (float)profile["hollow"].AsReal(); + data.ProfileCurve = (ProfileCurve)profile["curve"].AsInteger(); + data.ProfileHole = (HoleType)profile["hole"].AsInteger(); + + #endregion Path/Profile + + prim.PrimData = data; + + if (map["phantom"].AsBoolean()) + prim.Flags |= PrimFlags.Phantom; + + if (map["physical"].AsBoolean()) + prim.Flags |= PrimFlags.Physics; + + if (map["shadows"].AsBoolean()) + prim.Flags |= PrimFlags.CastShadows; + + prim.ID = map["id"].AsUUID(); + prim.LocalID = map["localid"].AsUInteger(); + prim.ParentID = map["parentid"].AsUInteger(); + prim.Position = ((OSDArray)map["position"]).AsVector3(); + prim.Rotation = ((OSDArray)map["rotation"]).AsQuaternion(); + prim.Scale = ((OSDArray)map["scale"]).AsVector3(); + + if (map["flex"]) + prim.Flexible = FlexibleData.FromOSD(map["flex"]); + + if (map["light"]) + prim.Light = LightData.FromOSD(map["light"]); + + if (map["sculpt"]) + prim.Sculpt = SculptData.FromOSD(map["sculpt"]); + + prim.Textures = TextureEntry.FromOSD(map["textures"]); + prim.Properties = new ObjectProperties(); + + if (!string.IsNullOrEmpty(map["name"].AsString())) + { + prim.Properties.Name = map["name"].AsString(); + } + + if (!string.IsNullOrEmpty(map["description"].AsString())) + { + prim.Properties.Description = map["description"].AsString(); + } + + return prim; + } + + public int SetExtraParamsFromBytes(byte[] data, int pos) + { + int i = pos; + int totalLength = 1; + + if (data.Length == 0 || pos >= data.Length) + return 0; + + byte extraParamCount = data[i++]; + + for (int k = 0; k < extraParamCount; k++) + { + ExtraParamType type = (ExtraParamType)Utils.BytesToUInt16(data, i); + i += 2; + + uint paramLength = Utils.BytesToUInt(data, i); + i += 4; + + if (type == ExtraParamType.Flexible) + Flexible = new FlexibleData(data, i); + else if (type == ExtraParamType.Light) + Light = new LightData(data, i); + else if (type == ExtraParamType.Sculpt) + Sculpt = new SculptData(data, i); + + i += (int)paramLength; + totalLength += (int)paramLength + 6; + } + + return totalLength; + } + + public byte[] GetExtraParamsBytes() + { + byte[] flexible = null; + byte[] light = null; + byte[] sculpt = null; + byte[] buffer = null; + int size = 1; + int pos = 0; + byte count = 0; + + if (Flexible != null) + { + flexible = Flexible.GetBytes(); + size += flexible.Length + 6; + ++count; + } + if (Light != null) + { + light = Light.GetBytes(); + size += light.Length + 6; + ++count; + } + if (Sculpt != null) + { + sculpt = Sculpt.GetBytes(); + size += sculpt.Length + 6; + ++count; + } + + buffer = new byte[size]; + buffer[0] = count; + ++pos; + + if (flexible != null) + { + Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Flexible), 0, buffer, pos, 2); + pos += 2; + + Buffer.BlockCopy(Utils.UIntToBytes((uint)flexible.Length), 0, buffer, pos, 4); + pos += 4; + + Buffer.BlockCopy(flexible, 0, buffer, pos, flexible.Length); + pos += flexible.Length; + } + if (light != null) + { + Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Light), 0, buffer, pos, 2); + pos += 2; + + Buffer.BlockCopy(Utils.UIntToBytes((uint)light.Length), 0, buffer, pos, 4); + pos += 4; + + Buffer.BlockCopy(light, 0, buffer, pos, light.Length); + pos += light.Length; + } + if (sculpt != null) + { + Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Sculpt), 0, buffer, pos, 2); + pos += 2; + + Buffer.BlockCopy(Utils.UIntToBytes((uint)sculpt.Length), 0, buffer, pos, 4); + pos += 4; + + Buffer.BlockCopy(sculpt, 0, buffer, pos, sculpt.Length); + pos += sculpt.Length; + } + + return buffer; + } + + #endregion Public Methods + + #region Overrides + + public override bool Equals(object obj) + { + return (obj is Primitive) ? this == (Primitive)obj : false; + } + + public bool Equals(Primitive other) + { + return this == other; + } + + public override string ToString() + { + switch (PrimData.PCode) + { + case PCode.Prim: + return String.Format("{0} ({1})", Type, ID); + default: + return String.Format("{0} ({1})", PrimData.PCode, ID); + } + } + + public override int GetHashCode() + { + return + Position.GetHashCode() ^ + Velocity.GetHashCode() ^ + Acceleration.GetHashCode() ^ + Rotation.GetHashCode() ^ + AngularVelocity.GetHashCode() ^ + ClickAction.GetHashCode() ^ + (Flexible != null ? Flexible.GetHashCode() : 0) ^ + (Light != null ? Light.GetHashCode() : 0) ^ + (Sculpt != null ? Sculpt.GetHashCode() : 0) ^ + Flags.GetHashCode() ^ + PrimData.Material.GetHashCode() ^ + MediaURL.GetHashCode() ^ + //TODO: NameValues? + (Properties != null ? Properties.OwnerID.GetHashCode() : 0) ^ + ParentID.GetHashCode() ^ + PrimData.PathBegin.GetHashCode() ^ + PrimData.PathCurve.GetHashCode() ^ + PrimData.PathEnd.GetHashCode() ^ + PrimData.PathRadiusOffset.GetHashCode() ^ + PrimData.PathRevolutions.GetHashCode() ^ + PrimData.PathScaleX.GetHashCode() ^ + PrimData.PathScaleY.GetHashCode() ^ + PrimData.PathShearX.GetHashCode() ^ + PrimData.PathShearY.GetHashCode() ^ + PrimData.PathSkew.GetHashCode() ^ + PrimData.PathTaperX.GetHashCode() ^ + PrimData.PathTaperY.GetHashCode() ^ + PrimData.PathTwist.GetHashCode() ^ + PrimData.PathTwistBegin.GetHashCode() ^ + PrimData.PCode.GetHashCode() ^ + PrimData.ProfileBegin.GetHashCode() ^ + PrimData.ProfileCurve.GetHashCode() ^ + PrimData.ProfileEnd.GetHashCode() ^ + PrimData.ProfileHollow.GetHashCode() ^ + ParticleSys.GetHashCode() ^ + TextColor.GetHashCode() ^ + TextureAnim.GetHashCode() ^ + (Textures != null ? Textures.GetHashCode() : 0) ^ + SoundRadius.GetHashCode() ^ + Scale.GetHashCode() ^ + Sound.GetHashCode() ^ + PrimData.State.GetHashCode() ^ + Text.GetHashCode() ^ + TreeSpecies.GetHashCode(); + } + + #endregion Overrides + + #region Operators + + public static bool operator ==(Primitive lhs, Primitive rhs) + { + if ((Object)lhs == null || (Object)rhs == null) + { + return (Object)rhs == (Object)lhs; + } + return (lhs.ID == rhs.ID); + } + + public static bool operator !=(Primitive lhs, Primitive rhs) + { + if ((Object)lhs == null || (Object)rhs == null) + { + return (Object)rhs != (Object)lhs; + } + return !(lhs.ID == rhs.ID); + } + + #endregion Operators + + #region Parameter Packing Methods + + public static ushort PackBeginCut(float beginCut) + { + return (ushort)Math.Round(beginCut / CUT_QUANTA); + } + + public static ushort PackEndCut(float endCut) + { + return (ushort)(50000 - (ushort)Math.Round(endCut / CUT_QUANTA)); + } + + public static byte PackPathScale(float pathScale) + { + return (byte)(200 - (byte)Math.Round(pathScale / SCALE_QUANTA)); + } + + public static sbyte PackPathShear(float pathShear) + { + return (sbyte)Math.Round(pathShear / SHEAR_QUANTA); + } + + /// + /// Packs PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew + /// parameters in to signed eight bit values + /// + /// Floating point parameter to pack + /// Signed eight bit value containing the packed parameter + public static sbyte PackPathTwist(float pathTwist) + { + return (sbyte)Math.Round(pathTwist / SCALE_QUANTA); + } + + public static sbyte PackPathTaper(float pathTaper) + { + return (sbyte)Math.Round(pathTaper / TAPER_QUANTA); + } + + public static byte PackPathRevolutions(float pathRevolutions) + { + return (byte)Math.Round((pathRevolutions - 1f) / REV_QUANTA); + } + + public static ushort PackProfileHollow(float profileHollow) + { + return (ushort)Math.Round(profileHollow / HOLLOW_QUANTA); + } + + #endregion Parameter Packing Methods + + #region Parameter Unpacking Methods + + public static float UnpackBeginCut(ushort beginCut) + { + return (float)beginCut * CUT_QUANTA; + } + + public static float UnpackEndCut(ushort endCut) + { + return (float)(50000 - endCut) * CUT_QUANTA; + } + + public static float UnpackPathScale(byte pathScale) + { + return (float)(200 - pathScale) * SCALE_QUANTA; + } + + public static float UnpackPathShear(sbyte pathShear) + { + return (float)pathShear * SHEAR_QUANTA; + } + + /// + /// Unpacks PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew + /// parameters from signed eight bit integers to floating point values + /// + /// Signed eight bit value to unpack + /// Unpacked floating point value + public static float UnpackPathTwist(sbyte pathTwist) + { + return (float)pathTwist * SCALE_QUANTA; + } + + public static float UnpackPathTaper(sbyte pathTaper) + { + return (float)pathTaper * TAPER_QUANTA; + } + + public static float UnpackPathRevolutions(byte pathRevolutions) + { + return (float)pathRevolutions * REV_QUANTA + 1f; + } + + public static float UnpackProfileHollow(ushort profileHollow) + { + return (float)profileHollow * HOLLOW_QUANTA; + } + + #endregion Parameter Unpacking Methods + } +} diff --git a/OpenMetaverseTypes/EnumsPrimitive.cs b/OpenMetaverseTypes/EnumsPrimitive.cs index 33076b18..e5005e63 100644 --- a/OpenMetaverseTypes/EnumsPrimitive.cs +++ b/OpenMetaverseTypes/EnumsPrimitive.cs @@ -533,4 +533,17 @@ namespace OpenMetaverse /// Open parcel media OpenMedia = 6 } + + /// + /// Type of physics representation used for this prim in the simulator + /// + public enum PhysicsShapeType : byte + { + /// Use prim physics form this object + Prim = 0, + /// No physics, prim doesn't collide + None, + /// Use convex hull represantion of this prim + ConvexHull + } } \ No newline at end of file