/*
* Copyright (c) 2006-2008, 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
{
///////////////////////////////////////////////////////////////////////////
//
// The 3D math makes several assumptions to maintain consistency across
// types. The math system is right-handed and the matrices are row-order.
// Z axis is up.
//
// X = At = Roll = Bank
// Y = Left = Pitch = Attitude
// Z = Up = Yaw = Heading
//
///////////////////////////////////////////////////////////////////////////
///
/// A 128-bit Universally Unique Identifier, used throughout the Second
/// Life networking protocol
///
[Serializable]
public struct UUID : IComparable
{
/// The System.Guid object this struct wraps around
public Guid Guid;
#region Constructors
///
/// Constructor that takes a string UUID representation
///
/// A string representation of a UUID, case
/// insensitive and can either be hyphenated or non-hyphenated
/// UUID("11f8aa9c-b071-4242-836b-13b7abe0d489")
public UUID(string val)
{
if (String.IsNullOrEmpty(val))
Guid = new Guid();
else
Guid = new Guid(val);
}
///
/// Constructor that takes a System.Guid object
///
/// A Guid object that contains the unique identifier
/// to be represented by this UUID
public UUID(Guid val)
{
Guid = val;
}
///
/// Constructor that takes a byte array containing a UUID
///
/// Byte array containing a 16 byte UUID
/// Beginning offset in the array
public UUID(byte[] source, int pos)
{
Guid = UUID.Zero.Guid;
FromBytes(source, pos);
}
///
/// Constructor that takes an unsigned 64-bit unsigned integer to
/// convert to a UUID
///
/// 64-bit unsigned integer to convert to a UUID
public UUID(ulong val)
{
Guid = new Guid(0, 0, 0, BitConverter.GetBytes(val));
}
///
/// Copy constructor
///
/// UUID to copy
public UUID(UUID val)
{
Guid = val.Guid;
}
#endregion Constructors
#region Public Methods
///
/// IComparable.CompareTo implementation
///
public int CompareTo(UUID id)
{
return Guid.CompareTo(id.Guid);
}
///
///
///
///
///
public void FromBytes(byte[] source, int pos)
{
Guid = new Guid(
(source[pos + 0] << 24) | (source[pos + 1] << 16) | (source[pos + 2] << 8) | source[pos + 3],
(short)((source[pos + 4] << 8) | source[pos + 5]),
(short)((source[pos + 6] << 8) | source[pos + 7]),
source[pos + 8], source[pos + 9], source[pos + 10], source[pos + 11],
source[pos + 12], source[pos + 13], source[pos + 14], source[pos + 15]);
}
///
/// Returns a copy of the raw bytes for this UUID
///
/// A 16 byte array containing this UUID
public byte[] GetBytes()
{
byte[] bytes = Guid.ToByteArray();
byte[] output = new byte[16];
output[0] = bytes[3];
output[1] = bytes[2];
output[2] = bytes[1];
output[3] = bytes[0];
output[4] = bytes[5];
output[5] = bytes[4];
output[6] = bytes[7];
output[7] = bytes[6];
Buffer.BlockCopy(bytes, 8, output, 8, 8);
return output;
}
///
/// Calculate an LLCRC (cyclic redundancy check) for this UUID
///
/// The CRC checksum for this UUID
public uint CRC()
{
uint retval = 0;
byte[] bytes = GetBytes();
retval += (uint)((bytes[ 3] << 24) + (bytes[ 2] << 16) + (bytes[ 1] << 8) + bytes[ 0]);
retval += (uint)((bytes[ 7] << 24) + (bytes[ 6] << 16) + (bytes[ 5] << 8) + bytes[ 4]);
retval += (uint)((bytes[11] << 24) + (bytes[10] << 16) + (bytes[ 9] << 8) + bytes[ 8]);
retval += (uint)((bytes[15] << 24) + (bytes[14] << 16) + (bytes[13] << 8) + bytes[12]);
return retval;
}
///
/// Create a 64-bit integer representation of the first half of this UUID
///
/// An integer created from the first eight bytes of this UUID
public ulong GetULong()
{
return Helpers.BytesToUInt64(Guid.ToByteArray());
}
#endregion Public Methods
#region Static Methods
///
/// Generate a UUID from a string
///
/// A string representation of a UUID, case
/// insensitive and can either be hyphenated or non-hyphenated
/// UUID.Parse("11f8aa9c-b071-4242-836b-13b7abe0d489")
public static UUID Parse(string val)
{
return new UUID(val);
}
///
/// Generate a UUID 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
/// UUID.TryParse("11f8aa9c-b071-4242-836b-13b7abe0d489", result)
public static bool TryParse(string val, out UUID result)
{
try
{
result = Parse(val);
return true;
}
catch (Exception)
{
result = UUID.Zero;
return false;
}
}
///
/// Combine two UUIDs together by taking the MD5 hash of a byte array
/// containing both UUIDs
///
/// First UUID to combine
/// Second UUID to combine
/// The UUID product of the combination
public static UUID Combine(UUID first, UUID second)
{
// Construct the buffer that MD5ed
byte[] input = new byte[32];
Buffer.BlockCopy(first.GetBytes(), 0, input, 0, 16);
Buffer.BlockCopy(second.GetBytes(), 0, input, 16, 16);
return new UUID(Helpers.MD5Builder.ComputeHash(input), 0);
}
///
///
///
///
public static UUID Random()
{
return new UUID(Guid.NewGuid());
}
#endregion Static Methods
#region Overrides
///
/// 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()
{
return Guid.GetHashCode();
}
///
/// Comparison function
///
/// An object to compare to this UUID
/// False if the object is not an UUID, true if it is and
/// byte for byte identical to this
public override bool Equals(object o)
{
if (!(o is UUID)) return false;
UUID uuid = (UUID)o;
return Guid == uuid.Guid;
}
///
/// Get a hyphenated string representation of this UUID
///
/// A string representation of this UUID, lowercase and
/// with hyphens
/// 11f8aa9c-b071-4242-836b-13b7abe0d489
public override string ToString()
{
return Guid.ToString();
}
#endregion Overrides
#region Operators
///
/// Equals operator
///
/// First UUID for comparison
/// Second UUID for comparison
/// True if the UUIDs are byte for byte equal, otherwise false
public static bool operator==(UUID lhs, UUID rhs)
{
return lhs.Guid == rhs.Guid;
}
///
/// Not equals operator
///
/// First UUID for comparison
/// Second UUID for comparison
/// True if the UUIDs are not equal, otherwise true
public static bool operator!=(UUID lhs, UUID rhs)
{
return !(lhs == rhs);
}
///
/// XOR operator
///
/// First UUID
/// Second UUID
/// A UUID that is a XOR combination of the two input UUIDs
public static UUID operator ^(UUID lhs, UUID rhs)
{
byte[] lhsbytes = lhs.GetBytes();
byte[] rhsbytes = rhs.GetBytes();
byte[] output = new byte[16];
for (int i = 0; i < 16; i++)
{
output[i] = (byte)(lhsbytes[i] ^ rhsbytes[i]);
}
return new UUID(output, 0);
}
///
/// 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 UUID(string val)
{
return new UUID(val);
}
#endregion Operators
/// An UUID with a value of all zeroes
public static readonly UUID Zero = new UUID();
}
///
/// A two-dimensional vector with floating-point values
///
[Serializable]
public struct Vector2 : IComparable
{
/// X value
public float X;
/// Y value
public float Y;
// Used for little to big endian conversion on big endian architectures
private byte[] conversionBuffer;
#region Constructors
///
/// Constructor, builds a vector for individual float values
///
/// X value
/// Y value
public Vector2(float x, float y)
{
conversionBuffer = null;
X = x;
Y = y;
}
///
/// Copy constructor
///
/// Vector to copy
public Vector2(Vector2 vector)
{
conversionBuffer = null;
X = vector.X;
Y = vector.Y;
}
#endregion Constructors
#region Public Methods
///
/// Test if this vector is equal to another vector, within a given
/// tolerance range
///
/// Vector to test against
/// The acceptable magnitude of difference
/// between the two vectors
/// True if the magnitude of difference between the two vectors
/// is less than the given tolerance, otherwise false
public bool ApproxEquals(Vector2 vec, float tolerance)
{
Vector2 diff = this - vec;
float d = Vector2.Mag(diff);
return (d <= tolerance);
}
///
/// IComparable.CompareTo implementation
///
public int CompareTo(Vector2 vector)
{
float thisMag = X * X + Y * Y;
float thatMag = vector.X * vector.X + vector.Y * vector.Y;
return thisMag.CompareTo(thatMag);
}
///
/// Builds a vector from a byte array
///
/// Byte array containing two four-byte floats
/// Beginning position in the byte array
public void FromBytes(byte[] byteArray, int pos)
{
if (!BitConverter.IsLittleEndian)
{
// Big endian architecture
if (conversionBuffer == null)
conversionBuffer = new byte[8];
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 8);
Array.Reverse(conversionBuffer, 0, 4);
Array.Reverse(conversionBuffer, 4, 4);
X = BitConverter.ToSingle(conversionBuffer, 0);
Y = BitConverter.ToSingle(conversionBuffer, 4);
}
else
{
// Little endian architecture
X = BitConverter.ToSingle(byteArray, pos);
Y = BitConverter.ToSingle(byteArray, pos + 4);
}
}
///
/// Returns the raw bytes for this vector
///
/// An eight-byte array containing X and Y
public byte[] GetBytes()
{
byte[] byteArray = new byte[8];
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, byteArray, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, byteArray, 4, 4);
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(byteArray, 0, 4);
Array.Reverse(byteArray, 4, 4);
}
return byteArray;
}
#endregion Public Methods
#region Static Methods
///
/// Calculate the magnitude of the supplied vector
///
public static float Mag(Vector2 v)
{
return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y);
}
///
/// Calculate the squared magnitude of the supplied vector
///
///
///
public static float MagSquared(Vector2 v)
{
return v.X * v.X + v.Y * v.Y;
}
#endregion Static Methods
#region Overrides
///
/// 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());
}
///
///
///
///
///
public override bool Equals(object o)
{
if (!(o is Vector2)) return false;
Vector2 vector = (Vector2)o;
return (X == vector.X && Y == vector.Y);
}
///
/// Get a formatted string representation of the vector
///
/// A string representation of the vector
public override string ToString()
{
return String.Format(Helpers.EnUsCulture, "<{0}, {1}>", X, Y);
}
#endregion Overrides
#region Operators
public static bool operator ==(Vector2 lhs, Vector2 rhs)
{
return (lhs.X == rhs.X && lhs.Y == rhs.Y);
}
public static bool operator !=(Vector2 lhs, Vector2 rhs)
{
return !(lhs == rhs);
}
public static Vector2 operator +(Vector2 lhs, Vector2 rhs)
{
return new Vector2(lhs.X + rhs.X, lhs.Y + rhs.Y);
}
public static Vector2 operator -(Vector2 vec)
{
return new Vector2(-vec.X, -vec.Y);
}
public static Vector2 operator -(Vector2 lhs, Vector2 rhs)
{
return new Vector2(lhs.X - rhs.X, lhs.Y - rhs.Y);
}
public static Vector2 operator *(Vector2 vec, float val)
{
return new Vector2(vec.X * val, vec.Y * val);
}
public static Vector2 operator *(float val, Vector2 vec)
{
return new Vector2(vec.X * val, vec.Y * val);
}
public static Vector2 operator *(Vector2 lhs, Vector2 rhs)
{
return new Vector2(lhs.X * rhs.X, lhs.Y * rhs.Y);
}
public static Vector2 operator /(Vector2 lhs, Vector2 rhs)
{
return new Vector2(lhs.X / rhs.X, lhs.Y / rhs.Y);
}
public static Vector2 operator /(Vector2 vec, float val)
{
return new Vector2(vec.X / val, vec.Y / val);
}
#endregion Operators
/// An Vector2 with a value of 0,0,0
public readonly static Vector2 Zero = new Vector2();
}
///
/// A three-dimensional vector with floating-point values
///
[Serializable]
public struct Vector3 : IComparable
{
/// X value
public float X;
/// Y value
public float Y;
/// Z value
public float Z;
// Used for little to big endian conversion on big endian architectures
private byte[] conversionBuffer;
#region Constructors
///
/// Constructor, builds a vector from a byte array
///
/// Byte array containing three four-byte floats
/// Beginning position in the byte array
public Vector3(byte[] byteArray, int pos)
{
conversionBuffer = null;
X = Y = Z = 0;
FromBytes(byteArray, pos);
}
///
/// Constructor, builds a vector for individual float values
///
/// X value
/// Y value
/// Z value
public Vector3(float x, float y, float z)
{
conversionBuffer = null;
X = x;
Y = y;
Z = z;
}
///
/// Copy constructor
///
/// Vector to copy
public Vector3(Vector3 vector)
{
conversionBuffer = null;
X = (float)vector.X;
Y = (float)vector.Y;
Z = (float)vector.Z;
}
#endregion Constructors
#region Public Methods
///
/// Test if this vector is equal to another vector, within a given
/// tolerance range
///
/// Vector to test against
/// The acceptable magnitude of difference
/// between the two vectors
/// True if the magnitude of difference between the two vectors
/// is less than the given tolerance, otherwise false
public bool ApproxEquals(Vector3 vec, float tolerance)
{
Vector3 diff = this - vec;
float d = Vector3.Mag(diff);
return (d <= tolerance);
}
///
/// IComparable.CompareTo implementation
///
public int CompareTo(Vector3 vector)
{
float thisMag = X * X + Y * Y + Z * Z;
float thatMag = vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
return thisMag.CompareTo(thatMag);
}
///
/// Test if this vector is composed of all finite numbers
///
public bool IsFinite()
{
if (Helpers.IsFinite(X) && Helpers.IsFinite(Y) && Helpers.IsFinite(Z))
return true;
else
return false;
}
///
/// Builds a vector from a byte array
///
/// Byte array containing a 12 byte vector
/// Beginning position in the byte array
public void FromBytes(byte[] byteArray, int pos)
{
if (!BitConverter.IsLittleEndian)
{
// Big endian architecture
if (conversionBuffer == null)
conversionBuffer = new byte[12];
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 12);
Array.Reverse(conversionBuffer, 0, 4);
Array.Reverse(conversionBuffer, 4, 4);
Array.Reverse(conversionBuffer, 8, 4);
X = BitConverter.ToSingle(conversionBuffer, 0);
Y = BitConverter.ToSingle(conversionBuffer, 4);
Z = BitConverter.ToSingle(conversionBuffer, 8);
}
else
{
// Little endian architecture
X = BitConverter.ToSingle(byteArray, pos);
Y = BitConverter.ToSingle(byteArray, pos + 4);
Z = BitConverter.ToSingle(byteArray, pos + 8);
}
}
///
/// Returns the raw bytes for this vector
///
/// A 12 byte array containing X, Y, and Z
public byte[] GetBytes()
{
byte[] byteArray = new byte[12];
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, byteArray, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, byteArray, 4, 4);
Buffer.BlockCopy(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;
}
public LLSD ToLLSD()
{
LLSDArray array = new LLSDArray();
array.Add(LLSD.FromReal(X));
array.Add(LLSD.FromReal(Y));
array.Add(LLSD.FromReal(Z));
return array;
}
public void FromLLSD(LLSD llsd)
{
if (llsd.Type == LLSDType.Array)
{
LLSDArray array = (LLSDArray)llsd;
if (array.Count == 3)
{
X = (float)array[0].AsReal();
Y = (float)array[1].AsReal();
Z = (float)array[2].AsReal();
return;
}
}
this = Vector3.Zero;
}
#endregion Public Methods
#region Static Methods
///
/// Calculate the magnitude of the supplied vector
///
public static float Mag(Vector3 v)
{
return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
}
///
/// Calculate the squared magnitude of the supplied vector
///
///
///
public static float MagSquared(Vector3 v)
{
return v.X * v.X + v.Y * v.Y + v.Z * v.Z;
}
///
/// Returns a normalized version of the supplied vector
///
/// The vector to normalize
/// A normalized version of the vector
public static Vector3 Norm(Vector3 vector)
{
float mag = Mag(vector);
return new Vector3(vector.X / mag, vector.Y / mag, vector.Z / mag);
}
///
/// Return the cross product of two vectors
///
/// First vector
/// Second vector
/// Cross product of first and second vector
public static Vector3 Cross(Vector3 v1, Vector3 v2)
{
return new Vector3
(
v1.Y * v2.Z - v1.Z * v2.Y,
v1.Z * v2.X - v1.X * v2.Z,
v1.X * v2.Y - v1.Y * v2.X
);
}
///
/// Returns the dot product of two vectors
///
public static float Dot(Vector3 v1, Vector3 v2)
{
return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
}
///
/// Calculates the distance between two vectors
///
public static float Dist(Vector3 pointA, Vector3 pointB)
{
float xd = pointB.X - pointA.X;
float yd = pointB.Y - pointA.Y;
float zd = pointB.Z - pointA.Z;
return (float)Math.Sqrt(xd * xd + yd * yd + zd * zd);
}
public static Vector3 Rot(Vector3 vector, Quaternion rotation)
{
return vector * rotation;
}
public static Vector3 Rot(Vector3 vector, Matrix3 rotation)
{
return vector * rotation;
}
///
/// Calculate the rotation between two vectors
///
/// Directional vector, such as 1,0,0 for the forward face
/// Target vector - normalize first with VecNorm
public static Quaternion RotBetween(Vector3 a, Vector3 b)
{
//A and B should both be normalized
float dotProduct = Dot(a, b);
Vector3 crossProduct = Cross(a, b);
float magProduct = Mag(a) * Mag(b);
double angle = Math.Acos(dotProduct / magProduct);
Vector3 axis = Norm(crossProduct);
float s = (float)Math.Sin(angle / 2);
return new Quaternion(
axis.X * s,
axis.Y * s,
axis.Z * s,
(float)Math.Cos(angle / 2));
}
public static Vector3 Transform(Vector3 vector, Matrix3 matrix)
{
// Operates "from the right" on row vector
return new Vector3(
vector.X * matrix.M11 + vector.Y * matrix.M21 + vector.Z * matrix.M31,
vector.X * matrix.M12 + vector.Y * matrix.M22 + vector.Z * matrix.M32,
vector.X * matrix.M13 + vector.Y * matrix.M23 + vector.Z * matrix.M33
);
}
///
/// Generate an Vector3 from a string
///
/// A string representation of a 3D vector, enclosed
/// in arrow brackets and separated by commas
public static Vector3 Parse(string val)
{
char[] splitChar = { ',' };
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
return new Vector3(
float.Parse(split[0].Trim(), Helpers.EnUsCulture),
float.Parse(split[1].Trim(), Helpers.EnUsCulture),
float.Parse(split[2].Trim(), Helpers.EnUsCulture));
}
public static bool TryParse(string val, out Vector3 result)
{
try
{
result = Parse(val);
return true;
}
catch (Exception)
{
result = new Vector3();
return false;
}
}
#endregion Static Methods
#region Overrides
///
/// 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 Vector3)) return false;
Vector3 vector = (Vector3)o;
return (X == vector.X && Y == vector.Y && Z == vector.Z);
}
///
/// Get a formatted string representation of the vector
///
/// A string representation of the vector
public override string ToString()
{
return String.Format(Helpers.EnUsCulture, "<{0}, {1}, {2}>", X, Y, Z);
}
#endregion Overrides
#region Operators
public static bool operator==(Vector3 lhs, Vector3 rhs)
{
return (lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z);
}
public static bool operator!=(Vector3 lhs, Vector3 rhs)
{
return !(lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z);
}
public static Vector3 operator +(Vector3 lhs, Vector3 rhs)
{
return new Vector3(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z);
}
public static Vector3 operator -(Vector3 vec)
{
return new Vector3(-vec.X, -vec.Y, -vec.Z);
}
public static Vector3 operator -(Vector3 lhs, Vector3 rhs)
{
return new Vector3(lhs.X - rhs.X,lhs.Y - rhs.Y, lhs.Z - rhs.Z);
}
public static Vector3 operator *(Vector3 vec, float val)
{
return new Vector3(vec.X * val, vec.Y * val, vec.Z * val);
}
public static Vector3 operator *(float val, Vector3 vec)
{
return new Vector3(vec.X * val, vec.Y * val, vec.Z * val);
}
public static Vector3 operator *(Vector3 lhs, Vector3 rhs)
{
return new Vector3(lhs.X * rhs.X, lhs.Y * rhs.Y, lhs.Z * rhs.Z);
}
public static Vector3 operator *(Vector3 vec, Quaternion rot)
{
float rw = -rot.X * vec.X - rot.Y * vec.Y - rot.Z * vec.Z;
float rx = rot.W * vec.X + rot.Y * vec.Z - rot.Z * vec.Y;
float ry = rot.W * vec.Y + rot.Z * vec.X - rot.X * vec.Z;
float rz = rot.W * vec.Z + rot.X * vec.Y - rot.Y * vec.X;
float nx = -rw * rot.X + rx * rot.W - ry * rot.Z + rz * rot.Y;
float ny = -rw * rot.Y + ry * rot.W - rz * rot.X + rx * rot.Z;
float nz = -rw * rot.Z + rz * rot.W - rx * rot.Y + ry * rot.X;
return new Vector3(nx, ny, nz);
}
public static Vector3 operator *(Vector3 vector, Matrix3 matrix)
{
return Vector3.Transform(vector, matrix);
}
public static Vector3 operator /(Vector3 lhs, Vector3 rhs)
{
return new Vector3(lhs.X / rhs.X, lhs.Y / rhs.Y, lhs.Z / rhs.Z);
}
public static Vector3 operator /(Vector3 vec, float val)
{
return new Vector3(vec.X / val, vec.Y / val, vec.Z / val);
}
public static Vector3 operator %(Vector3 lhs, Vector3 rhs)
{
return new Vector3(
lhs.Y * rhs.Z - rhs.Y * lhs.Z,
lhs.Z * rhs.X - rhs.Z * lhs.X,
lhs.X * rhs.Y - rhs.X * lhs.Y);
}
#endregion Operators
/// An Vector3 with a value of 0,0,0
public readonly static Vector3 Zero = new Vector3();
/// A unit vector facing up (Z axis)
public readonly static Vector3 Up = new Vector3(0f, 0f, 1f);
/// A unit vector facing forward (X axis)
public readonly static Vector3 Fwd = new Vector3(1f, 0f, 0f);
/// A unit vector facing left (Y axis)
public readonly static Vector3 Left = new Vector3(0f, 1f, 0f);
}
///
/// A double-precision three-dimensional vector
///
[Serializable]
public struct Vector3d : IComparable
{
/// X value
public double X;
/// Y value
public double Y;
/// Z value
public double Z;
// Used for little to big endian conversion on big endian architectures
private byte[] conversionBuffer;
#region Constructors
///
///
///
///
///
///
public Vector3d(double x, double y, double z)
{
conversionBuffer = null;
X = x;
Y = y;
Z = z;
}
///
/// Create a double precision vector from a float vector
///
///
public Vector3d(Vector3 llv3)
{
conversionBuffer = null;
X = llv3.X;
Y = llv3.Y;
Z = llv3.Z;
}
///
///
///
///
///
public Vector3d(byte[] byteArray, int pos)
{
conversionBuffer = null;
X = Y = Z = 0;
FromBytes(byteArray, pos);
}
///
/// Copy constructor
///
/// Vector to copy
public Vector3d(Vector3d vector)
{
conversionBuffer = null;
X = vector.X;
Y = vector.Y;
Z = vector.Z;
}
#endregion Constructors
#region Public Methods
///
/// Test if this vector is equal to another vector, within a given
/// tolerance range
///
/// Vector to test against
/// The acceptable magnitude of difference
/// between the two vectors
/// True if the magnitude of difference between the two vectors
/// is less than the given tolerance, otherwise false
public bool ApproxEquals(Vector3d vec, double tolerance)
{
Vector3d diff = this - vec;
double d = Vector3d.Mag(diff);
return (d <= tolerance);
}
///
/// IComparable.CompareTo implementation
///
public int CompareTo(Vector3d vector)
{
double thisMag = X * X + Y * Y + Z * Z;
double thatMag = vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
return thisMag.CompareTo(thatMag);
}
///
///
///
///
///
public void FromBytes(byte[] byteArray, int pos)
{
if (!BitConverter.IsLittleEndian)
{
// Big endian architecture
if (conversionBuffer == null)
conversionBuffer = new byte[24];
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 24);
Array.Reverse(conversionBuffer, 0, 8);
Array.Reverse(conversionBuffer, 8, 8);
Array.Reverse(conversionBuffer, 16, 8);
X = BitConverter.ToDouble(conversionBuffer, 0);
Y = BitConverter.ToDouble(conversionBuffer, 8);
Z = BitConverter.ToDouble(conversionBuffer, 16);
}
else
{
// Little endian architecture
X = BitConverter.ToDouble(byteArray, pos);
Y = BitConverter.ToDouble(byteArray, pos + 8);
Z = BitConverter.ToDouble(byteArray, pos + 16);
}
}
///
///
///
///
public byte[] GetBytes()
{
byte[] byteArray = new byte[24];
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, byteArray, 0, 8);
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, byteArray, 8, 8);
Buffer.BlockCopy(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 LLSD ToLLSD()
{
LLSDArray array = new LLSDArray();
array.Add(LLSD.FromReal(X));
array.Add(LLSD.FromReal(Y));
array.Add(LLSD.FromReal(Z));
return array;
}
public void FromLLSD(LLSD llsd)
{
if (llsd.Type == LLSDType.Array)
{
LLSDArray array = (LLSDArray)llsd;
if (array.Count == 3)
{
X = array[0].AsReal();
Y = array[1].AsReal();
Z = array[2].AsReal();
return;
}
}
this = Vector3d.Zero;
}
#endregion Public Methods
#region Static Methods
///
/// Calculate the magnitude of the supplied vector
///
public static double Mag(Vector3d v)
{
return Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
}
///
/// Calculate the squared magnitude of the supplied vector
///
///
///
public static double MagSquared(Vector3d v)
{
return v.X * v.X + v.Y * v.Y + v.Z * v.Z;
}
///
/// Calculates the distance between two vectors
///
public static double Dist(Vector3d pointA, Vector3d pointB)
{
double xd = pointB.X - pointA.X;
double yd = pointB.Y - pointA.Y;
double zd = pointB.Z - pointA.Z;
return Math.Sqrt(xd * xd + yd * yd + zd * zd);
}
///
/// Generate an Vector3d from a string
///
/// A string representation of a 3D vector, enclosed
/// in arrow brackets and separated by commas
public static Vector3d Parse(string val)
{
char[] splitChar = { ',' };
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
return new Vector3d(
double.Parse(split[0].Trim(), Helpers.EnUsCulture),
double.Parse(split[1].Trim(), Helpers.EnUsCulture),
double.Parse(split[2].Trim(), Helpers.EnUsCulture));
}
public static bool TryParse(string val, out Vector3d result)
{
try
{
result = Parse(val);
return true;
}
catch (Exception)
{
result = new Vector3d();
return false;
}
}
#endregion Static Methods
#region Overrides
///
/// 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 Vector3d)) return false;
Vector3d vector = (Vector3d)o;
return (X == vector.X && Y == vector.Y && Z == vector.Z);
}
///
///
///
///
public override string ToString()
{
return String.Format("<{0}, {1}, {2}>", X, Y, Z);
}
#endregion Overrides
#region Operators
public static bool operator ==(Vector3d lhs, Vector3d rhs)
{
return (lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z);
}
public static bool operator !=(Vector3d lhs, Vector3d rhs)
{
return !(lhs == rhs);
}
public static Vector3d operator +(Vector3d lhs, Vector3d rhs)
{
return new Vector3d(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z);
}
public static Vector3d operator -(Vector3d vec)
{
return new Vector3d(-vec.X, -vec.Y, -vec.Z);
}
public static Vector3d operator -(Vector3d lhs, Vector3d rhs)
{
return new Vector3d(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z);
}
public static Vector3d operator *(Vector3d vec, float val)
{
return new Vector3d(vec.X * val, vec.Y * val, vec.Z * val);
}
public static Vector3d operator *(double val, Vector3d vec)
{
return new Vector3d(vec.X * val, vec.Y * val, vec.Z * val);
}
public static Vector3d operator *(Vector3d lhs, Vector3d rhs)
{
return new Vector3d(lhs.X * rhs.X, lhs.Y * rhs.Y, lhs.Z * rhs.Z);
}
public static Vector3d operator /(Vector3d lhs, Vector3d rhs)
{
return new Vector3d(lhs.X / rhs.X, lhs.Y / rhs.Y, lhs.Z / rhs.Z);
}
public static Vector3d operator /(Vector3d vec, double val)
{
return new Vector3d(vec.X / val, vec.Y / val, vec.Z / val);
}
public static Vector3d operator %(Vector3d lhs, Vector3d rhs)
{
return new Vector3d(
lhs.Y * rhs.Z - rhs.Y * lhs.Z,
lhs.Z * rhs.X - rhs.Z * lhs.X,
lhs.X * rhs.Y - rhs.X * lhs.Y);
}
#endregion Operators
/// An Vector3d with a value of 0,0,0
public static readonly Vector3d Zero = new Vector3d();
}
///
/// A four-dimensional vector
///
[Serializable]
public struct Vector4 : IComparable
{
///
public float X;
///
public float Y;
///
public float Z;
///
public float S;
// Used for little to big endian conversion on big endian architectures
private byte[] conversionBuffer;
#region Constructors
///
/// Constructor, sets the vector members according to parameters
///
/// X value
/// Y value
/// Z value
/// S value
public Vector4(float x, float y, float z, float s)
{
conversionBuffer = null;
X = x;
Y = y;
Z = z;
S = s;
}
///
///
///
///
///
public Vector4(byte[] byteArray, int pos)
{
conversionBuffer = null;
X = Y = Z = S = 0;
FromBytes(byteArray, pos);
}
///
/// Copy constructor
///
/// Vector to copy
public Vector4(Vector4 vector)
{
conversionBuffer = null;
X = vector.X;
Y = vector.Y;
Z = vector.Z;
S = vector.S;
}
#endregion Constructors
#region Public Methods
///
/// Test if this vector is equal to another vector, within a given
/// tolerance range
///
/// Vector to test against
/// The acceptable magnitude of difference
/// between the two vectors
/// True if the magnitude of difference between the two vectors
/// is less than the given tolerance, otherwise false
public bool ApproxEquals(Vector4 vec, float tolerance)
{
Vector4 diff = this - vec;
float d = Vector4.Mag(diff);
return (d <= tolerance);
}
///
/// IComparable.CompareTo implementation
///
public int CompareTo(Vector4 vector)
{
float thisMag = X * X + Y * Y + Z * Z + S * S;
float thatMag = vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z + vector.S * vector.S;
return thisMag.CompareTo(thatMag);
}
///
///
///
///
///
public void FromBytes(byte[] byteArray, int pos)
{
if (!BitConverter.IsLittleEndian)
{
// Big endian architecture
if (conversionBuffer == null)
conversionBuffer = new byte[16];
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 16);
Array.Reverse(conversionBuffer, 0, 4);
Array.Reverse(conversionBuffer, 4, 4);
Array.Reverse(conversionBuffer, 8, 4);
Array.Reverse(conversionBuffer, 12, 4);
X = BitConverter.ToSingle(conversionBuffer, 0);
Y = BitConverter.ToSingle(conversionBuffer, 4);
Z = BitConverter.ToSingle(conversionBuffer, 8);
S = BitConverter.ToSingle(conversionBuffer, 12);
}
else
{
// Little endian architecture
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];
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, byteArray, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, byteArray, 4, 4);
Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, byteArray, 8, 4);
Buffer.BlockCopy(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 LLSD ToLLSD()
{
LLSDArray array = new LLSDArray();
array.Add(LLSD.FromReal(X));
array.Add(LLSD.FromReal(Y));
array.Add(LLSD.FromReal(Z));
array.Add(LLSD.FromReal(S));
return array;
}
public void FromLLSD(LLSD llsd)
{
if (llsd.Type == LLSDType.Array)
{
LLSDArray array = (LLSDArray)llsd;
if (array.Count == 4)
{
X = (float)array[0].AsReal();
Y = (float)array[1].AsReal();
Z = (float)array[2].AsReal();
S = (float)array[3].AsReal();
return;
}
}
this = Vector4.Zero;
}
#endregion Public Methods
#region Static Methods
///
/// Calculate the magnitude of the supplied vector
///
public static float Mag(Vector4 v)
{
return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z + v.S * v.S);
}
///
/// Calculate the squared magnitude of the supplied vector
///
///
///
public static float MagSquared(Vector4 v)
{
return v.X * v.X + v.Y * v.Y + v.Z * v.Z + v.S * v.S;
}
public static Vector4 Parse(string val)
{
char[] splitChar = { ',' };
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
return new Vector4(
float.Parse(split[0].Trim(), Helpers.EnUsCulture),
float.Parse(split[1].Trim(), Helpers.EnUsCulture),
float.Parse(split[2].Trim(), Helpers.EnUsCulture),
float.Parse(split[3].Trim(), Helpers.EnUsCulture));
}
public static bool TryParse(string val, out Vector4 result)
{
try
{
result = Parse(val);
return true;
}
catch (Exception)
{
result = new Vector4();
return false;
}
}
#endregion Static Methods
#region Overrides
///
/// 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() ^ S.GetHashCode());
}
///
///
///
///
///
public override bool Equals(object o)
{
if (!(o is Vector4)) return false;
Vector4 vector = (Vector4)o;
return (X == vector.X && Y == vector.Y && Z == vector.Z && S == vector.S);
}
///
///
///
///
public override string ToString()
{
return String.Format("<{0}, {1}, {2}, {3}>", X, Y, Z, S);
}
#endregion Overrides
#region Operators
public static bool operator ==(Vector4 lhs, Vector4 rhs)
{
return (lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z && lhs.S == rhs.S);
}
public static bool operator !=(Vector4 lhs, Vector4 rhs)
{
return !(lhs == rhs);
}
public static Vector4 operator +(Vector4 lhs, Vector4 rhs)
{
return new Vector4(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z, lhs.S + rhs.S);
}
public static Vector4 operator -(Vector4 vec)
{
return new Vector4(-vec.X, -vec.Y, -vec.Z, -vec.S);
}
public static Vector4 operator -(Vector4 lhs, Vector4 rhs)
{
return new Vector4(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z, lhs.S - rhs.S);
}
public static Vector4 operator *(Vector4 vec, float val)
{
return new Vector4(vec.X * val, vec.Y * val, vec.Z * val, vec.S * val);
}
public static Vector4 operator *(float val, Vector4 vec)
{
return new Vector4(vec.X * val, vec.Y * val, vec.Z * val, vec.S * val);
}
public static Vector4 operator *(Vector4 lhs, Vector4 rhs)
{
return new Vector4(lhs.X * rhs.X, lhs.Y * rhs.Y, lhs.Z * rhs.Z, lhs.S * rhs.S);
}
public static Vector4 operator /(Vector4 lhs, Vector4 rhs)
{
return new Vector4(lhs.X / rhs.X, lhs.Y / rhs.Y, lhs.Z / rhs.Z, lhs.S / rhs.S);
}
public static Vector4 operator /(Vector4 vec, float val)
{
return new Vector4(vec.X / val, vec.Y / val, vec.Z / val, vec.S / val);
}
#endregion Operators
/// An Vector4 with a value of 0,0,0,0
public readonly static Vector4 Zero = new Vector4();
}
///
/// An 8-bit color structure including an alpha channel
///
[Serializable]
public struct Color4 : IComparable
{
/// Red
public float R;
/// Green
public float G;
/// Blue
public float B;
/// Alpha
public float A;
#region Constructors
///
///
///
///
///
///
///
public Color4(byte r, byte g, byte b, byte a)
{
const float quanta = 1.0f / 255.0f;
R = (float)r * quanta;
G = (float)g * quanta;
B = (float)b * quanta;
A = (float)a * quanta;
}
public Color4(float r, float g, float b, float a)
{
if (r > 1f || g > 1f || b > 1f || a > 1f)
Logger.Log(
String.Format("Attempting to initialize Color4 with out of range values <{0},{1},{2},{3}>",
r, g, b, a), Helpers.LogLevel.Warning);
// Valid range is from 0.0 to 1.0
R = Helpers.Clamp(r, 0f, 1f);
G = Helpers.Clamp(g, 0f, 1f);
B = Helpers.Clamp(b, 0f, 1f);
A = Helpers.Clamp(a, 0f, 1f);
}
public Color4(byte[] byteArray, int pos, bool inverted)
{
R = G = B = A = 0f;
FromBytes(byteArray, pos, inverted);
}
public Color4(byte[] byteArray, int pos, bool inverted, bool alphaInverted)
{
R = G = B = A = 0f;
FromBytes(byteArray, pos, inverted, alphaInverted);
}
///
/// Copy constructor
///
/// Color to copy
public Color4(Color4 color)
{
R = color.R;
G = color.G;
B = color.B;
A = color.A;
}
#endregion Constructors
#region Public Methods
///
/// IComparable.CompareTo implementation
///
/// Sorting ends up like this: |--Greyscale--||--Color--|.
/// Alpha is only used when the colors are otherwise equivalent
public int CompareTo(Color4 color)
{
float thisHue = GetHue();
float thatHue = color.GetHue();
if (thisHue < 0f && thatHue < 0f)
{
// Both monochromatic
if (R == color.R)
{
// Monochromatic and equal, compare alpha
return A.CompareTo(color.A);
}
else
{
// Compare lightness
return R.CompareTo(R);
}
}
else
{
if (thisHue == thatHue)
{
// RGB is equal, compare alpha
return A.CompareTo(color.A);
}
else
{
// Compare hues
return thisHue.CompareTo(thatHue);
}
}
}
public void FromBytes(byte[] byteArray, int pos, bool inverted)
{
const float quanta = 1.0f / 255.0f;
if (inverted)
{
R = (float)(255 - byteArray[pos]) * quanta;
G = (float)(255 - byteArray[pos + 1]) * quanta;
B = (float)(255 - byteArray[pos + 2]) * quanta;
A = (float)(255 - byteArray[pos + 3]) * quanta;
}
else
{
R = (float)byteArray[pos] * quanta;
G = (float)byteArray[pos + 1] * quanta;
B = (float)byteArray[pos + 2] * quanta;
A = (float)byteArray[pos + 3] * quanta;
}
}
public void FromBytes(byte[] byteArray, int pos, bool inverted, bool alphaInverted)
{
FromBytes(byteArray, pos, inverted);
if (alphaInverted)
A = 1.0f - A;
}
public byte[] GetBytes()
{
return GetBytes(false);
}
///
///
///
///
public byte[] GetBytes(bool inverted)
{
byte[] byteArray = new byte[4];
byteArray[0] = Helpers.FloatToByte(R, 0f, 1f);
byteArray[1] = Helpers.FloatToByte(G, 0f, 1f);
byteArray[2] = Helpers.FloatToByte(B, 0f, 1f);
byteArray[3] = Helpers.FloatToByte(A, 0f, 1f);
if (inverted)
{
byteArray[0] = (byte)(255 - byteArray[0]);
byteArray[1] = (byte)(255 - byteArray[1]);
byteArray[2] = (byte)(255 - byteArray[2]);
byteArray[3] = (byte)(255 - byteArray[3]);
}
return byteArray;
}
public byte[] GetFloatBytes()
{
byte[] bytes = new byte[16];
Buffer.BlockCopy(BitConverter.GetBytes(R), 0, bytes, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(G), 0, bytes, 4, 4);
Buffer.BlockCopy(BitConverter.GetBytes(B), 0, bytes, 8, 4);
Buffer.BlockCopy(BitConverter.GetBytes(A), 0, bytes, 12, 4);
return bytes;
}
public float GetHue()
{
const float HUE_MAX = 360f;
float max = Math.Max(Math.Max(R, G), B);
float min = Math.Min(Math.Min(R, B), B);
if (max == min)
{
// Achromatic, hue is undefined
return -1f;
}
else
{
if (R == max)
{
float bDelta = (((max - B) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
float gDelta = (((max - G) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
return bDelta - gDelta;
}
else if (G == max)
{
float rDelta = (((max - R) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
float bDelta = (((max - B) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
return (HUE_MAX / 3f) + rDelta - bDelta;
}
else // B == max
{
float gDelta = (((max - G) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
float rDelta = (((max - R) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
return ((2f * HUE_MAX) / 3f) + gDelta - rDelta;
}
}
}
///
///
///
///
public LLSD ToLLSD()
{
LLSDArray array = new LLSDArray();
array.Add(LLSD.FromReal(R));
array.Add(LLSD.FromReal(G));
array.Add(LLSD.FromReal(B));
array.Add(LLSD.FromReal(A));
return array;
}
public void FromLLSD(LLSD llsd)
{
if (llsd.Type == LLSDType.Array)
{
LLSDArray array = (LLSDArray)llsd;
if (array.Count == 4)
{
R = (float)array[0].AsReal();
G = (float)array[1].AsReal();
B = (float)array[2].AsReal();
A = (float)array[3].AsReal();
return;
}
}
this = Color4.Black;
}
///
///
///
///
public string ToStringRGB()
{
return String.Format("<{0}, {1}, {2}>", R, G, B);
}
#endregion Public Methods
#region Static Methods
#endregion Static Methods
#region Overrides
///
///
///
///
public override string ToString()
{
return String.Format("<{0}, {1}, {2}, {3}>", R, G, B, A);
}
///
///
///
///
///
public override bool Equals(object obj)
{
if (obj is Color4)
{
Color4 c = (Color4)obj;
return (R == c.R) && (G == c.G) && (B == c.B) && (A == c.A);
}
return false;
}
///
///
///
///
public override int GetHashCode()
{
return R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
}
#endregion Overrides
#region Operators
///
/// Comparison operator
///
///
///
///
public static bool operator ==(Color4 lhs, Color4 rhs)
{
// Return true if the fields match:
return lhs.R == rhs.R && lhs.G == rhs.G && lhs.B == rhs.B && lhs.A == rhs.A;
}
///
/// Not comparison operator
///
///
///
///
public static bool operator !=(Color4 lhs, Color4 rhs)
{
return !(lhs == rhs);
}
#endregion Operators
/// A Color4 with zero RGB values and full alpha (1.0)
public readonly static Color4 Black = new Color4(0f, 0f, 0f, 1f);
/// A Color4 with full RGB values (1.0) and full alpha (1.0)
public readonly static Color4 White = new Color4(1f, 1f, 1f, 1f);
}
///
/// A quaternion, used for rotations
///
[Serializable]
public struct Quaternion
{
/// X value
public float X;
/// Y value
public float Y;
/// Z value
public float Z;
/// W value
public float W;
// Used for little to big endian conversion on big endian architectures
private byte[] conversionBuffer;
#region Properties
///
/// Returns the conjugate (spatial inverse) of this quaternion
///
public Quaternion Conjugate
{
get
{
return new Quaternion(-this.X, -this.Y, -this.Z, this.W);
}
}
#endregion Properties
#region Constructors
///
/// Constructor, builds a quaternion object from a byte array
///
/// Byte array containing four four-byte floats
/// 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 Quaternion(byte[] byteArray, int pos, bool normalized)
{
conversionBuffer = null;
X = Y = Z = W = 0;
FromBytes(byteArray, pos, normalized);
}
///
/// 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 Quaternion(float x, float y, float z)
{
conversionBuffer = null;
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 Quaternion(float x, float y, float z, float w)
{
conversionBuffer = null;
X = x;
Y = y;
Z = z;
W = w;
}
///
/// Build a quaternion from an angle and a vector
///
/// Angle value
/// Vector value
public Quaternion(float angle, Vector3 vec)
{
conversionBuffer = null;
X = Y = Z = W = 0f;
SetQuaternion(angle, vec);
}
///
/// Copy constructor
///
/// Quaternion to copy
public Quaternion(Quaternion quaternion)
{
conversionBuffer = null;
X = quaternion.X;
Y = quaternion.Y;
Z = quaternion.Z;
W = quaternion.W;
}
#endregion Constructors
#region Public Methods
///
/// Builds 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 void FromBytes(byte[] byteArray, int pos, bool normalized)
{
if (!normalized)
{
if (!BitConverter.IsLittleEndian)
{
// Big endian architecture
if (conversionBuffer == null)
conversionBuffer = new byte[16];
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 16);
Array.Reverse(conversionBuffer, 0, 4);
Array.Reverse(conversionBuffer, 4, 4);
Array.Reverse(conversionBuffer, 8, 4);
Array.Reverse(conversionBuffer, 12, 4);
X = BitConverter.ToSingle(conversionBuffer, 0);
Y = BitConverter.ToSingle(conversionBuffer, 4);
Z = BitConverter.ToSingle(conversionBuffer, 8);
W = BitConverter.ToSingle(conversionBuffer, 12);
}
else
{
// Little endian architecture
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)
{
// Big endian architecture
if (conversionBuffer == null)
conversionBuffer = new byte[16];
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 12);
Array.Reverse(conversionBuffer, 0, 4);
Array.Reverse(conversionBuffer, 4, 4);
Array.Reverse(conversionBuffer, 8, 4);
X = BitConverter.ToSingle(conversionBuffer, 0);
Y = BitConverter.ToSingle(conversionBuffer, 4);
Z = BitConverter.ToSingle(conversionBuffer, 8);
}
else
{
// Little endian architecture
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;
}
}
///
/// 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;
float x, y, z;
if (W >= 0) {
x = X; y = Y; z = Z;
} else {
x = -X; y = -Y; z = -Z;
}
Buffer.BlockCopy(BitConverter.GetBytes(norm * x), 0, bytes, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(norm * y), 0, bytes, 4, 4);
Buffer.BlockCopy(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 LLSD ToLLSD()
{
LLSDArray array = new LLSDArray();
array.Add(LLSD.FromReal(X));
array.Add(LLSD.FromReal(Y));
array.Add(LLSD.FromReal(Z));
array.Add(LLSD.FromReal(W));
return array;
}
public void FromLLSD(LLSD llsd)
{
if (llsd.Type == LLSDType.Array)
{
LLSDArray array = (LLSDArray)llsd;
if (array.Count == 4)
{
X = (float)array[0].AsReal();
Y = (float)array[1].AsReal();
Z = (float)array[2].AsReal();
W = (float)array[3].AsReal();
return;
}
}
this = Quaternion.Identity;
}
///
/// Convert this quaternion to euler angles
///
/// X euler angle
/// Y euler angle
/// Z euler angle
public void GetEulerAngles(out float roll, out float pitch, out float yaw)
{
// From http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
float sqx = X * X;
float sqy = Y * Y;
float sqz = Z * Z;
float sqw = W * W;
// Unit will be a correction factor if the quaternion is not normalized
float unit = sqx + sqy + sqz + sqw;
double test = X * Y + Z * W;
if (test > 0.499f * unit)
{
// Singularity at north pole
yaw = 2f * (float)Math.Atan2(X, W);
pitch = (float)Math.PI / 2f;
roll = 0f;
}
else if (test < -0.499f * unit)
{
// Singularity at south pole
yaw = -2f * (float)Math.Atan2(X, W);
pitch = -(float)Math.PI / 2f;
roll = 0f;
}
else
{
yaw = (float)Math.Atan2(2f * Y * W - 2f * X * Z, sqx - sqy - sqz + sqw);
pitch = (float)Math.Asin(2f * test / unit);
roll = (float)Math.Atan2(2f * X * W - 2f * Y * Z, -sqx + sqy - sqz + sqw);
}
}
///
/// Converts this quaternion to a 3x3 row-major matrix
///
/// A matrix representation of this quaternion
public Matrix3 ToMatrix3()
{
// From the Matrix and Quaternion FAQ: http://www.j3d.org/matrix_faq/matrfaq_latest.html
Matrix3 m = new Matrix3();
float xx, xy, xz, xw, yy, yz, yw, zz, zw;
xx = X * X;
xy = X * Y;
xz = X * Z;
xw = X * W;
yy = Y * Y;
yz = Y * Z;
yw = Y * W;
zz = Z * Z;
zw = Z * W;
m.M11 = 1f - 2f * (yy + zz);
m.M12 = 2f * (xy + zw);
m.M13 = 2f * (xz - yw);
m.M21 = 2f * (xy - zw);
m.M22 = 1f - 2f * (xx + zz);
m.M23 = 2f * (yz + xw);
m.M31 = 2f * (xz + yw);
m.M32 = 2f * (yz - xw);
m.M33 = 1f - 2f * (xx + yy);
return m;
}
///
/// Converts this quaternion to a 4x4 row-major matrix
///
/// A matrix representation of this quaternion
public Matrix4 ToMatrix4()
{
// From the Matrix and Quaternion FAQ: http://www.j3d.org/matrix_faq/matrfaq_latest.html
Matrix4 m = new Matrix4();
float xx, xy, xz, xw, yy, yz, yw, zz, zw;
xx = X * X;
xy = X * Y;
xz = X * Z;
xw = X * W;
yy = Y * Y;
yz = Y * Z;
yw = Y * W;
zz = Z * Z;
zw = Z * W;
m.M11 = 1f - 2f * (yy + zz);
m.M12 = 2f * (xy + zw);
m.M13 = 2f * (xz - yw);
m.M14 = 0f;
m.M21 = 2f * (xy - zw);
m.M22 = 1f - 2f * (xx + zz);
m.M23 = 2f * (yz + xw);
m.M24 = 0f;
m.M31 = 2f * (xz + yw);
m.M32 = 2f * (yz - xw);
m.M33 = 1f - 2f * (xx + yy);
m.M34 = 0f;
m.M41 = m.M42 = m.M43 = 0f;
m.M44 = 1f;
return m;
}
///
/// Construct this quaternion from an axis angle
///
/// Angle
/// X component of axis
/// Y component of axis
/// Z component of axis
public void SetQuaternion(float angle, float x, float y, float z)
{
Vector3 vec = new Vector3(x, y, z);
SetQuaternion(angle, vec);
}
///
/// Construct this quaternion from an axis angle
///
/// Angle
/// Axis
public void SetQuaternion(float angle, Vector3 vec)
{
vec = Vector3.Norm(vec);
angle *= 0.5f;
float c = (float)Math.Cos(angle);
float s = (float)Math.Sin(angle);
X = vec.X * s;
Y = vec.Y * s;
Z = vec.Z * s;
W = c;
this = Quaternion.Norm(this);
}
#endregion Public Methods
#region Static Methods
///
/// Creates a quaternion from a vector containing roll, pitch, and yaw
/// in radians
///
/// Vector representation of the euler angles in
/// radians
/// Quaternion representation of the euler angles
public static Quaternion FromEulers(Vector3 euler)
{
return FromEulers(euler.X, euler.Y, euler.Z);
}
///
/// Creates a quaternion from roll, pitch, and yaw euler angles in
/// radians
///
/// X angle in radians
/// Y angle in radians
/// Z angle in radians
/// Quaternion representation of the euler angles
public static Quaternion FromEulers(float roll, float pitch, float yaw)
{
if (roll > Helpers.TWO_PI || pitch > Helpers.TWO_PI || yaw > Helpers.TWO_PI)
throw new ArgumentException("Euler angles must be in radians");
double atCos = Math.Cos(roll / 2);
double atSin = Math.Sin(roll / 2);
double leftCos = Math.Cos(pitch / 2);
double leftSin = Math.Sin(pitch / 2);
double upCos = Math.Cos(yaw / 2);
double upSin = Math.Sin(yaw / 2);
double atLeftCos = atCos * leftCos;
double atLeftSin = atSin * leftSin;
return new Quaternion(
(float)(atSin * leftCos * upCos + atCos * leftSin * upSin),
(float)(atCos * leftSin * upCos - atSin * leftCos * upSin),
(float)(atLeftCos * upSin + atLeftSin * upCos),
(float)(atLeftCos * upCos - atLeftSin * upSin)
);
}
///
/// Calculate the magnitude of the supplied quaternion
///
public static float Mag(Quaternion q)
{
return (float)Math.Sqrt(q.W * q.W + q.X * q.X + q.Y * q.Y + q.Z * q.Z);
}
///
/// Returns a normalized version of the supplied quaternion
///
/// The quaternion to normalize
/// A normalized version of the quaternion
public static Quaternion Norm(Quaternion q)
{
const float MAG_THRESHOLD = 0.0000001f;
float mag = (float)Math.Sqrt(q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W);
if (mag > MAG_THRESHOLD)
{
float oomag = 1.0f / mag;
q.X *= oomag;
q.Y *= oomag;
q.Z *= oomag;
q.W *= oomag;
}
else
{
q.X = 0.0f;
q.Y = 0.0f;
q.Z = 0.0f;
q.W = 1.0f;
}
return q;
}
public static Quaternion Parse(string val)
{
char[] splitChar = { ',' };
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
if (split.Length == 3)
{
return new Quaternion(
float.Parse(split[0].Trim(), Helpers.EnUsCulture),
float.Parse(split[1].Trim(), Helpers.EnUsCulture),
float.Parse(split[2].Trim(), Helpers.EnUsCulture));
}
else
{
return new Quaternion(
float.Parse(split[0].Trim(), Helpers.EnUsCulture),
float.Parse(split[1].Trim(), Helpers.EnUsCulture),
float.Parse(split[2].Trim(), Helpers.EnUsCulture),
float.Parse(split[3].Trim(), Helpers.EnUsCulture));
}
}
public static bool TryParse(string val, out Quaternion result)
{
try
{
result = Parse(val);
return true;
}
catch (Exception)
{
result = new Quaternion();
return false;
}
}
#endregion Static Methods
#region Overrides
///
///
///
///
public override int GetHashCode()
{
return (X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode());
}
///
///
///
///
///
public override bool Equals(object o)
{
if (!(o is Quaternion)) return false;
Quaternion quaternion = (Quaternion)o;
return X == quaternion.X && Y == quaternion.Y && Z == quaternion.Z && W == quaternion.W;
}
///
///
///
///
public override string ToString()
{
return "<" + X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ", " + W.ToString() + ">";
}
#endregion Overrides
#region Operators
public static bool operator ==(Quaternion lhs, Quaternion rhs)
{
// Return true if the fields match:
return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z && lhs.W == rhs.W;
}
public static bool operator !=(Quaternion lhs, Quaternion rhs)
{
return !(lhs == rhs);
}
///
/// Performs quaternion multiplication
///
///
///
///
public static Quaternion operator *(Quaternion lhs, Quaternion rhs)
{
Quaternion ret = new Quaternion(
(lhs.W * rhs.X) + (lhs.X * rhs.W) + (lhs.Y * rhs.Z) - (lhs.Z * rhs.Y),
(lhs.W * rhs.Y) - (lhs.X * rhs.Z) + (lhs.Y * rhs.W) + (lhs.Z * rhs.X),
(lhs.W * rhs.Z) + (lhs.X * rhs.Y) - (lhs.Y * rhs.X) + (lhs.Z * rhs.W),
(lhs.W * rhs.W) - (lhs.X * rhs.X) - (lhs.Y * rhs.Y) - (lhs.Z * rhs.Z)
);
return ret;
}
///
/// Division operator (multiply by the conjugate)
///
///
///
///
public static Quaternion operator /(Quaternion lhs, Quaternion rhs)
{
return lhs * rhs.Conjugate;
}
#endregion Operators
/// An Quaternion with a value of 0,0,0,1
public readonly static Quaternion Identity = new Quaternion(0f, 0f, 0f, 1f);
}
///
/// A 3x3 row-major matrix
///
[Serializable]
public sealed class Matrix3
{
public float M11, M12, M13;
public float M21, M22, M23;
public float M31, M32, M33;
#region Properties
public float Trace
{
get
{
return M11 + M22 + M33;
}
}
public float Determinant
{
get
{
return M11 * M22 * M33 + M12 * M23 * M31 + M13 * M21 * M32 -
M13 * M22 * M31 - M11 * M23 * M32 - M12 * M21 * M33;
}
}
#endregion Properties
#region Constructors
public Matrix3()
{
}
public Matrix3(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33)
{
M11 = m11;
M12 = m12;
M13 = m13;
M21 = m21;
M22 = m22;
M23 = m23;
M31 = m31;
M32 = m32;
M33 = m33;
}
public Matrix3(float roll, float pitch, float yaw)
{
M11 = M12 = M13 = M21 = M22 = M23 = M31 = M32 = M33 = 0f;
FromEulers(roll, pitch, yaw);
}
///
/// Copy constructor
///
/// Matrix to copy
public Matrix3(Matrix3 m)
{
M11 = m.M11;
M12 = m.M12;
M13 = m.M13;
M21 = m.M21;
M22 = m.M22;
M23 = m.M23;
M31 = m.M31;
M32 = m.M32;
M33 = m.M33;
}
#endregion Constructors
#region Public Methods
///
/// Construct this matrix from euler rotation values
///
/// X euler angle
/// Y euler angle
/// Z euler angle
public void FromEulers(float roll, float pitch, float yaw)
{
// From the Matrix and Quaternion FAQ: http://www.j3d.org/matrix_faq/matrfaq_latest.html
float a, b, c, d, e, f;
float ad, bd;
a = (float)Math.Cos(roll);
b = (float)Math.Sin(roll);
c = (float)Math.Cos(pitch);
d = (float)Math.Sin(pitch);
e = (float)Math.Cos(yaw);
f = (float)Math.Sin(yaw);
ad = a * d;
bd = b * d;
M11 = c * e;
M12 = -c * f;
M13 = d;
M21 = bd * e + a * f;
M22 = -bd * f + a * e;
M23 = -b * c;
M31 = -ad * e + b * f;
M32 = ad * f + b * e;
M33 = a * c;
}
///
/// Convert this matrix to euler rotations
///
/// X euler angle
/// Y euler angle
/// Z euler angle
public void GetEulerAngles(out float roll, out float pitch, out float yaw)
{
// From the Matrix and Quaternion FAQ: http://www.j3d.org/matrix_faq/matrfaq_latest.html
double angleX, angleY, angleZ;
double cx, cy, cz; // cosines
double sx, sz; // sines
angleY = Math.Asin(Helpers.Clamp(M13, -1f, 1f));
cy = Math.Cos(angleY);
if (Math.Abs(cy) > 0.005f)
{
// No gimbal lock
cx = M33 / cy;
sx = (-M23) / cy;
angleX = (float)Math.Atan2(sx, cx);
cz = M11 / cy;
sz = (-M12) / cy;
angleZ = (float)Math.Atan2(sz, cz);
}
else
{
// Gimbal lock
angleX = 0;
cz = M22;
sz = M21;
angleZ = Math.Atan2(sz, cz);
}
// Return only positive angles in [0,360]
if (angleX < 0) angleX += 360d;
if (angleY < 0) angleY += 360d;
if (angleZ < 0) angleZ += 360d;
roll = (float)angleX;
pitch = (float)angleY;
yaw = (float)angleZ;
}
///
/// Conver this matrix to a quaternion rotation
///
/// A quaternion representation of this rotation matrix
public Quaternion ToQuaternion()
{
// From http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
Quaternion quat = new Quaternion();
float trace = Trace + 1f;
if (trace > Single.Epsilon)
{
float s = 0.5f / (float)Math.Sqrt(trace);
quat.X = (M32 - M23) * s;
quat.Y = (M13 - M31) * s;
quat.Z = (M21 - M12) * s;
quat.W = 0.25f / s;
}
else
{
if (M11 > M22 && M11 > M33)
{
float s = 2.0f * (float)Math.Sqrt(1.0f + M11 - M22 - M33);
quat.X = 0.25f * s;
quat.Y = (M12 + M21) / s;
quat.Z = (M13 + M31) / s;
quat.W = (M23 - M32) / s;
}
else if (M22 > M33)
{
float s = 2.0f * (float)Math.Sqrt(1.0f + M22 - M11 - M33);
quat.X = (M12 + M21) / s;
quat.Y = 0.25f * s;
quat.Z = (M23 + M32) / s;
quat.W = (M13 - M31) / s;
}
else
{
float s = 2.0f * (float)Math.Sqrt(1.0f + M33 - M11 - M22);
quat.X = (M13 + M31) / s;
quat.Y = (M23 + M32) / s;
quat.Z = 0.25f * s;
quat.W = (M12 - M21) / s;
}
}
return quat;
}
#endregion Public Methods
#region Static Methods
public static Matrix3 Add(Matrix3 left, Matrix3 right)
{
return new Matrix3(
left.M11 + right.M11, left.M12 + right.M12, left.M13 + right.M13,
left.M21 + right.M21, left.M22 + right.M22, left.M23 + right.M23,
left.M31 + right.M31, left.M32 + right.M32, left.M33 + right.M33
);
}
public static Matrix3 Add(Matrix3 matrix, float scalar)
{
return new Matrix3(
matrix.M11 + scalar, matrix.M12 + scalar, matrix.M13 + scalar,
matrix.M21 + scalar, matrix.M22 + scalar, matrix.M23 + scalar,
matrix.M31 + scalar, matrix.M32 + scalar, matrix.M33 + scalar
);
}
public static Matrix3 Subtract(Matrix3 left, Matrix3 right)
{
return new Matrix3(
left.M11 - right.M11, left.M12 - right.M12, left.M13 - right.M13,
left.M21 - right.M21, left.M22 - right.M22, left.M23 - right.M23,
left.M31 - right.M31, left.M32 - right.M32, left.M33 - right.M33
);
}
public static Matrix3 Subtract(Matrix3 matrix, float scalar)
{
return new Matrix3(
matrix.M11 - scalar, matrix.M12 - scalar, matrix.M13 - scalar,
matrix.M21 - scalar, matrix.M22 - scalar, matrix.M23 - scalar,
matrix.M31 - scalar, matrix.M32 - scalar, matrix.M33 - scalar
);
}
public static Matrix3 Multiply(Matrix3 left, Matrix3 right)
{
return new Matrix3(
left.M11 * right.M11 + left.M12 * right.M21 + left.M13 * right.M31,
left.M11 * right.M12 + left.M12 * right.M22 + left.M13 * right.M32,
left.M11 * right.M13 + left.M12 * right.M23 + left.M13 * right.M33,
left.M21 * right.M11 + left.M22 * right.M21 + left.M23 * right.M31,
left.M21 * right.M12 + left.M22 * right.M22 + left.M23 * right.M32,
left.M21 * right.M13 + left.M22 * right.M23 + left.M23 * right.M33,
left.M31 * right.M11 + left.M32 * right.M21 + left.M33 * right.M31,
left.M31 * right.M12 + left.M32 * right.M22 + left.M33 * right.M32,
left.M31 * right.M13 + left.M32 * right.M23 + left.M33 * right.M33
);
}
///
/// Transposes a matrix
///
/// Matrix to transpose
public static void Transpose(Matrix3 m)
{
Helpers.Swap(ref m.M12, ref m.M21);
Helpers.Swap(ref m.M13, ref m.M31);
Helpers.Swap(ref m.M23, ref m.M32);
}
#endregion Static Methods
#region Overrides
public override int GetHashCode()
{
return
M11.GetHashCode() ^ M12.GetHashCode() ^ M13.GetHashCode() ^
M21.GetHashCode() ^ M22.GetHashCode() ^ M23.GetHashCode() ^
M31.GetHashCode() ^ M32.GetHashCode() ^ M33.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is Matrix3)
{
Matrix3 m = (Matrix3)obj;
return
(M11 == m.M11) && (M12 == m.M12) && (M13 == m.M13) &&
(M21 == m.M21) && (M22 == m.M22) && (M23 == m.M23) &&
(M31 == m.M31) && (M32 == m.M32) && (M33 == m.M33);
}
return false;
}
public bool Equals(Matrix3 m)
{
return
(M11 == m.M11) && (M12 == m.M12) && (M13 == m.M13) &&
(M21 == m.M21) && (M22 == m.M22) && (M23 == m.M23) &&
(M31 == m.M31) && (M32 == m.M32) && (M33 == m.M33);
}
public override string ToString()
{
return string.Format("|{0}, {1}, {2}|\n|{3}, {4}, {5}|\n|{6}, {7}, {8}|",
M11, M12, M13, M21, M22, M23, M31, M32, M33);
}
#endregion Overrides
#region Operators
public static bool operator ==(Matrix3 left, Matrix3 right)
{
return left.Equals(right);
}
public static bool operator !=(Matrix3 left, Matrix3 right)
{
return !left.Equals(right);
}
public static Matrix3 operator +(Matrix3 left, Matrix3 right)
{
return Matrix3.Add(left, right);
}
public static Matrix3 operator +(Matrix3 matrix, float scalar)
{
return Matrix3.Add(matrix, scalar);
}
public static Matrix3 operator -(Matrix3 left, Matrix3 right)
{
return Matrix3.Subtract(left, right); ;
}
public static Matrix3 operator -(Matrix3 matrix, float scalar)
{
return Matrix3.Subtract(matrix, scalar);
}
public static Matrix3 operator *(Matrix3 left, Matrix3 right)
{
return Matrix3.Multiply(left, right); ;
}
public Vector3 this[int row]
{
get
{
switch (row)
{
case 0:
return new Vector3(M11, M12, M13);
case 1:
return new Vector3(M21, M22, M23);
case 2:
return new Vector3(M31, M32, M33);
default:
throw new IndexOutOfRangeException("Matrix3 row index must be from 0-2");
}
}
set
{
switch (row)
{
case 0:
M11 = value.X;
M12 = value.Y;
M13 = value.Z;
break;
case 1:
M21 = value.X;
M22 = value.Y;
M23 = value.Z;
break;
case 2:
M31 = value.X;
M32 = value.Y;
M33 = value.Z;
break;
default:
throw new IndexOutOfRangeException("Matrix3 row index must be from 0-2");
}
}
}
public float this[int row, int column]
{
get
{
switch (row)
{
case 0:
switch (column)
{
case 0:
return M11;
case 1:
return M12;
case 2:
return M13;
default:
throw new IndexOutOfRangeException("Matrix3 row and column values must be from 0-2");
}
case 1:
switch (column)
{
case 0:
return M21;
case 1:
return M22;
case 2:
return M23;
default:
throw new IndexOutOfRangeException("Matrix3 row and column values must be from 0-2");
}
case 2:
switch (column)
{
case 0:
return M31;
case 1:
return M32;
case 2:
return M33;
default:
throw new IndexOutOfRangeException("Matrix3 row and column values must be from 0-2");
}
default:
throw new IndexOutOfRangeException("Matrix3 row and column values must be from 0-2");
}
}
}
#endregion Operators
/// A 3x3 matrix set to all zeroes
public static readonly Matrix3 Zero = new Matrix3();
/// A 3x3 identity matrix
public static readonly Matrix3 Identity = new Matrix3(
1f, 0f, 0f,
0f, 1f, 0f,
0f, 0f, 1f
);
}
///
/// A 4x4 row-major matrix
///
[Serializable]
public sealed class Matrix4
{
public float M11, M12, M13, M14;
public float M21, M22, M23, M24;
public float M31, M32, M33, M34;
public float M41, M42, M43, M44;
#region Properties
public float Trace
{
get
{
return M11 + M22 + M33 + M44;
}
}
public float Determinant
{
get
{
return
M14 * M23 * M32 * M41 - M13 * M24 * M32 * M41 - M14 * M22 * M33 * M41 + M12 * M24 * M33 * M41 +
M13 * M22 * M34 * M41 - M12 * M23 * M34 * M41 - M14 * M23 * M31 * M42 + M13 * M24 * M31 * M42 +
M14 * M21 * M33 * M42 - M11 * M24 * M33 * M42 - M13 * M21 * M34 * M42 + M11 * M23 * M34 * M42 +
M14 * M22 * M31 * M43 - M12 * M24 * M31 * M43 - M14 * M21 * M32 * M43 + M11 * M24 * M32 * M43 +
M12 * M21 * M34 * M43 - M11 * M22 * M34 * M43 - M13 * M22 * M31 * M44 + M12 * M23 * M31 * M44 +
M13 * M21 * M32 * M44 - M11 * M23 * M32 * M44 - M12 * M21 * M33 * M44 + M11 * M22 * M33 * M44;
}
}
#endregion Properties
#region Constructors
public Matrix4()
{
}
public Matrix4(
float m11, float m12, float m13, float m14,
float m21, float m22, float m23, float m24,
float m31, float m32, float m33, float m34,
float m41, float m42, float m43, float m44)
{
M11 = m11;
M12 = m12;
M13 = m13;
M14 = m14;
M21 = m21;
M22 = m22;
M23 = m23;
M24 = m24;
M31 = m31;
M32 = m32;
M33 = m33;
M34 = m34;
M41 = m41;
M42 = m42;
M43 = m43;
M44 = m44;
}
public Matrix4(float roll, float pitch, float yaw)
{
M11 = M12 = M13 = M14 = M21 = M22 = M23 = M24 = M31 = M32 = M33 = M34 = M41 = M42 = M43 = M44 = 0f;
FromEulers(roll, pitch, yaw);
}
public Matrix4(Matrix3 m)
{
M11 = m.M11;
M12 = m.M12;
M13 = m.M13;
M14 = 0f;
M21 = m.M21;
M22 = m.M22;
M23 = m.M23;
M24 = 0f;
M31 = m.M31;
M32 = m.M32;
M33 = m.M33;
M34 = 0f;
M41 = 0f;
M42 = 0f;
M43 = 0f;
M44 = 1f;
}
public Matrix4(Matrix3 m, Vector3 translation)
{
M11 = m.M11;
M12 = m.M12;
M13 = m.M13;
M14 = 0f;
M21 = m.M21;
M22 = m.M22;
M23 = m.M23;
M24 = 0f;
M31 = m.M31;
M32 = m.M32;
M33 = m.M33;
M34 = 0f;
M41 = translation.X;
M42 = translation.Y;
M43 = translation.Z;
M44 = 1f;
}
///
/// Copy constructor
///
/// Matrix to copy
public Matrix4(Matrix4 m)
{
M11 = m.M11;
M12 = m.M12;
M13 = m.M13;
M14 = m.M14;
M21 = m.M21;
M22 = m.M22;
M23 = m.M23;
M24 = m.M24;
M31 = m.M31;
M32 = m.M32;
M33 = m.M33;
M34 = m.M34;
M41 = m.M41;
M42 = m.M42;
M43 = m.M43;
M44 = m.M44;
}
#endregion Constructors
#region Public Methods
///
/// Construct this matrix from euler rotation values
///
/// X euler angle
/// Y euler angle
/// Z euler angle
public void FromEulers(float roll, float pitch, float yaw)
{
// From the Matrix and Quaternion FAQ: http://www.j3d.org/matrix_faq/matrfaq_latest.html
float a, b, c, d, e, f;
float ad, bd;
a = (float)Math.Cos(roll);
b = (float)Math.Sin(roll);
c = (float)Math.Cos(pitch);
d = (float)Math.Sin(pitch);
e = (float)Math.Cos(yaw);
f = (float)Math.Sin(yaw);
ad = a * d;
bd = b * d;
M11 = c * e;
M12 = -c * f;
M13 = d;
M14 = 0f;
M21 = bd * e + a * f;
M22 = -bd * f + a * e;
M23 = -b * c;
M24 = 0f;
M31 = -ad * e + b * f;
M32 = ad * f + b * e;
M33 = a * c;
M34 = 0f;
M41 = M42 = M43 = 0f;
M44 = 1f;
}
///
/// Convert this matrix to euler rotations
///
/// X euler angle
/// Y euler angle
/// Z euler angle
public void GetEulerAngles(out float roll, out float pitch, out float yaw)
{
// From the Matrix and Quaternion FAQ: http://www.j3d.org/matrix_faq/matrfaq_latest.html
double angleX, angleY, angleZ;
double cx, cy, cz; // cosines
double sx, sz; // sines
angleY = Math.Asin(Helpers.Clamp(M13, -1f, 1f));
cy = Math.Cos(angleY);
if (Math.Abs(cy) > 0.005f)
{
// No gimbal lock
cx = M33 / cy;
sx = (-M23) / cy;
angleX = (float)Math.Atan2(sx, cx);
cz = M11 / cy;
sz = (-M12) / cy;
angleZ = (float)Math.Atan2(sz, cz);
}
else
{
// Gimbal lock
angleX = 0;
cz = M22;
sz = M21;
angleZ = Math.Atan2(sz, cz);
}
// Return only positive angles in [0,360]
if (angleX < 0) angleX += 360d;
if (angleY < 0) angleY += 360d;
if (angleZ < 0) angleZ += 360d;
roll = (float)angleX;
pitch = (float)angleY;
yaw = (float)angleZ;
}
///
/// Conver this matrix to a quaternion rotation
///
/// A quaternion representation of this rotation matrix
public Quaternion ToQuaternion()
{
// From http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
Quaternion quat = new Quaternion();
float trace = Trace + 1f;
if (trace > Single.Epsilon)
{
float s = 0.5f / (float)Math.Sqrt(trace);
quat.X = (M32 - M23) * s;
quat.Y = (M13 - M31) * s;
quat.Z = (M21 - M12) * s;
quat.W = 0.25f / s;
}
else
{
if (M11 > M22 && M11 > M33)
{
float s = 2.0f * (float)Math.Sqrt(1.0f + M11 - M22 - M33);
quat.X = 0.25f * s;
quat.Y = (M12 + M21) / s;
quat.Z = (M13 + M31) / s;
quat.W = (M23 - M32) / s;
}
else if (M22 > M33)
{
float s = 2.0f * (float)Math.Sqrt(1.0f + M22 - M11 - M33);
quat.X = (M12 + M21) / s;
quat.Y = 0.25f * s;
quat.Z = (M23 + M32) / s;
quat.W = (M13 - M31) / s;
}
else
{
float s = 2.0f * (float)Math.Sqrt(1.0f + M33 - M11 - M22);
quat.X = (M13 + M31) / s;
quat.Y = (M23 + M32) / s;
quat.Z = 0.25f * s;
quat.W = (M12 - M21) / s;
}
}
return quat;
}
#endregion Public Methods
#region Static Methods
public static Matrix4 Add(Matrix4 left, Matrix4 right)
{
return new Matrix4(
left.M11 + right.M11, left.M12 + right.M12, left.M13 + right.M13, left.M14 + right.M14,
left.M21 + right.M21, left.M22 + right.M22, left.M23 + right.M23, left.M24 + right.M24,
left.M31 + right.M31, left.M32 + right.M32, left.M33 + right.M33, left.M34 + right.M34,
left.M41 + right.M41, left.M42 + right.M42, left.M43 + right.M43, left.M44 + right.M44
);
}
public static Matrix4 Add(Matrix4 matrix, float scalar)
{
return new Matrix4(
matrix.M11 + scalar, matrix.M12 + scalar, matrix.M13 + scalar, matrix.M14 + scalar,
matrix.M21 + scalar, matrix.M22 + scalar, matrix.M23 + scalar, matrix.M24 + scalar,
matrix.M31 + scalar, matrix.M32 + scalar, matrix.M33 + scalar, matrix.M34 + scalar,
matrix.M41 + scalar, matrix.M42 + scalar, matrix.M43 + scalar, matrix.M44 + scalar
);
}
public static Matrix4 Subtract(Matrix4 left, Matrix4 right)
{
return new Matrix4(
left.M11 - right.M11, left.M12 - right.M12, left.M13 - right.M13, left.M14 - right.M14,
left.M21 - right.M21, left.M22 - right.M22, left.M23 - right.M23, left.M24 - right.M24,
left.M31 - right.M31, left.M32 - right.M32, left.M33 - right.M33, left.M34 - right.M34,
left.M41 - right.M41, left.M42 - right.M42, left.M43 - right.M43, left.M44 - right.M44
);
}
public static Matrix4 Subtract(Matrix4 matrix, float scalar)
{
return new Matrix4(
matrix.M11 - scalar, matrix.M12 - scalar, matrix.M13 - scalar, matrix.M14 - scalar,
matrix.M21 - scalar, matrix.M22 - scalar, matrix.M23 - scalar, matrix.M24 - scalar,
matrix.M31 - scalar, matrix.M32 - scalar, matrix.M33 - scalar, matrix.M34 - scalar,
matrix.M41 - scalar, matrix.M42 - scalar, matrix.M43 - scalar, matrix.M44 - scalar
);
}
public static Matrix4 Multiply(Matrix4 left, Matrix4 right)
{
return new Matrix4(
left.M11*right.M11 + left.M12*right.M21 + left.M13*right.M31 + left.M14*right.M41,
left.M11*right.M12 + left.M12*right.M22 + left.M13*right.M32 + left.M14*right.M42,
left.M11*right.M13 + left.M12*right.M23 + left.M13*right.M33 + left.M14*right.M43,
left.M11*right.M14 + left.M12*right.M24 + left.M13*right.M34 + left.M14*right.M44,
left.M21*right.M11 + left.M22*right.M21 + left.M23*right.M31 + left.M24*right.M41,
left.M21*right.M12 + left.M22*right.M22 + left.M23*right.M32 + left.M24*right.M42,
left.M21*right.M13 + left.M22*right.M23 + left.M23*right.M33 + left.M24*right.M43,
left.M21*right.M14 + left.M22*right.M24 + left.M23*right.M34 + left.M24*right.M44,
left.M31*right.M11 + left.M32*right.M21 + left.M33*right.M31 + left.M34*right.M41,
left.M31*right.M12 + left.M32*right.M22 + left.M33*right.M32 + left.M34*right.M42,
left.M31*right.M13 + left.M32*right.M23 + left.M33*right.M33 + left.M34*right.M43,
left.M31*right.M14 + left.M32*right.M24 + left.M33*right.M34 + left.M34*right.M44,
left.M41*right.M11 + left.M42*right.M21 + left.M43*right.M31 + left.M44*right.M41,
left.M41*right.M12 + left.M42*right.M22 + left.M43*right.M32 + left.M44*right.M42,
left.M41*right.M13 + left.M42*right.M23 + left.M43*right.M33 + left.M44*right.M43,
left.M41*right.M14 + left.M42*right.M24 + left.M43*right.M34 + left.M44*right.M44
);
}
///
/// Transposes a matrix
///
/// Matrix to transpose
public static void Transpose(Matrix4 m)
{
Helpers.Swap(ref m.M12, ref m.M21);
Helpers.Swap(ref m.M13, ref m.M31);
Helpers.Swap(ref m.M14, ref m.M41);
Helpers.Swap(ref m.M23, ref m.M32);
Helpers.Swap(ref m.M24, ref m.M42);
Helpers.Swap(ref m.M34, ref m.M43);
}
#endregion Static Methods
#region Overrides
public override int GetHashCode()
{
return
M11.GetHashCode() ^ M12.GetHashCode() ^ M13.GetHashCode() ^ M14.GetHashCode() ^
M21.GetHashCode() ^ M22.GetHashCode() ^ M23.GetHashCode() ^ M24.GetHashCode() ^
M31.GetHashCode() ^ M32.GetHashCode() ^ M33.GetHashCode() ^ M34.GetHashCode() ^
M41.GetHashCode() ^ M42.GetHashCode() ^ M43.GetHashCode() ^ M44.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is Matrix4)
{
Matrix4 m = (Matrix4)obj;
return
(M11 == m.M11) && (M12 == m.M12) && (M13 == m.M13) && (M14 == m.M14) &&
(M21 == m.M21) && (M22 == m.M22) && (M23 == m.M23) && (M24 == m.M24) &&
(M31 == m.M31) && (M32 == m.M32) && (M33 == m.M33) && (M34 == m.M34) &&
(M41 == m.M41) && (M42 == m.M42) && (M43 == m.M43) && (M44 == m.M44);
}
return false;
}
public bool Equals(Matrix4 m)
{
return
(M11 == m.M11) && (M12 == m.M12) && (M13 == m.M13) && (M14 == m.M14) &&
(M21 == m.M21) && (M22 == m.M22) && (M23 == m.M23) && (M24 == m.M24) &&
(M31 == m.M31) && (M32 == m.M32) && (M33 == m.M33) && (M34 == m.M34) &&
(M41 == m.M41) && (M42 == m.M42) && (M43 == m.M43) && (M44 == m.M44);
}
public override string ToString()
{
return string.Format(
"|{0}, {1}, {2}, {3}|\n|{4}, {5}, {6}, {7}|\n|{8}, {9}, {10}, {11}|\n|{12}, {13}, {14}, {15}|",
M11, M12, M13, M14, M21, M22, M23, M24, M31, M32, M33, M34, M41, M42, M43, M44);
}
#endregion Overrides
#region Operators
public static bool operator ==(Matrix4 left, Matrix4 right)
{
return left.Equals(right);
}
public static bool operator !=(Matrix4 left, Matrix4 right)
{
return !left.Equals(right);
}
public static Matrix4 operator +(Matrix4 left, Matrix4 right)
{
return Matrix4.Add(left, right);
}
public static Matrix4 operator +(Matrix4 matrix, float scalar)
{
return Matrix4.Add(matrix, scalar);
}
public static Matrix4 operator -(Matrix4 left, Matrix4 right)
{
return Matrix4.Subtract(left, right); ;
}
public static Matrix4 operator -(Matrix4 matrix, float scalar)
{
return Matrix4.Subtract(matrix, scalar);
}
public static Matrix4 operator *(Matrix4 left, Matrix4 right)
{
return Matrix4.Multiply(left, right); ;
}
public Vector4 this[int row]
{
get
{
switch (row)
{
case 0:
return new Vector4(M11, M12, M13, M14);
case 1:
return new Vector4(M21, M22, M23, M24);
case 2:
return new Vector4(M31, M32, M33, M34);
case 3:
return new Vector4(M41, M42, M43, M44);
default:
throw new IndexOutOfRangeException("Matrix4 row index must be from 0-3");
}
}
set
{
switch (row)
{
case 0:
M11 = value.X;
M12 = value.Y;
M13 = value.Z;
M14 = value.S;
break;
case 1:
M21 = value.X;
M22 = value.Y;
M23 = value.Z;
M24 = value.S;
break;
case 2:
M31 = value.X;
M32 = value.Y;
M33 = value.Z;
M34 = value.S;
break;
case 3:
M41 = value.X;
M42 = value.Y;
M43 = value.Z;
M44 = value.S;
break;
default:
throw new IndexOutOfRangeException("Matrix4 row index must be from 0-3");
}
}
}
public float this[int row, int column]
{
get
{
switch (row)
{
case 0:
switch (column)
{
case 0:
return M11;
case 1:
return M12;
case 2:
return M13;
case 3:
return M14;
default:
throw new IndexOutOfRangeException("Matrix4 row and column values must be from 0-3");
}
case 1:
switch (column)
{
case 0:
return M21;
case 1:
return M22;
case 2:
return M23;
case 3:
return M24;
default:
throw new IndexOutOfRangeException("Matrix4 row and column values must be from 0-3");
}
case 2:
switch (column)
{
case 0:
return M31;
case 1:
return M32;
case 2:
return M33;
case 3:
return M34;
default:
throw new IndexOutOfRangeException("Matrix4 row and column values must be from 0-3");
}
case 3:
switch (column)
{
case 0:
return M41;
case 1:
return M42;
case 2:
return M43;
case 3:
return M44;
default:
throw new IndexOutOfRangeException("Matrix4 row and column values must be from 0-3");
}
default:
throw new IndexOutOfRangeException("Matrix4 row and column values must be from 0-3");
}
}
}
#endregion Operators
/// A 4x4 matrix set to all zeroes
public static readonly Matrix4 Zero = new Matrix4();
/// A 4x4 identity matrix
public static readonly Matrix4 Identity = new Matrix4(
1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f
);
}
}