/* * Copyright (c) 2006-2014, 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 /// /// Calculdates hash code for prim construction data /// /// The has public override int GetHashCode() { return profileCurve.GetHashCode() ^ PathCurve.GetHashCode() ^ PathEnd.GetHashCode() ^ PathRadiusOffset.GetHashCode() ^ PathSkew.GetHashCode() ^ PathScaleX.GetHashCode() ^ PathScaleY.GetHashCode() ^ PathShearX.GetHashCode() ^ PathShearY.GetHashCode() ^ PathTaperX.GetHashCode() ^ PathTaperY.GetHashCode() ^ PathBegin.GetHashCode() ^ PathTwist.GetHashCode() ^ PathTwistBegin.GetHashCode() ^ PathRevolutions.GetHashCode() ^ ProfileBegin.GetHashCode() ^ ProfileEnd.GetHashCode() ^ ProfileHollow.GetHashCode() ^ Material.GetHashCode() ^ State.GetHashCode() ^ PCode.GetHashCode(); } } /// /// 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 light properties of a primitive as texture map /// public class LightImage { /// public UUID LightTexture; /// public Vector3 Params; /// /// Default constructor /// public LightImage() { } /// /// /// /// /// public LightImage(byte[] data, int pos) { if (data.Length - pos >= 28) { LightTexture = new UUID(data, pos); Params = new Vector3(data, pos + 16); } else { LightTexture = UUID.Zero; Params = Vector3.Zero; } } /// /// /// /// public byte[] GetBytes() { byte[] data = new byte[28]; // Alpha channel in color is intensity LightTexture.ToBytes(data, 0); Params.ToBytes(data, 16); return data; } public OSD GetOSD() { OSDMap map = new OSDMap(); map["texture"] = OSD.FromUUID(LightTexture); map["params"] = OSD.FromVector3(Params); return map; } public static LightImage FromOSD(OSD osd) { LightImage light = new LightImage(); if (osd.Type == OSDType.Map) { OSDMap map = (OSDMap)osd; light.LightTexture = map["texture"].AsUUID(); light.Params = map["params"].AsVector3(); } return light; } public override int GetHashCode() { return LightTexture.GetHashCode() ^ Params.GetHashCode(); } /// /// /// /// public override string ToString() { return String.Format("LightTexture: {0} Params; {1]", LightTexture, Params); } } /// /// 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 LightImage LightMap; /// 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; /// Extra data about primitive public object Tag; /// Indicates if prim is attached to an avatar public bool IsAttachment; /// Number of clients referencing this prim public int ActiveClients = 0; #endregion Public Members #region Properties /// Uses basic heuristics to estimate the primitive shape public PrimType Type { get { if (Sculpt != null && Sculpt.Type != SculptType.None && Sculpt.SculptTexture != UUID.Zero) { if (Sculpt.Type == SculptType.Mesh) return PrimType.Mesh; else 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; LightMap = prim.LightMap; 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 ((TextureAnim.Flags & TextureAnimMode.ANIM_ON) != 0) prim["texture_anim"] = TextureAnim.GetOSD(); if (Light != null) prim["light"] = Light.GetOSD(); if (LightMap != null) prim["light_image"] = LightMap.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["light_image"]) prim.LightMap = LightImage.FromOSD(map["light_image"]); if (map["sculpt"]) prim.Sculpt = SculptData.FromOSD(map["sculpt"]); prim.Textures = TextureEntry.FromOSD(map["textures"]); if (map["texture_anim"]) prim.TextureAnim = TextureAnimation.FromOSD(map["texture_anim"]); 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.LightImage) LightMap = new LightImage(data, i); else if (type == ExtraParamType.Sculpt || type == ExtraParamType.Mesh) Sculpt = new SculptData(data, i); i += (int)paramLength; totalLength += (int)paramLength + 6; } return totalLength; } public byte[] GetExtraParamsBytes() { byte[] flexible = null; byte[] light = null; byte[] lightmap = 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 (LightMap != null) { lightmap = LightMap.GetBytes(); size += lightmap.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 (lightmap != null) { Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.LightImage), 0, buffer, pos, 2); pos += 2; Buffer.BlockCopy(Utils.UIntToBytes((uint)lightmap.Length), 0, buffer, pos, 4); pos += 4; Buffer.BlockCopy(lightmap, 0, buffer, pos, lightmap.Length); pos += lightmap.Length; } if (sculpt != null) { if (Sculpt.Type == SculptType.Mesh) { Buffer.BlockCopy(Utils.UInt16ToBytes((ushort)ExtraParamType.Mesh), 0, buffer, pos, 2); } else { 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 } }