* Added Primitive.TreeSpecies and Primitive.ScratchPad * Converted Primitive.SoundFlags to the new SoundFlags enum * Added a Utils.BytesToString() overload that accepts index and count parameters * Added Utils.FloatToUInt16() [Simian] * Lots of changes in Simian to use the new unified ISceneProvider.ObjectAddOrUpdate() function * Update flags are checked to determine the minimum sized packet that needs to be sent out for an update. ImprovedTerseObjectUpdate is working, and started work on ObjectUpdateCached (updates using this will currently not send) * Adding three new variables to SimulationObject to store attachment-related state git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2478 52acb1d6-8a22-11de-b505-999d5b087335
3000 lines
93 KiB
C#
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 = Utils.EmptyBytes;
|
|
|
|
// 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.GetBytes(); }
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|