/* * 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.Net; using System.Xml.Serialization; namespace libsecondlife { /// /// A 128-bit Universally Unique Identifier, used throughout the Second /// Life networking protocol /// [Serializable] public class LLUUID : IXmlSerializable { /// Get a byte array of the 16 raw bytes making up the UUID public byte[] Data { get { return data; } } /// The 16 bytes that make up the UUID protected byte[] data; /// /// Default constructor /// public LLUUID() { data = new byte[16]; } /// /// Constructor that takes a string UUID representation /// /// A string representation of a UUID, case /// insensitive and can either be hyphenated or non-hyphenated /// LLUUID("11f8aa9c-b071-4242-836b-13b7abe0d489") public LLUUID(string val) { data = StringToBytes(val); } /// /// Constructor that takes a byte array containing a UUID /// /// Byte array containing a 16 byte UUID /// Beginning offset in the array public LLUUID(byte[] byteArray, int pos) { data = new byte[16]; Array.Copy(byteArray, pos, data, 0, 16); } /// /// Constructor that takes an unsigned 64-bit unsigned integer to /// convert to a UUID /// /// 64-bit unsigned integer to convert to a UUID public LLUUID(ulong val) { data = new byte[16]; byte[] bytes = BitConverter.GetBytes(val); Array.Copy(bytes, data, bytes.Length); } /// /// Returns the raw bytes for this UUID /// /// A 16 byte array containing this UUID public byte[] GetBytes() { return data; } /// /// Calculate an LLCRC (cyclic redundancy check) for this LLUUID /// /// The CRC checksum for this LLUUID public uint CRC() { uint retval = 0; retval += (uint)((Data[3] << 24) + (Data[2] << 16) + (Data[1] << 8) + Data[0]); retval += (uint)((Data[7] << 24) + (Data[6] << 16) + (Data[5] << 8) + Data[4]); retval += (uint)((Data[11] << 24) + (Data[10] << 16) + (Data[9] << 8) + Data[8]); retval += (uint)((Data[15] << 24) + (Data[14] << 16) + (Data[13] << 8) + Data[12]); return retval; } /// /// Combine two UUIDs together by taking the MD5 hash of a byte array /// containing both UUIDs /// /// The UUID to combine with this one /// The UUID product of the combination public LLUUID Combine(LLUUID other) { // Build the buffer to MD5 byte[] input = new byte[32]; Array.Copy(data, input, 16); Array.Copy(other.Data, 0, input, 16, 16); return new LLUUID(Helpers.MD5Builder.ComputeHash(input), 0); } /// /// Parse the bytes for a LLUUID from a string /// /// String containing 32 (or 36 with hyphens) character UUID /// protected static byte[] StringToBytes(string val) { if (val.Length == 36) val = val.Replace("-", ""); if (val.Length != 32) throw new Exception("Malformed LLUUID: " + val); byte[] parseData = new byte[16]; for (int i = 0; i < 16; ++i) { parseData[i] = Convert.ToByte(val.Substring(i * 2, 2), 16); } return parseData; } /// /// Generate a LLUUID from a string /// /// A string representation of a UUID, case /// insensitive and can either be hyphenated or non-hyphenated /// LLUUID.Parse("11f8aa9c-b071-4242-836b-13b7abe0d489") public static LLUUID Parse(string val) { return new LLUUID(StringToBytes(val), 0); } /// /// Generate a LLUUID from a string /// /// A string representation of a UUID, case /// insensitive and can either be hyphenated or non-hyphenated /// Will contain the parsed UUID if successful, /// otherwise null /// True if the string was successfully parse, otherwise false /// LLUUID.TryParse("11f8aa9c-b071-4242-836b-13b7abe0d489", result) public static bool TryParse(string val, out LLUUID result) { try { result = Parse(val); return true; } catch (Exception) { result = null; return false; } } /// /// /// /// public static LLUUID Random() { return new LLUUID(Guid.NewGuid().ToByteArray(), 0); } /// /// Required implementation for XML serialization /// /// null public System.Xml.Schema.XmlSchema GetSchema() { return null; } /// /// Deserializes an XML UUID /// /// XmlReader containing the UUID to deserialize public void ReadXml(System.Xml.XmlReader reader) { string val = reader.ReadString(); if (val.Length > 0) { if (val.Length == 36) val = val.Replace("-", ""); if (val.Length != 32) throw new Exception("Malformed data passed to LLUUID constructor: " + val); for (int i = 0; i < 16; ++i) { data[i] = Convert.ToByte(val.Substring(i * 2, 2), 16); } } reader.Skip(); } /// /// Serialize this UUID to XML /// /// XmlWriter to serialize to public void WriteXml(System.Xml.XmlWriter writer) { if (this != LLUUID.Zero) writer.WriteString(this.ToString()); } /// /// Return a hash code for this UUID, used by .NET for hash tables /// /// An integer composed of all the UUID bytes XORed together public override int GetHashCode() { int hash = data[0]; for (int i = 1; i < 16; i++) { hash ^= data[i]; } return hash; } /// /// Comparison function /// /// An object to compare to this UUID /// False if the object is not an LLUUID, true if it is and /// byte for byte identical to this public override bool Equals(object o) { if (!(o is LLUUID)) return false; LLUUID uuid = (LLUUID)o; for (int i = 0; i < 16; ++i) { if (Data[i] != uuid.Data[i]) return false; } return true; } /// /// Equals operator /// /// First LLUUID for comparison /// Second LLUUID for comparison /// True if the UUIDs are byte for byte equal, otherwise false public static bool operator==(LLUUID lhs, LLUUID rhs) { // If both are null, or both are same instance, return true if (System.Object.ReferenceEquals(lhs, rhs)) { return true; } // If one is null, but not both, return false. if (((object)lhs == null) || ((object)rhs == null)) { return false; } for (int i = 0; i < 16; ++i) { if (lhs.Data[i] != rhs.Data[i]) return false; } return true; } /// /// Not equals operator /// /// First LLUUID for comparison /// Second LLUUID for comparison /// True if the UUIDs are not equal, otherwise true public static bool operator!=(LLUUID lhs, LLUUID rhs) { return !(lhs == rhs); } /// /// XOR operator /// /// First LLUUID /// Second LLUUID /// A UUID that is a XOR combination of the two input UUIDs public static LLUUID operator ^(LLUUID lhs, LLUUID rhs) { LLUUID returnUUID = new LLUUID(); for (int count = 0; count < returnUUID.Data.Length; count++) { returnUUID.Data[count] = (byte)(lhs.Data[count] ^ rhs.Data[count]); } return returnUUID; } /// /// String typecasting operator /// /// A UUID in string form. Case insensitive, /// hyphenated or non-hyphenated /// A UUID built from the string representation public static implicit operator LLUUID(string val) { return new LLUUID(val); } /// /// Get a string representation of this UUID /// /// A string representation of this UUID, lowercase and /// without hyphens /// 11f8aa9cb0714242836b13b7abe0d489 public override string ToString() { string uuid = String.Empty; for (int i = 0; i < 16; ++i) { uuid += Data[i].ToString("x2"); } return uuid; } /// /// Get a hyphenated string representation of this UUID /// /// A string representation of this UUID, lowercase and /// with hyphens /// 11f8aa9c-b071-4242-836b-13b7abe0d489 public string ToStringHyphenated() { string uuid = String.Empty; for (int i = 0; i < 16; ++i) { uuid += Data[i].ToString("x2"); } uuid = uuid.Insert(20,"-"); uuid = uuid.Insert(16,"-"); uuid = uuid.Insert(12,"-"); uuid = uuid.Insert(8,"-"); return uuid; } /// /// An LLUUID with a value of all zeroes /// public static readonly LLUUID Zero = new LLUUID(); } /// /// A three-dimensional vector with floating-point values /// [Serializable] public struct LLVector3 { /// X value [XmlAttribute("x"), DefaultValue(0)] public float X; /// Y value [XmlAttribute("y"), DefaultValue(0)] public float Y; /// Z value [XmlAttribute("z"), DefaultValue(0)] public float Z; /// /// Constructor, builds a single-precision vector from a /// double-precision one /// /// A double-precision vector public LLVector3(LLVector3d vector) { X = (float)vector.X; Y = (float)vector.Y; Z = (float)vector.Z; } /// /// Constructor, builds a vector from a byte array /// /// Byte array containing a 12 byte vector /// Beginning position in the byte array public LLVector3(byte[] byteArray, int pos) { if (!BitConverter.IsLittleEndian) { byte[] newArray = new byte[12]; Array.Copy(byteArray, pos, newArray, 0, 12); Array.Reverse(newArray, 0, 4); Array.Reverse(newArray, 4, 4); Array.Reverse(newArray, 8, 4); X = BitConverter.ToSingle(newArray, 0); Y = BitConverter.ToSingle(newArray, 4); Z = BitConverter.ToSingle(newArray, 8); } else { X = BitConverter.ToSingle(byteArray, pos); Y = BitConverter.ToSingle(byteArray, pos + 4); Z = BitConverter.ToSingle(byteArray, pos + 8); } } /// /// Constructor, builds a vector for individual float values /// /// X value /// Y value /// Z value public LLVector3(float x, float y, float z) { X = x; Y = y; Z = z; } /// /// Returns the raw bytes for this vector /// /// A 12 byte array containing X, Y, and Z public byte[] GetBytes() { byte[] byteArray = new byte[12]; Array.Copy(BitConverter.GetBytes(X), 0, byteArray, 0, 4); Array.Copy(BitConverter.GetBytes(Y), 0, byteArray, 4, 4); Array.Copy(BitConverter.GetBytes(Z), 0, byteArray, 8, 4); if(!BitConverter.IsLittleEndian) { Array.Reverse(byteArray, 0, 4); Array.Reverse(byteArray, 4, 4); Array.Reverse(byteArray, 8, 4); } return byteArray; } /// /// Get the distance to point /// /// /// public double GetDistanceTo(LLVector3 Pt) { return Math.Sqrt(((X - Pt.X) * (X - Pt.X)) + ((Y - Pt.Y) * (Y - Pt.Y)) + ((Z - Pt.Z) * (Z - Pt.Z))); } /// /// Get a formatted string representation of the vector /// /// A string representation of the vector, similar to the LSL /// vector to string conversion in Second Life public override string ToString() { return "<" + X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ">"; } /// /// A hash of the vector, used by .NET for hash tables /// /// The hashes of the individual components XORed together public override int GetHashCode() { return (X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode()); } /// /// Generate an LLVector3 from a string /// /// A string representation of a 3D vector, enclosed /// in arrow brackets and separated by commas public static LLVector3 Parse(string val) { char[] splitChar = { ',', ' ' }; string[] split = val.Replace("<","").Replace(">","").Split(splitChar, StringSplitOptions.RemoveEmptyEntries); return new LLVector3(float.Parse(split[0].Trim()), float.Parse(split[1].Trim()), float.Parse(split[2].Trim())); } public static bool TryParse(string val, out LLVector3 result) { try { result = Parse(val); return true; } catch (Exception) { result = new LLVector3(); return false; } } /// /// /// /// /// public override bool Equals(object o) { if (!(o is LLVector3)) return false; LLVector3 vector = (LLVector3)o; return (X == vector.X && Y == vector.Y && Z == vector.Z); } /// /// /// /// /// /// public static bool operator==(LLVector3 lhs, LLVector3 rhs) { // If both are null, or both are same instance, return true if (System.Object.ReferenceEquals(lhs, rhs)) { return true; } // If one is null, but not both, return false. if (((object)lhs == null) || ((object)rhs == null)) { return false; } return (lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z); } /// /// /// /// /// /// public static bool operator!=(LLVector3 lhs, LLVector3 rhs) { return !(lhs == rhs); } public static LLVector3 operator +(LLVector3 lhs, LLVector3 rhs) { return new LLVector3(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z); } /// /// /// /// /// /// public static LLVector3 operator -(LLVector3 lhs, LLVector3 rhs) { return new LLVector3(lhs.X - rhs.X,lhs.Y - rhs.Y, lhs.Z - rhs.Z); } public static LLVector3 operator *(LLVector3 vec, LLQuaternion quat) { LLQuaternion vq = new LLQuaternion(vec.X, vec.Y, vec.Z, 0); LLQuaternion nq = new LLQuaternion(-quat.X, -quat.Y, -quat.Z, quat.W); LLQuaternion result = (quat * vq) * nq; return new LLVector3(result.X, result.Y, result.Z); } /// /// An LLVector3 with a value of 0,0,0 /// public readonly static LLVector3 Zero = new LLVector3(); } /// /// A double-precision three-dimensional vector /// [Serializable] public struct LLVector3d { /// X value [XmlAttribute("x"), DefaultValue(0)] public double X; /// Y value [XmlAttribute("y"), DefaultValue(0)] public double Y; /// Z value [XmlAttribute("z"), DefaultValue(0)] public double Z; /// /// /// /// /// /// public LLVector3d(double x, double y, double z) { X = x; Y = y; Z = z; } /// /// Create a double precision vector from a float vector /// /// public LLVector3d(LLVector3 llv3) { X = llv3.X; Y = llv3.Y; Z = llv3.Z; } /// /// /// /// /// public LLVector3d(byte[] byteArray, int pos) { if (!BitConverter.IsLittleEndian) { byte[] newArray = new byte[24]; Array.Copy(byteArray, pos, newArray, 0, 24); Array.Reverse(newArray, 0, 8); Array.Reverse(newArray, 8, 8); Array.Reverse(newArray, 16, 8); X = BitConverter.ToDouble(newArray, 0); Y = BitConverter.ToDouble(newArray, 8); Z = BitConverter.ToDouble(newArray, 16); } else { X = BitConverter.ToDouble(byteArray, pos); Y = BitConverter.ToDouble(byteArray, pos + 8); Z = BitConverter.ToDouble(byteArray, pos + 16); } } /// /// A hash of the vector, used by .NET for hash tables /// /// The hashes of the individual components XORed together public override int GetHashCode() { return (X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode()); } /// /// /// /// /// public override bool Equals(object o) { if (!(o is LLVector3d)) return false; LLVector3d vector = (LLVector3d)o; return (X == vector.X && Y == vector.Y && Z == vector.Z); } /// /// /// /// /// /// public static bool operator ==(LLVector3d lhs, LLVector3d rhs) { // If both are null, or both are same instance, return true if (System.Object.ReferenceEquals(lhs, rhs)) { return true; } // If one is null, but not both, return false. if (((object)lhs == null) || ((object)rhs == null)) { return false; } return (lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z); } /// /// /// /// /// /// public static bool operator !=(LLVector3d lhs, LLVector3d rhs) { return !(lhs == rhs); } /// /// Get the distance to point /// /// /// public double GetDistanceTo(LLVector3d Pt) { return Math.Sqrt(((X - Pt.X) * (X - Pt.X)) + ((Y - Pt.Y) * (Y - Pt.Y)) + ((Z - Pt.Z) * (Z - Pt.Z))); } /// /// /// /// public byte[] GetBytes() { byte[] byteArray = new byte[24]; Array.Copy(BitConverter.GetBytes(X), 0, byteArray, 0, 8); Array.Copy(BitConverter.GetBytes(Y), 0, byteArray, 8, 8); Array.Copy(BitConverter.GetBytes(Z), 0, byteArray, 16, 8); if(!BitConverter.IsLittleEndian) { Array.Reverse(byteArray, 0, 8); Array.Reverse(byteArray, 8, 8); Array.Reverse(byteArray, 16, 8); } return byteArray; } /// /// /// /// public override string ToString() { return "<" + X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ">"; } /// /// An LLVector3d with a value of 0,0,0 /// public static readonly LLVector3d Zero = new LLVector3d(); } /// /// A four-dimensional vector /// [Serializable] public struct LLVector4 { /// [XmlAttribute("x"), DefaultValue(0)] public float X; /// [XmlAttribute("y"), DefaultValue(0)] public float Y; /// [XmlAttribute("z"), DefaultValue(0)] public float Z; /// [XmlAttribute("s"), DefaultValue(0)] public float S; /// /// /// /// /// public LLVector4(byte[] byteArray, int pos) { if (!BitConverter.IsLittleEndian) { byte[] newArray = new byte[16]; Array.Copy(byteArray, pos, newArray, 0, 16); Array.Reverse(newArray, 0, 4); Array.Reverse(newArray, 4, 4); Array.Reverse(newArray, 8, 4); Array.Reverse(newArray, 12, 4); X = BitConverter.ToSingle(newArray, 0); Y = BitConverter.ToSingle(newArray, 4); Z = BitConverter.ToSingle(newArray, 8); S = BitConverter.ToSingle(newArray, 12); } else { X = BitConverter.ToSingle(byteArray, pos); Y = BitConverter.ToSingle(byteArray, pos + 4); Z = BitConverter.ToSingle(byteArray, pos + 8); S = BitConverter.ToSingle(byteArray, pos + 12); } } /// /// /// /// public byte[] GetBytes() { byte[] byteArray = new byte[16]; Array.Copy(BitConverter.GetBytes(X), 0, byteArray, 0, 4); Array.Copy(BitConverter.GetBytes(Y), 0, byteArray, 4, 4); Array.Copy(BitConverter.GetBytes(Z), 0, byteArray, 8, 4); Array.Copy(BitConverter.GetBytes(S), 0, byteArray, 12, 4); if(!BitConverter.IsLittleEndian) { Array.Reverse(byteArray, 0, 4); Array.Reverse(byteArray, 4, 4); Array.Reverse(byteArray, 8, 4); Array.Reverse(byteArray, 12, 4); } return byteArray; } /// /// /// /// public override string ToString() { return "<" + X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ", " + S.ToString() + ">"; } /// /// An LLVector4 with a value of 0,0,0,0 /// public readonly static LLVector4 Zero = new LLVector4(); } /// /// An 8-bit color structure including an alpha channel /// [Serializable] public struct LLColor { /// Red [XmlAttribute("r"), DefaultValue(0)] public byte R; /// Green [XmlAttribute("g"), DefaultValue(0)] public byte G; /// Blue [XmlAttribute("b"), DefaultValue(0)] public byte B; /// Alpha [XmlAttribute("a"), DefaultValue(0)] public byte A; /// /// /// /// /// /// /// public LLColor(byte r, byte g, byte b, byte a) { R = r; G = g; B = b; A = a; } /// /// /// /// /// public LLColor(byte[] byteArray, int pos) { R = byteArray[pos]; G = byteArray[pos + 1]; B = byteArray[pos + 2]; A = byteArray[pos + 3]; } /// /// /// /// public byte[] GetBytes() { byte[] byteArray = new byte[4]; byteArray[0] = R; byteArray[1] = G; byteArray[2] = B; byteArray[3] = A; return byteArray; } /// /// /// /// public override string ToString() { return "<" + R.ToString() + ", " + G.ToString() + ", " + B.ToString() + ", " + A.ToString() + ">"; } /// /// An LLColor with a value of 0,0,0,255 /// public readonly static LLColor Black = new LLColor(0, 0, 0, 255); } /// /// A quaternion, used for rotations /// [Serializable] public struct LLQuaternion { /// X value [XmlAttribute("x"), DefaultValue(0)] public float X; /// Y value [XmlAttribute("y"), DefaultValue(0)] public float Y; /// Z value [XmlAttribute("z"), DefaultValue(0)] public float Z; /// W value [XmlAttribute("w"), DefaultValue(0)] public float W; /// /// Build a quaternion object from a byte array /// /// The source byte array /// Offset in the byte array to start reading at /// Whether the source data is normalized or /// not. If this is true 12 bytes will be read, otherwise 16 bytes will /// be read. public LLQuaternion(byte[] byteArray, int pos, bool normalized) { if (!normalized) { if (!BitConverter.IsLittleEndian) { byte[] newArray = new byte[16]; Array.Copy(byteArray, pos, newArray, 0, 16); Array.Reverse(newArray, 0, 4); Array.Reverse(newArray, 4, 4); Array.Reverse(newArray, 8, 4); Array.Reverse(newArray, 12, 4); X = BitConverter.ToSingle(newArray, 0); Y = BitConverter.ToSingle(newArray, 4); Z = BitConverter.ToSingle(newArray, 8); W = BitConverter.ToSingle(newArray, 12); } else { X = BitConverter.ToSingle(byteArray, pos); Y = BitConverter.ToSingle(byteArray, pos + 4); Z = BitConverter.ToSingle(byteArray, pos + 8); W = BitConverter.ToSingle(byteArray, pos + 12); } } else { if (!BitConverter.IsLittleEndian) { byte[] newArray = new byte[12]; Array.Copy(byteArray, pos, newArray, 0, 12); Array.Reverse(newArray, 0, 4); Array.Reverse(newArray, 4, 4); Array.Reverse(newArray, 8, 4); X = BitConverter.ToSingle(newArray, 0); Y = BitConverter.ToSingle(newArray, 4); Z = BitConverter.ToSingle(newArray, 8); } else { X = BitConverter.ToSingle(byteArray, pos); Y = BitConverter.ToSingle(byteArray, pos + 4); Z = BitConverter.ToSingle(byteArray, pos + 8); } float xyzsum = 1 - X * X - Y * Y - Z * Z; W = (xyzsum > 0) ? (float)Math.Sqrt(xyzsum) : 0; } } /// /// Build a quaternion from normalized float values /// /// X value from -1.0 to 1.0 /// Y value from -1.0 to 1.0 /// Z value from -1.0 to 1.0 public LLQuaternion(float x, float y, float z) { X = x; Y = y; Z = z; float xyzsum = 1 - X * X - Y * Y - Z * Z; W = (xyzsum > 0) ? (float)Math.Sqrt(xyzsum) : 0; } /// /// Build a quaternion from individual float values /// /// X value /// Y value /// Z value /// W value public LLQuaternion(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; } /// /// Normalize this quaternion and serialize it to a byte array /// /// A 12 byte array containing normalized X, Y, and Z floating /// point values in order using little endian byte ordering public byte[] GetBytes() { byte[] bytes = new byte[12]; float norm; norm = (float)Math.Sqrt(X*X + Y*Y + Z*Z + W*W); if (norm != 0) { norm = 1 / norm; Array.Copy(BitConverter.GetBytes(norm * X), 0, bytes, 0, 4); Array.Copy(BitConverter.GetBytes(norm * Y), 0, bytes, 4, 4); Array.Copy(BitConverter.GetBytes(norm * Z), 0, bytes, 8, 4); if (!BitConverter.IsLittleEndian) { Array.Reverse(bytes, 0, 4); Array.Reverse(bytes, 4, 4); Array.Reverse(bytes, 8, 4); } } else { throw new Exception("Quaternion " + this.ToString() + " normalized to zero"); } return bytes; } /// /// /// /// public override int GetHashCode() { return (X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode()); } /// /// /// /// /// public override bool Equals(object o) { if (!(o is LLQuaternion)) return false; LLQuaternion quaternion = (LLQuaternion)o; return X == quaternion.X && Y == quaternion.Y && Z == quaternion.Z && W == quaternion.W; } /// /// Comparison operator /// /// /// /// public static bool operator ==(LLQuaternion lhs, LLQuaternion rhs) { // If both are null, or both are same instance, return true if (System.Object.ReferenceEquals(lhs, rhs)) { return true; } // If one is null, but not both, return false. if (((object)lhs == null) || ((object)rhs == null)) { return false; } // Return true if the fields match: return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z && lhs.W == rhs.W; } /// /// Not comparison operator /// /// /// /// public static bool operator !=(LLQuaternion lhs, LLQuaternion rhs) { return !(lhs == rhs); } /// /// Multiplication operator /// /// /// /// public static LLQuaternion operator *(LLQuaternion lhs, LLQuaternion rhs) { LLQuaternion ret = new LLQuaternion(); ret.W = lhs.W * rhs.W - lhs.X * rhs.X - lhs.Y * rhs.Y - lhs.Z * rhs.Z; ret.X = lhs.W * rhs.X + lhs.X * rhs.W + lhs.Y * rhs.Z - lhs.Z * rhs.Y; ret.Y = lhs.W * rhs.Y + lhs.Y * rhs.W + lhs.Z * rhs.X - lhs.X * rhs.Z; ret.Z = lhs.W * rhs.Z + lhs.Z * rhs.W + lhs.X * rhs.Y - lhs.Y * rhs.X; return ret; } /// /// /// /// public override string ToString() { return "<" + X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ", " + W.ToString() + ">"; } /// /// An LLQuaternion with a value of 0,0,0,1 /// public readonly static LLQuaternion Identity = new LLQuaternion(0, 0, 0, 1); } }