corrade-vassal – Rev 1
?pathlinks?
/*
* 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<Primitive>
{
// 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
/// <summary>
/// Parameters used to construct a visual representation of a primitive
/// </summary>
public struct ConstructionData
{
private const byte PROFILE_MASK = 0x0F;
private const byte HOLE_MASK = 0xF0;
/// <summary></summary>
public byte profileCurve;
/// <summary></summary>
public PathCurve PathCurve;
/// <summary></summary>
public float PathEnd;
/// <summary></summary>
public float PathRadiusOffset;
/// <summary></summary>
public float PathSkew;
/// <summary></summary>
public float PathScaleX;
/// <summary></summary>
public float PathScaleY;
/// <summary></summary>
public float PathShearX;
/// <summary></summary>
public float PathShearY;
/// <summary></summary>
public float PathTaperX;
/// <summary></summary>
public float PathTaperY;
/// <summary></summary>
public float PathBegin;
/// <summary></summary>
public float PathTwist;
/// <summary></summary>
public float PathTwistBegin;
/// <summary></summary>
public float PathRevolutions;
/// <summary></summary>
public float ProfileBegin;
/// <summary></summary>
public float ProfileEnd;
/// <summary></summary>
public float ProfileHollow;
/// <summary></summary>
public Material Material;
/// <summary></summary>
public byte State;
/// <summary></summary>
public PCode PCode;
#region Properties
/// <summary>Attachment point to an avatar</summary>
public AttachmentPoint AttachmentPoint
{
get { return (AttachmentPoint)Utils.SwapWords(State); }
set { State = (byte)Utils.SwapWords((byte)value); }
}
/// <summary></summary>
public ProfileCurve ProfileCurve
{
get { return (ProfileCurve)(profileCurve & PROFILE_MASK); }
set
{
profileCurve &= HOLE_MASK;
profileCurve |= (byte)value;
}
}
/// <summary></summary>
public HoleType ProfileHole
{
get { return (HoleType)(profileCurve & HOLE_MASK); }
set
{
profileCurve &= PROFILE_MASK;
profileCurve |= (byte)value;
}
}
/// <summary></summary>
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;
}
}
/// <summary></summary>
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
/// <summary>
/// Calculdates hash code for prim construction data
/// </summary>
/// <returns>The has</returns>
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();
}
}
/// <summary>
/// Information on the flexible properties of a primitive
/// </summary>
public class FlexibleData
{
/// <summary></summary>
public int Softness;
/// <summary></summary>
public float Gravity;
/// <summary></summary>
public float Drag;
/// <summary></summary>
public float Wind;
/// <summary></summary>
public float Tension;
/// <summary></summary>
public Vector3 Force;
/// <summary>
/// Default constructor
/// </summary>
public FlexibleData()
{
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="pos"></param>
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;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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();
}
}
/// <summary>
/// Information on the light properties of a primitive
/// </summary>
public class LightData
{
/// <summary></summary>
public Color4 Color;
/// <summary></summary>
public float Intensity;
/// <summary></summary>
public float Radius;
/// <summary></summary>
public float Cutoff;
/// <summary></summary>
public float Falloff;
/// <summary>
/// Default constructor
/// </summary>
public LightData()
{
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="pos"></param>
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;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("Color: {0} Intensity: {1} Radius: {2} Cutoff: {3} Falloff: {4}",
Color, Intensity, Radius, Cutoff, Falloff);
}
}
/// <summary>
/// Information on the light properties of a primitive as texture map
/// </summary>
public class LightImage
{
/// <summary></summary>
public UUID LightTexture;
/// <summary></summary>
public Vector3 Params;
/// <summary>
/// Default constructor
/// </summary>
public LightImage()
{
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="pos"></param>
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;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("LightTexture: {0} Params; {1]", LightTexture, Params);
}
}
/// <summary>
/// Information on the sculpt properties of a sculpted primitive
/// </summary>
public class SculptData
{
public UUID SculptTexture;
private byte type;
public SculptType Type
{
get { return (SculptType)(type & 7); }
set { type = (byte)value; }
}
/// <summary>
/// Render inside out (inverts the normals).
/// </summary>
public bool Invert
{
get { return ((type & (byte)SculptType.Invert) != 0); }
}
/// <summary>
/// Render an X axis mirror of the sculpty.
/// </summary>
public bool Mirror
{
get { return ((type & (byte)SculptType.Mirror) != 0); }
}
/// <summary>
/// Default constructor
/// </summary>
public SculptData()
{
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="pos"></param>
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();
}
}
/// <summary>
/// Extended properties to describe an object
/// </summary>
public class ObjectProperties
{
/// <summary></summary>
public UUID ObjectID;
/// <summary></summary>
public UUID CreatorID;
/// <summary></summary>
public UUID OwnerID;
/// <summary></summary>
public UUID GroupID;
/// <summary></summary>
public DateTime CreationDate;
/// <summary></summary>
public Permissions Permissions;
/// <summary></summary>
public int OwnershipCost;
/// <summary></summary>
public SaleType SaleType;
/// <summary></summary>
public int SalePrice;
/// <summary></summary>
public byte AggregatePerms;
/// <summary></summary>
public byte AggregatePermTextures;
/// <summary></summary>
public byte AggregatePermTexturesOwner;
/// <summary></summary>
public ObjectCategory Category;
/// <summary></summary>
public short InventorySerial;
/// <summary></summary>
public UUID ItemID;
/// <summary></summary>
public UUID FolderID;
/// <summary></summary>
public UUID FromTaskID;
/// <summary></summary>
public UUID LastOwnerID;
/// <summary></summary>
public string Name;
/// <summary></summary>
public string Description;
/// <summary></summary>
public string TouchName;
/// <summary></summary>
public string SitName;
/// <summary></summary>
public UUID[] TextureIDs;
/// <summary>
/// Default constructor
/// </summary>
public ObjectProperties()
{
Name = String.Empty;
Description = String.Empty;
TouchName = String.Empty;
SitName = String.Empty;
}
/// <summary>
/// Set the properties that are set in an ObjectPropertiesFamily packet
/// </summary>
/// <param name="props"><seealso cref="ObjectProperties"/> that has
/// been partially filled by an ObjectPropertiesFamily packet</param>
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;
}
}
/// <summary>
/// Describes physics attributes of the prim
/// </summary>
public class PhysicsProperties
{
/// <summary>Primitive's local ID</summary>
public uint LocalID;
/// <summary>Density (1000 for normal density)</summary>
public float Density;
/// <summary>Friction</summary>
public float Friction;
/// <summary>Gravity multiplier (1 for normal gravity) </summary>
public float GravityMultiplier;
/// <summary>Type of physics representation of this primitive in the simulator</summary>
public PhysicsShapeType PhysicsShapeType;
/// <summary>Restitution</summary>
public float Restitution;
/// <summary>
/// Creates PhysicsProperties from OSD
/// </summary>
/// <param name="osd">OSDMap with incoming data</param>
/// <returns>Deserialized PhysicsProperties object</returns>
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;
}
/// <summary>
/// Serializes PhysicsProperties to OSD
/// </summary>
/// <returns>OSDMap with serialized PhysicsProperties data</returns>
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
/// <summary></summary>
public UUID ID;
/// <summary></summary>
public UUID GroupID;
/// <summary></summary>
public uint LocalID;
/// <summary></summary>
public uint ParentID;
/// <summary></summary>
public ulong RegionHandle;
/// <summary></summary>
public PrimFlags Flags;
/// <summary>Foliage type for this primitive. Only applicable if this
/// primitive is foliage</summary>
public Tree TreeSpecies;
/// <summary>Unknown</summary>
public byte[] ScratchPad;
/// <summary></summary>
public Vector3 Position;
/// <summary></summary>
public Vector3 Scale;
/// <summary></summary>
public Quaternion Rotation = Quaternion.Identity;
/// <summary></summary>
public Vector3 Velocity;
/// <summary></summary>
public Vector3 AngularVelocity;
/// <summary></summary>
public Vector3 Acceleration;
/// <summary></summary>
public Vector4 CollisionPlane;
/// <summary></summary>
public FlexibleData Flexible;
/// <summary></summary>
public LightData Light;
/// <summary></summary>
public LightImage LightMap;
/// <summary></summary>
public SculptData Sculpt;
/// <summary></summary>
public ClickAction ClickAction;
/// <summary></summary>
public UUID Sound;
/// <summary>Identifies the owner if audio or a particle system is
/// active</summary>
public UUID OwnerID;
/// <summary></summary>
public SoundFlags SoundFlags;
/// <summary></summary>
public float SoundGain;
/// <summary></summary>
public float SoundRadius;
/// <summary></summary>
public string Text;
/// <summary></summary>
public Color4 TextColor;
/// <summary></summary>
public string MediaURL;
/// <summary></summary>
public JointType Joint;
/// <summary></summary>
public Vector3 JointPivot;
/// <summary></summary>
public Vector3 JointAxisOrAnchor;
/// <summary></summary>
public NameValue[] NameValues;
/// <summary></summary>
public ConstructionData PrimData;
/// <summary></summary>
public ObjectProperties Properties;
/// <summary>Objects physics engine propertis</summary>
public PhysicsProperties PhysicsProps;
/// <summary>Extra data about primitive</summary>
public object Tag;
/// <summary>Indicates if prim is attached to an avatar</summary>
public bool IsAttachment;
/// <summary>Number of clients referencing this prim</summary>
public int ActiveClients = 0;
#endregion Public Members
#region Properties
/// <summary>Uses basic heuristics to estimate the primitive shape</summary>
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
/// <summary>
/// Default constructor
/// </summary>
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);
}
/// <summary>
/// Packs PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew
/// parameters in to signed eight bit values
/// </summary>
/// <param name="pathTwist">Floating point parameter to pack</param>
/// <returns>Signed eight bit value containing the packed parameter</returns>
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;
}
/// <summary>
/// Unpacks PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew
/// parameters from signed eight bit integers to floating point values
/// </summary>
/// <param name="pathTwist">Signed eight bit value to unpack</param>
/// <returns>Unpacked floating point value</returns>
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
}
}