/* * 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.ComponentModel; 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] public class PrimObject { /// [XmlAttribute("pathtwistbegin"), DefaultValue(0)] public int PathTwistBegin; /// [XmlAttribute("pathend"), DefaultValue(0)] public float PathEnd; /// [XmlAttribute("profilebegin"), DefaultValue(0)] public float ProfileBegin; /// [XmlAttribute("pathradiusoffset"), DefaultValue(0)] public float PathRadiusOffset; /// [XmlAttribute("pathskew"), DefaultValue(0)] public float PathSkew; /// [XmlAttribute("profilecurve"), DefaultValue(0)] public uint ProfileCurve; /// [XmlAttribute("pathscalex"), DefaultValue(0)] public float PathScaleX; /// [XmlAttribute("pathscaley"), DefaultValue(0)] public float PathScaleY; /// [XmlAttribute("localid"), DefaultValue(0)] public uint LocalID; /// [XmlAttribute("parentid"), DefaultValue(0)] public uint ParentID; /// [XmlAttribute("material"), DefaultValue(0)] public uint Material; /// [XmlAttribute("name"), DefaultValue("")] public string Name = ""; /// [XmlAttribute("description"), DefaultValue("")] public string Description = ""; /// [XmlAttribute("pathshearx"), DefaultValue(0)] public float PathShearX; /// [XmlAttribute("pathsheary"), DefaultValue(0)] public float PathShearY; /// [XmlAttribute("pathtaperx"), DefaultValue(0)] public float PathTaperX; /// [XmlAttribute("pathtapery"), DefaultValue(0)] public float PathTaperY; /// [XmlAttribute("profileend"), DefaultValue(0)] public float ProfileEnd; /// [XmlAttribute("pathbegin"), DefaultValue(0)] public float PathBegin; /// [XmlAttribute("pathcurve"), DefaultValue(0)] public uint PathCurve; /// [XmlAttribute("pathtwist"), DefaultValue(0)] public int PathTwist; /// [XmlAttribute("profilehollow"), DefaultValue(0)] public uint ProfileHollow; /// [XmlAttribute("pathrevolutions"), DefaultValue(0)] public float PathRevolutions; /// [XmlAttribute("state"), DefaultValue(0)] public uint State; /// [XmlAttribute("text"), DefaultValue("")] public string Text = ""; /// [XmlAttribute("regionhandle"), DefaultValue(0)] public ulong RegionHandle; /// [XmlAttribute("flags"), DefaultValue(ObjectFlags.None)] 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; /// public LLVector3 Position = LLVector3.Zero; /// public LLVector3 Scale = LLVector3.Zero; /// public LLQuaternion Rotation = LLQuaternion.Identity; /// public TextureEntry Textures = new TextureEntry(); /// public TextureAnimation TextureAnim = new TextureAnimation(); /// public PrimFlexibleData Flexible = new PrimFlexibleData(); /// public PrimLightData Light = new PrimLightData(); /// 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 /// [Serializable] public class PrimFlexibleData { /// [XmlAttribute("softness"), DefaultValue(0)] public int Softness; /// [XmlAttribute("gravity"), DefaultValue(0)] public float Gravity; /// [XmlAttribute("drag"), DefaultValue(0)] public float Drag; /// [XmlAttribute("wind"), DefaultValue(0)] public float Wind; /// [XmlAttribute("tension"), DefaultValue(0)] public float Tension; /// 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 /// [Serializable] public class PrimLightData { /// [XmlAttribute("red"), DefaultValue(0)] public byte R; /// [XmlAttribute("green"), DefaultValue(0)] public byte G; /// [XmlAttribute("blue"), DefaultValue(0)] public byte B; /// [XmlAttribute("intensity"), DefaultValue(0)] public float Intensity; /// [XmlAttribute("radius"), DefaultValue(0)] public float Radius; /// [XmlAttribute("falloff"), DefaultValue(0)] 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); } } }