/* * Copyright (c) 2007-2008, Second Life Reverse Engineering Team * 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 Second Life Reverse Engineering Team 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 libsecondlife.Packets; namespace libsecondlife { /// /// Base class for primitives and avatars /// public abstract partial class LLObject { // 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 Enumerations /// /// Primary parameters for primitives such as Physics Enabled or Phantom /// [Flags] public enum ObjectFlags : uint { /// None of the primary flags are enabled None = 0, /// Whether physics are enabled for this object Physics = 0x00000001, /// CreateSelected = 0x00000002, /// ObjectModify = 0x00000004, /// ObjectCopy = 0x00000008, /// ObjectAnyOwner = 0x00000010, /// ObjectYouOwner = 0x00000020, /// Scripted = 0x00000040, /// Whether this object contains an active touch script Touch = 0x00000080, /// ObjectMove = 0x00000100, /// Whether this object can receive payments Money = 0x00000200, /// Whether this object is phantom (no collisions) Phantom = 0x00000400, /// InventoryEmpty = 0x00000800, /// JointHinge = 0x00001000, /// JointP2P = 0x00002000, /// JointLP2P = 0x00004000, /// Deprecated JointWheel = 0x00008000, /// AllowInventoryDrop = 0x00010000, /// ObjectTransfer = 0x00020000, /// ObjectGroupOwned = 0x00040000, /// Deprecated ObjectYouOfficer = 0x00080000, /// CameraDecoupled = 0x00100000, /// AnimSource = 0x00200000, /// CameraSource = 0x00400000, /// CastShadows = 0x00800000, /// ObjectOwnerModify = 0x10000000, /// TemporaryOnRez = 0x20000000, /// Temporary = 0x40000000, /// ZlibCompressed = 0x80000000 } public enum ProfileCurve : byte { Circle = 0x00, Square = 0x01, IsoTriangle = 0x02, EqualTriangle = 0x03, RightTriangle = 0x04, HalfCircle = 0x05 } public enum HoleType : byte { Same = 0x00, Circle = 0x10, Square = 0x20, Triangle = 0x30 } public enum PathCurve : byte { Line = 0x10, Circle = 0x20, Circle2 = 0x30, Test = 0x40, Flexible = 0x80 } /// /// Material type for a primitive /// public enum MaterialType : byte { /// Stone = 0, /// Metal, /// Glass, /// Wood, /// Flesh, /// Plastic, /// Rubber, /// Light } public enum PrimType { Unknown, Box, Cylinder, Prism, Sphere, Torus, Tube, Ring, Sculpt } #endregion Enumerations #region Structs /// /// /// public struct ObjectData { private const byte PROFILE_MASK = 0x0F; private const byte HOLE_MASK = 0xF0; /// internal 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 MaterialType Material; /// public byte State; /// public PCode PCode; #region Properties /// 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 LLVector2 PathBeginScale { get { LLVector2 begin = new LLVector2(1f, 1f); if (PathScaleX > 1f) begin.X = 2f - PathScaleX; if (PathScaleY > 1f) begin.Y = 2f - PathScaleY; return begin; } } /// public LLVector2 PathEndScale { get { LLVector2 end = new LLVector2(1f, 1f); if (PathScaleX < 1f) end.X = PathScaleX; if (PathScaleY < 1f) end.Y = PathScaleY; return end; } } public PrimType Type { get { bool linearPath = (PathCurve == LLObject.PathCurve.Line || PathCurve == LLObject.PathCurve.Flexible); float scaleX = PathScaleX; float scaleY = PathScaleY; if (linearPath && ProfileCurve == LLObject.ProfileCurve.Circle) return PrimType.Cylinder; else if (linearPath && ProfileCurve == LLObject.ProfileCurve.Square) return PrimType.Box; else if (linearPath && ProfileCurve == LLObject.ProfileCurve.IsoTriangle) return PrimType.Prism; else if (linearPath && ProfileCurve == LLObject.ProfileCurve.EqualTriangle) return PrimType.Prism; else if (linearPath && ProfileCurve == LLObject.ProfileCurve.RightTriangle) return PrimType.Prism; else if (PathCurve == LLObject.PathCurve.Flexible) return PrimType.Unknown; else if (PathCurve == LLObject.PathCurve.Circle && ProfileCurve == LLObject.ProfileCurve.Circle && scaleY > 0.75f) return PrimType.Sphere; else if (PathCurve == LLObject.PathCurve.Circle && ProfileCurve == LLObject.ProfileCurve.Circle && scaleY <= 0.75f) return PrimType.Torus; else if (PathCurve == LLObject.PathCurve.Circle && ProfileCurve == LLObject.ProfileCurve.HalfCircle) return PrimType.Sphere; else if (PathCurve == LLObject.PathCurve.Circle2 && ProfileCurve == LLObject.ProfileCurve.Circle) return PrimType.Sphere; // Spiral/sphere else if (PathCurve == LLObject.PathCurve.Circle && ProfileCurve == LLObject.ProfileCurve.EqualTriangle) return PrimType.Ring; else if (PathCurve == LLObject.PathCurve.Circle && ProfileCurve == LLObject.ProfileCurve.Square && scaleY <= 0.75f) return PrimType.Tube; else return PrimType.Unknown; } } #endregion Properties public override string ToString() { return Type.ToString(); } } /// /// /// public struct ObjectProperties { /// public LLUUID ObjectID; /// public LLUUID CreatorID; /// This is a protocol hack, it will only be set if the /// object has a non-null sound so you can mute the owner public LLUUID OwnerID; /// public LLUUID GroupID; /// public ulong CreationDate; /// public Permissions Permissions; /// public int OwnershipCost; /// public byte SaleType; /// public int SalePrice; /// public byte AggregatePerms; /// public byte AggregatePermTextures; /// public byte AggregatePermTexturesOwner; /// public uint Category; /// public short InventorySerial; /// public LLUUID ItemID; /// public LLUUID FolderID; /// public LLUUID FromTaskID; /// public LLUUID LastOwnerID; /// public string Name; /// public string Description; /// public string TouchName; /// public string SitName; /// public LLUUID[] TextureIDs; } /// /// /// public struct ObjectPropertiesFamily { /// /// /// public enum RequestFlagsType { /// None = 0, /// BugReportRequest = 1, /// ComplaintReportRequest = 2 } /// public RequestFlagsType RequestFlags; /// public LLUUID ObjectID; /// public LLUUID OwnerID; /// public LLUUID GroupID; /// public Permissions Permissions; /// public int OwnershipCost; /// public byte SaleType; /// public int SalePrice; /// public uint Category; /// public LLUUID LastOwnerID; /// public string Name; /// public string Description; //public bool IsOwnedBy(SecondLife client) //{ // if (GroupID != LLUUID.Zero) // { // // Group owned, iterate through all of this clients groups // // and see if it is a member // //client.Groups. // // FIXME: Current groups should be stored in GroupManager and auto-requested (with a setting to turn off) // } // else // { // // Avatar owned // } //} } #endregion Structs #region Public Members /// public LLUUID ID; /// public LLUUID GroupID; /// public uint LocalID; /// public uint ParentID; /// public ulong RegionHandle; /// public ObjectFlags Flags; /// Unknown public byte[] GenericData; /// public LLVector3 Position; /// public LLVector3 Scale; /// public LLQuaternion Rotation; /// public LLVector3 Velocity; /// public LLVector3 AngularVelocity; /// public LLVector3 Acceleration; /// public LLVector4 CollisionPlane; /// public TextureEntry Textures; /// public ObjectProperties Properties; /// public ObjectPropertiesFamily PropertiesFamily; /// public NameValue[] NameValues; /// public ObjectData Data; #endregion Public Members /// /// /// /// /// public override bool Equals(object obj) { LLObject llobj = obj as LLObject; if (llobj == null) return false; return ID.Equals(llobj.ID); } /// /// /// /// public override int GetHashCode() { return ID.GetHashCode(); } #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 } }