/* * Copyright (c) 2006, Second Life Reverse Engineering Team * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the Second Life Reverse Engineering Team nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Xml; using System.Xml.Serialization; namespace libsecondlife { /// /// Primary parameters for primitives such as Physics Enabled or Phantom /// [Flags] public enum ObjectFlags { /// None of the primary flags are enabled None = 0, /// Whether physics are enabled for this object Physics = 1 << 0, /// CreateSelected = 1 << 1, Unknown1 = 1 << 2, Unknown2 = 1 << 3, Unknown3 = 1 << 4, Unknown4 = 1 << 5, Script = 1 << 6, /// Whether this object contains an active touch script Touch = 1 << 7, Unknown5 = 1 << 8, /// Whether this object can receive payments Money = 1 << 9, /// Whether this object is phantom (no collisions) Phantom = 1 << 10, Unknown6 = 1 << 11, Unknown7 = 1 << 12, Unknown8 = 1 << 13, Unknown9 = 1 << 14, Unknown10 = 1 << 15, Unknown11 = 1 << 16, Unknown12 = 1 << 17, Unknown13 = 1 << 18, Unknown14 = 1 << 19, Unknown15 = 1 << 20, Unknown16 = 1 << 21, Unknown17 = 1 << 22, Unknown18 = 1 << 23, Unknown19 = 1 << 24, Unknown20 = 1 << 25, Unknown21 = 1 << 26, Unknown22 = 1 << 27, Unknown23 = 1 << 28, Unknown24 = 1 << 29, /// Whether this object is temporary Temp = 1 << 30, Unknown25 = 1 << 31, Unknown26 = 1 << 32 } /// /// Extra parameters for primitives, these flags are for features that have /// been added after the original ObjectFlags that has all eight bits /// reserved already /// public enum ExtraParamType : ushort { /// Whether this object has flexible parameters [XmlEnum("Flexible")] Flexible = 0x10, /// Whether this object has light parameters [XmlEnum("Light")] Light = 0x20 } /// /// Sweet delicious prims. /// [Serializable, XmlRoot("prim")] public class PrimObject { /// [XmlAttribute("pathtwistbegin")] public int PathTwistBegin = 0; /// [XmlAttribute("pathend")] public float PathEnd = 0; /// [XmlAttribute("profilebegin")] public float ProfileBegin = 0; /// [XmlAttribute("pathradiusoffset")] public float PathRadiusOffset = 0; /// [XmlAttribute("pathskew")] public float PathSkew = 0; /// [XmlAttribute("profilecurve")] public uint ProfileCurve = 0; /// [XmlAttribute("pathscalex")] public float PathScaleX = 0; /// [XmlAttribute("pathscaley")] public float PathScaleY = 0; /// [XmlAttribute("localid")] public uint LocalID = 0; /// [XmlAttribute("parentid")] public uint ParentID = 0; /// [XmlAttribute("material")] public uint Material = 0; /// [XmlAttribute("name")] public string Name = ""; /// [XmlAttribute("description")] public string Description = ""; /// [XmlAttribute("pathshearx")] public float PathShearX = 0; /// [XmlAttribute("pathsheary")] public float PathShearY = 0; /// [XmlAttribute("pathtaperx")] public float PathTaperX = 0; /// [XmlAttribute("pathtapery")] public float PathTaperY = 0; /// [XmlAttribute("profileend")] public float ProfileEnd = 0; /// [XmlAttribute("pathbegin")] public float PathBegin = 0; /// [XmlAttribute("pathcurve")] public uint PathCurve = 0; /// [XmlAttribute("pathtwist")] public int PathTwist = 0; /// [XmlAttribute("profilehollow")] public uint ProfileHollow = 0; /// [XmlAttribute("pathrevolutions")] public float PathRevolutions = 0; /// [XmlAttribute("state")] public uint State; /// [XmlAttribute("text")] public string Text; /// [XmlAttribute("regionhandle")] public ulong RegionHandle; /// [XmlAttribute("flags")] public ObjectFlags Flags; /// [XmlIgnore] public ObjectManager.PCode PCode = ObjectManager.PCode.Prim; /// [XmlElement("id")] public LLUUID ID = LLUUID.Zero; /// [XmlElement("groupid")] public LLUUID GroupID = LLUUID.Zero; /// [XmlElement("position")] public LLVector3 Position = LLVector3.Zero; /// [XmlElement("scale")] public LLVector3 Scale = LLVector3.Zero; /// [XmlElement("rotation")] public LLQuaternion Rotation = LLQuaternion.Identity; /// [XmlElement("textures")] public TextureEntry Textures = new TextureEntry(); /// [XmlElement("textureanimation")] public TextureAnimation TextureAnim = new TextureAnimation(); /// [XmlElement("flexible")] public PrimFlexibleData Flexible = new PrimFlexibleData(); /// [XmlElement("light")] public PrimLightData Light = new PrimLightData(); /// [XmlElement("particles")] public ParticleSystem ParticleSys = new ParticleSystem(); /// /// Default constructor, zeroes out or sets default prim parameters /// public PrimObject() { } /// /// /// /// /// public static byte PathScaleByte(float pathScale) { // Y = 100 + 100X int scale = (int)Math.Round(100.0f * pathScale); return (byte)(100 + scale); } /// /// /// /// /// public static float PathScaleFloat(byte pathScale) { // Y = -1 + 0.01X return (float)Math.Round((double)pathScale * 0.01d - 1.0d, 6); } /// /// /// /// /// public static byte PathShearByte(float pathShear) { // Y = 256 + 100X int shear = (int)Math.Round(100.0f * pathShear); shear += 256; return (byte)(shear % 256); } /// /// /// /// /// public static float PathShearFloat(byte pathShear) { if (pathShear == 0) return 0.0f; if (pathShear > 150) { // Negative value return ((float)pathShear - 256.0f) / 100.0f; } else { // Positive value return (float)pathShear / 100.0f; } } /// /// /// /// /// public static byte ProfileBeginByte(float profileBegin) { // Y = ceil (200X) return (byte)Math.Round(200.0f * profileBegin); } /// /// /// /// /// public static float ProfileBeginFloat(byte profileBegin) { // Y = 0.005X return (float)Math.Round((double)profileBegin * 0.005d, 6); } /// /// /// /// /// public static byte ProfileEndByte(float profileEnd) { // Y = 200 - 200X int end = (int)Math.Round(200.0d * (double)profileEnd); return (byte)(200 - end); } /// /// /// /// /// public static float ProfileEndFloat(byte profileEnd) { // Y = 1 - 0.005X return (float)Math.Round(1.0d - ((double)profileEnd * 0.005d), 6); } /// /// /// /// /// public static byte PathBeginByte(float pathBegin) { // Y = 100X return (byte)Convert.ToInt16(100.0f * pathBegin); } /// /// /// /// /// public static float PathBeginFloat(byte pathBegin) { // Y = X / 100 return (float)pathBegin / 100.0f; } /// /// /// /// /// public static byte PathEndByte(float pathEnd) { // Y = 100 - 100X int end = (int)Math.Round(100.0f * pathEnd); return (byte)(100 - end); } /// /// /// /// /// public static float PathEndFloat(byte pathEnd) { // Y = 1 - X / 100 return 1.0f - (float)pathEnd / 100.0f; } /// /// /// /// /// public static sbyte PathRadiusOffsetByte(float pathRadiusOffset) { // Y = 256 + 100X return (sbyte)PathShearByte(pathRadiusOffset); } /// /// /// /// /// public static float PathRadiusOffsetFloat(sbyte pathRadiusOffset) { // Y = X / 100 return (float)pathRadiusOffset / 100.0f; } /// /// /// /// /// public static byte PathRevolutionsByte(float pathRevolutions) { // Y = 66.5X - 66 int revolutions = (int)Math.Round(66.5d * (double)pathRevolutions); return (byte)(revolutions - 66); } /// /// /// /// /// public static float PathRevolutionsFloat(byte pathRevolutions) { // Y = 1 + 0.015X return (float)Math.Round(1.0d + (double)pathRevolutions * 0.015d, 6); } /// /// /// /// /// public static sbyte PathSkewByte(float pathSkew) { return PathTaperByte(pathSkew); } /// /// /// /// /// public static float PathSkewFloat(sbyte pathSkew) { return PathTaperFloat(pathSkew); } /// /// /// /// /// public static sbyte PathTaperByte(float pathTaper) { // Y = 256 + 100X return (sbyte)PathShearByte(pathTaper); } /// /// /// /// /// public static float PathTaperFloat(sbyte pathTaper) { return (float)pathTaper / 100.0f; } /// /// /// /// /// /// public int SetExtraParamsFromBytes(byte[] data, int pos) { int i = pos; int totalLength = 1; if (data.Length == 0) return 0; byte extraParamCount = data[i++]; for (int k = 0; k < extraParamCount; k++) { ExtraParamType type = (ExtraParamType)(data[i++] + (data[i++] << 8)); uint paramLength = (uint)(data[i++] + (data[i++] << 8) + (data[i++] << 16) + (data[i++] << 24)); if (type == ExtraParamType.Flexible) { Flexible = new PrimFlexibleData(data, i); } else if (type == ExtraParamType.Light) { Light = new PrimLightData(data, i); } i += (int)paramLength; totalLength += (int)paramLength + 6; } return totalLength; } public override string ToString() { string output = ""; output += (Name.Length != 0) ? Name : "Unnamed"; output += ": " + ((Description.Length != 0) ? Description : "No description") + Environment.NewLine; output += "ID: " + ID + ", "; output += "GroupID: " + GroupID + ", "; output += "ParentID: " + ParentID + ", "; output += "LocalID: " + LocalID + ", "; output += "Flags: " + Flags + ", "; output += "State: " + State + ", "; output += "PCode: " + PCode + ", "; output += "Material: " + Material + ", "; return output; } public void ToXml(XmlWriter xmlWriter) { //XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); //ns.Add("", ""); XmlSerializer serializer = new XmlSerializer(typeof(PrimObject)); serializer.Serialize(xmlWriter, this); } public static PrimObject FromXml(XmlReader xmlReader) { XmlSerializer serializer = new XmlSerializer(typeof(PrimObject)); return (PrimObject)serializer.Deserialize(xmlReader); } } /// /// OMG Flexi /// public class PrimFlexibleData { /// [XmlAttribute("softness")] public int Softness; /// [XmlAttribute("gravity")] public float Gravity; /// [XmlAttribute("drag")] public float Drag; /// [XmlAttribute("wind")] public float Wind; /// [XmlAttribute("tension")] public float Tension; /// [XmlElement("force")] public LLVector3 Force = LLVector3.Zero; /// /// /// public PrimFlexibleData() { } /// /// /// /// /// public PrimFlexibleData(byte[] data, int pos) { FromBytes(data, pos); } /// /// /// /// public byte[] GetBytes() { byte[] data = new byte[16]; int i = 0; data[i] = (byte)((Softness & 2) << 6); data[i + 1] = (byte)((Softness & 1) << 7); data[i++] |= (byte)((byte)(Tension * 10.0f) & 0x7F); data[i++] |= (byte)((byte)(Drag * 10.0f) & 0x7F); data[i++] = (byte)((Gravity + 10.0f) * 10.0f); data[i++] = (byte)(Wind * 10.0f); Force.GetBytes().CopyTo(data, i); return data; } private void FromBytes(byte[] data, int pos) { int i = pos; Softness = ((data[i] & 0x80) >> 6) | ((data[i + 1] & 0x80) >> 7); Tension = (data[i++] & 0x7F) / 10.0f; Drag = (data[i++] & 0x7F) / 10.0f; Gravity = (data[i++] / 10.0f) - 10.0f; Wind = data[i++] / 10.0f; Force = new LLVector3(data, i); } } /// /// Information on the light property associated with a prim /// public class PrimLightData { /// [XmlAttribute("red")] public byte R; /// [XmlAttribute("green")] public byte G; /// [XmlAttribute("blue")] public byte B; /// [XmlAttribute("intensity")] public float Intensity; /// [XmlAttribute("radius")] public float Radius; /// [XmlAttribute("falloff")] public float Falloff; /// /// /// public PrimLightData() { } /// /// /// /// /// public PrimLightData(byte[] data, int pos) { FromBytes(data, pos); } /// /// /// /// public byte[] GetBytes() { byte[] data = new byte[16]; int i = 0; data[i++] = R; data[i++] = G; data[i++] = B; data[i++] = (byte)(Intensity * 255.0f); BitConverter.GetBytes(Radius).CopyTo(data, i); BitConverter.GetBytes(Falloff).CopyTo(data, i + 8); if (!BitConverter.IsLittleEndian) { Array.Reverse(data, i, 4); Array.Reverse(data, i + 8, 4); } return data; } private void FromBytes(byte[] data, int pos) { int i = pos; R = data[i++]; G = data[i++]; B = data[i++]; Intensity = data[i++] / 255.0f; if (!BitConverter.IsLittleEndian) { Array.Reverse(data, i, 4); Array.Reverse(data, i + 8, 4); } Radius = BitConverter.ToSingle(data, i); Falloff = BitConverter.ToSingle(data, i + 8); } } }