Skip to content
Browse files

Cleaner interface (use ContourVertex for input/output)

Support for custom user data on vertices
Add OpenGL specific samples
  • Loading branch information...
1 parent e4ea803 commit f3ffde49e94726d315c33887a4767bd0df437f6f @speps committed Mar 10, 2012
View
2 LibTessDotNet/LibTessDotNet.csproj
@@ -24,6 +24,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>default</LangVersion>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -33,6 +34,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>default</LangVersion>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
View
2 LibTessDotNet/Sources/Geom.cs
@@ -42,7 +42,7 @@ public static bool IsWindingInside(WindingRule rule, int n)
{
switch (rule)
{
- case WindingRule.OddEven:
+ case WindingRule.EvenOdd:
return (n & 1) == 1;
case WindingRule.NonZero:
return n != 0;
View
107 LibTessDotNet/Sources/MeshUtils.cs
@@ -36,70 +36,70 @@
namespace LibTessDotNet
{
- internal static class MeshUtils
+ public struct Vec3
{
- public const int Undef = ~0;
+ public readonly static Vec3 Zero = new Vec3();
- public struct Vec3
- {
- public readonly static Vec3 Zero = new Vec3();
+ public float X, Y, Z;
- public float X, Y, Z;
-
- public float this[int index]
+ public float this[int index]
+ {
+ get
{
- get
- {
- if (index == 0) return X;
- if (index == 1) return Y;
- if (index == 2) return Z;
- throw new IndexOutOfRangeException();
- }
- set
- {
- if (index == 0) X = value;
- else if (index == 1) Y = value;
- else if (index == 2) Z = value;
- else throw new IndexOutOfRangeException();
- }
+ if (index == 0) return X;
+ if (index == 1) return Y;
+ if (index == 2) return Z;
+ throw new IndexOutOfRangeException();
}
-
- public static void Sub(ref Vec3 lhs, ref Vec3 rhs, out Vec3 result)
+ set
{
- result.X = lhs.X - rhs.X;
- result.Y = lhs.Y - rhs.Y;
- result.Z = lhs.Z - rhs.Z;
+ if (index == 0) X = value;
+ else if (index == 1) Y = value;
+ else if (index == 2) Z = value;
+ else throw new IndexOutOfRangeException();
}
+ }
- public static void Neg(ref Vec3 v)
- {
- v.X = -v.X;
- v.Y = -v.Y;
- v.Z = -v.Z;
- }
+ public static void Sub(ref Vec3 lhs, ref Vec3 rhs, out Vec3 result)
+ {
+ result.X = lhs.X - rhs.X;
+ result.Y = lhs.Y - rhs.Y;
+ result.Z = lhs.Z - rhs.Z;
+ }
- public static void Dot(ref Vec3 u, ref Vec3 v, out float dot)
- {
- dot = u.X * v.X + u.Y * v.Y + u.Z * v.Z;
- }
- public static void Normalize(ref Vec3 v)
- {
- float len = v.X * v.X + v.Y * v.Y + v.Z * v.Z;
- Debug.Assert(len >= 0.0f);
- len = 1.0f / (float)Math.Sqrt(len);
- v.X *= len;
- v.Y *= len;
- v.Z *= len;
- }
- public static int LongAxis(ref Vec3 v)
- {
- int i = 0;
- if (Math.Abs(v.Y) > Math.Abs(v.X)) i = 1;
- if (Math.Abs(v.Z) > Math.Abs(i == 0 ? v.X : v.Y)) i = 2;
- return i;
- }
+ public static void Neg(ref Vec3 v)
+ {
+ v.X = -v.X;
+ v.Y = -v.Y;
+ v.Z = -v.Z;
}
+ public static void Dot(ref Vec3 u, ref Vec3 v, out float dot)
+ {
+ dot = u.X * v.X + u.Y * v.Y + u.Z * v.Z;
+ }
+ public static void Normalize(ref Vec3 v)
+ {
+ float len = v.X * v.X + v.Y * v.Y + v.Z * v.Z;
+ Debug.Assert(len >= 0.0f);
+ len = 1.0f / (float)Math.Sqrt(len);
+ v.X *= len;
+ v.Y *= len;
+ v.Z *= len;
+ }
+ public static int LongAxis(ref Vec3 v)
+ {
+ int i = 0;
+ if (Math.Abs(v.Y) > Math.Abs(v.X)) i = 1;
+ if (Math.Abs(v.Z) > Math.Abs(i == 0 ? v.X : v.Y)) i = 2;
+ return i;
+ }
+ }
+
+ internal static class MeshUtils
+ {
+ public const int Undef = ~0;
+
public class Vertex
{
internal Vertex _prev, _next;
@@ -109,6 +109,7 @@ public class Vertex
internal float _s, _t;
internal PQHandle _pqHandle;
internal int _n, _idx;
+ internal object _data;
}
public class Face
View
22 LibTessDotNet/Sources/Sweep.cs
@@ -339,13 +339,13 @@ private void SpliceMergeVertices(MeshUtils.Edge e1, MeshUtils.Edge e2)
/// splits the weight between its org and dst according to the
/// relative distance to "isect".
/// </summary>
- private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUtils.Vertex dst)
+ private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUtils.Vertex dst, out float w0, out float w1)
{
var t1 = Geom.VertL1dist(org, isect);
var t2 = Geom.VertL1dist(dst, isect);
- var w0 = 0.5f * t2 / (t1 + t2);
- var w1 = 0.5f * t1 / (t1 + t2);
+ w0 = 0.5f * t2 / (t1 + t2);
+ w1 = 0.5f * t1 / (t1 + t2);
isect._coords.X += w0 * org._coords.X + w1 * dst._coords.X;
isect._coords.Y += w0 * org._coords.Y + w1 * dst._coords.Y;
@@ -359,10 +359,20 @@ private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUti
/// </summary>
private void GetIntersectData(MeshUtils.Vertex isect, MeshUtils.Vertex orgUp, MeshUtils.Vertex dstUp, MeshUtils.Vertex orgLo, MeshUtils.Vertex dstLo)
{
- isect._coords = MeshUtils.Vec3.Zero;
+ isect._coords = Vec3.Zero;
isect._idx = MeshUtils.Undef;
- VertexWeights(isect, orgUp, dstUp);
- VertexWeights(isect, orgLo, dstLo);
+ float w0, w1, w2, w3;
+ VertexWeights(isect, orgUp, dstUp, out w0, out w1);
+ VertexWeights(isect, orgLo, dstLo, out w2, out w3);
+
+ if (_combineCallback != null)
+ {
+ isect._data = _combineCallback(
+ isect._coords,
+ new object[] { orgUp._data, dstUp._data, orgLo._data, dstLo._data },
+ new float[] { w0, w1, w2, w3 }
+ );
+ }
}
/// <summary>
View
135 LibTessDotNet/Sources/Tess.cs
@@ -37,7 +37,7 @@ namespace LibTessDotNet
{
public enum WindingRule
{
- OddEven,
+ EvenOdd,
NonZero,
Positive,
Negative,
@@ -51,12 +51,20 @@ public enum ElementType
BoundaryContours
}
+ public struct ContourVertex
+ {
+ public Vec3 Position;
+ public object Data;
+ }
+
+ public delegate object CombineCallback(Vec3 position, object[] data, float[] weights);
+
public partial class Tess
{
private Mesh _mesh;
- private MeshUtils.Vec3 _normal;
- private MeshUtils.Vec3 _sUnit;
- private MeshUtils.Vec3 _tUnit;
+ private Vec3 _normal;
+ private Vec3 _sUnit;
+ private Vec3 _tUnit;
private float _bminX, _bminY, _bmaxX, _bmaxY;
@@ -66,25 +74,33 @@ public partial class Tess
private PriorityQueue<MeshUtils.Vertex> _pq;
private MeshUtils.Vertex _event;
+ private CombineCallback _combineCallback;
private int _vertexIndexCounter;
- private float[] _vertices;
- public float[] Vertices { get { return _vertices; } }
+ private ContourVertex[] _vertices;
private int[] _vertexIndices;
- public int[] VertexIndices { get { return _vertexIndices; } }
private int _vertexCount;
- public int VertexCount { get { return _vertexCount; } }
private int[] _elements;
- public int[] Elements { get { return _elements; } }
private int _elementCount;
+
+ public Vec3 Normal { get { return _normal; } set { _normal = value; } }
+
+ public float SUnitX = 1.0f;
+ public float SUnitY = 1.0f;
+
+ public ContourVertex[] Vertices { get { return _vertices; } }
+ public int[] VertexIndices { get { return _vertexIndices; } }
+ public int VertexCount { get { return _vertexCount; } }
+
+ public int[] Elements { get { return _elements; } }
public int ElementCount { get { return _elementCount; } }
public Tess()
{
- _normal = MeshUtils.Vec3.Zero;
+ _normal = Vec3.Zero;
_bminX = _bminY = _bmaxX = _bmaxY = 0.0f;
- _windingRule = WindingRule.OddEven;
+ _windingRule = WindingRule.EvenOdd;
_mesh = null;
_vertexIndexCounter = 0;
@@ -96,7 +112,7 @@ public Tess()
_elementCount = 0;
}
- private void ComputeNormal(ref MeshUtils.Vec3 norm)
+ private void ComputeNormal(ref Vec3 norm)
{
var v = _mesh._vHead._next;
@@ -123,7 +139,7 @@ private void ComputeNormal(ref MeshUtils.Vec3 norm)
if (minVal[i] >= maxVal[i])
{
// All vertices are the same -- normal doesn't matter
- norm = new MeshUtils.Vec3 { X = 0.0f, Y = 0.0f, Z = 1.0f };
+ norm = new Vec3 { X = 0.0f, Y = 0.0f, Z = 1.0f };
return;
}
@@ -132,11 +148,11 @@ private void ComputeNormal(ref MeshUtils.Vec3 norm)
float maxLen2 = 0.0f, tLen2;
var v1 = minVert[i];
var v2 = maxVert[i];
- MeshUtils.Vec3 d1, d2, tNorm;
- MeshUtils.Vec3.Sub(ref v1._coords, ref v2._coords, out d1);
+ Vec3 d1, d2, tNorm;
+ Vec3.Sub(ref v1._coords, ref v2._coords, out d1);
for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
{
- MeshUtils.Vec3.Sub(ref v._coords, ref v2._coords, out d2);
+ Vec3.Sub(ref v._coords, ref v2._coords, out d2);
tNorm.X = d1.Y * d2.Z - d1.Z * d2.Y;
tNorm.Y = d1.Z * d2.X - d1.X * d2.Z;
tNorm.Z = d1.X * d2.Y - d1.Y * d2.X;
@@ -151,8 +167,8 @@ private void ComputeNormal(ref MeshUtils.Vec3 norm)
if (maxLen2 <= 0.0f)
{
// All points lie on a single line -- any decent normal will do
- norm = MeshUtils.Vec3.Zero;
- i = MeshUtils.Vec3.LongAxis(ref d1);
+ norm = Vec3.Zero;
+ i = Vec3.LongAxis(ref d1);
norm[i] = 1.0f;
}
}
@@ -181,13 +197,10 @@ private void CheckOrientation()
{
v._t = -v._t;
}
- MeshUtils.Vec3.Neg(ref _tUnit);
+ Vec3.Neg(ref _tUnit);
}
}
- public static float SUnitX = 1.0f;
- public static float SUnitY = 1.0f;
-
private void ProjectPolygon()
{
var norm = _normal;
@@ -199,7 +212,7 @@ private void ProjectPolygon()
computedNormal = true;
}
- int i = MeshUtils.Vec3.LongAxis(ref norm);
+ int i = Vec3.LongAxis(ref norm);
_sUnit[i] = 0.0f;
_sUnit[(i + 1) % 3] = SUnitX;
@@ -212,8 +225,8 @@ private void ProjectPolygon()
// Project the vertices onto the sweep plane
for (var v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
{
- MeshUtils.Vec3.Dot(ref v._coords, ref _sUnit, out v._s);
- MeshUtils.Vec3.Dot(ref v._coords, ref _tUnit, out v._t);
+ Vec3.Dot(ref v._coords, ref _sUnit, out v._s);
+ Vec3.Dot(ref v._coords, ref _tUnit, out v._t);
}
if (computedNormal)
{
@@ -404,7 +417,7 @@ private int GetNeighbourFace(MeshUtils.Edge edge)
return edge._Rface._n;
}
- private void OutputPolymesh(ElementType elementType, int polySize, int vertexSize)
+ private void OutputPolymesh(ElementType elementType, int polySize)
{
MeshUtils.Vertex v;
MeshUtils.Face f;
@@ -460,7 +473,7 @@ private void OutputPolymesh(ElementType elementType, int polySize, int vertexSiz
_elements = new int[maxFaceCount * polySize];
_vertexCount = maxVertexCount;
- _vertices = new float[_vertexCount * vertexSize];
+ _vertices = new ContourVertex[_vertexCount];
_vertexIndices = new int[_vertexCount];
// Output vertices.
@@ -469,11 +482,9 @@ private void OutputPolymesh(ElementType elementType, int polySize, int vertexSiz
if (v._n != MeshUtils.Undef)
{
// Store coordinate
- int n = v._n * vertexSize;
- _vertices[n + 0] = v._coords.X;
- _vertices[n + 1] = v._coords.Y;
- if (vertexSize > 2)
- _vertices[n + 2] = v._coords.Z;
+ int n = v._n;
+ _vertices[v._n].Position = v._coords;
+ _vertices[v._n].Data = v._data;
// Store vertex index.
_vertexIndices[v._n] = v._idx;
}
@@ -496,7 +507,9 @@ private void OutputPolymesh(ElementType elementType, int polySize, int vertexSiz
} while (edge != f._anEdge);
// Fill unused.
for (i = faceVerts; i < polySize; ++i)
+ {
_elements[elementIndex++] = MeshUtils.Undef;
+ }
// Store polygon connectivity
if (elementType == ElementType.ConnectedPolygons)
@@ -506,16 +519,17 @@ private void OutputPolymesh(ElementType elementType, int polySize, int vertexSiz
{
_elements[elementIndex++] = GetNeighbourFace(edge);
edge = edge._Lnext;
- }
- while (edge != f._anEdge);
+ } while (edge != f._anEdge);
// Fill unused.
for (i = faceVerts; i < polySize; ++i)
+ {
_elements[elementIndex++] = MeshUtils.Undef;
+ }
}
}
}
- private void OutputContours(int vertexSize)
+ private void OutputContours()
{
MeshUtils.Face f;
MeshUtils.Edge edge, start;
@@ -541,7 +555,7 @@ private void OutputContours(int vertexSize)
}
_elements = new int[_elementCount * 2];
- _vertices = new float[_vertexCount * vertexSize];
+ _vertices = new ContourVertex[_vertexCount];
_vertexIndices = new int[_vertexCount];
int vertIndex = 0;
@@ -558,10 +572,8 @@ private void OutputContours(int vertexSize)
start = edge = f._anEdge;
do
{
- _vertices[vertIndex++] = edge._Org._coords.X;
- _vertices[vertIndex++] = edge._Org._coords.Y;
- if (vertexSize > 2)
- _vertices[vertIndex++] = edge._Org._coords.Z;
+ _vertices[vertIndex++].Position = edge._Org._coords;
+ _vertices[vertIndex++].Data = edge._Org._data;
_vertexIndices[vertIndsIndex++] = edge._Org._idx;
++vertCount;
edge = edge._Lnext;
@@ -575,21 +587,15 @@ private void OutputContours(int vertexSize)
}
}
- public void AddContour(int size, float[] vertices)
+ public void AddContour(ContourVertex[] vertices)
{
if (_mesh == null)
{
_mesh = new Mesh();
}
- if (size < 2)
- size = 2;
- if (size > 3)
- size = 3;
-
MeshUtils.Edge e = null;
- int numVertices = vertices.Length / size;
- for (int i = 0; i < numVertices; ++i)
+ for (int i = 0; i < vertices.Length; ++i)
{
if (e == null)
{
@@ -605,13 +611,8 @@ public void AddContour(int size, float[] vertices)
}
// The new vertex is now e._Org.
- int n = i * size;
- e._Org._coords.X = vertices[n + 0];
- e._Org._coords.Y = vertices[n + 1];
- if ( size > 2 )
- e._Org._coords.Z = vertices[n + 2];
- else
- e._Org._coords.Z = 0.0f;
+ e._Org._coords = vertices[i].Position;
+ e._Org._data = vertices[i].Data;
// Store the insertion number so that the vertex can be later recognized.
e._Org._idx = _vertexIndexCounter++;
@@ -625,27 +626,21 @@ public void AddContour(int size, float[] vertices)
}
}
- public void Tesselate(WindingRule windingRule, ElementType elementType, int polySize, int vertexSize, float[] normal)
+ public void Tessellate(WindingRule windingRule, ElementType elementType, int polySize)
+ {
+ Tessellate(windingRule, elementType, polySize, null);
+ }
+
+ public void Tessellate(WindingRule windingRule, ElementType elementType, int polySize, CombineCallback combineCallback)
{
_vertices = null;
_elements = null;
_vertexIndices = null;
_vertexIndexCounter = 0;
- if (normal != null && normal.Length == 3)
- {
- _normal.X = normal[0];
- _normal.Y = normal[1];
- _normal.Z = normal[2];
- }
-
_windingRule = windingRule;
-
- if (vertexSize < 2)
- vertexSize = 2;
- if (vertexSize > 3)
- vertexSize = 3;
+ _combineCallback = combineCallback;
if (_mesh == null)
{
@@ -679,11 +674,11 @@ public void Tesselate(WindingRule windingRule, ElementType elementType, int poly
if (elementType == ElementType.BoundaryContours)
{
- OutputContours(vertexSize);
+ OutputContours();
}
else
{
- OutputPolymesh(elementType, polySize, vertexSize);
+ OutputPolymesh(elementType, polySize);
}
_mesh = null;
View
20 README.md
@@ -1,20 +1,23 @@
LibTessDotNet
=============
-### Requirements
-* .NET framework 2.0 (pure CLR, should work with Mono and Unity)
-* C# 3.0 compiler if you want to compile (I am guilty of using 'var')
-* WinForms for the testbed (optional)
-
### Goal
Provide a robust and fast tessellator (polygons with N vertices in the output) for .NET, also does triangulation.
+### Requirements
+
+* .NET framework 2.0 (pure CLR, should work with Mono, Unity or XNA)
+* C# 3.0 compiler if you want to compile (I am guilty of using 'var')
+ - WinForms for the testbed
+ - Solution file for Visual Studio 2010
+
### Features
* Tessellate arbitrary complex polygons
- self-intersecting (see "star-intersect" sample)
- with coincident vertices (see "clipper" sample)
+ - advanced winding rules : even/odd, non zero, positive, negative, |winding| >= 2 (see "redbook-winding" sample)
* Choice of output
- polygons with N vertices (with N >= 3)
- connected polygons (didn't quite tried this yet, but should work)
@@ -27,13 +30,14 @@ TBD
### TODO
-* More .NET-style interface (ie. don't use float[] for input/output)
-* Custom vertex attributes (eg. UV coordinates)
+* Better performance timing (eg. multiple loops instead of one)
+* Profile GC allocations
### License
SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+More information in LICENSE.txt.
### Links
-* [Reference implementation](http://oss.sgi.com/projects/ogl-sample) - the original reference implementation
+* [Reference implementation](http://oss.sgi.com/projects/ogl-sample) - the original SGI reference implementation
* [libtess2](http://code.google.com/p/libtess2/) - Mikko Mononen cleaned up the original GLU tesselator
View
68 TessBed/Canvas.cs
@@ -1,17 +1,16 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Windows.Forms;
-using System.Drawing.Drawing2D;
using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
namespace TessBed
{
public class Canvas : Control
{
public PolygonSet Input;
public PolygonSet Output;
+ public bool ShowInput = true;
+ public bool ShowWinding = false;
public Canvas()
{
@@ -50,18 +49,25 @@ protected override void OnPaint(PaintEventArgs pe)
Func<PolygonPoint, PointF> f = (p) => new PointF { X = (p.X - xmid) * zoom, Y = (p.Y - ymid) * zoom };
using (var penContour = new Pen(Color.FromArgb(128, 0, 0, 128), 2.0f))
- using (var brushPoint = new SolidBrush(Color.FromArgb(128, 0, 0, 0)))
+ using (var penWinding = new Pen(Color.FromArgb(255, 0, 0, 128), 6.0f))
+ using (var brushPoint = new SolidBrush(Color.FromArgb(255, 0, 0, 0)))
using (var penOutput = new Pen(Color.FromArgb(32, 0, 0, 0), 1.0f))
+ using (var penPolys = new Pen(Color.FromArgb(64, 0, 0, 0), 1.0f))
+ using (var brushPolys = new SolidBrush(Color.FromArgb(64, 255, 207, 130)))
{
- foreach (var polygon in Input)
+ penWinding.EndCap = LineCap.ArrowAnchor;
+ if (ShowInput)
{
- for (int i = 0; i < polygon.Count; i++)
+ foreach (var polygon in Input)
{
- var p0 = f(polygon[i]);
- var p1 = f(polygon[(i + 1) % polygon.Count]);
+ for (int i = 0; i < polygon.Count; i++)
+ {
+ var p0 = f(polygon[i]);
+ var p1 = f(polygon[(i + 1) % polygon.Count]);
- g.DrawLine(penContour, p0, p1);
- g.FillEllipse(brushPoint, p0.X - 2.0f, p0.Y - 2.0f, 4.0f, 4.0f);
+ g.DrawLine(ShowWinding ? penWinding : penContour, p0, p1);
+ g.FillEllipse(brushPoint, p0.X - 2.0f, p0.Y - 2.0f, 4.0f, 4.0f);
+ }
}
}
@@ -70,12 +76,44 @@ protected override void OnPaint(PaintEventArgs pe)
foreach (var polygon in Output)
{
+ var pts = new PointF[polygon.Count];
for (int i = 0; i < polygon.Count; i++)
{
- var p0 = f(polygon[i]);
- var p1 = f(polygon[(i + 1) % polygon.Count]);
+ pts[i] = f(polygon[i]);
+ }
+
+ if (Input.HasColors)
+ {
+ var colors = new Color[pts.Length];
+ int[] mean = new int[4];
+ for (int i = 0; i < pts.Length; i++)
+ {
+ colors[i] = polygon[i].Color;
+ mean[0] += colors[i].R;
+ mean[1] += colors[i].G;
+ mean[2] += colors[i].B;
+ mean[3] += colors[i].A;
+ }
+
+ using (var brush = new PathGradientBrush(pts))
+ using (var pen = new Pen(brush, 20.0f))
+ {
+ brush.SurroundColors = colors;
+ brush.CenterColor = Color.FromArgb(mean[3] / colors.Length, mean[0] / colors.Length, mean[1] / colors.Length, mean[2] / colors.Length);
+
+ g.FillPolygon(brush, pts);
+ }
+ }
+ else
+ {
+ g.FillPolygon(brushPolys, pts);
+ }
+ for (int i = 0; i < pts.Length; i++)
+ {
+ var p0 = pts[i];
+ var p1 = pts[(i + 1) % polygon.Count];
- g.DrawLine(penOutput, p0, p1);
+ g.DrawLine(ShowInput ? penOutput : penPolys, p0, p1);
}
}
}
View
66 TessBed/Data/redbook-winding.dat
@@ -0,0 +1,66 @@
+// redbook tesswind.c
+
+// rects same direction
+ 50.0, 50.0
+300.0, 50.0
+300.0, 300.0
+ 50.0, 300.0
+
+100.0, 100.0
+250.0, 100.0
+250.0, 250.0
+100.0, 250.0
+
+150.0, 150.0
+200.0, 150.0
+200.0, 200.0
+150.0, 200.0
+
+// rects alternate
+ 50.0, 550.0
+300.0, 550.0
+300.0, 800.0
+ 50.0, 800.0
+
+100.0, 750.0
+250.0, 750.0
+250.0, 600.0
+100.0, 600.0
+
+150.0, 700.0
+200.0, 700.0
+200.0, 650.0
+150.0, 650.0
+
+// spiral
+900.0, 250.0
+900.0, 50.0
+550.0, 50.0
+550.0, 400.0
+850.0, 400.0
+850.0, 100.0
+600.0, 100.0
+600.0, 350.0
+800.0, 350.0
+800.0, 150.0
+650.0, 150.0
+650.0, 300.0
+750.0, 300.0
+750.0, 200.0
+700.0, 200.0
+700.0, 250.0
+
+// misc
+550.0, 650.0
+850.0, 650.0
+850.0, 700.0
+550.0, 700.0
+
+600.0, 600.0
+800.0, 600.0
+800.0, 850.0
+600.0, 850.0
+
+700.0, 550.0
+750.0, 800.0
+650.0, 800.0
View
10 TessBed/Data/star-intersect.dat
@@ -0,0 +1,10 @@
+color 255 0 0
+ 0.0, 3.0
+color 0 255 0
+-1.0, 0.0
+color 255 0 255
+ 1.6, 1.9
+color 255 255 0
+-1.6, 1.9
+color 0 0 255
+ 1.0, 0.0
View
42 TessBed/DataLoader.cs
@@ -1,15 +1,16 @@
-using System.Collections.Generic;
-using System.IO;
-using System;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
using System.Globalization;
+using System.IO;
using System.Reflection;
-using System.Diagnostics;
namespace TessBed
{
public struct PolygonPoint
{
public float X, Y;
+ public Color Color;
}
public class Polygon : List<PolygonPoint>
@@ -28,7 +29,7 @@ public Polygon(ICollection<PolygonPoint> points)
public class PolygonSet : List<Polygon>
{
-
+ public bool HasColors = false;
}
public class DataLoader
@@ -46,6 +47,7 @@ public PolygonSet LoadDat(Stream fileStream)
int lineNum = 0;
string line;
bool skipLine = false;
+ Color currentColor = Color.White;
using (var stream = new StreamReader(fileStream))
{
while ((line = stream.ReadLine()) != null)
@@ -81,15 +83,41 @@ public PolygonSet LoadDat(Stream fileStream)
}
continue;
}
+ if (line.StartsWith("color", true, CultureInfo.InvariantCulture))
+ {
+ var rgba = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+ int r = 255, g = 255, b = 255, a = 255;
+ if (rgba != null)
+ {
+ if (rgba.Length == 4 &&
+ int.TryParse(rgba[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out r) &&
+ int.TryParse(rgba[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out g) &&
+ int.TryParse(rgba[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out b))
+ {
+ currentColor = Color.FromArgb(r, g, b);
+ polys.HasColors = true;
+ }
+ else if (rgba.Length == 5 &&
+ int.TryParse(rgba[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out r) &&
+ int.TryParse(rgba[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out g) &&
+ int.TryParse(rgba[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out b) &&
+ int.TryParse(rgba[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out a))
+ {
+ currentColor = Color.FromArgb(a, r, g, b);
+ polys.HasColors = true;
+ }
+ }
+ }
+ else
{
float x, y;
- var xy = line.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
+ var xy = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (xy != null &&
xy.Length >= 2 &&
float.TryParse(xy[0], NumberStyles.Float, CultureInfo.InvariantCulture, out x) &&
float.TryParse(xy[1], NumberStyles.Float, CultureInfo.InvariantCulture, out y))
{
- points.Add(new PolygonPoint { X = x, Y = y });
+ points.Add(new PolygonPoint { X = x, Y = y, Color = currentColor });
}
else
{
View
38 TessBed/MainForm.Designer.cs
@@ -28,6 +28,7 @@ protected override void Dispose(bool disposing)
/// </summary>
private void InitializeComponent()
{
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.statusStrip = new System.Windows.Forms.StatusStrip();
this.statusMain = new System.Windows.Forms.ToolStripStatusLabel();
this.toolStrip = new System.Windows.Forms.ToolStrip();
@@ -39,7 +40,10 @@ private void InitializeComponent()
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripLabelPolySize = new System.Windows.Forms.ToolStripLabel();
this.toolStripPolySize = new System.Windows.Forms.ToolStripTextBox();
+ this.toolStripButtonShowInput = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonShowWinding = new System.Windows.Forms.ToolStripButton();
this.panel = new System.Windows.Forms.Panel();
+ this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.statusStrip.SuspendLayout();
this.toolStrip.SuspendLayout();
this.SuspendLayout();
@@ -69,7 +73,10 @@ private void InitializeComponent()
this.toolStripWinding,
this.toolStripSeparator2,
this.toolStripLabelPolySize,
- this.toolStripPolySize});
+ this.toolStripPolySize,
+ this.toolStripSeparator3,
+ this.toolStripButtonShowInput,
+ this.toolStripButtonShowWinding});
this.toolStrip.Location = new System.Drawing.Point(0, 0);
this.toolStrip.Name = "toolStrip";
this.toolStrip.Size = new System.Drawing.Size(684, 25);
@@ -120,6 +127,27 @@ private void InitializeComponent()
this.toolStripPolySize.Name = "toolStripPolySize";
this.toolStripPolySize.Size = new System.Drawing.Size(40, 25);
//
+ // toolStripButtonShowInput
+ //
+ this.toolStripButtonShowInput.CheckOnClick = true;
+ this.toolStripButtonShowInput.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+ this.toolStripButtonShowInput.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonShowInput.Image")));
+ this.toolStripButtonShowInput.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonShowInput.Name = "toolStripButtonShowInput";
+ this.toolStripButtonShowInput.Size = new System.Drawing.Size(71, 22);
+ this.toolStripButtonShowInput.Text = "Show input";
+ this.toolStripButtonShowInput.ToolTipText = "Show input polygon";
+ //
+ // toolStripButtonShowWinding
+ //
+ this.toolStripButtonShowWinding.CheckOnClick = true;
+ this.toolStripButtonShowWinding.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+ this.toolStripButtonShowWinding.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonShowWinding.Image")));
+ this.toolStripButtonShowWinding.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonShowWinding.Name = "toolStripButtonShowWinding";
+ this.toolStripButtonShowWinding.Size = new System.Drawing.Size(86, 22);
+ this.toolStripButtonShowWinding.Text = "Show winding";
+ //
// panel
//
this.panel.Dock = System.Windows.Forms.DockStyle.Fill;
@@ -128,6 +156,11 @@ private void InitializeComponent()
this.panel.Size = new System.Drawing.Size(684, 515);
this.panel.TabIndex = 2;
//
+ // toolStripSeparator3
+ //
+ this.toolStripSeparator3.Name = "toolStripSeparator3";
+ this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+ //
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -162,6 +195,9 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripLabel toolStripLabelPolySize;
private System.Windows.Forms.ToolStripTextBox toolStripPolySize;
+ private System.Windows.Forms.ToolStripButton toolStripButtonShowInput;
+ private System.Windows.Forms.ToolStripButton toolStripButtonShowWinding;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
}
}
View
61 TessBed/MainForm.cs
@@ -1,9 +1,8 @@
using System;
+using System.Diagnostics;
using System.Windows.Forms;
using LibTessDotNet;
-using System.Drawing.Drawing2D;
using System.Drawing;
-using System.Diagnostics;
namespace TessBed
{
@@ -28,6 +27,7 @@ public MainForm()
panel.Controls.Add(_canvas);
_assets = _data.AssetNames;
+ Array.Sort(_assets);
foreach (var asset in _assets)
{
toolStripAssets.Items.Add(asset);
@@ -56,9 +56,24 @@ public MainForm()
PolySizeEvent();
};
- SetAsset("clipper");
+ toolStripButtonShowInput.CheckedChanged += delegate(object sender, EventArgs e)
+ {
+ _canvas.ShowInput = toolStripButtonShowInput.Checked;
+ toolStripButtonShowWinding.Enabled = _canvas.ShowInput;
+ RefreshAsset(toolStripAssets.SelectedIndex);
+ };
+
+ toolStripButtonShowWinding.CheckedChanged += delegate(object sender, EventArgs e)
+ {
+ _canvas.ShowWinding = toolStripButtonShowWinding.Checked;
+ RefreshAsset(toolStripAssets.SelectedIndex);
+ };
+
+ SetAsset("star-intersect");
+ SetShowInput(true);
+ SetShowWinding(false);
SetPolySize(3);
- SetWindingRule(WindingRule.OddEven);
+ SetWindingRule(WindingRule.EvenOdd);
}
private void SetAsset(string name)
@@ -85,6 +100,16 @@ private void SetWindingRule(WindingRule windingRule)
}
}
+ private void SetShowInput(bool show)
+ {
+ toolStripButtonShowInput.Checked = show;
+ }
+
+ private void SetShowWinding(bool show)
+ {
+ toolStripButtonShowWinding.Checked = show;
+ }
+
private void PolySizeEvent()
{
int result;
@@ -115,6 +140,18 @@ private void RefreshAsset(int index)
RefreshAsset(_assets[index]);
}
+ private object VertexCombine(Vec3 position, object[] data, float[] weights)
+ {
+ var colors = new Color[] { (Color)data[0], (Color)data[1], (Color)data[2], (Color)data[3] };
+ var rgba = new float[] {
+ colors[0].R * weights[0] + colors[1].R * weights[1] + colors[2].R * weights[2] + colors[3].R * weights[3],
+ colors[0].G * weights[0] + colors[1].G * weights[1] + colors[2].G * weights[2] + colors[3].G * weights[3],
+ colors[0].B * weights[0] + colors[1].B * weights[1] + colors[2].B * weights[2] + colors[3].B * weights[3],
+ colors[0].A * weights[0] + colors[1].A * weights[1] + colors[2].A * weights[2] + colors[3].A * weights[3]
+ };
+ return Color.FromArgb((int)rgba[3], (int)rgba[0], (int)rgba[1], (int)rgba[2]);
+ }
+
private void RefreshAsset(string name)
{
var asset = _data.GetAsset(name);
@@ -123,19 +160,19 @@ private void RefreshAsset(string name)
foreach (var poly in asset.Polygons)
{
- var v = new float[poly.Count * 2];
+ var v = new ContourVertex[poly.Count];
for (int i = 0; i < poly.Count; i++)
{
- v[i * 2 + 0] = poly[i].X;
- v[i * 2 + 1] = poly[i].Y;
+ v[i].Position = new Vec3 { X = poly[i].X, Y = poly[i].Y };
+ v[i].Data = poly[i].Color;
}
_sw.Start();
- _tess.AddContour(2, v);
+ _tess.AddContour(v);
_sw.Stop();
}
_sw.Start();
- _tess.Tesselate(_windingRule, ElementType.Polygons, _polySize, 2, null);
+ _tess.Tessellate(_windingRule, ElementType.Polygons, _polySize, VertexCombine);
_sw.Stop();
var output = new PolygonSet();
@@ -147,7 +184,11 @@ private void RefreshAsset(string name)
int index = _tess.Elements[i * _polySize + j];
if (index == -1)
continue;
- var v = new PolygonPoint { X = _tess.Vertices[index * 2 + 0], Y = _tess.Vertices[index * 2 + 1] };
+ var v = new PolygonPoint {
+ X = _tess.Vertices[index].Position.X,
+ Y = _tess.Vertices[index].Position.Y,
+ Color = (Color)_tess.Vertices[index].Data
+ };
poly.Add(v);
}
output.Add(poly);
View
31 TessBed/MainForm.resx
@@ -123,4 +123,35 @@
<metadata name="toolStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>241, 17</value>
</metadata>
+ <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+ <data name="toolStripButtonShowInput.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
+ ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
+ HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
+ rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
+ TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
+ oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
+ 7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
+ xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
+ LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
+ KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
+</value>
+ </data>
+ <data name="toolStripButtonShowWinding.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
+ ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
+ HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
+ rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
+ TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
+ oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
+ 7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
+ xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
+ LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
+ KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
+</value>
+ </data>
</root>
View
2 TessBed/Program.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Windows.Forms;
namespace TessBed
View
2 TessBed/TessBed.csproj
@@ -82,6 +82,8 @@
<EmbeddedResource Include="Data\tank.dat" />
<EmbeddedResource Include="Data\test.dat" />
<EmbeddedResource Include="Data\letterE.dat" />
+ <EmbeddedResource Include="Data\star-intersect.dat" />
+ <EmbeddedResource Include="Data\redbook-winding.dat" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>

0 comments on commit f3ffde4

Please sign in to comment.
Something went wrong with that request. Please try again.