/* * 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 { #region Enums /// /// Identifier code for primitive types /// public enum PCode : byte { /// None None = 0, /// A Primitive Prim = 9, /// A Avatar Avatar = 47, /// Linden grass Grass = 95, /// Linden tree NewTree = 111, /// A primitive that acts as the source for a particle stream ParticleSystem = 143, /// A Linden tree Tree = 255 } /// /// Primary parameters for primitives such as Physics Enabled or Phantom /// [Flags] public enum PrimFlags : uint { /// Deprecated 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, /// Server flag, will not be sent to clients. Specifies that /// the object is destroyed when it touches a simulator edge DieAtEdge = 0x00100000, /// Server flag, will not be sent to clients. Specifies that /// the object will be returned to the owner's inventory when it /// touches a simulator edge ReturnAtEdge = 0x00200000, /// Server flag, will not be sent to clients. Sandbox = 0x00400000, /// Server flag, will not be sent to client. Specifies that /// the object is hovering/flying Flying = 0x00800000, /// CameraDecoupled = 0x00100000, /// AnimSource = 0x00200000, /// CameraSource = 0x00400000, /// CastShadows = 0x00800000, Placeholder5 = 0x01000000, Placeholder6 = 0x02000000, Placeholder7 = 0x04000000, Placeholder8 = 0x08000000, /// 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 Material : byte { /// Stone = 0, /// Metal, /// Glass, /// Wood, /// Flesh, /// Plastic, /// Rubber, /// Light } /// /// Used in a helper function to roughly determine prim shape /// public enum PrimType { Unknown, Box, Cylinder, Prism, Sphere, Torus, Tube, Ring, Sculpt } /// /// Extra parameters for primitives, these flags are for features that have /// been added after the original ObjectFlags that has all eight bits /// reserved already /// [Flags] public enum ExtraParamType : ushort { /// Whether this object has flexible parameters Flexible = 0x10, /// Whether this object has light parameters Light = 0x20, /// Whether this object is a sculpted prim Sculpt = 0x30 } /// /// /// public enum JointType : byte { /// Invalid = 0, /// Hinge = 1, /// Point = 2, // //[Obsolete] //LPoint = 3, //[Obsolete] //Wheel = 4 } /// /// /// public enum SculptType : byte { /// None = 0, /// Sphere = 1, /// Torus = 2, /// Plane = 3, /// Cylinder = 4 } /// /// /// public enum FaceType : ushort { /// PathBegin = 0x1 << 0, /// PathEnd = 0x1 << 1, /// InnerSide = 0x1 << 2, /// ProfileBegin = 0x1 << 3, /// ProfileEnd = 0x1 << 4, /// OuterSide0 = 0x1 << 5, /// OuterSide1 = 0x1 << 6, /// OuterSide2 = 0x1 << 7, /// OuterSide3 = 0x1 << 8 } /// /// /// public enum ObjectCategory { /// Invalid = -1, /// None = 0, /// Owner, /// Group, /// Other, /// Selected, /// Temporary } /// /// Attachment points for objects on avatar bodies /// /// /// Both InventoryObject and InventoryAttachment types can be attached /// public enum AttachmentPoint : byte { /// Right hand if object was not previously attached Default = 0, /// Chest Chest = 1, /// Skull Skull, /// Left shoulder LeftShoulder, /// Right shoulder RightShoulder, /// Left hand LeftHand, /// Right hand RightHand, /// Left foot LeftFoot, /// Right foot RightFoot, /// Spine Spine, /// Pelvis Pelvis, /// Mouth Mouth, /// Chin Chin, /// Left ear LeftEar, /// Right ear RightEar, /// Left eyeball LeftEyeball, /// Right eyeball RightEyeball, /// Nose Nose, /// Right upper arm RightUpperArm, /// Right forearm RightForearm, /// Left upper arm LeftUpperArm, /// Left forearm LeftForearm, /// Right hip RightHip, /// Right upper leg RightUpperLeg, /// Right lower leg RightLowerLeg, /// Left hip LeftHip, /// Left upper leg LeftUpperLeg, /// Left lower leg LeftLowerLeg, /// Stomach Stomach, /// Left pectoral LeftPec, /// Right pectoral RightPec, /// HUD Center position 2 HUDCenter2, /// HUD Top-right HUDTopRight, /// HUD Top HUDTop, /// HUD Top-left HUDTopLeft, /// HUD Center HUDCenter, /// HUD Bottom-left HUDBottomLeft, /// HUD Bottom HUDBottom, /// HUD Bottom-right HUDBottomRight } /// /// Tree foliage types /// public enum Tree : byte { /// Pine1 tree Pine1 = 0, /// Oak tree Oak, /// Tropical Bush1 TropicalBush1, /// Palm1 tree Palm1, /// Dogwood tree Dogwood, /// Tropical Bush2 TropicalBush2, /// Palm2 tree Palm2, /// Cypress1 tree Cypress1, /// Cypress2 tree Cypress2, /// Pine2 tree Pine2, /// Plumeria Plumeria, /// Winter pinetree1 WinterPine1, /// Winter Aspen tree WinterAspen, /// Winter pinetree2 WinterPine2, /// Eucalyptus tree Eucalyptus, /// Fern Fern, /// Eelgrass Eelgrass, /// Sea Sword SeaSword, /// Kelp1 plant Kelp1, /// Beach grass BeachGrass1, /// Kelp2 plant Kelp2 } /// /// Grass foliage types /// public enum Grass : byte { /// Grass0 = 0, /// Grass1, /// Grass2, /// Grass3, /// Grass4, /// Undergrowth1 } /// /// Action associated with clicking on an object /// public enum ClickAction : byte { /// Touch object Touch = 0, /// Sit on object Sit = 1, /// Purchase object or contents Buy = 2, /// Pay the object Pay = 3, /// Open task inventory OpenTask = 4, /// Play parcel media PlayMedia = 5, /// Open parcel media OpenMedia = 6 } #endregion Enums 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; } } /// /// 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 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; public SculptType Type; /// /// Default constructor /// public SculptData() { } /// /// /// /// /// public SculptData(byte[] data, int pos) { if (data.Length >= 17) { SculptTexture = new UUID(data, pos); Type = (SculptType)data[pos + 16]; } else { SculptTexture = UUID.Zero; Type = SculptType.None; } } public byte[] GetBytes() { byte[] data = new byte[17]; SculptTexture.GetBytes().CopyTo(data, 0); data[16] = (byte)Type; return data; } public OSD GetOSD() { OSDMap map = new OSDMap(); map["texture"] = OSD.FromUUID(SculptTexture); map["type"] = OSD.FromInteger((int)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 = (SculptType)map["type"].AsInteger(); } return sculpt; } } /// /// 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; } } #endregion Subclasses #region Public Members /// public UUID ID; /// public UUID GroupID; /// public uint LocalID; /// public uint ParentID; /// public ulong RegionHandle; /// public PrimFlags Flags; /// Unknown public byte[] GenericData; /// 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 byte 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; if (prim.GenericData != null) { if (GenericData == null || GenericData.Length != prim.GenericData.Length) GenericData = new byte[prim.GenericData.Length]; Buffer.BlockCopy(prim.GenericData, 0, GenericData, 0, prim.GenericData.Length); } else GenericData = null; 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.ToBytes(); Textures = new TextureEntry(textureBytes, 0, textureBytes.Length); } else { Textures = null; } TextureAnim = prim.TextureAnim; ParticleSys = prim.ParticleSys; } #endregion Constructors #region Public Methods public 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(9); prim["name"] = OSD.FromString(Properties.Name); prim["description"] = OSD.FromString(Properties.Description); 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["material"] = OSD.FromInteger((int)PrimData.Material); prim["shadows"] = OSD.FromBoolean(((Flags & PrimFlags.CastShadows) != 0)); prim["textures"] = Textures.GetOSD(); prim["volume"] = volume; if (ParentID != 0) prim["parentid"] = OSD.FromInteger(ParentID); 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.State = 0; data.Material = (Material)map["material"].AsInteger(); data.PCode = PCode.Prim; // TODO: Put this in SD 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.ParentID = (uint)map["parentid"].AsInteger(); prim.Position = ((OSDArray)map["position"]).AsVector3(); prim.Rotation = ((OSDArray)map["rotation"]).AsQuaternion(); prim.Scale = ((OSDArray)map["scale"]).AsVector3(); prim.Flexible = FlexibleData.FromOSD(map["flex"]); prim.Light = LightData.FromOSD(map["light"]); 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; Flexible = null; Light = null; Sculpt = null; 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 ID.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 } }