Files
libremetaverse/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerTypes.cs
John Hurliman 3e5fcb7fab * Moving conversion functions from Helpers to Utils (in OpenMetaverseTypes)
* Added GetLLSD() and FromLLSD() to Permissions
* Started on inventory persistence for Simian

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@2267 52acb1d6-8a22-11de-b505-999d5b087335
2008-10-06 22:34:38 +00:00

3000 lines
93 KiB
C#

/*
* Copyright (c) 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.
*
*
* This code comes from the OpenSim project. Meshmerizer is written by dahlia
* <dahliatrimble@gmail.com>
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Globalization;
namespace OpenMetaverse.Rendering
{
public enum ProfileShape : byte
{
Circle = 0,
Square = 1,
IsometricTriangle = 2,
EquilateralTriangle = 3,
RightTriangle = 4,
HalfCircle = 5
}
public enum HollowShape : byte
{
Same = 0,
Circle = 16,
Square = 32,
Triangle = 48
}
public enum PCodeEnum : byte
{
Primitive = 9,
Avatar = 47,
Grass = 95,
NewTree = 111,
ParticleSystem = 143,
Tree = 255
}
public enum Extrusion : byte
{
Straight = 16,
Curve1 = 32,
Curve2 = 48,
Flexible = 128
}
public class PhysicsVector
{
public float X;
public float Y;
public float Z;
public PhysicsVector()
{
}
public PhysicsVector(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public void setValues(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public static readonly PhysicsVector Zero = new PhysicsVector(0f, 0f, 0f);
public override string ToString()
{
return "<" + X + "," + Y + "," + Z + ">";
}
/// <summary>
/// These routines are the easiest way to store XYZ values in an Vector3 without requiring 3 calls.
/// </summary>
/// <returns></returns>
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 void FromBytes(byte[] byteArray, int pos)
{
byte[] conversionBuffer = null;
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);
}
}
// Operations
public static PhysicsVector operator +(PhysicsVector a, PhysicsVector b)
{
return new PhysicsVector(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
}
public static PhysicsVector operator -(PhysicsVector a, PhysicsVector b)
{
return new PhysicsVector(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
public static PhysicsVector cross(PhysicsVector a, PhysicsVector b)
{
return new PhysicsVector(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
}
public float length()
{
return (float)Math.Sqrt(X * X + Y * Y + Z * Z);
}
public static float GetDistanceTo(PhysicsVector a, PhysicsVector b)
{
float dx = a.X - b.X;
float dy = a.Y - b.Y;
float dz = a.Z - b.Z;
return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
public static PhysicsVector operator /(PhysicsVector v, float f)
{
return new PhysicsVector(v.X / f, v.Y / f, v.Z / f);
}
public static PhysicsVector operator *(PhysicsVector v, float f)
{
return new PhysicsVector(v.X * f, v.Y * f, v.Z * f);
}
public static PhysicsVector operator *(float f, PhysicsVector v)
{
return v * f;
}
public virtual bool IsIdentical(PhysicsVector v, float tolerance)
{
PhysicsVector diff = this - v;
float d = diff.length();
if (d <= tolerance)
return true;
return false;
}
}
public class MeshmerizerVertex : PhysicsVector, IComparable<MeshmerizerVertex>
{
public MeshmerizerVertex(float x, float y, float z)
: base(x, y, z)
{
}
public MeshmerizerVertex normalize()
{
float tlength = length();
if (tlength != 0)
{
float mul = 1.0f / tlength;
return new MeshmerizerVertex(X * mul, Y * mul, Z * mul);
}
else
{
return new MeshmerizerVertex(0, 0, 0);
}
}
public MeshmerizerVertex cross(MeshmerizerVertex v)
{
return new MeshmerizerVertex(Y * v.Z - Z * v.Y, Z * v.X - X * v.Z, X * v.Y - Y * v.X);
}
// disable warning: mono compiler moans about overloading
// operators hiding base operator but should not according to C#
// language spec
#pragma warning disable 0108
public static MeshmerizerVertex operator *(MeshmerizerVertex v, Quaternion q)
{
// From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/
MeshmerizerVertex v2 = new MeshmerizerVertex(0f, 0f, 0f);
v2.X = q.W * q.W * v.X +
2f * q.Y * q.W * v.Z -
2f * q.Z * q.W * v.Y +
q.X * q.X * v.X +
2f * q.Y * q.X * v.Y +
2f * q.Z * q.X * v.Z -
q.Z * q.Z * v.X -
q.Y * q.Y * v.X;
v2.Y =
2f * q.X * q.Y * v.X +
q.Y * q.Y * v.Y +
2f * q.Z * q.Y * v.Z +
2f * q.W * q.Z * v.X -
q.Z * q.Z * v.Y +
q.W * q.W * v.Y -
2f * q.X * q.W * v.Z -
q.X * q.X * v.Y;
v2.Z =
2f * q.X * q.Z * v.X +
2f * q.Y * q.Z * v.Y +
q.Z * q.Z * v.Z -
2f * q.W * q.Y * v.X -
q.Y * q.Y * v.Z +
2f * q.W * q.X * v.Y -
q.X * q.X * v.Z +
q.W * q.W * v.Z;
return v2;
}
public static MeshmerizerVertex operator +(MeshmerizerVertex v1, MeshmerizerVertex v2)
{
return new MeshmerizerVertex(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
public static MeshmerizerVertex operator -(MeshmerizerVertex v1, MeshmerizerVertex v2)
{
return new MeshmerizerVertex(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
public static MeshmerizerVertex operator *(MeshmerizerVertex v1, MeshmerizerVertex v2)
{
return new MeshmerizerVertex(v1.X * v2.X, v1.Y * v2.Y, v1.Z * v2.Z);
}
public static MeshmerizerVertex operator +(MeshmerizerVertex v1, float am)
{
v1.X += am;
v1.Y += am;
v1.Z += am;
return v1;
}
public static MeshmerizerVertex operator -(MeshmerizerVertex v1, float am)
{
v1.X -= am;
v1.Y -= am;
v1.Z -= am;
return v1;
}
public static MeshmerizerVertex operator *(MeshmerizerVertex v1, float am)
{
v1.X *= am;
v1.Y *= am;
v1.Z *= am;
return v1;
}
public static MeshmerizerVertex operator /(MeshmerizerVertex v1, float am)
{
if (am == 0f)
{
return new MeshmerizerVertex(0f, 0f, 0f);
}
float mul = 1.0f / am;
v1.X *= mul;
v1.Y *= mul;
v1.Z *= mul;
return v1;
}
#pragma warning restore 0108
public float dot(MeshmerizerVertex v)
{
return X * v.X + Y * v.Y + Z * v.Z;
}
public MeshmerizerVertex(PhysicsVector v)
: base(v.X, v.Y, v.Z)
{
}
public MeshmerizerVertex Clone()
{
return new MeshmerizerVertex(X, Y, Z);
}
public static MeshmerizerVertex FromAngle(double angle)
{
return new MeshmerizerVertex((float)Math.Cos(angle), (float)Math.Sin(angle), 0.0f);
}
public virtual bool Equals(MeshmerizerVertex v, float tolerance)
{
PhysicsVector diff = this - v;
float d = diff.length();
if (d < tolerance)
return true;
return false;
}
public int CompareTo(MeshmerizerVertex other)
{
if (X < other.X)
return -1;
if (X > other.X)
return 1;
if (Y < other.Y)
return -1;
if (Y > other.Y)
return 1;
if (Z < other.Z)
return -1;
if (Z > other.Z)
return 1;
return 0;
}
public static bool operator >(MeshmerizerVertex me, MeshmerizerVertex other)
{
return me.CompareTo(other) > 0;
}
public static bool operator <(MeshmerizerVertex me, MeshmerizerVertex other)
{
return me.CompareTo(other) < 0;
}
public String ToRaw()
{
// Why this stuff with the number formatter?
// Well, the raw format uses the english/US notation of numbers
// where the "," separates groups of 1000 while the "." marks the border between 1 and 10E-1.
// The german notation uses these characters exactly vice versa!
// The Float.ToString() routine is a localized one, giving different results depending on the country
// settings your machine works with. Unusable for a machine readable file format :-(
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ".";
nfi.NumberDecimalDigits = 3;
String s1 = X.ToString("N2", nfi) + " " + Y.ToString("N2", nfi) + " " + Z.ToString("N2", nfi);
return s1;
}
}
public class Triangle
{
public MeshmerizerVertex v1;
public MeshmerizerVertex v2;
public MeshmerizerVertex v3;
private float radius_square;
private float cx;
private float cy;
public Triangle(MeshmerizerVertex _v1, MeshmerizerVertex _v2, MeshmerizerVertex _v3)
{
v1 = _v1;
v2 = _v2;
v3 = _v3;
CalcCircle();
}
public bool isInCircle(float x, float y)
{
float dx, dy;
float dd;
dx = x - cx;
dy = y - cy;
dd = dx * dx + dy * dy;
if (dd < radius_square)
return true;
else
return false;
}
public bool isDegraded()
{
// This means, the vertices of this triangle are somewhat strange.
// They either line up or at least two of them are identical
return (radius_square == 0.0);
}
private void CalcCircle()
{
// Calculate the center and the radius of a circle given by three points p1, p2, p3
// It is assumed, that the triangles vertices are already set correctly
double p1x, p2x, p1y, p2y, p3x, p3y;
// Deviation of this routine:
// A circle has the general equation (M-p)^2=r^2, where M and p are vectors
// this gives us three equations f(p)=r^2, each for one point p1, p2, p3
// putting respectively two equations together gives two equations
// f(p1)=f(p2) and f(p1)=f(p3)
// bringing all constant terms to one side brings them to the form
// M*v1=c1 resp.M*v2=c2 where v1=(p1-p2) and v2=(p1-p3) (still vectors)
// and c1, c2 are scalars (Naming conventions like the variables below)
// Now using the equations that are formed by the components of the vectors
// and isolate Mx lets you make one equation that only holds My
// The rest is straight forward and eaasy :-)
//
/* helping variables for temporary results */
double c1, c2;
double v1x, v1y, v2x, v2y;
double z, n;
double rx, ry;
// Readout the three points, the triangle consists of
p1x = v1.X;
p1y = v1.Y;
p2x = v2.X;
p2y = v2.Y;
p3x = v3.X;
p3y = v3.Y;
/* calc helping values first */
c1 = (p1x * p1x + p1y * p1y - p2x * p2x - p2y * p2y) / 2;
c2 = (p1x * p1x + p1y * p1y - p3x * p3x - p3y * p3y) / 2;
v1x = p1x - p2x;
v1y = p1y - p2y;
v2x = p1x - p3x;
v2y = p1y - p3y;
z = (c1 * v2x - c2 * v1x);
n = (v1y * v2x - v2y * v1x);
if (n == 0.0) // This is no triangle, i.e there are (at least) two points at the same location
{
radius_square = 0.0f;
return;
}
cy = (float)(z / n);
if (v2x != 0.0)
{
cx = (float)((c2 - v2y * cy) / v2x);
}
else if (v1x != 0.0)
{
cx = (float)((c1 - v1y * cy) / v1x);
}
else
{
throw new Exception("Malformed triangle");
}
rx = (p1x - cx);
ry = (p1y - cy);
radius_square = (float)(rx * rx + ry * ry);
}
public List<Simplex> GetSimplices()
{
List<Simplex> result = new List<Simplex>();
Simplex s1 = new Simplex(v1, v2);
Simplex s2 = new Simplex(v2, v3);
Simplex s3 = new Simplex(v3, v1);
result.Add(s1);
result.Add(s2);
result.Add(s3);
return result;
}
public override String ToString()
{
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.CurrencyDecimalDigits = 2;
nfi.CurrencyDecimalSeparator = ".";
String s1 = "<" + v1.X.ToString(nfi) + "," + v1.Y.ToString(nfi) + "," + v1.Z.ToString(nfi) + ">";
String s2 = "<" + v2.X.ToString(nfi) + "," + v2.Y.ToString(nfi) + "," + v2.Z.ToString(nfi) + ">";
String s3 = "<" + v3.X.ToString(nfi) + "," + v3.Y.ToString(nfi) + "," + v3.Z.ToString(nfi) + ">";
return s1 + ";" + s2 + ";" + s3;
}
public PhysicsVector getNormal()
{
// Vertices
// Vectors for edges
PhysicsVector e1;
PhysicsVector e2;
e1 = new PhysicsVector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
e2 = new PhysicsVector(v1.X - v3.X, v1.Y - v3.Y, v1.Z - v3.Z);
// Cross product for normal
PhysicsVector n = PhysicsVector.cross(e1, e2);
// Length
float l = n.length();
// Normalized "normal"
n = n / l;
return n;
}
public void invertNormal()
{
MeshmerizerVertex vt;
vt = v1;
v1 = v2;
v2 = vt;
}
// Dumps a triangle in the "raw faces" format, blender can import. This is for visualisation and
// debugging purposes
public String ToStringRaw()
{
String output = v1.ToRaw() + " " + v2.ToRaw() + " " + v3.ToRaw();
return output;
}
}
public struct Coord
{
public float X;
public float Y;
public float Z;
public Coord(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public override string ToString()
{
return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString();
}
}
public struct MeshmerizerFace
{
public int v1;
public int v2;
public int v3;
public MeshmerizerFace(int v1, int v2, int v3)
{
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
}
internal struct Angle
{
internal float angle;
internal float X;
internal float Y;
internal Angle(float angle, float x, float y)
{
this.angle = angle;
this.X = x;
this.Y = y;
}
}
class AngleList
{
private float iX, iY; // intersection point
private Angle[] angles3 =
{
new Angle(0.0f, 1.0f, 0.0f),
new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
new Angle(1.0f, 1.0f, 0.0f)
};
private Angle[] angles4 =
{
new Angle(0.0f, 1.0f, 0.0f),
new Angle(0.25f, 0.0f, 1.0f),
new Angle(0.5f, -1.0f, 0.0f),
new Angle(0.75f, 0.0f, -1.0f),
new Angle(1.0f, 1.0f, 0.0f)
};
private Angle[] angles24 =
{
new Angle(0.0f, 1.0f, 0.0f),
new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f),
new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f),
new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f),
new Angle(0.25f, 0.0f, 1.0f),
new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f),
new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f),
new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f),
new Angle(0.5f, -1.0f, 0.0f),
new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f),
new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f),
new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f),
new Angle(0.75f, 0.0f, -1.0f),
new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f),
new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f),
new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f),
new Angle(1.0f, 1.0f, 0.0f)
};
private Angle interpolatePoints(float newPoint, Angle p1, Angle p2)
{
float m = (newPoint - p1.angle) / (p2.angle - p1.angle);
return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y));
}
private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{ // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
if (denom != 0.0)
{
double ua = uaNumerator / denom;
iX = (float)(x1 + ua * (x2 - x1));
iY = (float)(y1 + ua * (y2 - y1));
}
}
internal List<Angle> angles;
internal void makeAngles(int sides, float startAngle, float stopAngle)
{
angles = new List<Angle>();
double twoPi = System.Math.PI * 2.0;
float twoPiInv = 1.0f / (float)twoPi;
if (sides < 1)
throw new Exception("number of sides not greater than zero");
if (stopAngle <= startAngle)
throw new Exception("stopAngle not greater than startAngle");
if ((sides == 3 || sides == 4 || sides == 24))
{
startAngle *= twoPiInv;
stopAngle *= twoPiInv;
Angle[] sourceAngles;
if (sides == 3)
sourceAngles = angles3;
else if (sides == 4)
sourceAngles = angles4;
else sourceAngles = angles24;
int startAngleIndex = (int)(startAngle * sides);
int endAngleIndex = sourceAngles.Length - 1;
if (stopAngle < 1.0f)
endAngleIndex = (int)(stopAngle * sides) + 1;
if (endAngleIndex == startAngleIndex)
endAngleIndex++;
for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++)
angles.Add(sourceAngles[angleIndex]);
if (startAngle > 0.0f)
angles[0] = interpolatePoints(startAngle, angles[0], angles[1]);
if (stopAngle < 1.0f)
{
int lastAngleIndex = angles.Count - 1;
angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]);
}
}
else
{
double stepSize = twoPi / sides;
int startStep = (int)(startAngle / stepSize);
double angle = stepSize * startStep;
int step = startStep;
double stopAngleTest = stopAngle;
if (stopAngle < twoPi)
{
stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1);
if (stopAngleTest < stopAngle)
stopAngleTest += stepSize;
if (stopAngleTest > twoPi)
stopAngleTest = twoPi;
}
while (angle <= stopAngleTest)
{
Angle newAngle;
newAngle.angle = (float)angle;
newAngle.X = (float)System.Math.Cos(angle);
newAngle.Y = (float)System.Math.Sin(angle);
angles.Add(newAngle);
step += 1;
angle = stepSize * step;
}
if (startAngle > angles[0].angle)
{
Angle newAngle;
intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle));
newAngle.angle = startAngle;
newAngle.X = iX;
newAngle.Y = iY;
angles[0] = newAngle;
}
int index = angles.Count - 1;
if (stopAngle < angles[index].angle)
{
Angle newAngle;
intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle));
newAngle.angle = stopAngle;
newAngle.X = iX;
newAngle.Y = iY;
angles[index] = newAngle;
}
}
}
}
// A simplex is a section of a straight line.
// It is defined by its endpoints, i.e. by two vertices
// Operation on vertices are
public class Simplex : IComparable<Simplex>
{
public MeshmerizerVertex v1;
public MeshmerizerVertex v2;
public Simplex(MeshmerizerVertex _v1, MeshmerizerVertex _v2)
{
v1 = _v1;
v2 = _v2;
}
public int CompareTo(Simplex other)
{
MeshmerizerVertex lv1, lv2, ov1, ov2, temp;
lv1 = v1;
lv2 = v2;
ov1 = other.v1;
ov2 = other.v2;
if (lv1 > lv2)
{
temp = lv1;
lv1 = lv2;
lv2 = temp;
}
if (ov1 > ov2)
{
temp = ov1;
ov1 = ov2;
ov2 = temp;
}
if (lv1 > ov1)
{
return 1;
}
if (lv1 < ov1)
{
return -1;
}
if (lv2 > ov2)
{
return 1;
}
if (lv2 < ov2)
{
return -1;
}
return 0;
}
private static void intersectParameter(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, PhysicsVector r2,
ref float lambda, ref float mu)
{
// Intersects two straights
// p1, p2, points on the straight
// r1, r2, directional vectors of the straight. Not necessarily of length 1!
// note, that l, m can be scaled such, that the range 0..1 is mapped to the area between two points,
// thus allowing to decide whether an intersection is between two points
float r1x = r1.X;
float r1y = r1.Y;
float r2x = r2.X;
float r2y = r2.Y;
float denom = r1y * r2x - r1x * r2y;
float p1x = p1.X;
float p1y = p1.Y;
float p2x = p2.X;
float p2y = p2.Y;
float z1 = -p2x * r2y + p1x * r2y + (p2y - p1y) * r2x;
float z2 = -p2x * r1y + p1x * r1y + (p2y - p1y) * r1x;
if (denom == 0.0f) // Means the straights are parallel. Either no intersection or an infinite number of them
{
if (z1 == 0.0f)
{
// Means they are identical -> many, many intersections
lambda = Single.NaN;
mu = Single.NaN;
}
else
{
lambda = Single.PositiveInfinity;
mu = Single.PositiveInfinity;
}
return;
}
lambda = z1 / denom;
mu = z2 / denom;
}
// Intersects the simplex with another one.
// the borders are used to deal with float inaccuracies
// As a rule of thumb, the borders are
// lowerBorder1 : 0.0
// lowerBorder2 : 0.0
// upperBorder1 : 1.0
// upperBorder2 : 1.0
// Set these to values near the given parameters (e.g. 0.001 instead of 1 to exclude simplex starts safely, or to -0.001 to include them safely)
public static PhysicsVector Intersect(
Simplex s1,
Simplex s2,
float lowerBorder1,
float lowerBorder2,
float upperBorder1,
float upperBorder2)
{
PhysicsVector firstSimplexDirection = s1.v2 - s1.v1;
PhysicsVector secondSimplexDirection = s2.v2 - s2.v1;
float lambda = 0.0f;
float mu = 0.0f;
// Give us the parameters of an intersection. This subroutine does *not* take the constraints
// (intersection must be between v1 and v2 and it must be in the positive direction of the ray)
// into account. We do that afterwards.
intersectParameter(s1.v1, firstSimplexDirection, s2.v1, secondSimplexDirection, ref lambda, ref mu);
if (Single.IsInfinity(lambda)) // Special case. No intersection at all. directions parallel.
return null;
if (Single.IsNaN(lambda)) // Special case. many, many intersections.
return null;
if (lambda > upperBorder1) // We're behind v2
return null;
if (lambda < lowerBorder1)
return null;
if (mu < lowerBorder2) // outside simplex 2
return null;
if (mu > upperBorder2) // outside simplex 2
return null;
return s1.v1 + lambda * firstSimplexDirection;
}
// Intersects the simplex with a ray. The ray is defined as all p=origin + lambda*direction
// where lambda >= 0
public PhysicsVector RayIntersect(MeshmerizerVertex origin, PhysicsVector direction, bool bEndsIncluded)
{
PhysicsVector simplexDirection = v2 - v1;
float lambda = 0.0f;
float mu = 0.0f;
// Give us the parameters of an intersection. This subroutine does *not* take the constraints
// (intersection must be between v1 and v2 and it must be in the positive direction of the ray)
// into account. We do that afterwards.
intersectParameter(v1, simplexDirection, origin, direction, ref lambda, ref mu);
if (Single.IsInfinity(lambda)) // Special case. No intersection at all. directions parallel.
return null;
if (Single.IsNaN(lambda)) // Special case. many, many intersections.
return null;
if (mu < 0.0) // We're on the wrong side of the ray
return null;
if (lambda > 1.0) // We're behind v2
return null;
if (lambda == 1.0 && !bEndsIncluded)
return null; // The end of the simplices are not included
if (lambda < 0.0f) // we're before v1;
return null;
return v1 + lambda * simplexDirection;
}
}
public class PrimitiveBaseShape
{
private byte[] m_textureEntry;
private ushort _pathBegin;
private byte _pathCurve;
private ushort _pathEnd;
private sbyte _pathRadiusOffset;
private byte _pathRevolutions;
private byte _pathScaleX;
private byte _pathScaleY;
private byte _pathShearX;
private byte _pathShearY;
private sbyte _pathSkew;
private sbyte _pathTaperX;
private sbyte _pathTaperY;
private sbyte _pathTwist;
private sbyte _pathTwistBegin;
private byte _pCode;
private ushort _profileBegin;
private ushort _profileEnd;
private ushort _profileHollow;
private Vector3 _scale;
private byte _state;
private ProfileShape _profileShape;
private HollowShape _hollowShape;
// Sculpted
private UUID _sculptTexture = UUID.Zero;
private byte _sculptType = (byte)0;
private byte[] _sculptData = new byte[0];
// Flexi
private int _flexiSoftness = 0;
private float _flexiTension = 0f;
private float _flexiDrag = 0f;
private float _flexiGravity = 0f;
private float _flexiWind = 0f;
private float _flexiForceX = 0f;
private float _flexiForceY = 0f;
private float _flexiForceZ = 0f;
//Bright n sparkly
private float _lightColorR = 0f;
private float _lightColorG = 0f;
private float _lightColorB = 0f;
private float _lightColorA = 1f;
private float _lightRadius = 0f;
private float _lightCutoff = 0f;
private float _lightFalloff = 0f;
private float _lightIntensity = 1f;
private bool _flexiEntry = false;
private bool _lightEntry = false;
private bool _sculptEntry = false;
public byte ProfileCurve
{
get { return (byte)((byte)HollowShape | (byte)ProfileShape); }
set
{
// Handle hollow shape component
byte hollowShapeByte = (byte)(value & 0xf0);
if (!Enum.IsDefined(typeof(HollowShape), hollowShapeByte))
{
this._hollowShape = HollowShape.Same;
}
else
{
this._hollowShape = (HollowShape)hollowShapeByte;
}
// Handle profile shape component
byte profileShapeByte = (byte)(value & 0xf);
if (!Enum.IsDefined(typeof(ProfileShape), profileShapeByte))
{
this._profileShape = ProfileShape.Square;
}
else
{
this._profileShape = (ProfileShape)profileShapeByte;
}
}
}
public PrimitiveBaseShape(Primitive prim)
{
ExtraParams = new byte[1];
_pathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin);
_pathCurve = (byte)prim.PrimData.PathCurve;
_pathEnd = Primitive.PackEndCut(prim.PrimData.PathEnd);
_pathRadiusOffset = Primitive.PackPathTwist(prim.PrimData.PathRadiusOffset);
_pathRevolutions = Primitive.PackPathRevolutions(prim.PrimData.PathRevolutions);
_pathScaleX = Primitive.PackPathScale(prim.PrimData.PathScaleX);
_pathScaleY = Primitive.PackPathScale(prim.PrimData.PathScaleY);
_pathShearX = (byte)Primitive.PackPathShear(prim.PrimData.PathShearX);
_pathShearY = (byte)Primitive.PackPathShear(prim.PrimData.PathShearY);
_pathSkew = Primitive.PackPathTwist(prim.PrimData.PathSkew);
_pathTaperX = Primitive.PackPathTaper(prim.PrimData.PathTaperX);
_pathTaperY = Primitive.PackPathTaper(prim.PrimData.PathTaperY);
_pathTwist = Primitive.PackPathTwist(prim.PrimData.PathTwist);
_pathTwistBegin = Primitive.PackPathTwist(prim.PrimData.PathTwistBegin);
_pCode = (byte)prim.PrimData.PCode;
_profileBegin = Primitive.PackBeginCut(prim.PrimData.ProfileBegin);
_profileEnd = Primitive.PackEndCut(prim.PrimData.ProfileEnd);
_profileHollow = Primitive.PackProfileHollow(prim.PrimData.ProfileHollow);
_scale = prim.Scale;
_state = prim.PrimData.State;
_profileShape = (ProfileShape)(byte)prim.PrimData.ProfileCurve;
_hollowShape = (HollowShape)(byte)prim.PrimData.ProfileHole;
//Textures = prim.Textures;
}
public Primitive.TextureEntry Textures
{
get
{
return new Primitive.TextureEntry(m_textureEntry, 0, m_textureEntry.Length);
}
set { m_textureEntry = value.ToBytes(); }
}
public byte[] TextureEntry
{
get { return m_textureEntry; }
set
{
if (value == null)
m_textureEntry = new byte[1];
else
m_textureEntry = value;
}
}
/*
public static PrimitiveBaseShape Default
{
get
{
PrimitiveBaseShape boxShape = CreateBox();
boxShape.SetScale(0.5f);
return boxShape;
}
}
public static PrimitiveBaseShape Create()
{
PrimitiveBaseShape shape = new PrimitiveBaseShape();
return shape;
}
public static PrimitiveBaseShape CreateBox()
{
PrimitiveBaseShape shape = Create();
shape._pathCurve = (byte)Extrusion.Straight;
shape._profileShape = ProfileShape.Square;
shape._pathScaleX = 100;
shape._pathScaleY = 100;
return shape;
}
public static PrimitiveBaseShape CreateCylinder()
{
PrimitiveBaseShape shape = Create();
shape._pathCurve = (byte)Extrusion.Curve1;
shape._profileShape = ProfileShape.Square;
shape._pathScaleX = 100;
shape._pathScaleY = 100;
return shape;
}
public static PrimitiveBaseShape CreateCylinder(float radius, float heigth)
{
PrimitiveBaseShape shape = CreateCylinder();
shape.SetHeigth(heigth);
shape.SetRadius(radius);
return shape;
}
*/
public void SetScale(float side)
{
_scale = new Vector3(side, side, side);
}
public void SetHeigth(float heigth)
{
_scale.Z = heigth;
}
public void SetRadius(float radius)
{
_scale.X = _scale.Y = radius * 2f;
}
// TODO: void returns need to change of course
public virtual void GetMesh()
{
}
public PrimitiveBaseShape Copy()
{
return (PrimitiveBaseShape)MemberwiseClone();
}
public void SetPathRange(Vector3 pathRange)
{
_pathBegin = Primitive.PackBeginCut(pathRange.X);
_pathEnd = Primitive.PackEndCut(pathRange.Y);
}
public void SetSculptData(byte sculptType, UUID SculptTextureUUID)
{
_sculptType = sculptType;
_sculptTexture = SculptTextureUUID;
}
public void SetProfileRange(Vector3 profileRange)
{
_profileBegin = Primitive.PackBeginCut(profileRange.X);
_profileEnd = Primitive.PackEndCut(profileRange.Y);
}
public byte[] ExtraParams
{
get
{
return ExtraParamsToBytes();
}
set
{
ReadInExtraParamsBytes(value);
}
}
public ushort PathBegin
{
get
{
return _pathBegin;
}
set
{
_pathBegin = value;
}
}
public byte PathCurve
{
get
{
return _pathCurve;
}
set
{
_pathCurve = value;
}
}
public ushort PathEnd
{
get
{
return _pathEnd;
}
set
{
_pathEnd = value;
}
}
public sbyte PathRadiusOffset
{
get
{
return _pathRadiusOffset;
}
set
{
_pathRadiusOffset = value;
}
}
public byte PathRevolutions
{
get
{
return _pathRevolutions;
}
set
{
_pathRevolutions = value;
}
}
public byte PathScaleX
{
get
{
return _pathScaleX;
}
set
{
_pathScaleX = value;
}
}
public byte PathScaleY
{
get
{
return _pathScaleY;
}
set
{
_pathScaleY = value;
}
}
public byte PathShearX
{
get
{
return _pathShearX;
}
set
{
_pathShearX = value;
}
}
public byte PathShearY
{
get
{
return _pathShearY;
}
set
{
_pathShearY = value;
}
}
public sbyte PathSkew
{
get
{
return _pathSkew;
}
set
{
_pathSkew = value;
}
}
public sbyte PathTaperX
{
get
{
return _pathTaperX;
}
set
{
_pathTaperX = value;
}
}
public sbyte PathTaperY
{
get
{
return _pathTaperY;
}
set
{
_pathTaperY = value;
}
}
public sbyte PathTwist
{
get
{
return _pathTwist;
}
set
{
_pathTwist = value;
}
}
public sbyte PathTwistBegin
{
get
{
return _pathTwistBegin;
}
set
{
_pathTwistBegin = value;
}
}
public byte PCode
{
get
{
return _pCode;
}
set
{
_pCode = value;
}
}
public ushort ProfileBegin
{
get
{
return _profileBegin;
}
set
{
_profileBegin = value;
}
}
public ushort ProfileEnd
{
get
{
return _profileEnd;
}
set
{
_profileEnd = value;
}
}
public ushort ProfileHollow
{
get
{
return _profileHollow;
}
set
{
_profileHollow = value;
}
}
public Vector3 Scale
{
get
{
return _scale;
}
set
{
_scale = value;
}
}
public byte State
{
get
{
return _state;
}
set
{
_state = value;
}
}
public ProfileShape ProfileShape
{
get
{
return _profileShape;
}
set
{
_profileShape = value;
}
}
public HollowShape HollowShape
{
get
{
return _hollowShape;
}
set
{
_hollowShape = value;
}
}
public UUID SculptTexture
{
get
{
return _sculptTexture;
}
set
{
_sculptTexture = value;
}
}
public byte SculptType
{
get
{
return _sculptType;
}
set
{
_sculptType = value;
}
}
public byte[] SculptData
{
get
{
return _sculptData;
}
set
{
_sculptData = value;
}
}
public int FlexiSoftness
{
get
{
return _flexiSoftness;
}
set
{
_flexiSoftness = value;
}
}
public float FlexiTension
{
get
{
return _flexiTension;
}
set
{
_flexiTension = value;
}
}
public float FlexiDrag
{
get
{
return _flexiDrag;
}
set
{
_flexiDrag = value;
}
}
public float FlexiGravity
{
get
{
return _flexiGravity;
}
set
{
_flexiGravity = value;
}
}
public float FlexiWind
{
get
{
return _flexiWind;
}
set
{
_flexiWind = value;
}
}
public float FlexiForceX
{
get
{
return _flexiForceX;
}
set
{
_flexiForceX = value;
}
}
public float FlexiForceY
{
get
{
return _flexiForceY;
}
set
{
_flexiForceY = value;
}
}
public float FlexiForceZ
{
get
{
return _flexiForceZ;
}
set
{
_flexiForceZ = value;
}
}
public float LightColorR
{
get
{
return _lightColorR;
}
set
{
_lightColorR = value;
}
}
public float LightColorG
{
get
{
return _lightColorG;
}
set
{
_lightColorG = value;
}
}
public float LightColorB
{
get
{
return _lightColorB;
}
set
{
_lightColorB = value;
}
}
public float LightColorA
{
get
{
return _lightColorA;
}
set
{
_lightColorA = value;
}
}
public float LightRadius
{
get
{
return _lightRadius;
}
set
{
_lightRadius = value;
}
}
public float LightCutoff
{
get
{
return _lightCutoff;
}
set
{
_lightCutoff = value;
}
}
public float LightFalloff
{
get
{
return _lightFalloff;
}
set
{
_lightFalloff = value;
}
}
public float LightIntensity
{
get
{
return _lightIntensity;
}
set
{
_lightIntensity = value;
}
}
public bool FlexiEntry
{
get
{
return _flexiEntry;
}
set
{
_flexiEntry = value;
}
}
public bool LightEntry
{
get
{
return _lightEntry;
}
set
{
_lightEntry = value;
}
}
public bool SculptEntry
{
get
{
return _sculptEntry;
}
set
{
_sculptEntry = value;
}
}
public byte[] ExtraParamsToBytes()
{
ushort FlexiEP = 0x10;
ushort LightEP = 0x20;
ushort SculptEP = 0x30;
int i = 0;
uint TotalBytesLength = 1; // ExtraParamsNum
uint ExtraParamsNum = 0;
if (_flexiEntry)
{
ExtraParamsNum++;
TotalBytesLength += 16;// data
TotalBytesLength += 2 + 4; // type
}
if (_lightEntry)
{
ExtraParamsNum++;
TotalBytesLength += 16;// data
TotalBytesLength += 2 + 4; // type
}
if (_sculptEntry)
{
ExtraParamsNum++;
TotalBytesLength += 17;// data
TotalBytesLength += 2 + 4; // type
}
byte[] returnbytes = new byte[TotalBytesLength];
// uint paramlength = ExtraParamsNum;
// Stick in the number of parameters
returnbytes[i++] = (byte)ExtraParamsNum;
if (_flexiEntry)
{
byte[] FlexiData = GetFlexiBytes();
returnbytes[i++] = (byte)(FlexiEP % 256);
returnbytes[i++] = (byte)((FlexiEP >> 8) % 256);
returnbytes[i++] = (byte)(FlexiData.Length % 256);
returnbytes[i++] = (byte)((FlexiData.Length >> 8) % 256);
returnbytes[i++] = (byte)((FlexiData.Length >> 16) % 256);
returnbytes[i++] = (byte)((FlexiData.Length >> 24) % 256);
Array.Copy(FlexiData, 0, returnbytes, i, FlexiData.Length);
i += FlexiData.Length;
}
if (_lightEntry)
{
byte[] LightData = GetLightBytes();
returnbytes[i++] = (byte)(LightEP % 256);
returnbytes[i++] = (byte)((LightEP >> 8) % 256);
returnbytes[i++] = (byte)(LightData.Length % 256);
returnbytes[i++] = (byte)((LightData.Length >> 8) % 256);
returnbytes[i++] = (byte)((LightData.Length >> 16) % 256);
returnbytes[i++] = (byte)((LightData.Length >> 24) % 256);
Array.Copy(LightData, 0, returnbytes, i, LightData.Length);
i += LightData.Length;
}
if (_sculptEntry)
{
byte[] SculptData = GetSculptBytes();
returnbytes[i++] = (byte)(SculptEP % 256);
returnbytes[i++] = (byte)((SculptEP >> 8) % 256);
returnbytes[i++] = (byte)(SculptData.Length % 256);
returnbytes[i++] = (byte)((SculptData.Length >> 8) % 256);
returnbytes[i++] = (byte)((SculptData.Length >> 16) % 256);
returnbytes[i++] = (byte)((SculptData.Length >> 24) % 256);
Array.Copy(SculptData, 0, returnbytes, i, SculptData.Length);
i += SculptData.Length;
}
if (!_flexiEntry && !_lightEntry && !_sculptEntry)
{
byte[] returnbyte = new byte[1];
returnbyte[0] = 0;
return returnbyte;
}
return returnbytes;
}
public void ReadInUpdateExtraParam(ushort type, bool inUse, byte[] data)
{
const ushort FlexiEP = 0x10;
const ushort LightEP = 0x20;
const ushort SculptEP = 0x30;
switch (type)
{
case FlexiEP:
if (!inUse)
{
_flexiEntry = false;
return;
}
ReadFlexiData(data, 0);
break;
case LightEP:
if (!inUse)
{
_lightEntry = false;
return;
}
ReadLightData(data, 0);
break;
case SculptEP:
if (!inUse)
{
_sculptEntry = false;
return;
}
ReadSculptData(data, 0);
break;
}
}
public void ReadInExtraParamsBytes(byte[] data)
{
if (data == null)
return;
const ushort FlexiEP = 0x10;
const ushort LightEP = 0x20;
const ushort SculptEP = 0x30;
bool lGotFlexi = false;
bool lGotLight = false;
bool lGotSculpt = false;
int i = 0;
byte extraParamCount = 0;
if (data.Length > 0)
{
extraParamCount = data[i++];
}
for (int k = 0; k < extraParamCount; k++)
{
ushort epType = Utils.BytesToUInt16(data, i);
i += 2;
// uint paramLength = Helpers.BytesToUIntBig(data, i);
i += 4;
switch (epType)
{
case FlexiEP:
ReadFlexiData(data, i);
i += 16;
lGotFlexi = true;
break;
case LightEP:
ReadLightData(data, i);
i += 16;
lGotLight = true;
break;
case SculptEP:
ReadSculptData(data, i);
i += 17;
lGotSculpt = true;
break;
}
}
if (!lGotFlexi)
_flexiEntry = false;
if (!lGotLight)
_lightEntry = false;
if (!lGotSculpt)
_sculptEntry = false;
}
public void ReadSculptData(byte[] data, int pos)
{
byte[] SculptTextureUUID = new byte[16];
UUID SculptUUID = UUID.Zero;
byte SculptTypel = data[16 + pos];
if (data.Length + pos >= 17)
{
_sculptEntry = true;
SculptTextureUUID = new byte[16];
SculptTypel = data[16 + pos];
Array.Copy(data, pos, SculptTextureUUID, 0, 16);
SculptUUID = new UUID(SculptTextureUUID, 0);
}
else
{
_sculptEntry = false;
SculptUUID = UUID.Zero;
SculptTypel = 0x00;
}
if (_sculptEntry)
{
if (_sculptType != (byte)1 && _sculptType != (byte)2 && _sculptType != (byte)3 && _sculptType != (byte)4)
_sculptType = 4;
}
_sculptTexture = SculptUUID;
_sculptType = SculptTypel;
//m_log.Info("[SCULPT]:" + SculptUUID.ToString());
}
public byte[] GetSculptBytes()
{
byte[] data = new byte[17];
_sculptTexture.GetBytes().CopyTo(data, 0);
data[16] = (byte)_sculptType;
return data;
}
public void ReadFlexiData(byte[] data, int pos)
{
if (data.Length - pos >= 16)
{
_flexiEntry = true;
_flexiSoftness = ((data[pos] & 0x80) >> 6) | ((data[pos + 1] & 0x80) >> 7);
_flexiTension = (float)(data[pos++] & 0x7F) / 10.0f;
_flexiDrag = (float)(data[pos++] & 0x7F) / 10.0f;
_flexiGravity = (float)(data[pos++] / 10.0f) - 10.0f;
_flexiWind = (float)data[pos++] / 10.0f;
Vector3 lForce = new Vector3(data, pos);
_flexiForceX = lForce.X;
_flexiForceY = lForce.Y;
_flexiForceZ = lForce.Z;
}
else
{
_flexiEntry = false;
_flexiSoftness = 0;
_flexiTension = 0.0f;
_flexiDrag = 0.0f;
_flexiGravity = 0.0f;
_flexiWind = 0.0f;
_flexiForceX = 0f;
_flexiForceY = 0f;
_flexiForceZ = 0f;
}
}
public byte[] GetFlexiBytes()
{
byte[] data = new byte[16];
int i = 0;
// Softness is packed in the upper bits of tension and drag
data[i] = (byte)((_flexiSoftness & 2) << 6);
data[i + 1] = (byte)((_flexiSoftness & 1) << 7);
data[i++] |= (byte)((byte)(_flexiTension * 10.01f) & 0x7F);
data[i++] |= (byte)((byte)(_flexiDrag * 10.01f) & 0x7F);
data[i++] = (byte)((_flexiGravity + 10.0f) * 10.01f);
data[i++] = (byte)(_flexiWind * 10.01f);
Vector3 lForce = new Vector3(_flexiForceX, _flexiForceY, _flexiForceZ);
lForce.GetBytes().CopyTo(data, i);
return data;
}
public void ReadLightData(byte[] data, int pos)
{
if (data.Length - pos >= 16)
{
_lightEntry = true;
Color4 lColor = new Color4(data, pos, false);
_lightIntensity = lColor.A;
_lightColorA = 1f;
_lightColorR = lColor.R;
_lightColorG = lColor.G;
_lightColorB = lColor.B;
_lightRadius = Utils.BytesToFloat(data, pos + 4);
_lightCutoff = Utils.BytesToFloat(data, pos + 8);
_lightFalloff = Utils.BytesToFloat(data, pos + 12);
}
else
{
_lightEntry = false;
_lightColorA = 1f;
_lightColorR = 0f;
_lightColorG = 0f;
_lightColorB = 0f;
_lightRadius = 0f;
_lightCutoff = 0f;
_lightFalloff = 0f;
_lightIntensity = 0f;
}
}
public byte[] GetLightBytes()
{
byte[] data = new byte[16];
// Alpha channel in color is intensity
Color4 tmpColor = new Color4(_lightColorR, _lightColorG, _lightColorB, _lightIntensity);
tmpColor.GetBytes().CopyTo(data, 0);
Utils.FloatToBytes(_lightRadius).CopyTo(data, 4);
Utils.FloatToBytes(_lightCutoff).CopyTo(data, 8);
Utils.FloatToBytes(_lightFalloff).CopyTo(data, 12);
return data;
}
}
// A simple hull is a set of vertices building up to simplices that border a region
// The word simple referes to the fact, that this class assumes, that all simplices
// do not intersect
// Simple hulls can be added and subtracted.
// Vertices can be checked to lie inside a hull
// Also note, that the sequence of the vertices is important and defines if the region that
// is defined by the hull lies inside or outside the simplex chain
public class SimpleHull
{
//private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private List<MeshmerizerVertex> vertices = new List<MeshmerizerVertex>();
private List<MeshmerizerVertex> holeVertices = new List<MeshmerizerVertex>(); // Only used, when the hull is hollow
// Adds a MeshmerizerVertex to the end of the list
public void AddVertex(MeshmerizerVertex v)
{
vertices.Add(v);
}
public override String ToString()
{
String result = String.Empty;
foreach (MeshmerizerVertex v in vertices)
{
result += "b:" + v.ToString() + "\n";
}
return result;
}
public List<MeshmerizerVertex> getVertices()
{
List<MeshmerizerVertex> newVertices = new List<MeshmerizerVertex>();
newVertices.AddRange(vertices);
newVertices.Add(null);
newVertices.AddRange(holeVertices);
return newVertices;
}
public SimpleHull Clone()
{
SimpleHull result = new SimpleHull();
foreach (MeshmerizerVertex v in vertices)
{
result.AddVertex(v.Clone());
}
foreach (MeshmerizerVertex v in holeVertices)
{
result.holeVertices.Add(v.Clone());
}
return result;
}
public bool IsPointIn(MeshmerizerVertex v1)
{
int iCounter = 0;
List<Simplex> simplices = buildSimplexList();
foreach (Simplex s in simplices)
{
// Send a ray along the positive X-Direction
// Note, that this direction must correlate with the "below" interpretation
// of handling for the special cases below
PhysicsVector intersection = s.RayIntersect(v1, new PhysicsVector(1.0f, 0.0f, 0.0f), true);
if (intersection == null)
continue; // No intersection. Done. More tests to follow otherwise
// Did we hit the end of a simplex?
// Then this can be one of two special cases:
// 1. we go through a border exactly at a joint
// 2. we have just marginally touched a corner
// 3. we can slide along a border
// Solution: If the other vertex is "below" the ray, we don't count it
// Thus corners pointing down are counted twice, corners pointing up are not counted
// borders are counted once
if (intersection.IsIdentical(s.v1, 0.001f))
{
if (s.v2.Y < v1.Y)
continue;
}
// Do this for the other vertex two
if (intersection.IsIdentical(s.v2, 0.001f))
{
if (s.v1.Y < v1.Y)
continue;
}
iCounter++;
}
return iCounter % 2 == 1; // Point is inside if the number of intersections is odd
}
public bool containsPointsFrom(SimpleHull otherHull)
{
foreach (MeshmerizerVertex v in otherHull.vertices)
{
if (IsPointIn(v))
return true;
}
return false;
}
private List<Simplex> buildSimplexList()
{
List<Simplex> result = new List<Simplex>();
// Not asserted but assumed: at least three vertices
for (int i = 0; i < vertices.Count - 1; i++)
{
Simplex s = new Simplex(vertices[i], vertices[i + 1]);
result.Add(s);
}
Simplex s1 = new Simplex(vertices[vertices.Count - 1], vertices[0]);
result.Add(s1);
if (holeVertices.Count == 0)
return result;
// Same here. At least three vertices in hole assumed
for (int i = 0; i < holeVertices.Count - 1; i++)
{
Simplex s = new Simplex(holeVertices[i], holeVertices[i + 1]);
result.Add(s);
}
s1 = new Simplex(holeVertices[holeVertices.Count - 1], holeVertices[0]);
result.Add(s1);
return result;
}
private MeshmerizerVertex getNextVertex(MeshmerizerVertex currentVertex)
{
int iCurrentIndex;
iCurrentIndex = vertices.IndexOf(currentVertex);
// Error handling for iCurrentIndex==-1 should go here (and probably never will)
iCurrentIndex++;
if (iCurrentIndex == vertices.Count)
iCurrentIndex = 0;
return vertices[iCurrentIndex];
}
public MeshmerizerVertex FindVertex(MeshmerizerVertex vBase, float tolerance)
{
foreach (MeshmerizerVertex v in vertices)
{
if (v.IsIdentical(vBase, tolerance))
return v;
}
return null;
}
public void FindIntersection(Simplex s, ref MeshmerizerVertex Intersection, ref MeshmerizerVertex nextVertex)
{
MeshmerizerVertex bestIntersection = null;
float distToV1 = Single.PositiveInfinity;
Simplex bestIntersectingSimplex = null;
List<Simplex> simple = buildSimplexList();
foreach (Simplex sTest in simple)
{
PhysicsVector vvTemp = Simplex.Intersect(sTest, s, -.001f, -.001f, 0.999f, .999f);
MeshmerizerVertex vTemp = null;
if (vvTemp != null)
vTemp = new MeshmerizerVertex(vvTemp);
if (vTemp != null)
{
PhysicsVector diff = (s.v1 - vTemp);
float distTemp = diff.length();
if (bestIntersection == null || distTemp < distToV1)
{
bestIntersection = vTemp;
distToV1 = distTemp;
bestIntersectingSimplex = sTest;
}
}
}
Intersection = bestIntersection;
if (bestIntersectingSimplex != null)
nextVertex = bestIntersectingSimplex.v2;
else
nextVertex = null;
}
public static SimpleHull SubtractHull(SimpleHull baseHull, SimpleHull otherHull)
{
SimpleHull baseHullClone = baseHull.Clone();
SimpleHull otherHullClone = otherHull.Clone();
bool intersects = false;
//m_log.Debug("State before intersection detection");
//m_log.DebugFormat("The baseHull is:\n{1}", 0, baseHullClone.ToString());
//m_log.DebugFormat("The otherHull is:\n{1}", 0, otherHullClone.ToString());
{
int iBase, iOther;
// Insert into baseHull
for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++)
{
int iBaseNext = (iBase + 1) % baseHullClone.vertices.Count;
Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]);
for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++)
{
int iOtherNext = (iOther + 1) % otherHullClone.vertices.Count;
Simplex sOther =
new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]);
PhysicsVector intersect = Simplex.Intersect(sBase, sOther, 0.001f, -.001f, 0.999f, 1.001f);
if (intersect != null)
{
MeshmerizerVertex vIntersect = new MeshmerizerVertex(intersect);
baseHullClone.vertices.Insert(iBase + 1, vIntersect);
sBase.v2 = vIntersect;
intersects = true;
}
}
}
}
//m_log.Debug("State after intersection detection for the base hull");
//m_log.DebugFormat("The baseHull is:\n{1}", 0, baseHullClone.ToString());
{
int iOther, iBase;
// Insert into otherHull
for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++)
{
int iOtherNext = (iOther + 1) % otherHullClone.vertices.Count;
Simplex sOther = new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]);
for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++)
{
int iBaseNext = (iBase + 1) % baseHullClone.vertices.Count;
Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]);
PhysicsVector intersect = Simplex.Intersect(sBase, sOther, -.001f, 0.001f, 1.001f, 0.999f);
if (intersect != null)
{
MeshmerizerVertex vIntersect = new MeshmerizerVertex(intersect);
otherHullClone.vertices.Insert(iOther + 1, vIntersect);
sOther.v2 = vIntersect;
intersects = true;
}
}
}
}
//m_log.Debug("State after intersection detection for the base hull");
//m_log.DebugFormat("The otherHull is:\n{1}", 0, otherHullClone.ToString());
bool otherIsInBase = baseHullClone.containsPointsFrom(otherHullClone);
if (!intersects && otherIsInBase)
{
// We have a hole here
baseHullClone.holeVertices = otherHullClone.vertices;
return baseHullClone;
}
SimpleHull result = new SimpleHull();
// Find a good starting Simplex from baseHull
// A good starting simplex is one that is outside otherHull
// Such a simplex must exist, otherwise the result will be empty
MeshmerizerVertex baseStartVertex = null;
{
int iBase;
for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++)
{
int iBaseNext = (iBase + 1) % baseHullClone.vertices.Count;
MeshmerizerVertex center = new MeshmerizerVertex((baseHullClone.vertices[iBase] + baseHullClone.vertices[iBaseNext]) / 2.0f);
bool isOutside = !otherHullClone.IsPointIn(center);
if (isOutside)
{
baseStartVertex = baseHullClone.vertices[iBaseNext];
break;
}
}
}
if (baseStartVertex == null) // i.e. no simplex fulfilled the "outside" condition.
// In otherwords, subtractHull completely embraces baseHull
{
return result;
}
// The simplex that *starts* with baseStartVertex is outside the cutting hull,
// so we can start our walk with the next vertex without loosing a branch
MeshmerizerVertex V1 = baseStartVertex;
bool onBase = true;
// And here is how we do the magic :-)
// Start on the base hull.
// Walk the vertices in the positive direction
// For each vertex check, whether it is a vertex shared with the other hull
// if this is the case, switch over to walking the other vertex list.
// Note: The other hull *must* go backwards to our starting point (via several orther vertices)
// Thus it is important that the cutting hull has the inverse directional sense than the
// base hull!!!!!!!!! (means if base goes CW around it's center cutting hull must go CCW)
bool done = false;
while (!done)
{
result.AddVertex(V1);
MeshmerizerVertex nextVertex = null;
if (onBase)
{
nextVertex = otherHullClone.FindVertex(V1, 0.001f);
}
else
{
nextVertex = baseHullClone.FindVertex(V1, 0.001f);
}
if (nextVertex != null) // A node that represents an intersection
{
V1 = nextVertex; // Needed to find the next vertex on the other hull
onBase = !onBase;
}
if (onBase)
V1 = baseHullClone.getNextVertex(V1);
else
V1 = otherHullClone.getNextVertex(V1);
if (V1 == baseStartVertex)
done = true;
}
//m_log.DebugFormat("The resulting Hull is:\n{1}", 0, result.ToString());
return result;
}
}
public class MeshmerizerMesh
{
public List<MeshmerizerVertex> vertices;
public List<Triangle> triangles;
GCHandle pinnedVirtexes;
GCHandle pinnedIndex;
public PrimMesh primMesh = null;
public MeshmerizerMesh()
{
vertices = new List<MeshmerizerVertex>();
triangles = new List<Triangle>();
}
public MeshmerizerMesh Clone()
{
MeshmerizerMesh result = new MeshmerizerMesh();
foreach (MeshmerizerVertex v in vertices)
{
if (v == null)
result.vertices.Add(null);
else
result.vertices.Add(v.Clone());
}
foreach (Triangle t in triangles)
{
int iV1, iV2, iV3;
iV1 = vertices.IndexOf(t.v1);
iV2 = vertices.IndexOf(t.v2);
iV3 = vertices.IndexOf(t.v3);
Triangle newT = new Triangle(result.vertices[iV1], result.vertices[iV2], result.vertices[iV3]);
result.Add(newT);
}
return result;
}
public void Add(Triangle triangle)
{
int i;
i = vertices.IndexOf(triangle.v1);
if (i < 0)
throw new ArgumentException("Vertex v1 not known to mesh");
i = vertices.IndexOf(triangle.v2);
if (i < 0)
throw new ArgumentException("Vertex v2 not known to mesh");
i = vertices.IndexOf(triangle.v3);
if (i < 0)
throw new ArgumentException("Vertex v3 not known to mesh");
triangles.Add(triangle);
}
public void Add(MeshmerizerVertex v)
{
vertices.Add(v);
}
public void Remove(MeshmerizerVertex v)
{
int i;
// First, remove all triangles that are build on v
for (i = 0; i < triangles.Count; i++)
{
Triangle t = triangles[i];
if (t.v1 == v || t.v2 == v || t.v3 == v)
{
triangles.RemoveAt(i);
i--;
}
}
// Second remove v itself
vertices.Remove(v);
}
public void RemoveTrianglesOutside(SimpleHull hull)
{
int i;
for (i = 0; i < triangles.Count; i++)
{
Triangle t = triangles[i];
MeshmerizerVertex v1 = t.v1;
MeshmerizerVertex v2 = t.v2;
MeshmerizerVertex v3 = t.v3;
PhysicsVector m = v1 + v2 + v3;
m /= 3.0f;
if (!hull.IsPointIn(new MeshmerizerVertex(m)))
{
triangles.RemoveAt(i);
i--;
}
}
}
public void Add(List<MeshmerizerVertex> lv)
{
foreach (MeshmerizerVertex v in lv)
{
vertices.Add(v);
}
}
public List<PhysicsVector> getVertexList()
{
List<PhysicsVector> result = new List<PhysicsVector>();
foreach (MeshmerizerVertex v in vertices)
{
result.Add(v);
}
return result;
}
public float[] getVertexListAsFloatLocked()
{
float[] result;
if (primMesh == null)
{
result = new float[vertices.Count * 3];
for (int i = 0; i < vertices.Count; i++)
{
MeshmerizerVertex v = vertices[i];
if (v == null)
continue;
result[3 * i + 0] = v.X;
result[3 * i + 1] = v.Y;
result[3 * i + 2] = v.Z;
}
pinnedVirtexes = GCHandle.Alloc(result, GCHandleType.Pinned);
}
else
{
int count = primMesh.coords.Count;
result = new float[count * 3];
for (int i = 0; i < count; i++)
{
Coord c = primMesh.coords[i];
{
int resultIndex = 3 * i;
result[resultIndex] = c.X;
result[resultIndex + 1] = c.Y;
result[resultIndex + 2] = c.Z;
}
}
pinnedVirtexes = GCHandle.Alloc(result, GCHandleType.Pinned);
}
return result;
}
public int[] getIndexListAsInt()
{
int[] result;
if (primMesh == null)
{
result = new int[triangles.Count * 3];
for (int i = 0; i < triangles.Count; i++)
{
Triangle t = triangles[i];
result[3 * i + 0] = vertices.IndexOf(t.v1);
result[3 * i + 1] = vertices.IndexOf(t.v2);
result[3 * i + 2] = vertices.IndexOf(t.v3);
}
}
else
{
int numFaces = primMesh.faces.Count;
result = new int[numFaces * 3];
for (int i = 0; i < numFaces; i++)
{
MeshmerizerFace f = primMesh.faces[i];
int resultIndex = i * 3;
result[resultIndex] = f.v1;
result[resultIndex + 1] = f.v2;
result[resultIndex + 2] = f.v3;
}
}
return result;
}
/// <summary>
/// creates a list of index values that defines triangle faces. THIS METHOD FREES ALL NON-PINNED MESH DATA
/// </summary>
/// <returns></returns>
public int[] getIndexListAsIntLocked()
{
int[] result = getIndexListAsInt();
pinnedIndex = GCHandle.Alloc(result, GCHandleType.Pinned);
return result;
}
public void releasePinned()
{
pinnedVirtexes.Free();
pinnedIndex.Free();
}
/// <summary>
/// frees up the source mesh data to minimize memory - call this method after calling get*Locked() functions
/// </summary>
public void releaseSourceMeshData()
{
triangles = null;
vertices = null;
primMesh = null;
}
public void Append(MeshmerizerMesh newMesh)
{
foreach (MeshmerizerVertex v in newMesh.vertices)
vertices.Add(v);
foreach (Triangle t in newMesh.triangles)
Add(t);
}
// Do a linear transformation of mesh.
public void TransformLinear(float[,] matrix, float[] offset)
{
foreach (MeshmerizerVertex v in vertices)
{
if (v == null)
continue;
float x, y, z;
x = v.X * matrix[0, 0] + v.Y * matrix[1, 0] + v.Z * matrix[2, 0];
y = v.X * matrix[0, 1] + v.Y * matrix[1, 1] + v.Z * matrix[2, 1];
z = v.X * matrix[0, 2] + v.Y * matrix[1, 2] + v.Z * matrix[2, 2];
v.X = x + offset[0];
v.Y = y + offset[1];
v.Z = z + offset[2];
}
}
}
/// <summary>
/// generates a profile for extrusion
/// </summary>
public class MeshmerizerProfile
{
private const float twoPi = 2.0f * (float)Math.PI;
internal List<Coord> coords;
internal List<MeshmerizerFace> faces;
internal MeshmerizerProfile()
{
this.coords = new List<Coord>();
this.faces = new List<MeshmerizerFace>();
}
public MeshmerizerProfile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces)
{
this.coords = new List<Coord>();
this.faces = new List<MeshmerizerFace>();
Coord center = new Coord(0.0f, 0.0f, 0.0f);
List<Coord> hollowCoords = new List<Coord>();
AngleList angles = new AngleList();
AngleList hollowAngles = new AngleList();
float xScale = 0.5f;
float yScale = 0.5f;
if (sides == 4) // corners of a square are sqrt(2) from center
{
xScale = 0.707f;
yScale = 0.707f;
}
float startAngle = profileStart * twoPi;
float stopAngle = profileEnd * twoPi;
// float stepSize = twoPi / sides;
try { angles.makeAngles(sides, startAngle, stopAngle); }
catch (Exception ex)
{
Console.WriteLine("makeAngles failed: Exception: " + ex.ToString());
Console.WriteLine("sides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString());
return;
}
if (hollow > 0.001f)
{
if (sides == hollowSides)
hollowAngles = angles;
else
{
try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); }
catch (Exception ex)
{
Console.WriteLine("makeAngles failed: Exception: " + ex.ToString());
Console.WriteLine("sides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString());
return;
}
}
}
else
this.coords.Add(center);
float z = 0.0f;
Angle angle;
Coord newVert = new Coord();
if (hollow > 0.001f && hollowSides != sides)
{
int numHollowAngles = hollowAngles.angles.Count;
for (int i = 0; i < numHollowAngles; i++)
{
angle = hollowAngles.angles[i];
newVert.X = hollow * xScale * angle.X;
newVert.Y = hollow * yScale * angle.Y;
newVert.Z = z;
hollowCoords.Add(newVert);
}
}
int index = 0;
int numAngles = angles.angles.Count;
for (int i = 0; i < numAngles; i++)
{
angle = angles.angles[i];
newVert.X = angle.X * xScale;
newVert.Y = angle.Y * yScale;
newVert.Z = z;
this.coords.Add(newVert);
if (hollow > 0.0f)
{
if (hollowSides == sides)
{
newVert.X *= hollow;
newVert.Y *= hollow;
newVert.Z = z;
hollowCoords.Add(newVert);
}
}
else if (createFaces && angle.angle > 0.0001f)
{
MeshmerizerFace newFace = new MeshmerizerFace();
newFace.v1 = 0;
newFace.v2 = index;
newFace.v3 = index + 1;
this.faces.Add(newFace);
}
index += 1;
}
if (hollow > 0.0f)
{
hollowCoords.Reverse();
if (createFaces)
{
int numOuterVerts = this.coords.Count;
int numHollowVerts = hollowCoords.Count;
int numTotalVerts = numOuterVerts + numHollowVerts;
if (numOuterVerts == numHollowVerts)
{
MeshmerizerFace newFace = new MeshmerizerFace();
for (int coordIndex = 0; coordIndex < numOuterVerts - 1; coordIndex++)
{
newFace.v1 = coordIndex;
newFace.v2 = coordIndex + 1;
newFace.v3 = numTotalVerts - coordIndex - 1;
this.faces.Add(newFace);
newFace.v1 = coordIndex + 1;
newFace.v2 = numTotalVerts - coordIndex - 2;
newFace.v3 = numTotalVerts - coordIndex - 1;
this.faces.Add(newFace);
}
}
else
{
if (numOuterVerts < numHollowVerts)
{
MeshmerizerFace newFace = new MeshmerizerFace();
int j = 0; // j is the index for outer vertices
int maxJ = numOuterVerts - 1;
for (int i = 0; i < numHollowVerts; i++) // i is the index for inner vertices
{
if (j < maxJ)
if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle <= hollowAngles.angles[i].angle - angles.angles[j].angle)
{
newFace.v1 = numTotalVerts - i - 1;
newFace.v2 = j;
newFace.v3 = j + 1;
this.faces.Add(newFace);
j += 1;
}
newFace.v1 = j;
newFace.v2 = numTotalVerts - i - 2;
newFace.v3 = numTotalVerts - i - 1;
this.faces.Add(newFace);
}
}
else // numHollowVerts < numOuterVerts
{
MeshmerizerFace newFace = new MeshmerizerFace();
int j = 0; // j is the index for inner vertices
int maxJ = numHollowVerts - 1;
for (int i = 0; i < numOuterVerts; i++)
{
if (j < maxJ)
if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle <= angles.angles[i].angle - hollowAngles.angles[j].angle)
{
newFace.v1 = i;
newFace.v2 = numTotalVerts - j - 2;
newFace.v3 = numTotalVerts - j - 1;
this.faces.Add(newFace);
j += 1;
}
newFace.v1 = numTotalVerts - j - 1;
newFace.v2 = i;
newFace.v3 = i + 1;
this.faces.Add(newFace);
}
}
}
}
this.coords.AddRange(hollowCoords);
}
}
public MeshmerizerProfile Clone()
{
return this.Clone(true);
}
public MeshmerizerProfile Clone(bool needFaces)
{
MeshmerizerProfile clone = new MeshmerizerProfile();
clone.coords.AddRange(this.coords);
if (needFaces)
clone.faces.AddRange(this.faces);
return clone;
}
public void AddPos(Coord v)
{
this.AddPos(v.X, v.Y, v.Z);
}
public void AddPos(float x, float y, float z)
{
int i;
int numVerts = this.coords.Count;
Coord vert;
for (i = 0; i < numVerts; i++)
{
vert = this.coords[i];
vert.X += x;
vert.Y += y;
vert.Z += z;
this.coords[i] = vert;
}
}
public void AddRot(Quaternion q)
{
int i;
int numVerts = this.coords.Count;
Coord vert;
for (i = 0; i < numVerts; i++)
{
vert = this.coords[i];
MeshmerizerVertex v = new MeshmerizerVertex(vert.X, vert.Y, vert.Z) * q;
vert.X = v.X;
vert.Y = v.Y;
vert.Z = v.Z;
this.coords[i] = vert;
}
}
public void Scale(float x, float y)
{
int i;
int numVerts = this.coords.Count;
Coord vert;
for (i = 0; i < numVerts; i++)
{
vert = this.coords[i];
vert.X *= x;
vert.Y *= y;
this.coords[i] = vert;
}
}
public void FlipNormals()
{
int i;
int numFaces = this.faces.Count;
MeshmerizerFace tmpFace;
int tmp;
for (i = 0; i < numFaces; i++)
{
tmpFace = this.faces[i];
tmp = tmpFace.v3;
tmpFace.v3 = tmpFace.v1;
tmpFace.v1 = tmp;
this.faces[i] = tmpFace;
}
}
public void AddValue2Faces(int num)
{
int numFaces = this.faces.Count;
MeshmerizerFace tmpFace;
for (int i = 0; i < numFaces; i++)
{
tmpFace = this.faces[i];
tmpFace.v1 += num;
tmpFace.v2 += num;
tmpFace.v3 += num;
this.faces[i] = tmpFace;
}
}
}
}