cleanup
This commit is contained in:
@@ -1,681 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FlaxEngine;
|
|
||||||
using Console = Cabrito.Console;
|
|
||||||
|
|
||||||
namespace Game
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum TriangleAttributes
|
|
||||||
{
|
|
||||||
NONE,
|
|
||||||
NORMAL = 2,
|
|
||||||
TEXCOORD = 4,
|
|
||||||
COLOR = 8
|
|
||||||
};
|
|
||||||
|
|
||||||
public class Triangle
|
|
||||||
{
|
|
||||||
public Int3 v;
|
|
||||||
public Vector3 n;
|
|
||||||
//public Vector3[3] uvs;
|
|
||||||
public Vector4 err;
|
|
||||||
public bool dirty;
|
|
||||||
public bool deleted;
|
|
||||||
public TriangleAttributes attr;
|
|
||||||
public int material;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Vertex
|
|
||||||
{
|
|
||||||
public Vector3 p;
|
|
||||||
public int tstart,tcount;
|
|
||||||
public SymetricMatrix q;
|
|
||||||
public bool border;
|
|
||||||
};
|
|
||||||
|
|
||||||
public class Ref
|
|
||||||
{
|
|
||||||
public int tid, tvertex;
|
|
||||||
|
|
||||||
/*public Ref()
|
|
||||||
{
|
|
||||||
tid = 0;
|
|
||||||
tvertex = 0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SymetricMatrix
|
|
||||||
{
|
|
||||||
// Constructor
|
|
||||||
|
|
||||||
public SymetricMatrix(double c = 0)
|
|
||||||
{
|
|
||||||
m = new double[10];
|
|
||||||
for (int i=0; i<10; i++) m[i] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SymetricMatrix( double m11, double m12, double m13, double m14,
|
|
||||||
double m22, double m23, double m24,
|
|
||||||
double m33, double m34,
|
|
||||||
double m44)
|
|
||||||
{
|
|
||||||
m = new double[10];
|
|
||||||
m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14;
|
|
||||||
m[4] = m22; m[5] = m23; m[6] = m24;
|
|
||||||
m[7] = m33; m[8] = m34;
|
|
||||||
m[9] = m44;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make plane
|
|
||||||
|
|
||||||
public SymetricMatrix(double a,double b,double c,double d)
|
|
||||||
{
|
|
||||||
m = new double[10];
|
|
||||||
m[0] = a*a; m[1] = a*b; m[2] = a*c; m[3] = a*d;
|
|
||||||
m[4] = b*b; m[5] = b*c; m[6] = b*d;
|
|
||||||
m[7 ] =c*c; m[8 ] = c*d;
|
|
||||||
m[9 ] = d*d;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double this[int index]
|
|
||||||
{
|
|
||||||
get { return m[index]; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
m[index] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Determinant
|
|
||||||
|
|
||||||
public double det( int a11, int a12, int a13,
|
|
||||||
int a21, int a22, int a23,
|
|
||||||
int a31, int a32, int a33)
|
|
||||||
{
|
|
||||||
double det = m[a11]*m[a22]*m[a33] + m[a13]*m[a21]*m[a32] + m[a12]*m[a23]*m[a31]
|
|
||||||
- m[a13]*m[a22]*m[a31] - m[a11]*m[a23]*m[a32]- m[a12]*m[a21]*m[a33];
|
|
||||||
return det;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static SymetricMatrix operator+(SymetricMatrix o, SymetricMatrix n)
|
|
||||||
{
|
|
||||||
return new SymetricMatrix( o.m[0]+n[0], o.m[1]+n[1], o.m[2]+n[2], o.m[3]+n[3],
|
|
||||||
o.m[4]+n[4], o.m[5]+n[5], o.m[6]+n[6],
|
|
||||||
o.m[ 7]+n[ 7], o.m[ 8]+n[8 ],
|
|
||||||
o.m[ 9]+n[9 ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*public static SymetricMatrix operator+=(SymetricMatrix o, SymetricMatrix n)
|
|
||||||
{
|
|
||||||
m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3];
|
|
||||||
m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7];
|
|
||||||
m[8]+=n[8]; m[9]+=n[9];
|
|
||||||
return *this;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
double[] m;
|
|
||||||
};
|
|
||||||
|
|
||||||
public static class ListExtras
|
|
||||||
{
|
|
||||||
// list: List<T> to resize
|
|
||||||
// size: desired new size
|
|
||||||
// element: default value to insert
|
|
||||||
|
|
||||||
public static void Resize<T>(this List<T> list, int size, T element = default(T)) where T : new()
|
|
||||||
{
|
|
||||||
int count = list.Count;
|
|
||||||
|
|
||||||
if (size < count)
|
|
||||||
{
|
|
||||||
list.RemoveRange(size, count - size);
|
|
||||||
}
|
|
||||||
else if (size > count)
|
|
||||||
{
|
|
||||||
if (size > list.Capacity) // Optimization
|
|
||||||
list.Capacity = size;
|
|
||||||
|
|
||||||
list.AddRange(Enumerable.Repeat(element, size - count));
|
|
||||||
for (int i = count; i < size; i++)
|
|
||||||
list[i] = new T();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast Quadratic Mesh Simplification
|
|
||||||
public class MeshSimplifier
|
|
||||||
{
|
|
||||||
private float ratio;
|
|
||||||
private float agressiveness;
|
|
||||||
|
|
||||||
private List<Triangle> triangles;
|
|
||||||
private List<Vertex> vertices;
|
|
||||||
private List<Ref> refs = new List<Ref>();
|
|
||||||
|
|
||||||
public MeshSimplifier(float ratio = 1.0f, float agressiveness = 7.0f)
|
|
||||||
{
|
|
||||||
this.ratio = ratio;
|
|
||||||
this.agressiveness = agressiveness;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Vector3> Simplify(Vector3[] input)
|
|
||||||
{
|
|
||||||
triangles = new List<Triangle>(input.Length / 3);
|
|
||||||
vertices = new List<Vertex>();
|
|
||||||
|
|
||||||
// TODO: no overlapping vertices, vertices must be unique
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
Dictionary<Vector3, int> verticeMap = new Dictionary<Vector3, int>();
|
|
||||||
for (int i = 0; i < input.Length; i++)
|
|
||||||
{
|
|
||||||
if (!verticeMap.ContainsKey(input[i]))
|
|
||||||
verticeMap[input[i]] = verticeMap.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < input.Length; i += 3)
|
|
||||||
{
|
|
||||||
int i1 = i + 0;
|
|
||||||
int i2 = i + 1;
|
|
||||||
int i3 = i + 2;
|
|
||||||
Vector3 v1 = input[i1];
|
|
||||||
Vector3 v2 = input[i2];
|
|
||||||
Vector3 v3 = input[i3];
|
|
||||||
|
|
||||||
if (verticeMap.ContainsKey(v1))
|
|
||||||
i1 = verticeMap[v1];
|
|
||||||
else
|
|
||||||
verticeMap.Add(v1, i1);
|
|
||||||
|
|
||||||
if (verticeMap.ContainsKey(v2))
|
|
||||||
i2 = verticeMap[v2];
|
|
||||||
else
|
|
||||||
verticeMap.Add(v2, i2);
|
|
||||||
|
|
||||||
if (verticeMap.ContainsKey(v3))
|
|
||||||
i3 = verticeMap[v3];
|
|
||||||
else
|
|
||||||
verticeMap.Add(v3, i3);
|
|
||||||
|
|
||||||
triangles.Add(new Triangle()
|
|
||||||
{
|
|
||||||
v = new Int3(i1, i2, i3),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (KeyValuePair<Vector3,int> kvp in verticeMap)
|
|
||||||
{
|
|
||||||
vertices.Add(new Vertex()
|
|
||||||
{
|
|
||||||
p = kvp.Key,
|
|
||||||
q = new SymetricMatrix(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*foreach (var vec in input)
|
|
||||||
{
|
|
||||||
vertices.Add(new Vertex()
|
|
||||||
{
|
|
||||||
p = vec,
|
|
||||||
q = new SymetricMatrix(),
|
|
||||||
});
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return Simplify();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Vector3> Simplify(Vector3[] verts, int[] indices)
|
|
||||||
{
|
|
||||||
triangles = new List<Triangle>(indices.Length / 3);
|
|
||||||
vertices = new List<Vertex>(verts.Length);
|
|
||||||
|
|
||||||
{
|
|
||||||
foreach (var vec in verts)
|
|
||||||
{
|
|
||||||
vertices.Add(new Vertex()
|
|
||||||
{
|
|
||||||
p = vec,
|
|
||||||
q = new SymetricMatrix(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < indices.Length; i += 3)
|
|
||||||
{
|
|
||||||
triangles.Add(new Triangle()
|
|
||||||
{
|
|
||||||
v = new Int3(indices[i]-1, indices[i+1]-1, indices[i+2]-1),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Simplify();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Vector3> Simplify()
|
|
||||||
{
|
|
||||||
|
|
||||||
// main iteration loop
|
|
||||||
int deleted_triangles=0;
|
|
||||||
List<bool> deleted0 = new List<bool>();
|
|
||||||
List<bool> deleted1 = new List<bool>();
|
|
||||||
int triangle_start_count = triangles.Count;
|
|
||||||
|
|
||||||
int target_count = (int)(ratio * (float)triangle_start_count);
|
|
||||||
|
|
||||||
//int iteration = 0;
|
|
||||||
//loop(iteration,0,100)
|
|
||||||
int iteration;
|
|
||||||
for (iteration = 0; iteration < 9999; iteration++)
|
|
||||||
{
|
|
||||||
if (ratio < 1.0f && triangle_start_count-deleted_triangles<=target_count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (ratio >= 1.0f || iteration % 5 == 0)
|
|
||||||
update_mesh(iteration);
|
|
||||||
// clear dirty flag
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
triangles[i].dirty=false;
|
|
||||||
//
|
|
||||||
// All triangles with edges below the threshold will be removed
|
|
||||||
//
|
|
||||||
// The following numbers works well for most models.
|
|
||||||
// If it does not, try to adjust the 3 parameters
|
|
||||||
//
|
|
||||||
//double threshold = 0.001; //1.0E-3 EPS;
|
|
||||||
double threshold = 1.0E-3;//1.0E-9;
|
|
||||||
if (ratio < 1.0f)
|
|
||||||
threshold = 0.000000001 * Math.Pow((double)(iteration+3),agressiveness);
|
|
||||||
//if (verbose) {
|
|
||||||
// printf("lossless iteration %d\n", iteration);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// remove vertices & mark deleted triangles
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
{
|
|
||||||
Triangle t = triangles[i];
|
|
||||||
if (t.err[3] > threshold)
|
|
||||||
{
|
|
||||||
t = t;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(t.deleted)
|
|
||||||
continue;
|
|
||||||
if(t.dirty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (int j = 0; j < 3; j++)
|
|
||||||
{
|
|
||||||
if (t.err[j] > threshold)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int i0=t.v[ j ];
|
|
||||||
Vertex v0 = vertices[i0];
|
|
||||||
int i1=t.v[(j+1)%3];
|
|
||||||
Vertex v1 = vertices[i1];
|
|
||||||
|
|
||||||
// Border check
|
|
||||||
if(v0.border != v1.border)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Compute vertex to collapse to
|
|
||||||
Vector3 p = new Vector3();
|
|
||||||
calculate_error(i0,i1,ref p);
|
|
||||||
|
|
||||||
deleted0.Resize(v0.tcount); // normals temporarily
|
|
||||||
deleted1.Resize(v1.tcount); // normals temporarily
|
|
||||||
|
|
||||||
// don't remove if flipped
|
|
||||||
if( flipped(ref p,i0,i1,ref v0,deleted0) )
|
|
||||||
continue;
|
|
||||||
if( flipped(ref p,i1,i0,ref v1,deleted1) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( (t.attr & TriangleAttributes.TEXCOORD) == TriangleAttributes.TEXCOORD )
|
|
||||||
{
|
|
||||||
update_uvs(i0,ref v0,ref p,deleted0);
|
|
||||||
update_uvs(i0,ref v1,ref p,deleted1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// not flipped, so remove edge
|
|
||||||
v0.p=p;
|
|
||||||
v0.q=v1.q+v0.q;
|
|
||||||
int tstart=refs.Count;
|
|
||||||
|
|
||||||
update_triangles(i0,ref v0,deleted0,ref deleted_triangles);
|
|
||||||
update_triangles(i0,ref v1,deleted1,ref deleted_triangles);
|
|
||||||
|
|
||||||
int tcount=refs.Count-tstart;
|
|
||||||
|
|
||||||
if(tcount<=v0.tcount)
|
|
||||||
{
|
|
||||||
// save ram
|
|
||||||
if (tcount != 0)
|
|
||||||
{
|
|
||||||
for (int m = 0; m < tcount; m++)
|
|
||||||
refs[v0.tstart] = refs[tstart];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// append
|
|
||||||
v0.tstart=tstart;
|
|
||||||
|
|
||||||
v0.tcount=tcount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ratio < 1.0f && triangle_start_count-deleted_triangles<=target_count)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ratio >= 1.0f)
|
|
||||||
{
|
|
||||||
if (deleted_triangles <= 0)
|
|
||||||
break;
|
|
||||||
deleted_triangles = 0;
|
|
||||||
}
|
|
||||||
} //for each iteration
|
|
||||||
// clean up mesh
|
|
||||||
compact_mesh();
|
|
||||||
|
|
||||||
if (triangles.Count == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
List<Vector3> finalVerts = new List<Vector3>();
|
|
||||||
foreach (var t in triangles)
|
|
||||||
{
|
|
||||||
finalVerts.Add(vertices[t.v[0]].p);
|
|
||||||
finalVerts.Add(vertices[t.v[1]].p);
|
|
||||||
finalVerts.Add(vertices[t.v[2]].p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalVerts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a triangle flips when this edge is removed
|
|
||||||
|
|
||||||
private bool flipped(ref Vector3 p,int i0,int i1,ref Vertex v0, List<bool> deleted)
|
|
||||||
{
|
|
||||||
for (int k = 0; k < v0.tcount; k++)
|
|
||||||
{
|
|
||||||
Triangle t = triangles[refs[v0.tstart+k].tid];
|
|
||||||
if(t.deleted)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int s=refs[v0.tstart+k].tvertex;
|
|
||||||
int id1=t.v[(s+1)%3];
|
|
||||||
int id2=t.v[(s+2)%3];
|
|
||||||
|
|
||||||
if(id1==i1 || id2==i1) // delete ?
|
|
||||||
{
|
|
||||||
deleted[k]=true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Vector3 d1 = vertices[id1].p-p; d1.Normalize();
|
|
||||||
Vector3 d2 = vertices[id2].p-p; d2.Normalize();
|
|
||||||
if(Mathf.Abs(Vector3.Dot(d1, d2))>0.999) return true;
|
|
||||||
Vector3 n = Vector3.Cross(d1, d2);
|
|
||||||
n.Normalize();
|
|
||||||
deleted[k]=false;
|
|
||||||
if(Vector3.Dot(n, t.n)<0.2) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update_uvs
|
|
||||||
|
|
||||||
private void update_uvs(int i0, ref Vertex v, ref Vector3 p, List<bool> deleted)
|
|
||||||
{
|
|
||||||
for (int k = 0; k < v.tcount; k++)
|
|
||||||
{
|
|
||||||
Ref r=refs[v.tstart+k];
|
|
||||||
Triangle t=triangles[r.tid];
|
|
||||||
if(t.deleted)continue;
|
|
||||||
if(deleted[k])continue;
|
|
||||||
Vector3 p1=vertices[t.v[0]].p;
|
|
||||||
Vector3 p2=vertices[t.v[1]].p;
|
|
||||||
Vector3 p3=vertices[t.v[2]].p;
|
|
||||||
//t.uvs[r.tvertex] = interpolate(p,p1,p2,p3,t.uvs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update triangle connections and edge error after a edge is collapsed
|
|
||||||
|
|
||||||
private void update_triangles(int i0,ref Vertex v,List<bool> deleted,ref int deleted_triangles)
|
|
||||||
{
|
|
||||||
Vector3 p = new Vector3();
|
|
||||||
for (int k = 0; k < v.tcount; k++)
|
|
||||||
{
|
|
||||||
Ref r=refs[v.tstart+k];
|
|
||||||
Triangle t=triangles[r.tid];
|
|
||||||
if(t.deleted)continue;
|
|
||||||
if(deleted[k])
|
|
||||||
{
|
|
||||||
t.deleted=true;
|
|
||||||
deleted_triangles++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
t.v[r.tvertex]=i0;
|
|
||||||
t.dirty=true;
|
|
||||||
t.err[0]=calculate_error(t.v[0],t.v[1],ref p);
|
|
||||||
t.err[1]=calculate_error(t.v[1],t.v[2],ref p);
|
|
||||||
t.err[2]=calculate_error(t.v[2],t.v[0],ref p);
|
|
||||||
t.err[3]=Math.Min(t.err[0],Math.Min(t.err[1],t.err[2]));
|
|
||||||
triangles[r.tid] = t;
|
|
||||||
refs.Add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compact triangles, compute edge error and build reference list
|
|
||||||
|
|
||||||
private void update_mesh(int iteration)
|
|
||||||
{
|
|
||||||
if(iteration>0) // compact triangles
|
|
||||||
{
|
|
||||||
int dst=0;
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
if(!triangles[i].deleted)
|
|
||||||
{
|
|
||||||
triangles[dst++]=triangles[i];
|
|
||||||
}
|
|
||||||
triangles.Resize(dst);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Init Quadrics by Plane & Edge Errors
|
|
||||||
//
|
|
||||||
// required at the beginning ( iteration == 0 )
|
|
||||||
// recomputing during the simplification is not required,
|
|
||||||
// but mostly improves the result for closed meshes
|
|
||||||
//
|
|
||||||
if( iteration == 0 )
|
|
||||||
{
|
|
||||||
//for (int i = 0; i < vertices.Count; i++)
|
|
||||||
// vertices[i].q=new SymetricMatrix();//vertices[i].q=Matrix(0.0);
|
|
||||||
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
{
|
|
||||||
Triangle t=triangles[i];
|
|
||||||
|
|
||||||
Vector3[] p = new Vector3[3];
|
|
||||||
for (int j = 0; j<3; j++)
|
|
||||||
p[j]=vertices[t.v[j]].p;
|
|
||||||
|
|
||||||
Vector3 n = Vector3.Cross(p[1]-p[0],p[2]-p[0]);
|
|
||||||
n.Normalize();
|
|
||||||
t.n=n;
|
|
||||||
for (int j = 0; j<3; j++)
|
|
||||||
vertices[t.v[j]].q = vertices[t.v[j]].q + new SymetricMatrix(n.X,n.Y,n.Z,Vector3.Dot(-n, p[0]));
|
|
||||||
}
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
{
|
|
||||||
// Calc Edge Error
|
|
||||||
Triangle t=triangles[i];
|
|
||||||
Vector3 p = new Vector3();
|
|
||||||
for (int j = 0; j<3; j++) t.err[j]=calculate_error(t.v[j],t.v[(j+1)%3], ref p);
|
|
||||||
t.err[3]=Math.Min(t.err[0],Math.Min(t.err[1],t.err[2]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init Reference ID list
|
|
||||||
for (int i = 0; i < vertices.Count; i++)
|
|
||||||
{
|
|
||||||
vertices[i].tstart=0;
|
|
||||||
vertices[i].tcount=0;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
{
|
|
||||||
Triangle t=triangles[i];
|
|
||||||
for (int j = 0; j<3; j++)
|
|
||||||
vertices[t.v[j]].tcount++;
|
|
||||||
}
|
|
||||||
int tstart=0;
|
|
||||||
for (int i = 0; i < vertices.Count; i++)
|
|
||||||
{
|
|
||||||
Vertex v=vertices[i];
|
|
||||||
v.tstart=tstart;
|
|
||||||
tstart+=v.tcount;
|
|
||||||
v.tcount=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write References
|
|
||||||
refs.Resize(triangles.Count*3);
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
{
|
|
||||||
Triangle t=triangles[i];
|
|
||||||
for (int j = 0; j<3; j++)
|
|
||||||
{
|
|
||||||
Vertex v=vertices[t.v[j]];
|
|
||||||
refs[v.tstart+v.tcount].tid=i;
|
|
||||||
refs[v.tstart+v.tcount].tvertex=j;
|
|
||||||
v.tcount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify boundary : vertices[].border=0,1
|
|
||||||
if( iteration == 0 )
|
|
||||||
{
|
|
||||||
List<int> vcount = new List<int>();
|
|
||||||
List<int> vids = new List<int>();
|
|
||||||
|
|
||||||
for (int i = 0; i < vertices.Count; i++)
|
|
||||||
vertices[i].border=false;
|
|
||||||
|
|
||||||
for (int i = 0; i < vertices.Count; i++)
|
|
||||||
{
|
|
||||||
Vertex v=vertices[i];
|
|
||||||
vcount.Clear();
|
|
||||||
vids.Clear();
|
|
||||||
for (int j = 0; j < v.tcount; j++)
|
|
||||||
{
|
|
||||||
int kk=refs[v.tstart+j].tid;
|
|
||||||
Triangle t=triangles[kk];
|
|
||||||
for (int k = 0; k<3; k++)
|
|
||||||
{
|
|
||||||
int ofs=0,id=t.v[k];
|
|
||||||
while(ofs<vcount.Count)
|
|
||||||
{
|
|
||||||
if(vids[ofs]==id)break;
|
|
||||||
ofs++;
|
|
||||||
}
|
|
||||||
if(ofs==vcount.Count)
|
|
||||||
{
|
|
||||||
vcount.Add(1);
|
|
||||||
vids.Add(id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++vcount[ofs];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int j = 0; j< vcount.Count; j++) if(vcount[j]==1)
|
|
||||||
vertices[vids[j]].border=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally compact mesh before exiting
|
|
||||||
|
|
||||||
private void compact_mesh()
|
|
||||||
{
|
|
||||||
int dst=0;
|
|
||||||
for (int i = 0; i < vertices.Count; i++)
|
|
||||||
{
|
|
||||||
vertices[i].tcount=0;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
if(!triangles[i].deleted)
|
|
||||||
{
|
|
||||||
Triangle t=triangles[i];
|
|
||||||
triangles[dst++]=t;
|
|
||||||
for (int j = 0; j<3; j++)vertices[t.v[j]].tcount=1;
|
|
||||||
}
|
|
||||||
triangles.Resize(dst);
|
|
||||||
dst=0;
|
|
||||||
for (int i = 0; i < vertices.Count; i++)
|
|
||||||
if(vertices[i].tcount != 0)
|
|
||||||
{
|
|
||||||
vertices[i].tstart=dst;
|
|
||||||
vertices[dst].p=vertices[i].p;
|
|
||||||
dst++;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < triangles.Count; i++)
|
|
||||||
{
|
|
||||||
Triangle t=triangles[i];
|
|
||||||
for (int j = 0; j<3; j++)t.v[j]=vertices[t.v[j]].tstart;
|
|
||||||
}
|
|
||||||
vertices.Resize(dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error between vertex and Quadric
|
|
||||||
|
|
||||||
private double vertex_error(ref SymetricMatrix q, double x, double y, double z)
|
|
||||||
{
|
|
||||||
return q[0]*x*x + 2*q[1]*x*y + 2*q[2]*x*z + 2*q[3]*x + q[4]*y*y
|
|
||||||
+ 2*q[5]*y*z + 2*q[6]*y + q[7]*z*z + 2*q[8]*z + q[9];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error for one edge
|
|
||||||
|
|
||||||
private float calculate_error(int id_v1, int id_v2, ref Vector3 p_result)
|
|
||||||
{
|
|
||||||
// compute interpolated vertex
|
|
||||||
|
|
||||||
SymetricMatrix q = vertices[id_v1].q + vertices[id_v2].q;
|
|
||||||
bool border = vertices[id_v1].border && vertices[id_v2].border;
|
|
||||||
double error=0;
|
|
||||||
double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7);
|
|
||||||
if ( det != 0 && !border )
|
|
||||||
{
|
|
||||||
|
|
||||||
// q_delta is invertible
|
|
||||||
p_result.X = (float)(-1/det*(q.det(1, 2, 3, 4, 5, 6, 5, 7, 8))); // vx = A41/det(q_delta)
|
|
||||||
p_result.Y = (float)( 1/det*(q.det(0, 2, 3, 1, 5, 6, 2, 7, 8))); // vy = A42/det(q_delta)
|
|
||||||
p_result.Z = (float)(-1/det*(q.det(0, 1, 3, 1, 4, 6, 2, 5, 8))); // vz = A43/det(q_delta)
|
|
||||||
|
|
||||||
error = vertex_error(ref q, p_result.X, p_result.Y, p_result.Z);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// det = 0 -> try to find best result
|
|
||||||
Vector3 p1=vertices[id_v1].p;
|
|
||||||
Vector3 p2=vertices[id_v2].p;
|
|
||||||
Vector3 p3=(p1+p2)*0.5f;
|
|
||||||
double error1 = vertex_error(ref q, p1.X,p1.Y,p1.Z);
|
|
||||||
double error2 = vertex_error(ref q, p2.X,p2.Y,p2.Z);
|
|
||||||
double error3 = vertex_error(ref q, p3.X,p3.Y,p3.Z);
|
|
||||||
error = Math.Min(error1, Math.Min(error2, error3));
|
|
||||||
if (error1 == error) p_result=p1;
|
|
||||||
if (error2 == error) p_result=p2;
|
|
||||||
if (error3 == error) p_result=p3;
|
|
||||||
}
|
|
||||||
return (float)error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,15 +4,9 @@ using System.Diagnostics;
|
|||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading;
|
|
||||||
using FlaxEngine.Assertions;
|
using FlaxEngine.Assertions;
|
||||||
using FlaxEngine.Utilities;
|
|
||||||
using Console = Cabrito.Console;
|
using Console = Cabrito.Console;
|
||||||
|
|
||||||
// TODO: remove coplanar/degenerate faces from the final mesh
|
|
||||||
// It seems the original algorithm is working but removing degenerate faces does not work
|
|
||||||
|
|
||||||
namespace Game
|
namespace Game
|
||||||
{
|
{
|
||||||
public class BrushGeometry
|
public class BrushGeometry
|
||||||
@@ -26,7 +20,6 @@ namespace Game
|
|||||||
public MaterialBase brushMaterial = null; // FIXME: brush can have multiple textures
|
public MaterialBase brushMaterial = null; // FIXME: brush can have multiple textures
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class Q3MapImporter : Script
|
public class Q3MapImporter : Script
|
||||||
{
|
{
|
||||||
//private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube_q1.map";
|
//private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube_q1.map";
|
||||||
@@ -40,13 +33,6 @@ namespace Game
|
|||||||
Model model;
|
Model model;
|
||||||
public MaterialBase material;
|
public MaterialBase material;
|
||||||
|
|
||||||
const float epsilon = 0.01f;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void QuickHull(Vector3[] points, out Vector3[] outVertices)
|
static void QuickHull(Vector3[] points, out Vector3[] outVertices)
|
||||||
{
|
{
|
||||||
var verts = new List<Vector3>();
|
var verts = new List<Vector3>();
|
||||||
@@ -66,81 +52,6 @@ namespace Game
|
|||||||
outVertices = finalPoints.ToArray();
|
outVertices = finalPoints.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color[] planeColors = new Color[]
|
|
||||||
{
|
|
||||||
Color.Red,
|
|
||||||
Color.Orange,
|
|
||||||
Color.Yellow,
|
|
||||||
Color.Green,
|
|
||||||
Color.Cyan,
|
|
||||||
Color.Blue,
|
|
||||||
Color.Purple,
|
|
||||||
Color.Magenta,
|
|
||||||
};
|
|
||||||
|
|
||||||
private int skipPlanes = 0;
|
|
||||||
private int takePlanes = 5;
|
|
||||||
|
|
||||||
private List<Vector3> debugPoints = new List<Vector3>();
|
|
||||||
|
|
||||||
private List<Tuple<Vector3, Vector3>> debugNormals = new List<Tuple<Vector3, Vector3>>();
|
|
||||||
|
|
||||||
public override void OnDebugDraw()
|
|
||||||
{
|
|
||||||
foreach (var cn in debugNormals)
|
|
||||||
{
|
|
||||||
DebugDraw.DrawLine(cn.Item1, cn.Item1 + cn.Item2 * 50f, Color.Red, 0f, false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
if (root == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//foreach (var p in debugPoints)
|
|
||||||
// DebugDraw.DrawSphere(new BoundingSphere(p, 30f), Color.LightBlue, 0f, false);
|
|
||||||
|
|
||||||
foreach (var brush in root.entities[0].brushes.Skip(1).Take(1))
|
|
||||||
{
|
|
||||||
int planeI = skipPlanes;
|
|
||||||
foreach (var plane in brush.planes.Take(takePlanes))
|
|
||||||
//foreach (var plane in brush.planes)
|
|
||||||
{
|
|
||||||
Plane p = new Plane(plane.v1, plane.v2, plane.v3);
|
|
||||||
Vector3 planeNormal = -p.Normal;
|
|
||||||
|
|
||||||
const float w = 300f;
|
|
||||||
|
|
||||||
Vector3 p1 = new Vector3(-w, -w, 0f);
|
|
||||||
Vector3 p2 = new Vector3(w, -w, 0f);
|
|
||||||
Vector3 p3 = new Vector3(-w, w, 0f);
|
|
||||||
Vector3 p4 = new Vector3(w, w, 0f);
|
|
||||||
|
|
||||||
Vector3 uu = Vector3.Up;
|
|
||||||
if (Mathf.Abs(Vector3.Dot(planeNormal, uu)) > 0.9999f)
|
|
||||||
uu = Vector3.Forward;
|
|
||||||
|
|
||||||
var q = Quaternion.LookAt(Vector3.Zero, planeNormal, -uu);
|
|
||||||
|
|
||||||
p1 = p1 * q;
|
|
||||||
p2 = p2 * q;
|
|
||||||
p3 = p3 * q;
|
|
||||||
p4 = p4 * q;
|
|
||||||
|
|
||||||
p1 += p.D * planeNormal;
|
|
||||||
p2 += p.D * planeNormal;
|
|
||||||
p3 += p.D * planeNormal;
|
|
||||||
p4 += p.D * planeNormal;
|
|
||||||
|
|
||||||
var color = planeColors[planeI%planeColors.Length] * 0.5f;
|
|
||||||
DebugDraw.DrawTriangle(p1, p2, p3, color);
|
|
||||||
DebugDraw.DrawTriangle(p2, p3, p4, color);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
planeI++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MapEntity root;
|
private MapEntity root;
|
||||||
|
|
||||||
private static IEnumerable<IEnumerable<T>> DifferentCombinations<T>(IEnumerable<T> elements, int k)
|
private static IEnumerable<IEnumerable<T>> DifferentCombinations<T>(IEnumerable<T> elements, int k)
|
||||||
@@ -246,513 +157,6 @@ namespace Game
|
|||||||
vertices = new Vector3[0];
|
vertices = new Vector3[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void TriangulateBrush2(MapBrush brush, out Vector3[] vertices)
|
|
||||||
{
|
|
||||||
const float cs = 3000f;
|
|
||||||
|
|
||||||
Vector3[] cubePoints = new[]
|
|
||||||
{
|
|
||||||
new Vector3(-cs, -cs, -cs),
|
|
||||||
new Vector3(cs, -cs, -cs),
|
|
||||||
new Vector3(-cs, cs, -cs),
|
|
||||||
new Vector3(cs, cs, -cs),
|
|
||||||
new Vector3(-cs, -cs, cs),
|
|
||||||
new Vector3(cs, -cs, cs),
|
|
||||||
new Vector3(-cs, cs, cs),
|
|
||||||
new Vector3(cs, cs, cs),
|
|
||||||
};
|
|
||||||
Vector3[] cubeVerts;
|
|
||||||
QuickHull(cubePoints, out cubeVerts);
|
|
||||||
List<Vector3> brushVertices = new List<Vector3>(cubeVerts);
|
|
||||||
|
|
||||||
foreach (var brushPlane in brush.planes)
|
|
||||||
{
|
|
||||||
Plane plane = brushPlane.plane;
|
|
||||||
List<Vector3> faceVertices = new List<Vector3>();
|
|
||||||
List<Vector3> clippedVertices = new List<Vector3>();
|
|
||||||
|
|
||||||
Func<float, bool> isFront = (f) => f > epsilon;
|
|
||||||
Func<float, bool> isBack = (f) => f < epsilon;
|
|
||||||
|
|
||||||
for (int i = 0; i < brushVertices.Count; i++)
|
|
||||||
{
|
|
||||||
int i2 = ((i + 1) % 3 == 0) ? (i - 2) : (i + 1);
|
|
||||||
Vector3 start = brushVertices[i];
|
|
||||||
Vector3 end = brushVertices[i2];
|
|
||||||
|
|
||||||
var d1 = Plane.DotCoordinate(plane, start);
|
|
||||||
var d2 = Plane.DotCoordinate(plane, end);
|
|
||||||
|
|
||||||
if (isBack(d1))
|
|
||||||
{
|
|
||||||
// include the point behind the clipping plane
|
|
||||||
faceVertices.Add(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBack(d1) && isFront(d2) || isFront(d1) && isBack(d2))
|
|
||||||
{
|
|
||||||
// the cutting plane clips the edge
|
|
||||||
//if (isFront(d2))
|
|
||||||
{
|
|
||||||
Ray ray = new Ray(start, (end - start).Normalized);
|
|
||||||
Ray ray2 = new Ray(end, (start - end).Normalized);
|
|
||||||
if (plane.Intersects(ref ray, out Vector3 point) || plane.Intersects(ref ray2, out point))
|
|
||||||
{
|
|
||||||
|
|
||||||
//faceVertices.Add(point);
|
|
||||||
//clippedVertices.Add(point);
|
|
||||||
|
|
||||||
/*
|
|
||||||
intersect
|
|
||||||
start __._ (end)
|
|
||||||
| |/
|
|
||||||
| /
|
|
||||||
|/
|
|
||||||
next
|
|
||||||
*/
|
|
||||||
if (isBack(d1) && isFront(d2))
|
|
||||||
{
|
|
||||||
// finish the current triangle and start the next one
|
|
||||||
// [start, intersect, next], [intersect, ...]
|
|
||||||
|
|
||||||
faceVertices.Add(point);
|
|
||||||
|
|
||||||
if ((faceVertices.Count % 3) == 2)
|
|
||||||
{
|
|
||||||
int i3 = ((i2 + 1) % 3 == 0) ? (i2 - 2) : (i2 + 1);
|
|
||||||
Vector3 next = brushVertices[i3];
|
|
||||||
faceVertices.Add(next);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ray = ray;
|
|
||||||
|
|
||||||
faceVertices.Add(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
____ (start)
|
|
||||||
| |/
|
|
||||||
| * intersect2
|
|
||||||
|/
|
|
||||||
end
|
|
||||||
*/
|
|
||||||
else if (isFront(d1) && isBack(d2))
|
|
||||||
{
|
|
||||||
// continue where we left off
|
|
||||||
// [intersect, intersect2, ...]
|
|
||||||
|
|
||||||
faceVertices.Add(point);
|
|
||||||
|
|
||||||
if ((i % 3) == 2)
|
|
||||||
{
|
|
||||||
int i3 = ((i2 + 1) % 3 == 0) ? (i2 - 2) : (i2 + 1);
|
|
||||||
Vector3 next = brushVertices[i3];
|
|
||||||
faceVertices.Add(next);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ray = ray;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ray = ray;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
d1 = d1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
brushVertices.Clear();
|
|
||||||
brushVertices.AddRange(faceVertices);
|
|
||||||
|
|
||||||
Assert.IsTrue(faceVertices.Count % 3 == 0);
|
|
||||||
|
|
||||||
/*var newMeshPoints = new List<Vector3>();
|
|
||||||
int duplis = 0;
|
|
||||||
foreach (var v in faceVertices)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
foreach (var vo in newMeshPoints)
|
|
||||||
{
|
|
||||||
if ((v - vo).Length < epsilon)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
duplis++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (!newMeshPoints.Contains(v))
|
|
||||||
if (!found)
|
|
||||||
newMeshPoints.Add(v);
|
|
||||||
}
|
|
||||||
if (duplis > 0)
|
|
||||||
Console.Print("duplicates: " + duplis);
|
|
||||||
|
|
||||||
if (newMeshPoints.Count > 0)
|
|
||||||
{
|
|
||||||
var tempPoints = newMeshPoints;
|
|
||||||
newMeshPoints = new List<Vector3>(tempPoints.Count);
|
|
||||||
foreach (var tp in tempPoints)
|
|
||||||
{
|
|
||||||
// Flip Y and Z
|
|
||||||
newMeshPoints.Add(new Vector3(tp.X, tp.Z, tp.Y));
|
|
||||||
}
|
|
||||||
|
|
||||||
var hullPoints = QuickHull(newMeshPoints.ToArray());
|
|
||||||
var ms = new MeshSimplifier();
|
|
||||||
var optimizedVerts = ms.Simplify(hullPoints);
|
|
||||||
brushVertices.Clear();
|
|
||||||
brushVertices.AddRange(hullPoints);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
brushVertices.Clear();
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices = brushVertices.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
static public void TriangulateBrush3(MapBrush brush, out Vector3[] vertices)
|
|
||||||
{
|
|
||||||
float cs = 4000f;
|
|
||||||
|
|
||||||
float maxD = 0f;
|
|
||||||
float minD = 0f;
|
|
||||||
foreach (var brushPlane in brush.planes)
|
|
||||||
{
|
|
||||||
var p = brushPlane.plane;
|
|
||||||
minD = Mathf.Min(p.D);
|
|
||||||
maxD = Mathf.Max(p.D);
|
|
||||||
}
|
|
||||||
|
|
||||||
//cs = maxD*2;
|
|
||||||
|
|
||||||
|
|
||||||
Vector3[] cubePoints = new[]
|
|
||||||
{
|
|
||||||
new Vector3(-1, -1, -1) * cs,
|
|
||||||
new Vector3(1, -1, -1) * cs,
|
|
||||||
new Vector3(-1, 1, -1) * cs,
|
|
||||||
new Vector3(1, 1, -1) * cs,
|
|
||||||
new Vector3(-1, -1, 1) * cs,
|
|
||||||
new Vector3(1, -1, 1) * cs,
|
|
||||||
new Vector3(-1, 1, 1) * cs,
|
|
||||||
new Vector3(1, 1, 1) * cs,
|
|
||||||
};
|
|
||||||
Vector3[] cubeVerts;
|
|
||||||
QuickHull(cubePoints, out cubeVerts);
|
|
||||||
List<Vector3> brushVertices = new List<Vector3>(cubeVerts);
|
|
||||||
|
|
||||||
//foreach (var brushPlane in brush.planes.Skip(skipPlanes).Take(takePlanes))
|
|
||||||
foreach (var brushPlane in brush.planes)
|
|
||||||
{
|
|
||||||
Plane plane = brushPlane.plane;
|
|
||||||
|
|
||||||
List<Vector3> faceVertices = new List<Vector3>();
|
|
||||||
List<Vector3> clippedVertices = new List<Vector3>();
|
|
||||||
|
|
||||||
Func<float, bool> isFront = (f) => f > epsilon;
|
|
||||||
Func<float, bool> isBack = (f) => f < epsilon;
|
|
||||||
|
|
||||||
List<Tuple<Vector3, Vector3>> planeEdges = new List<Tuple<Vector3, Vector3>>();
|
|
||||||
var faceEdges = new List<Tuple<Vector3, Vector3>>();
|
|
||||||
|
|
||||||
void ProcessEdges()
|
|
||||||
{
|
|
||||||
if (planeEdges.Count > 0)
|
|
||||||
{
|
|
||||||
// heal discontinued edges
|
|
||||||
for (int j = 0; j < planeEdges.Count; j++)
|
|
||||||
{
|
|
||||||
var edgePrev = planeEdges[j];
|
|
||||||
var edgeNext = planeEdges[(j + 1) % planeEdges.Count];
|
|
||||||
|
|
||||||
//if (edgePrev.Item2 != edgeNext.Item1)
|
|
||||||
if ((edgePrev.Item2 - edgeNext.Item1).Length > 0.0001f)
|
|
||||||
{
|
|
||||||
var newEdge = new Tuple<Vector3, Vector3>(edgePrev.Item2, edgeNext.Item1);
|
|
||||||
planeEdges.Insert(j + 1, newEdge);
|
|
||||||
j--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// triangulate edges
|
|
||||||
/*for (int j = 0; j < edges.Count - 1; j++)
|
|
||||||
{
|
|
||||||
var edgePrev = edges[j];
|
|
||||||
var edgeNext = edges[(j + 1) % edges.Count];
|
|
||||||
|
|
||||||
Vector3 v0 = edges[0].Item1;
|
|
||||||
Vector3 v1 = edgePrev.Item2;
|
|
||||||
Vector3 v2 = edgeNext.Item2;
|
|
||||||
|
|
||||||
faceVertices.Add(v0);
|
|
||||||
faceVertices.Add(v1);
|
|
||||||
faceVertices.Add(v2);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// triangulate clipped face
|
|
||||||
/*for (int j = 0; j < clippedVertices.Count-1; j++)
|
|
||||||
{
|
|
||||||
Vector3 v0 = clippedVertices[0];
|
|
||||||
Vector3 v1 = edgePrev.Item2;
|
|
||||||
Vector3 v2 = edgeNext.Item2;
|
|
||||||
}*/
|
|
||||||
// TODO: maybe optimize the triangles here instead of using QuickHull
|
|
||||||
}
|
|
||||||
else
|
|
||||||
plane = plane;
|
|
||||||
|
|
||||||
|
|
||||||
faceEdges.AddRange(planeEdges);
|
|
||||||
planeEdges = new List<Tuple<Vector3, Vector3>>();
|
|
||||||
//edges.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < brushVertices.Count; i++)
|
|
||||||
{
|
|
||||||
if (i > 0 && i % 3 == 0)
|
|
||||||
ProcessEdges();
|
|
||||||
|
|
||||||
int i2 = ((i + 1) % 3 == 0) ? (i - 2) : (i + 1);
|
|
||||||
Vector3 start = brushVertices[i];
|
|
||||||
Vector3 end = brushVertices[i2];
|
|
||||||
|
|
||||||
var d1 = Plane.DotCoordinate(plane, start);
|
|
||||||
var d2 = Plane.DotCoordinate(plane, end);
|
|
||||||
|
|
||||||
Vector3 edgeStart = start;
|
|
||||||
Vector3 edgeEnd = end;
|
|
||||||
|
|
||||||
|
|
||||||
if (isBack(d1))
|
|
||||||
{
|
|
||||||
edgeStart = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBack(d1) && isFront(d2) || isFront(d1) && isBack(d2))
|
|
||||||
{
|
|
||||||
Ray ray = new Ray(start, (end - start).Normalized);
|
|
||||||
Ray ray2 = new Ray(end, (start - end).Normalized);
|
|
||||||
if (plane.Intersects(ref ray, out Vector3 point) || plane.Intersects(ref ray2, out point))
|
|
||||||
{
|
|
||||||
edgeEnd = point;
|
|
||||||
clippedVertices.Add(point);
|
|
||||||
|
|
||||||
if (isFront(d1))
|
|
||||||
{
|
|
||||||
edgeStart = edgeEnd;
|
|
||||||
edgeEnd = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFront(d1) && isFront(d2))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var abs = Mathf.Abs((edgeEnd-edgeStart).Length);
|
|
||||||
if (abs < 0.000001f)
|
|
||||||
{
|
|
||||||
abs = abs;
|
|
||||||
//continue;
|
|
||||||
}
|
|
||||||
Tuple<Vector3, Vector3> edge = new Tuple<Vector3, Vector3>(edgeStart, edgeEnd);
|
|
||||||
planeEdges.Add(edge);
|
|
||||||
}
|
|
||||||
ProcessEdges();
|
|
||||||
|
|
||||||
if (true)
|
|
||||||
{
|
|
||||||
// triangulate edges
|
|
||||||
|
|
||||||
faceVertices.Clear();
|
|
||||||
//foreach (var edges in faceEdges)
|
|
||||||
{
|
|
||||||
// merge same points in edges
|
|
||||||
for (int j = 0; j < faceEdges.Count; j++)
|
|
||||||
{
|
|
||||||
Vector3 v0 = faceEdges[j].Item1;
|
|
||||||
Vector3 v1 = faceEdges[j].Item2;
|
|
||||||
|
|
||||||
var edgeNext = faceEdges[(j + 1) % faceEdges.Count];
|
|
||||||
Vector3 v2 = edgeNext.Item1;
|
|
||||||
Vector3 v3 = edgeNext.Item2;
|
|
||||||
|
|
||||||
var dot = Vector3.Dot(v1.Normalized, v2.Normalized);
|
|
||||||
if (v1 != v2 && dot > 0.9999999f)
|
|
||||||
{
|
|
||||||
v1 = v1;
|
|
||||||
//faceEdges[(j + 1) % faceEdges.Count] = new Tuple<Vector3, Vector3>(v1, v3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Tuple<Vector3, Vector3>> newEdges = new List<Tuple<Vector3, Vector3>>();
|
|
||||||
for (int j = 0; j < faceEdges.Count; j++)
|
|
||||||
{
|
|
||||||
Vector3 v0 = faceEdges[j].Item1;
|
|
||||||
Vector3 v1 = faceEdges[j].Item2;
|
|
||||||
|
|
||||||
var abs = Mathf.Abs((v1-v0).Length);
|
|
||||||
if (abs < 0.000001f)
|
|
||||||
{
|
|
||||||
v1 = v1;
|
|
||||||
//continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tuple<Vector3, Vector3> newEdge = new Tuple<Vector3, Vector3>(v0, v1);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var edgeNext = faceEdges[(j + 1) % faceEdges.Count];
|
|
||||||
Vector3 v2 = edgeNext.Item1;
|
|
||||||
Vector3 v3 = edgeNext.Item2;
|
|
||||||
|
|
||||||
//var dot = Vector3.Dot((v3 - v0).Normalized, (v1 - v0).Normalized);
|
|
||||||
/*var dot = Vector3.Dot((v3 - v2).Normalized, (v1 - v0).Normalized);
|
|
||||||
if (dot > 0.9f)
|
|
||||||
{
|
|
||||||
newEdge = new Tuple<Vector3, Vector3>(v0, v3);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
else*/
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*var dot = Vector3.Dot((v3 - v2).Normalized, (v1 - v0).Normalized);
|
|
||||||
if (dot > 0.9f)
|
|
||||||
{
|
|
||||||
newEdge = new Tuple<Vector3, Vector3>(v0, v3);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
newEdges.Add(newEdge);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < newEdges.Count - 1; j++)
|
|
||||||
{
|
|
||||||
var edgePrev = newEdges[j];
|
|
||||||
var edgeNext = newEdges[(j + 1) % newEdges.Count];
|
|
||||||
|
|
||||||
Vector3 v0 = newEdges[0].Item1;
|
|
||||||
Vector3 v1 = edgePrev.Item2;
|
|
||||||
Vector3 v2 = edgeNext.Item2;
|
|
||||||
|
|
||||||
faceVertices.Add(v0);
|
|
||||||
faceVertices.Add(v1);
|
|
||||||
faceVertices.Add(v2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Vector3> uniqPoints = new List<Vector3>();
|
|
||||||
foreach (var v in faceVertices)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
foreach (var v2 in uniqPoints)
|
|
||||||
{
|
|
||||||
if ((v - v2).Length < 0.01f)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
uniqPoints.Add(v);
|
|
||||||
//uniqPoints.Add(new Vector3((float)Math.Round(v.X, 3), (float)Math.Round(v.Y, 3), (float)Math.Round(v.Z, 3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
//debugPoints = new List<Vector3>(uniqPoints);
|
|
||||||
|
|
||||||
|
|
||||||
Vector3[] hullPoints;
|
|
||||||
QuickHull(uniqPoints.ToArray(), out hullPoints);
|
|
||||||
var hullVerts = new MeshSimplifier().Simplify(hullPoints);
|
|
||||||
|
|
||||||
if (false)
|
|
||||||
{
|
|
||||||
// create edges from clipped points
|
|
||||||
var clippedEdges = new List<Tuple<Vector3, Vector3>>();
|
|
||||||
|
|
||||||
//foreach (var e in edges)
|
|
||||||
// newEdges.Add(new Tuple<Vector3, Vector3>(e.Item1, e.Item2));
|
|
||||||
|
|
||||||
for (int i = 0; i < clippedVertices.Count; i++)
|
|
||||||
{
|
|
||||||
int i2 = (i + 1) % clippedVertices.Count;
|
|
||||||
Vector3 start = clippedVertices[i];
|
|
||||||
Vector3 end = clippedVertices[i2];
|
|
||||||
|
|
||||||
while (i < clippedVertices.Count)
|
|
||||||
{
|
|
||||||
int i3 = (i + 2) % clippedVertices.Count;
|
|
||||||
Vector3 end2 = clippedVertices[i3];
|
|
||||||
|
|
||||||
var edgeDirection = (end - start).Normalized;
|
|
||||||
var edgeDirection2 = (end2 - start).Normalized;
|
|
||||||
|
|
||||||
if ((edgeDirection2 - edgeDirection).Length < 0.0001f)
|
|
||||||
{
|
|
||||||
end = end2;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
clippedEdges.Add(new Tuple<Vector3, Vector3>(start, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
// triangulate edges
|
|
||||||
for (int j = 0; j < clippedEdges.Count; j++)
|
|
||||||
{
|
|
||||||
var edgePrev = clippedEdges[j];
|
|
||||||
var edgeNext = clippedEdges[(j + 1) % clippedEdges.Count];
|
|
||||||
|
|
||||||
Vector3 v0 = clippedEdges[0].Item1;
|
|
||||||
Vector3 v1 = edgePrev.Item2;
|
|
||||||
Vector3 v2 = edgeNext.Item2;
|
|
||||||
|
|
||||||
faceVertices.Add(v0);
|
|
||||||
faceVertices.Add(v1);
|
|
||||||
faceVertices.Add(v2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true)
|
|
||||||
{
|
|
||||||
|
|
||||||
uniqPoints = uniqPoints;
|
|
||||||
hullVerts = hullVerts;
|
|
||||||
hullPoints = hullPoints;
|
|
||||||
faceVertices = faceVertices;
|
|
||||||
|
|
||||||
var optimizedVerts = hullVerts;//new MeshSimplifier().Simplify(faceVertices.ToArray());
|
|
||||||
|
|
||||||
brushVertices.Clear();
|
|
||||||
brushVertices.AddRange(optimizedVerts);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//debugPoints = new List<Vector3>(faceVertices);
|
|
||||||
|
|
||||||
//var hullPoints = faceVertices;
|
|
||||||
|
|
||||||
var ms = new MeshSimplifier();
|
|
||||||
var optimizedVerts = hullPoints; //ms.Simplify(hullPoints);
|
|
||||||
|
|
||||||
brushVertices.Clear();
|
|
||||||
brushVertices.AddRange(optimizedVerts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices = brushVertices.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnStart()
|
public override void OnStart()
|
||||||
{
|
{
|
||||||
byte[] mapChars = File.ReadAllBytes(mapPath);
|
byte[] mapChars = File.ReadAllBytes(mapPath);
|
||||||
@@ -766,7 +170,6 @@ namespace Game
|
|||||||
bool oneMesh = false;
|
bool oneMesh = false;
|
||||||
bool convexMesh = true;
|
bool convexMesh = true;
|
||||||
|
|
||||||
|
|
||||||
if (!oneMesh)
|
if (!oneMesh)
|
||||||
{
|
{
|
||||||
Dictionary<string, MaterialBase> materials = null;
|
Dictionary<string, MaterialBase> materials = null;
|
||||||
@@ -775,7 +178,6 @@ namespace Game
|
|||||||
|
|
||||||
List<BrushGeometry> brushGeometries = new List<BrushGeometry>(root.entities[0].brushes.Count);
|
List<BrushGeometry> brushGeometries = new List<BrushGeometry>(root.entities[0].brushes.Count);
|
||||||
|
|
||||||
|
|
||||||
// pass 1: triangulation
|
// pass 1: triangulation
|
||||||
sw.Restart();
|
sw.Restart();
|
||||||
int brushIndex = 0;
|
int brushIndex = 0;
|
||||||
@@ -947,40 +349,7 @@ namespace Game
|
|||||||
geom.model.SetupLODs(new int[] { 1 });
|
geom.model.SetupLODs(new int[] { 1 });
|
||||||
geom.model.LODs[0].Meshes[0].UpdateMesh(geom.vertices, geom.indices, geom.normals,
|
geom.model.LODs[0].Meshes[0].UpdateMesh(geom.vertices, geom.indices, geom.normals,
|
||||||
null, geom.uvs);
|
null, geom.uvs);
|
||||||
|
|
||||||
/*
|
|
||||||
StaticModel childModel = Actor.AddChild<StaticModel>();
|
|
||||||
childModel.Name = "Brush_" + brushIndex;
|
|
||||||
childModel.Model = geom.model;
|
|
||||||
childModel.SetMaterial(0, geom.brushMaterial);
|
|
||||||
childModel.Parent = mapRootActor;
|
|
||||||
|
|
||||||
CollisionData collisionData = Content.CreateVirtualAsset<CollisionData>();
|
|
||||||
if (collisionData.CookCollision(convexMesh ? CollisionDataType.ConvexMesh : CollisionDataType.TriangleMesh, geom.vertices.ToArray(),
|
|
||||||
geom.indices.ToArray()))
|
|
||||||
{
|
|
||||||
bool failed = true;
|
|
||||||
if (convexMesh)
|
|
||||||
{
|
|
||||||
// fallback to triangle mesh
|
|
||||||
failed = collisionData.CookCollision(CollisionDataType.TriangleMesh,
|
|
||||||
geom.vertices.ToArray(),
|
|
||||||
geom.indices.ToArray());
|
|
||||||
if (!failed)
|
|
||||||
Console.PrintWarning("Hull brush " + brushIndex.ToString() + " is not convex");
|
|
||||||
}
|
|
||||||
if (failed)
|
|
||||||
throw new Exception("failed to cook final collision");
|
|
||||||
}
|
|
||||||
|
|
||||||
var meshCollider = childModel.AddChild<MeshCollider>();
|
|
||||||
meshCollider.CollisionData = collisionData;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//brushGeometries.Add(geom);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
Console.Print("Pass 2: texturing: " + sw.Elapsed.TotalMilliseconds + "ms");
|
Console.Print("Pass 2: texturing: " + sw.Elapsed.TotalMilliseconds + "ms");
|
||||||
@@ -1032,7 +401,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TriangulateBrush3(brush, out Vector3[] brushVertices);
|
TriangulateBrush(brush, out Vector3[] brushVertices);
|
||||||
Vector2[] brushUvs = new Vector2[brushVertices.Length];
|
Vector2[] brushUvs = new Vector2[brushVertices.Length];
|
||||||
Vector3[] brushNormals = new Vector3[brushVertices.Length];
|
Vector3[] brushNormals = new Vector3[brushVertices.Length];
|
||||||
|
|
||||||
@@ -1159,20 +528,6 @@ namespace Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnEnable()
|
|
||||||
{
|
|
||||||
// Here you can add code that needs to be called when script is enabled (eg. register for events)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDisable()
|
|
||||||
{
|
|
||||||
// Here you can add code that needs to be called when script is disabled (eg. unregister from events)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
Destroy(ref model);
|
Destroy(ref model);
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using FlaxEngine;
|
|
||||||
|
|
||||||
namespace Game
|
|
||||||
{
|
|
||||||
[ExecuteInEditMode]
|
|
||||||
public class QBrush : Script
|
|
||||||
{
|
|
||||||
Model model;
|
|
||||||
|
|
||||||
public MaterialBase material;
|
|
||||||
|
|
||||||
public override void OnAwake()
|
|
||||||
{
|
|
||||||
model = Content.CreateVirtualAsset<Model>();
|
|
||||||
model.SetupLODs(new int[] {1});
|
|
||||||
|
|
||||||
{
|
|
||||||
var mesh = model.LODs[0].Meshes[0];
|
|
||||||
|
|
||||||
const float X = 0.525731112119133606f * 100f;
|
|
||||||
const float Z = 0.850650808352039932f * 100f;
|
|
||||||
const float N = 0.0f;
|
|
||||||
var vertices = new[]
|
|
||||||
{
|
|
||||||
new Vector3(-X, N, Z),
|
|
||||||
new Vector3(X, N, Z),
|
|
||||||
new Vector3(-X, N, -Z),
|
|
||||||
new Vector3(X, N, -Z),
|
|
||||||
new Vector3(N, Z, X),
|
|
||||||
new Vector3(N, Z, -X),
|
|
||||||
new Vector3(N, -Z, X),
|
|
||||||
new Vector3(N, -Z, -X),
|
|
||||||
new Vector3(Z, X, N),
|
|
||||||
new Vector3(-Z, X, N),
|
|
||||||
new Vector3(Z, -X, N),
|
|
||||||
new Vector3(-Z, -X, N)
|
|
||||||
};
|
|
||||||
var triangles = new[]
|
|
||||||
{
|
|
||||||
1, 4, 0, 4, 9, 0, 4, 5, 9, 8, 5, 4,
|
|
||||||
1, 8, 4, 1, 10, 8, 10, 3, 8, 8, 3, 5,
|
|
||||||
3, 2, 5, 3, 7, 2, 3, 10, 7, 10, 6, 7,
|
|
||||||
6, 11, 7, 6, 0, 11, 6, 1, 0, 10, 1, 6,
|
|
||||||
11, 0, 9, 2, 11, 9, 5, 2, 9, 11, 2, 7
|
|
||||||
};
|
|
||||||
mesh.UpdateMesh(vertices, triangles, vertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
StaticModel childModel = Actor.GetOrAddChild<StaticModel>();
|
|
||||||
childModel.Model = model;
|
|
||||||
childModel.SetMaterial(0, material);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDestroy()
|
|
||||||
{
|
|
||||||
Destroy(ref model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@ namespace GoakeTests.MapParser
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
|
|
||||||
dm4Bytes = File.ReadAllBytes(@"C:\dev\GoakeFlax\Assets\Maps\dm4.map");
|
dm4Bytes = File.ReadAllBytes(@"C:\dev\GoakeFlax\Assets\Maps\dm4.map");
|
||||||
|
|
||||||
aerowalkBytes = File.ReadAllBytes(@"C:\dev\Goake\maps\aerowalk\aerowalk.map");
|
aerowalkBytes = File.ReadAllBytes(@"C:\dev\Goake\maps\aerowalk\aerowalk.map");
|
||||||
@@ -99,33 +98,6 @@ namespace GoakeTests.MapParser
|
|||||||
// warmup?
|
// warmup?
|
||||||
//var roott = Game.MapParser.Parse(aerowalkBytes);
|
//var roott = Game.MapParser.Parse(aerowalkBytes);
|
||||||
|
|
||||||
List<Game.MapEntity> mapEntities = new List<Game.MapEntity>(100);
|
|
||||||
aerowalkRoot = Game.MapParser.Parse(aerowalkBytes);
|
|
||||||
|
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
|
||||||
|
|
||||||
for (int i = 0; i < 1; i++)
|
|
||||||
{
|
|
||||||
foreach (var ent in aerowalkRoot.entities)
|
|
||||||
foreach (var brush in ent.brushes)
|
|
||||||
{
|
|
||||||
Q3MapImporter.TriangulateBrush3(brush, out Vector3[] verts);
|
|
||||||
Assert.IsTrue(verts.Length > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sw.Stop();
|
|
||||||
|
|
||||||
var elapsedMs = sw.Elapsed.TotalMilliseconds;
|
|
||||||
TestContext.Out.WriteLine("Triangulation time: " + elapsedMs + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Perf_TriangulateAerowalk2()
|
|
||||||
{
|
|
||||||
// warmup?
|
|
||||||
//var roott = Game.MapParser.Parse(aerowalkBytes);
|
|
||||||
|
|
||||||
List<Game.MapEntity> mapEntities = new List<Game.MapEntity>(100);
|
List<Game.MapEntity> mapEntities = new List<Game.MapEntity>(100);
|
||||||
aerowalkRoot = Game.MapParser.Parse(aerowalkBytes);
|
aerowalkRoot = Game.MapParser.Parse(aerowalkBytes);
|
||||||
/*for (int i = 0; i < 1; i++)
|
/*for (int i = 0; i < 1; i++)
|
||||||
@@ -172,7 +144,7 @@ namespace GoakeTests.MapParser
|
|||||||
foreach (var ent in root.entities)
|
foreach (var ent in root.entities)
|
||||||
foreach (var brush in ent.brushes)
|
foreach (var brush in ent.brushes)
|
||||||
{
|
{
|
||||||
Q3MapImporter.TriangulateBrush3(brush, out Vector3[] verts);
|
Q3MapImporter.TriangulateBrush(brush, out Vector3[] verts);
|
||||||
Assert.IsTrue(verts.Length > 0);
|
Assert.IsTrue(verts.Length > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user