using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.ProBuilder.Csg
{
///
/// Represents a plane in 3d space.
/// Does not include position.
///
sealed class Plane
{
public Vector3 normal;
public float w;
[System.Flags]
enum EPolygonType
{
Coplanar = 0,
Front = 1,
Back = 2,
Spanning = 3 /// 3 is Front | Back - not a separate entry
};
public Plane()
{
normal = Vector3.zero;
w = 0f;
}
public Plane(Vector3 a, Vector3 b, Vector3 c)
{
normal = Vector3.Cross(b - a, c - a);//.normalized;
w = Vector3.Dot(normal, a);
}
public override string ToString() => $"{normal} {w}";
public bool Valid()
{
return normal.magnitude > 0f;
}
public void Flip()
{
normal *= -1f;
w *= -1f;
}
// Split `polygon` by this plane if needed, then put the polygon or polygon
// fragments in the appropriate lists. Coplanar polygons go into either
// `coplanarFront` or `coplanarBack` depending on their orientation with
// respect to this plane. Polygons in front or in back of this plane go into
// either `front` or `back`.
public void SplitPolygon(Polygon polygon, List coplanarFront, List coplanarBack, List front, List back)
{
// Classify each point as well as the entire polygon into one of the above
// four classes.
EPolygonType polygonType = 0;
List types = new List();
for (int i = 0; i < polygon.vertices.Count; i++)
{
float t = Vector3.Dot(this.normal, polygon.vertices[i].position) - this.w;
EPolygonType type = (t < -CSG.epsilon) ? EPolygonType.Back : ((t > CSG.epsilon) ? EPolygonType.Front : EPolygonType.Coplanar);
polygonType |= type;
types.Add(type);
}
// Put the polygon in the correct list, splitting it when necessary.
switch (polygonType)
{
case EPolygonType.Coplanar:
{
if (Vector3.Dot(this.normal, polygon.plane.normal) > 0)
coplanarFront.Add(polygon);
else
coplanarBack.Add(polygon);
}
break;
case EPolygonType.Front:
{
front.Add(polygon);
}
break;
case EPolygonType.Back:
{
back.Add(polygon);
}
break;
case EPolygonType.Spanning:
{
List f = new List();
List b = new List();
for (int i = 0; i < polygon.vertices.Count; i++)
{
int j = (i + 1) % polygon.vertices.Count;
EPolygonType ti = types[i], tj = types[j];
Vertex vi = polygon.vertices[i], vj = polygon.vertices[j];
if (ti != EPolygonType.Back)
{
f.Add(vi);
}
if (ti != EPolygonType.Front)
{
b.Add(vi);
}
if ((ti | tj) == EPolygonType.Spanning)
{
float t = (this.w - Vector3.Dot(this.normal, vi.position)) / Vector3.Dot(this.normal, vj.position - vi.position);
Vertex v = VertexUtility.Mix(vi, vj, t);
f.Add(v);
b.Add(v);
}
}
if (f.Count >= 3)
{
if (f.SequenceEqual(polygon.vertices))
front.Add(polygon);
else
{
var p = new Polygon(f, polygon.material);
if (p.plane.Valid())
front.Add(p);
}
}
if (b.Count >= 3)
{
if (b.SequenceEqual(polygon.vertices))
back.Add(polygon);
else
{
var p = new Polygon(b, polygon.material);
if (p.plane.Valid())
back.Add(p);
}
}
}
break;
} // End switch(polygonType)
}
}
}