mirror of
https://github.com/brl/mutter.git
synced 2024-11-29 19:40:43 -05:00
7365c3aa77
This splits out the cogl_path_ api into a separate cogl-path sub-library like cogl-pango and cogl-gst. This enables developers to build Cogl with this sub-library disabled if they don't need it which can be useful when its important to keep the size of an application and its dependencies down to a minimum. The functions cogl_framebuffer_{fill,stroke}_path have been renamed to cogl_path_{fill,stroke}. There were a few places in core cogl and cogl-gst that referenced the CoglPath api and these have been decoupled by using the CoglPrimitive api instead. In the case of cogl_framebuffer_push_path_clip() the core clip stack no longer accepts path clips directly but it's now possible to get a CoglPrimitive for the fill of a path and so the implementation of cogl_framebuffer_push_path_clip() now lives in cogl-path and works as a shim that first gets a CoglPrimitive and uses cogl_framebuffer_push_primitive_clip instead. We may want to consider renaming cogl_framebuffer_push_path_clip to put it in the cogl_path_ namespace. Reviewed-by: Neil Roberts <neil@linux.intel.com> (cherry picked from commit 8aadfd829239534fb4ec8255cdea813d698c5a3f) So as to avoid breaking the 1.x API or even the ABI since we are quite late in the 1.16 development cycle the patch was modified to build cogl-path as a noinst_LTLIBRARY before building cogl and link the code directly into libcogl.so as it was previously. This way we can wait until the start of the 1.18 cycle before splitting the code into a separate libcogl-path.so. This also adds shims for cogl_framebuffer_fill/stroke_path() to avoid breaking the 1.x API/ABI.
447 lines
20 KiB
Plaintext
447 lines
20 KiB
Plaintext
/*
|
|
*/
|
|
|
|
General Polygon Tesselation
|
|
---------------------------
|
|
|
|
This note describes a tesselator for polygons consisting of one or
|
|
more closed contours. It is backward-compatible with the current
|
|
OpenGL Utilities tesselator, and is intended to replace it. Here is
|
|
a summary of the major differences:
|
|
|
|
- input contours can be intersecting, self-intersecting, or degenerate.
|
|
|
|
- supports a choice of several winding rules for determining which parts
|
|
of the polygon are on the "interior". This makes it possible to do
|
|
CSG operations on polygons.
|
|
|
|
- boundary extraction: instead of tesselating the polygon, returns a
|
|
set of closed contours which separate the interior from the exterior.
|
|
|
|
- returns the output as a small number of triangle fans and strips,
|
|
rather than a list of independent triangles (when possible).
|
|
|
|
- output is available as an explicit mesh (a quad-edge structure),
|
|
in addition to the normal callback interface.
|
|
|
|
- the algorithm used is extremely robust.
|
|
|
|
|
|
The interface
|
|
-------------
|
|
|
|
The tesselator state is maintained in a "tesselator object".
|
|
These are allocated and destroyed using
|
|
|
|
GLUtesselator *gluNewTess( void );
|
|
void gluDeleteTess( GLUtesselator *tess );
|
|
|
|
Several tesselator objects may be used simultaneously.
|
|
|
|
Inputs
|
|
------
|
|
|
|
The input contours are specified with the following routines:
|
|
|
|
void gluTessBeginPolygon( GLUtesselator *tess );
|
|
void gluTessBeginContour( GLUtesselator *tess );
|
|
void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data );
|
|
void gluTessEndContour( GLUtesselator *tess );
|
|
void gluTessEndPolygon( GLUtesselator *tess );
|
|
|
|
Within each BeginPolygon/EndPolygon pair, there can be zero or more
|
|
calls to BeginContour/EndContour. Within each contour, there are zero
|
|
or more calls to gluTessVertex(). The vertices specify a closed
|
|
contour (the last vertex of each contour is automatically linked to
|
|
the first).
|
|
|
|
"coords" give the coordinates of the vertex in 3-space. For useful
|
|
results, all vertices should lie in some plane, since the vertices
|
|
are projected onto a plane before tesselation. "data" is a pointer
|
|
to a user-defined vertex structure, which typically contains other
|
|
information such as color, texture coordinates, normal, etc. It is
|
|
used to refer to the vertex during rendering.
|
|
|
|
The library can be compiled in single- or double-precision; the type
|
|
GLUcoord represents either "float" or "double" accordingly. The GLU
|
|
version will be available in double-precision only. Compile with
|
|
GLU_TESS_API_FLOAT defined to get the single-precision version.
|
|
|
|
When EndPolygon is called, the tesselation algorithm determines
|
|
which regions are interior to the given contours, according to one
|
|
of several "winding rules" described below. The interior regions
|
|
are then tesselated, and the output is provided as callbacks.
|
|
|
|
|
|
Rendering Callbacks
|
|
-------------------
|
|
|
|
Callbacks are specified by the client using
|
|
|
|
void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)());
|
|
|
|
If "fn" is NULL, any previously defined callback is discarded.
|
|
|
|
The callbacks used to provide output are: /* which == */
|
|
|
|
void begin( GLenum type ); /* GLU_TESS_BEGIN */
|
|
void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */
|
|
void vertex( void *data ); /* GLU_TESS_VERTEX */
|
|
void end( void ); /* GLU_TESS_END */
|
|
|
|
Any of the callbacks may be left undefined; if so, the corresponding
|
|
information will not be supplied during rendering.
|
|
|
|
The "begin" callback indicates the start of a primitive; type is one
|
|
of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the
|
|
notes on "boundary extraction" below).
|
|
|
|
It is followed by any number of "vertex" callbacks, which supply the
|
|
vertices in the same order as expected by the corresponding glBegin()
|
|
call. After the last vertex of a given primitive, there is a callback
|
|
to "end".
|
|
|
|
If the "edgeFlag" callback is provided, no triangle fans or strips
|
|
will be used. When edgeFlag is called, if "flag" is GL_TRUE then each
|
|
vertex which follows begins an edge which lies on the polygon boundary
|
|
(ie. an edge which separates an interior region from an exterior one).
|
|
If "flag" is GL_FALSE, each vertex which follows begins an edge which lies
|
|
in the polygon interior. "edgeFlag" will be called before the first
|
|
call to "vertex".
|
|
|
|
Other Callbacks
|
|
---------------
|
|
|
|
void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */
|
|
|
|
- Returns an explicit mesh, represented using the quad-edge structure
|
|
(Guibas/Stolfi '85). Other implementations of this interface might
|
|
use a different mesh structure, so this is available only only as an
|
|
SGI extension. When the mesh is no longer needed, it should be freed
|
|
using
|
|
|
|
void gluDeleteMesh( GLUmesh *mesh );
|
|
|
|
There is a brief description of this data structure in the include
|
|
file "mesh.h". For the full details, see L. Guibas and J. Stolfi,
|
|
Primitives for the manipulation of general subdivisions and the
|
|
computation of Voronoi diagrams, ACM Transactions on Graphics,
|
|
4(2):74-123, April 1985. For an introduction, see the course notes
|
|
for CS348a, "Mathematical Foundations of Computer Graphics",
|
|
available at the Stanford bookstore (and taught during the fall
|
|
quarter).
|
|
|
|
void error( GLenum errno ); /* GLU_TESS_ERROR */
|
|
|
|
- errno is one of GLU_TESS_MISSING_BEGIN_POLYGON,
|
|
GLU_TESS_MISSING_END_POLYGON,
|
|
GLU_TESS_MISSING_BEGIN_CONTOUR,
|
|
GLU_TESS_MISSING_END_CONTOUR,
|
|
GLU_TESS_COORD_TOO_LARGE,
|
|
GLU_TESS_NEED_COMBINE_CALLBACK
|
|
|
|
The first four are obvious. The interface recovers from these
|
|
errors by inserting the missing call(s).
|
|
|
|
GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded
|
|
the predefined constant GLU_TESS_MAX_COORD in absolute value, and
|
|
that the value has been clamped. (Coordinate values must be small
|
|
enough so that two can be multiplied together without overflow.)
|
|
|
|
GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an
|
|
intersection between two edges in the input data, and the "combine"
|
|
callback (below) was not provided. No output will be generated.
|
|
|
|
|
|
void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */
|
|
GLUcoord weight[4], void **outData );
|
|
|
|
- When the algorithm detects an intersection, or wishes to merge
|
|
features, it needs to create a new vertex. The vertex is defined
|
|
as a linear combination of up to 4 existing vertices, referenced
|
|
by data[0..3]. The coefficients of the linear combination are
|
|
given by weight[0..3]; these weights always sum to 1.0. All vertex
|
|
pointers are valid even when some of the weights are zero.
|
|
"coords" gives the location of the new vertex.
|
|
|
|
The user must allocate another vertex, interpolate parameters
|
|
using "data" and "weights", and return the new vertex pointer in
|
|
"outData". This handle is supplied during rendering callbacks.
|
|
For example, if the polygon lies in an arbitrary plane in 3-space,
|
|
and we associate a color with each vertex, the combine callback might
|
|
look like this:
|
|
|
|
void myCombine( GLUcoord coords[3], VERTEX *d[4],
|
|
GLUcoord w[4], VERTEX **dataOut )
|
|
{
|
|
VERTEX *new = new_vertex();
|
|
|
|
new->x = coords[0];
|
|
new->y = coords[1];
|
|
new->z = coords[2];
|
|
new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r;
|
|
new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g;
|
|
new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b;
|
|
new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a;
|
|
*dataOut = new;
|
|
}
|
|
|
|
If the algorithm detects an intersection, then the "combine" callback
|
|
must be defined, and must write a non-NULL pointer into "dataOut".
|
|
Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no
|
|
output is generated. This is the only error that can occur during
|
|
tesselation and rendering.
|
|
|
|
|
|
Control over Tesselation
|
|
------------------------
|
|
|
|
void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value );
|
|
|
|
Properties defined:
|
|
|
|
- GLU_TESS_WINDING_RULE. Possible values:
|
|
|
|
GLU_TESS_WINDING_ODD
|
|
GLU_TESS_WINDING_NONZERO
|
|
GLU_TESS_WINDING_POSITIVE
|
|
GLU_TESS_WINDING_NEGATIVE
|
|
GLU_TESS_WINDING_ABS_GEQ_TWO
|
|
|
|
The input contours parition the plane into regions. A winding
|
|
rule determines which of these regions are inside the polygon.
|
|
|
|
For a single contour C, the winding number of a point x is simply
|
|
the signed number of revolutions we make around x as we travel
|
|
once around C (where CCW is positive). When there are several
|
|
contours, the individual winding numbers are summed. This
|
|
procedure associates a signed integer value with each point x in
|
|
the plane. Note that the winding number is the same for all
|
|
points in a single region.
|
|
|
|
The winding rule classifies a region as "inside" if its winding
|
|
number belongs to the chosen category (odd, nonzero, positive,
|
|
negative, or absolute value of at least two). The current GLU
|
|
tesselator implements the "odd" rule. The "nonzero" rule is another
|
|
common way to define the interior. The other three rules are
|
|
useful for polygon CSG operations (see below).
|
|
|
|
- GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero).
|
|
|
|
If TRUE, returns a set of closed contours which separate the
|
|
polygon interior and exterior (rather than a tesselation).
|
|
Exterior contours are oriented CCW with respect to the normal,
|
|
interior contours are oriented CW. The GLU_TESS_BEGIN callback
|
|
uses the type GL_LINE_LOOP for each contour.
|
|
|
|
- GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0.
|
|
|
|
This specifies a tolerance for merging features to reduce the size
|
|
of the output. For example, two vertices which are very close to
|
|
each other might be replaced by a single vertex. The tolerance
|
|
is multiplied by the largest coordinate magnitude of any input vertex;
|
|
this specifies the maximum distance that any feature can move as the
|
|
result of a single merge operation. If a single feature takes part
|
|
in several merge operations, the total distance moved could be larger.
|
|
|
|
Feature merging is completely optional; the tolerance is only a hint.
|
|
The implementation is free to merge in some cases and not in others,
|
|
or to never merge features at all. The default tolerance is zero.
|
|
|
|
The current implementation merges vertices only if they are exactly
|
|
coincident, regardless of the current tolerance. A vertex is
|
|
spliced into an edge only if the implementation is unable to
|
|
distinguish which side of the edge the vertex lies on.
|
|
Two edges are merged only when both endpoints are identical.
|
|
|
|
|
|
void gluTessNormal( GLUtesselator *tess,
|
|
GLUcoord x, GLUcoord y, GLUcoord z )
|
|
|
|
- Lets the user supply the polygon normal, if known. All input data
|
|
is projected into a plane perpendicular to the normal before
|
|
tesselation. All output triangles are oriented CCW with
|
|
respect to the normal (CW orientation can be obtained by
|
|
reversing the sign of the supplied normal). For example, if
|
|
you know that all polygons lie in the x-y plane, call
|
|
"gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons.
|
|
|
|
- If the supplied normal is (0,0,0) (the default value), the
|
|
normal is determined as follows. The direction of the normal,
|
|
up to its sign, is found by fitting a plane to the vertices,
|
|
without regard to how the vertices are connected. It is
|
|
expected that the input data lies approximately in plane;
|
|
otherwise projection perpendicular to the computed normal may
|
|
substantially change the geometry. The sign of the normal is
|
|
chosen so that the sum of the signed areas of all input contours
|
|
is non-negative (where a CCW contour has positive area).
|
|
|
|
- The supplied normal persists until it is changed by another
|
|
call to gluTessNormal.
|
|
|
|
|
|
Backward compatibility with the GLU tesselator
|
|
----------------------------------------------
|
|
|
|
The preferred interface is the one described above. The following
|
|
routines are obsolete, and are provided only for backward compatibility:
|
|
|
|
typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */
|
|
|
|
void gluBeginPolygon( GLUtesselator *tess );
|
|
void gluNextContour( GLUtesselator *tess, GLenum type );
|
|
void gluEndPolygon( GLUtesselator *tess );
|
|
|
|
"type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or
|
|
GLU_UNKNOWN. It is ignored by the current GLU tesselator.
|
|
|
|
GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined
|
|
as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END,
|
|
GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG.
|
|
|
|
|
|
Polygon CSG operations
|
|
----------------------
|
|
|
|
The features of the tesselator make it easy to find the union, difference,
|
|
or intersection of several polygons.
|
|
|
|
First, assume that each polygon is defined so that the winding number
|
|
is 0 for each exterior region, and 1 for each interior region. Under
|
|
this model, CCW contours define the outer boundary of the polygon, and
|
|
CW contours define holes. Contours may be nested, but a nested
|
|
contour must be oriented oppositely from the contour that contains it.
|
|
|
|
If the original polygons do not satisfy this description, they can be
|
|
converted to this form by first running the tesselator with the
|
|
GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of
|
|
contours satisfying the restriction above. By allocating two
|
|
tesselator objects, the callbacks from one tesselator can be fed
|
|
directly to the input of another.
|
|
|
|
Given two or more polygons of the form above, CSG operations can be
|
|
implemented as follows:
|
|
|
|
Union
|
|
Draw all the input contours as a single polygon. The winding number
|
|
of each resulting region is the number of original polygons
|
|
which cover it. The union can be extracted using the
|
|
GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules.
|
|
Note that with the nonzero rule, we would get the same result if
|
|
all contour orientations were reversed.
|
|
|
|
Intersection (two polygons at a time only)
|
|
Draw a single polygon using the contours from both input polygons.
|
|
Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this
|
|
winding rule looks at the absolute value, reversing all contour
|
|
orientations does not change the result.)
|
|
|
|
Difference
|
|
|
|
Suppose we want to compute A \ (B union C union D). Draw a single
|
|
polygon consisting of the unmodified contours from A, followed by
|
|
the contours of B,C,D with the vertex order reversed (this changes
|
|
the winding number of the interior regions to -1). To extract the
|
|
result, use the GLU_TESS_WINDING_POSITIVE rule.
|
|
|
|
If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an
|
|
alternative to reversing the vertex order is to reverse the sign of
|
|
the supplied normal. For example in the x-y plane, call
|
|
gluTessNormal( tess, 0.0, 0.0, -1.0 ).
|
|
|
|
|
|
Performance
|
|
-----------
|
|
|
|
The tesselator is not intended for immediate-mode rendering; when
|
|
possible the output should be cached in a user structure or display
|
|
list. General polygon tesselation is an inherently difficult problem,
|
|
especially given the goal of extreme robustness.
|
|
|
|
The implementation makes an effort to output a small number of fans
|
|
and strips; this should improve the rendering performance when the
|
|
output is used in a display list.
|
|
|
|
Single-contour input polygons are first tested to see whether they can
|
|
be rendered as a triangle fan with respect to the first vertex (to
|
|
avoid running the full decomposition algorithm on convex polygons).
|
|
Non-convex polygons may be rendered by this "fast path" as well, if
|
|
the algorithm gets lucky in its choice of a starting vertex.
|
|
|
|
For best performance follow these guidelines:
|
|
|
|
- supply the polygon normal, if available, using gluTessNormal().
|
|
This represents about 10% of the computation time. For example,
|
|
if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1).
|
|
|
|
- render many polygons using the same tesselator object, rather than
|
|
allocating a new tesselator for each one. (In a multi-threaded,
|
|
multi-processor environment you may get better performance using
|
|
several tesselators.)
|
|
|
|
|
|
Comparison with the GLU tesselator
|
|
----------------------------------
|
|
|
|
On polygons which make it through the "fast path", the tesselator is
|
|
3 to 5 times faster than the GLU tesselator.
|
|
|
|
On polygons which don't make it through the fast path (but which don't
|
|
have self-intersections or degeneracies), it is about 2 times slower.
|
|
|
|
On polygons with self-intersections or degeneraces, there is nothing
|
|
to compare against.
|
|
|
|
The new tesselator generates many more fans and strips, reducing the
|
|
number of vertices that need to be sent to the hardware.
|
|
|
|
Key to the statistics:
|
|
|
|
vert number of input vertices on all contours
|
|
cntr number of input contours
|
|
tri number of triangles in all output primitives
|
|
strip number of triangle strips
|
|
fan number of triangle fans
|
|
ind number of independent triangles
|
|
ms number of milliseconds for tesselation
|
|
(on a 150MHz R4400 Indy)
|
|
|
|
Convex polygon examples:
|
|
|
|
New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms
|
|
Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms
|
|
New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms
|
|
Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms
|
|
New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms
|
|
Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms
|
|
|
|
Concave single-contour polygons:
|
|
|
|
New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms
|
|
Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms
|
|
New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms
|
|
Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms
|
|
New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms
|
|
Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms
|
|
New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms
|
|
Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms
|
|
|
|
Multiple contours, but no intersections:
|
|
|
|
New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms
|
|
Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms
|
|
New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms
|
|
Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms
|
|
New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms
|
|
Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms
|
|
|
|
Self-intersecting and degenerate examples:
|
|
|
|
Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms
|
|
Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms
|
|
Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms
|
|
Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms
|
|
: 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms
|
|
: 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms
|
|
: 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms
|