818 lines
27 KiB
Plaintext
818 lines
27 KiB
Plaintext
=head1 NAME
|
|
|
|
OpenGL::Tessellation - discussion of tessellation in POGL
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
# somewhere in your drawing routine or drawlist compilation
|
|
|
|
my $tess = gluNewTess();
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
|
|
|
|
gluTessBeginPolygon($tess);
|
|
gluTessBeginContour($tess);
|
|
|
|
gluTessVertex_p($tess, 0, 200, 0);
|
|
gluTessVertex_p($tess, 150, -200, 0);
|
|
gluTessVertex_p($tess, 0, -100, 0);
|
|
gluTessVertex_p($tess, -150, -200, 0);
|
|
|
|
gluTessEndContour($tess);
|
|
gluTessEndPolygon($tess);
|
|
|
|
gluDeleteTess($tess);
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
OpenGL rendering hardware typically does not have support for drawing
|
|
concave polygons or drawing polygons with windows. OpenGL provides
|
|
glu extentions that allow for translating concave polygon vertices
|
|
into triangles that can be rendered quickly on GL hardware. The
|
|
OpenGL red book chapter 11 has the full discussion of Tessellators and
|
|
the OpenGL functions (http://glprogramming.com/red/chapter11.html, or
|
|
use your favorite search engine and search for "opengl gluNewTess").
|
|
It is a good idea to read that chapter before reading the rest of this
|
|
document.
|
|
|
|
As much as possible, the POGL implementation of the tessellation
|
|
functions tries to remain faithful to the OpenGL specification. Where
|
|
it doesn't match exactly, POGL follows the spirit of the specification,
|
|
but offloads what it can to c based implementations.
|
|
|
|
Tessellation functions are safe to call during drawlist creation. It
|
|
is advisable to use drawlists, or to store the generated polygon data
|
|
into OpenGL::Array objects as these methods offer faster redraws.
|
|
|
|
=head1 FUNCTIONS
|
|
|
|
=over 4
|
|
|
|
=item C<gluNewTess>
|
|
|
|
my $tess = gluNewTess();
|
|
|
|
Returns a reference that can be passed to the remaining tesselation
|
|
functions.
|
|
|
|
Note: this isn't the c-reference returned by the normal gluNewTess() c
|
|
function, it is a struct which contains that reference as well as
|
|
other members allowing callbacks to interface cleanly with the perl
|
|
code. This means that if you have loaded other c-libraries that use
|
|
standard opengl tessellation, you will not be able to use this perl
|
|
reference directly.
|
|
|
|
The POGL implementation of gluNewTess() allows for two additional
|
|
parameters to be passed. The first is a boolean value indicating that
|
|
default c callbacks and perl callbacks should be passed rgba color
|
|
data. The second is a boolean value indicating that xyz normal data
|
|
should be passed. Eventually one additional flag indicating that
|
|
texture data should be passed will be added as well.
|
|
|
|
my $tess = gluNewTess();
|
|
# gluTessVertex_p should be passed only x,y,z vertex data
|
|
# as in gluTessVertex_p($tess, $x, $y, $z);
|
|
|
|
my $tess = gluNewTess('do_colors');
|
|
# gluTessVertex_p should be passed x,y,z AND r,g,b,a vertex data
|
|
# as in gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a);
|
|
|
|
my $tess = gluNewTess('do_colors', 'do_normals');
|
|
# gluTessVertex_p should be passed x,y,z AND r,g,b,a AND nx,ny,nz vertex data
|
|
# as in gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a, $nx, $ny, $nz);
|
|
|
|
my $tess = gluNewTess(undef, 'do_normals');
|
|
# gluTessVertex_p should be passed x,y,z AND nx,ny,nz vertex data (no colors)
|
|
# as in gluTessVertex_p($tess, $x, $y, $z, $nx, $ny, $nz);
|
|
|
|
Any true value can be passed in place of 'do_colors' and 'do_normals'
|
|
though using 'do_colors' and 'do_normals' acts as documentation.
|
|
|
|
Behavior in these modes will be discussed further for functions to
|
|
which they apply.
|
|
|
|
=item C<gluDeleteTess>
|
|
|
|
gluDeleteTess($tess);
|
|
|
|
This deletes the tessellation structure and frees up any remaining
|
|
associated memory.
|
|
|
|
=item C<gluTessCallback>
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, \&glBegin);
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, sub { my $enum = shift; glBegin($enum) });
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN); # unsets current handler
|
|
|
|
Registers handlers for each of the tessellation callback types. Takes
|
|
a tessellation reference generated by gluNewTess, a type, and a
|
|
coderef or the word 'DEFAULT'. If the word 'DEFAULT' is passed, a
|
|
default c-level callback will be installed (which will be discussed
|
|
for each callback). If no 3rd argument is given, then any handler
|
|
currently set will be removed. Valid callback types are
|
|
|
|
GLU_TESS_BEGIN
|
|
GLU_TESS_END
|
|
GLU_TESS_VERTEX
|
|
GLU_TESS_COMBINE
|
|
GLU_TESS_ERROR
|
|
GLU_TESS_EDGE_FLAG
|
|
|
|
GLU_TESS_BEGIN_DATA
|
|
GLU_TESS_END_DATA
|
|
GLU_TESS_VERTEX_DATA
|
|
GLU_TESS_COMBINE_DATA
|
|
GLU_TESS_ERROR_DATA
|
|
GLU_TESS_EDGE_FLAG_DATA
|
|
|
|
These types and their passed parameters will be discussed in the
|
|
CALLBACKS section.
|
|
|
|
The types ending with "_DATA" are similar to their non-_DATA
|
|
counterpart, but when called are passed the option $polygon_data that
|
|
can be set during gluTessBeginPolygon.
|
|
|
|
=item C<gluTessBeginPolygon>
|
|
|
|
gluTessBeginPolygon($tess);
|
|
|
|
gluTessBeginPolygon($tess, $polygon_data);
|
|
|
|
Begins the tessellation transaction. It must eventually be
|
|
ended with a gluTessEndPolygon before the tessellator will normally
|
|
begin work.
|
|
|
|
An optional second argument can be passed which can be any perl
|
|
scalar or reference. If a callback is registered using a type ending
|
|
in _DATA, this perl scalar or reference will be passed as an additional
|
|
argument to that callback.
|
|
|
|
gluTessCallback($tess, GLU_TESS_END_DATA, sub {
|
|
my $polygon_data = shift;
|
|
glEnd();
|
|
print "glEnd: (".($polygon_data->[2] eq 8 ? "YES" : "NO").")\n";
|
|
});
|
|
|
|
gluTessBeginPoly($tess, [6,7,8]); # arrayref will be passed to _DATA callbacks
|
|
|
|
A sample Object Oriented tesselation sample listed at the end of this
|
|
document makes use of this "opaque" polygon data.
|
|
|
|
=item C<gluTessEndPolygon>
|
|
|
|
gluTessEndPolygon($tess);
|
|
|
|
Finishes the tessellation transaction, which normally will immediately
|
|
fire the necessary callbacks generated by the tessellation process.
|
|
Once finished, it cleans up any accumulated temporary vertice data.
|
|
|
|
=item C<gluTessBeginContour>
|
|
|
|
gluTessBeginContour($tess);
|
|
|
|
Starts a new contour of the tessellation of the current polygon.
|
|
Please read the OpenGL documentation, and red book chapter on
|
|
tessellation for more help on when to use different contours. Should
|
|
eventually be followed by a gluTessEndContour call.
|
|
|
|
(At a high level, tessellated polygons may have windows and multiple
|
|
separate portions. Each inner and outer border of these portions
|
|
should be represented by a different contour.)
|
|
|
|
=item C<gluTessVertex_p>
|
|
|
|
gluTessVertex_p($tess, $x, $y, $z);
|
|
|
|
gluTessVertex_p($tess, $x, $y, $z, $vertex_data);
|
|
|
|
Adds a vertex to the current contour of the current polygon being
|
|
tessellated.
|
|
|
|
If the vertex callback type is set to GLU_TESS_VERTEX, the optional
|
|
$vertex_data argument will be passed to the vertex callback, and to
|
|
the combine callback (if GLU_TESS_VERTEX_DATA is used, then the
|
|
$polygon_data passed to gluTessBeginPolygon will be passed instead).
|
|
This optional opaque vertex data can be any perl scalar or reference
|
|
and can be used to pass useful information along during the
|
|
tessellation process.
|
|
|
|
If the 'do_colors' or 'do_normals' parameters were passed to gluNewTess,
|
|
then those additional properties MUST be passed as additional arguments.
|
|
|
|
# my $tess = gluNewTess('do_colors');
|
|
gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a);
|
|
gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a, $vertex_data);
|
|
|
|
# my $tess = gluNewTess('do_colors', 'do_normals');
|
|
gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a, $nx, $ny, $nz);
|
|
gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a, $nx, $ny, $nz, $vertex_data);
|
|
|
|
# my $tess = gluNewTess(undef, 'do_normals');
|
|
gluTessVertex_p($tess, $x, $y, $z, $nx, $ny, $nz);
|
|
gluTessVertex_p($tess, $x, $y, $z, $nx, $ny, $nz, $vertex_data);
|
|
|
|
=back
|
|
|
|
=head1 CALLBACKS
|
|
|
|
All of the callbacks support a 'DEFAULT' handler that can be installed
|
|
by passing the word 'DEFAULT' in place of the callback code reference.
|
|
The DEFAULT c implementations are there to avoid needing to round trip
|
|
out to perl. The defaults employed are described for each of the
|
|
callback types.
|
|
|
|
With the exception of the COMBINE callback, return values from
|
|
callbacks are discarded.
|
|
|
|
=over 4
|
|
|
|
=item C<GLU_TESS_BEGIN>
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, \&glBegin);
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, sub {
|
|
my $enum = shift;
|
|
glBegin($enum);
|
|
});
|
|
|
|
The 'DEFAULT' option installs a c-handler that calls the glBegin c
|
|
function directly without round-tripping out to perl.
|
|
|
|
If $polygon_data was set during gluTessBeginPolygon, it is discarded.
|
|
|
|
=item C<GLU_TESS_BEGIN_DATA>
|
|
|
|
Similar to GLU_TESS_BEGIN but will be passed optional $polygon_data
|
|
set in gluTessBeginPolygon if any. The 'DEFAULT' handler will ignore
|
|
this data.
|
|
|
|
gluTessCallback($tess, GLU_TESS_BEGIN_DATA, sub {
|
|
my ($enum, $polygon_data) = @_;
|
|
glBegin($enum);
|
|
print "glBegin - and I received polygon_data\n" if $polygon_data;
|
|
});
|
|
|
|
=item C<GLU_TESS_END>
|
|
|
|
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
|
|
|
gluTessCallback($tess, GLU_TESS_END, \&glEnd);
|
|
|
|
gluTessCallback($tess, GLU_TESS_END, sub { glEnd() });
|
|
|
|
The 'DEFAULT' option installs a c-handler that calls the glEnd c
|
|
function directly without round-tripping out to perl.
|
|
|
|
If $polygon_data was set during gluTessBeginPolygon, it is discarded.
|
|
|
|
=item C<GLU_TESS_END_DATA>
|
|
|
|
Similar to GLU_TESS_END but will be passed optional $polygon_data set
|
|
in gluTessBeginPolygon if any. The 'DEFAULT' handler will ignore this
|
|
data.
|
|
|
|
gluTessCallback($tess, GLU_TESS_END_DATA, sub {
|
|
my ($polygon_data) = @_;
|
|
glEnd();
|
|
print "glEnd - and I received polygon_data\n" if $polygon_data;
|
|
});
|
|
|
|
=item C<GLU_TESS_VERTEX>
|
|
|
|
The GLU_TESS_VERTEX callback handler has slightly different
|
|
behavior depending on how gluNewTess was called. The optional behaviors
|
|
allow for sane default processing of colors and normals without needing
|
|
to roundtrip out to perl.
|
|
|
|
my $tess = gluNewTess();
|
|
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
|
|
# the following will break if vertex_data is passed to gluTessVertex_p
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, \&glVertex3f);
|
|
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
my ($x, $y, $z) = @_;
|
|
glVertex3f($x, $y, $z);
|
|
});
|
|
|
|
# you can also pass vertex_data to gluTessVertex_p
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
my ($x, $y, $z, $vertex_data) = @_;
|
|
glVertex3f($x, $y, $z);
|
|
print "glVertex - and I received vertex_data\n" if $vertex_data;
|
|
});
|
|
|
|
The 'DEFAULT' option installs a c-handler that calls the glVertex c
|
|
function directly without round-tripping out to perl. The DEFAULT
|
|
handler discards any polygon_data or vertex_data.
|
|
|
|
IF $vertex_data was set during gluTessVertex_p it will be passed as the final
|
|
argument.
|
|
|
|
If gluNewTess was passed 'do_colors' then the GLU_TESS_VERTEX callback
|
|
will also be passed the rgba information. The 'DEFAULT' option
|
|
will pass the color information to glColor4f before calling glVertex3f.
|
|
|
|
my $tess = gluNewTess('do_colors');
|
|
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
my ($x, $y, $z, $r, $g, $b, $a, $vertex_data) = @_;
|
|
glColor4f($r, $g, $b, $a);
|
|
glVertex3f($x, $y, $z);
|
|
});
|
|
|
|
If gluNewTess was passed 'do_normals' then the GLU_TESS_VERTEX callback
|
|
will also be passed the normal x,y,z information. The 'DEFAULT' option
|
|
will pass the normal information to glNormal3f before calling glVertex3f.
|
|
|
|
my $tess = gluNewTess('do_colors', 'do_normals');
|
|
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
my ($x, $y, $z, $r, $g, $b, $a, $nx, $ny, $nz, $vertex_data) = @_;
|
|
glColor4f($r, $g, $b, $a);
|
|
glNormalf($nx, $ny, $nz);
|
|
glVertex3f($x, $y, $z);
|
|
});
|
|
|
|
# OR
|
|
|
|
my $tess = gluNewTess(undef, 'do_normals');
|
|
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
my ($x, $y, $z, $nx, $ny, $nz, $vertex_data) = @_;
|
|
glNormalf($nx, $ny, $nz);
|
|
glVertex3f($x, $y, $z);
|
|
});
|
|
|
|
In all cases, any optional vertex_data will be passed as the final argument.
|
|
|
|
=item C<GLU_TESS_VERTEX_DATA>
|
|
|
|
Similar to GLU_TESS_VERTEX but will be passed optional $polygon_data
|
|
set in gluTessBeginPolygon (if any) rather than the optional
|
|
$vertex_data passed to gluTessVertex_p. The 'DEFAULT' handler will
|
|
ignore this data.
|
|
|
|
gluTessCallback($tess, GLU_TESS_VERTEX_DATA, sub {
|
|
my ($x, $y, $z, $vertex_data) = @_;
|
|
glVertex3f($x, $y, $z);
|
|
print "glVertex - and I received vertex_data\n" if $vertex_data;
|
|
});
|
|
|
|
=item C<GLU_TESS_COMBINE>
|
|
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
|
# works with gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
|
|
|
|
# OR
|
|
|
|
|
|
# the following callback is valid for gluNewTess() (no do_colors or do_normals)
|
|
# using gluTessVertex_p($tess, $x, $y, $z);
|
|
my $tess = gluNewTess();
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, sub {
|
|
my ($x, $y, $z, # new vertex location
|
|
$v0, $v1, $v2, $v3, # border vertex arrayrefs
|
|
$w0, $w1, $w2, $w3, # border vertex weights
|
|
$polygon_data) = @_; # optional data passed to gluTessBeginPolygon
|
|
return ($x, $y, $z);
|
|
});
|
|
# works with gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
|
|
|
|
# OR
|
|
|
|
|
|
# the following callback is valid for gluNewTess() when vertex data is passed
|
|
# using gluTessVertex_p($tess, $x, $y, $z, [$r, $g, $b, $a]);
|
|
# The DEFAULT callback cannot automatically proceess this type of data
|
|
# but passing data to a custom handler this way could handle any arbitrary data passed to it
|
|
my $tess = gluNewTess();
|
|
use constant _r => 0;
|
|
use constant _g => 1;
|
|
use constant _b => 2;
|
|
use constant _a => 3;
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, sub {
|
|
my ($x, $y, $z, # new vertex location
|
|
$v0, $v1, $v2, $v3, # border vertex arrayrefs
|
|
$w0, $w1, $w2, $w3, # border vertex weights
|
|
$polygon_data) = @_; # optional data passed to gluTessBeginPolygon
|
|
|
|
# $v0 will contain [$x, $y, $z, [$r, $g, $b, $a]]
|
|
my @rgba = map {$_->[3]} $v0, $v1, $v2, $v3;
|
|
|
|
# generate a point with color weighted from the surrounding vertices
|
|
# then return that color information in the same way we received it (an rgba arrayref)
|
|
return (
|
|
$x, $y, $z,
|
|
[$w0*$rgba[0]->[_r] + $w1*$rgba[1]->[_r] + $w2*$rgba[2]->[_r] + $w3*$rgba[3]->[_r],
|
|
$w0*$rgba[0]->[_g] + $w1*$rgba[1]->[_g] + $w2*$rgba[2]->[_g] + $w3*$rgba[3]->[_g],
|
|
$w0*$rgba[0]->[_b] + $w1*$rgba[1]->[_b] + $w2*$rgba[2]->[_b] + $w3*$rgba[3]->[_b],
|
|
$w0*$rgba[0]->[_a] + $w1*$rgba[1]->[_a] + $w2*$rgba[2]->[_a] + $w3*$rgba[3]->[_a]],
|
|
);
|
|
});
|
|
# works with gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
# my ($x, $y, $z, $rgba) = @_;
|
|
# glColor4f(@$rgba);
|
|
# glVertex3f($x, $y, $z);
|
|
# });
|
|
|
|
|
|
# OR
|
|
|
|
|
|
# the following callback is valid for gluNewTess('do_colors')
|
|
# using gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a);
|
|
# the DEFAULT callback COULD automatically proceess this type of data as well if additional vertex data is not passed
|
|
my $tess = gluNewTess('do_colors');
|
|
use constant _r => 3;
|
|
use constant _g => 4;
|
|
use constant _b => 5;
|
|
use constant _a => 6;
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, sub {
|
|
my ($x, $y, $z, # new vertex location
|
|
$v0, $v1, $v2, $v3, # border vertex arrayrefs
|
|
$w0, $w1, $w2, $w3, # border vertex weights
|
|
$polygon_data) = @_; # optional data passed to gluTessBeginPolygon
|
|
|
|
# $v0 will contain [$x, $y, $z, $r, $g, $b, $a]
|
|
|
|
return ( # generate a point with color weighted from the surrounding vertices
|
|
$x, $y, $z,
|
|
$w0*$v0->[_r] + $w1*$v1->[_r] + $w2*$v2->[_r] + $w3*$v3->[_r],
|
|
$w0*$v0->[_g] + $w1*$v1->[_g] + $w2*$v2->[_g] + $w3*$v3->[_g],
|
|
$w0*$v0->[_b] + $w1*$v1->[_b] + $w2*$v2->[_b] + $w3*$v3->[_b],
|
|
$w0*$v0->[_a] + $w1*$v1->[_a] + $w2*$v2->[_a] + $w3*$v3->[_a],
|
|
($v0->[7] || $v1->[7] || $v2->[7] || $v3->[7]), # if we received vertex data - return some for the new vertex
|
|
);
|
|
});
|
|
# works with gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
# OR
|
|
# works with gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
|
# my ($x, $y, $z, $r, $g, $b, $a, $vertex_data) = @_;
|
|
# glColor4f($r, $g, $b, $a);
|
|
# glVertex3f($x, $y, $z);
|
|
# });
|
|
|
|
The combine callback is called if the tessellator decides a new vertex
|
|
is needed. This will happen with self intersecting polygons. In this
|
|
case, the COMBINE callback can be used to interpolate appropriate
|
|
values for normals, and colors, or for any desired information.
|
|
|
|
The combine callback will be passed the following:
|
|
|
|
=over 4
|
|
|
|
=item C<$x, $y, $z>
|
|
|
|
The x y and z coordinates of the new vertex being created.
|
|
|
|
=item C<$v0, $v1, $v2, $v3>
|
|
|
|
Arrayrefs of vertex information for the vertices bordering this
|
|
new vertex (the ones that caused the new vertex to be created).
|
|
|
|
By default if gluNewTess() is called, these arrayrefs will be passed:
|
|
|
|
my ($x, $y, $z, $vertex_data) = @$v0;
|
|
# received from gluTessVertex_p($tess, $x, $y, $z, $vertex_data);
|
|
|
|
If gluNewTess('do_colors') is called, the following will be passed:
|
|
|
|
my ($x, $y, $z, $r, $g, $b, $a, $vertex_data) = @$v0;
|
|
# received from gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a, $vertex_data);
|
|
|
|
If gluNewTess('do_colors', 'do_normals') is called, the following will be passed:
|
|
|
|
my ($x, $y, $z, $r, $g, $b, $a, $nx, $ny, $nz, $vertex_data) = @$v0;
|
|
# received from gluTessVertex_p($tess, $x, $y, $z, $r, $g, $b, $a, $nx, $ny, $nz, $vertex_data);
|
|
|
|
If gluNewTess(undef, 'do_normals') is called, the following will be passed:
|
|
|
|
my ($x, $y, $z, $nx, $ny, $nz, $vertex_data) = @$v0;
|
|
# received from gluTessVertex_p($tess, $x, $y, $z, $nx, $ny, $nz, $vertex_data);
|
|
|
|
In all cases, the data returned by the COMBINE callback should be in the same
|
|
format that each of the vertices are in when passed into the COMBINE callback.
|
|
|
|
=item C<$w0, $w1, $w2, $w3>
|
|
|
|
Weights of the participating vertices (weight $w0 corresponds to vertex $v0).
|
|
|
|
=item C<optional $polygon_data>
|
|
|
|
Any optional data passed to gluTessBeginPolygon. Normally this would
|
|
only be passed to GLU_TESS_COMBINE_DATA, but GLU_TESS_COMBINE_DATA
|
|
and GLU_TESS_COMBINE share the same code implementation.
|
|
|
|
=back
|
|
|
|
=item C<GLU_TESS_COMBINE_DATA>
|
|
|
|
Identical in function to the GLU_TESS_COMBINE handler. They
|
|
use the same callback implementation.
|
|
|
|
=item C<GLU_TESS_ERROR>
|
|
|
|
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
|
|
|
gluTessCallback($tess, GLU_TESS_ERROR, \&glEdgeFlag);
|
|
|
|
gluTessCallback($tess, GLU_TESS_ERROR, sub {
|
|
my $errno = shift;
|
|
my $err = gluErrorString($errno);
|
|
warn "Received a glu tess error ($errno - $err)\n";
|
|
});
|
|
|
|
The 'DEFAULT' option installs a c-handler that warns with the
|
|
appropriate gluErrorString.
|
|
|
|
If $polygon_data was set during gluTessBeginPolygon, it is discarded.
|
|
|
|
=item C<GLU_TESS_ERROR_DATA>
|
|
|
|
Similar to GLU_TESS_ERROR but will be passed optional $polygon_data
|
|
set in gluTessBeginPolygon if any. The 'DEFAULT' handler will ignore
|
|
this data.
|
|
|
|
gluTessCallback($tess, GLU_TESS_ERROR_DATA, sub {
|
|
my ($errno, $polygon_data) = @_;
|
|
my $err = gluErrorString($errno);
|
|
warn "Received a glu tess error ($errno - $err)\n";
|
|
warn "And I received polygon_data\n" if $polygon_data;
|
|
});
|
|
|
|
=item C<GLU_TESS_EDGE_FLAG>
|
|
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
|
|
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, \&glEdgeFlag);
|
|
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, sub {
|
|
my ($flag) = @_;
|
|
glEdgeFlag($flag);
|
|
});
|
|
|
|
The 'DEFAULT' option installs a c-handler that calls the glEdgeFlag c
|
|
function directly without round-tripping out to perl.
|
|
|
|
If $polygon_data was set during gluTessBeginPolygon, it is discarded.
|
|
|
|
=item C<GLU_TESS_EDGE_FLAG_DATA>
|
|
|
|
Similar to GLU_TESS_EDGE_FLAG but will be passed $polygon_data set in
|
|
gluTessBeginPolygon if any. The 'DEFAULT' handler will ignore this
|
|
data.
|
|
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG_DATA, sub {
|
|
my ($flag, $polygon_data) = @_;
|
|
glEdgeFlag($flag);
|
|
print "glEdgeFlag - and I received polygon_data\n" if $polygon_data;
|
|
});
|
|
|
|
=back
|
|
|
|
=head1 Example: Basic Arrowhead
|
|
|
|
use OpenGL qw(:all);
|
|
|
|
glutInit();
|
|
glutInitWindowSize(501, 501);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
|
|
glutCreateWindow("Tessellation");
|
|
glMatrixMode(GL_PROJECTION());
|
|
glLoadIdentity();
|
|
glOrtho(-250,250,-250,250,-1.0,1.0);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
my $view_triangles = 1; # set to zero to show polygon
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) if $view_triangles;
|
|
|
|
glutDisplayFunc(sub {
|
|
glColor3f(1,1,1);
|
|
|
|
my $tess = gluNewTess();
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT') if ! $view_triangles;
|
|
gluTessBeginPolygon($tess);
|
|
gluTessBeginContour($tess);
|
|
|
|
gluTessVertex_p($tess, 0, 200, 0);
|
|
gluTessVertex_p($tess, 150, -200, 0);
|
|
gluTessVertex_p($tess, 0, -100, 0);
|
|
gluTessVertex_p($tess, -150, -200, 0);
|
|
|
|
gluTessEndContour($tess);
|
|
gluTessEndPolygon($tess);
|
|
gluDeleteTess($tess);
|
|
|
|
glutSwapBuffers();
|
|
});
|
|
|
|
glutMainLoop();
|
|
|
|
=head1 Example: Multiple contours
|
|
|
|
use OpenGL qw(:all);
|
|
|
|
glutInit();
|
|
glutInitWindowSize(501, 501);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
|
|
glutCreateWindow("Tessellation");
|
|
glMatrixMode(GL_PROJECTION());
|
|
glLoadIdentity();
|
|
glOrtho(-250,250,-250,250,-1.0,1.0);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
my $view_triangles = 1; # set to zero to show polygon
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) if $view_triangles;
|
|
|
|
glutDisplayFunc(sub {
|
|
glColor3f(1,1,1);
|
|
my $v = [[[125,0,0], [150,150,0], [0,125,0], [-150,150,0],
|
|
[-125,0,0], [-150,-150,0], [0,-125,0], [150,-150,0], [125,0,0]],
|
|
[[75,0,0], [100,100,0], [0,75,0], [-100,100,0],
|
|
[-75,0,0], [-100,-100,0], [0,-75,0], [100,-100,0], [75,0,0]]
|
|
];
|
|
|
|
my $tess = gluNewTess();
|
|
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT') if ! $view_triangles;
|
|
gluTessBeginPolygon($tess);
|
|
foreach (@$v) {
|
|
gluTessBeginContour($tess);
|
|
foreach (@$_) {
|
|
gluTessVertex_p($tess, @$_);
|
|
}
|
|
gluTessEndContour($tess);
|
|
}
|
|
gluTessEndPolygon($tess);
|
|
gluDeleteTess($tess);
|
|
|
|
glutSwapBuffers();
|
|
});
|
|
|
|
glutMainLoop();
|
|
|
|
=head1 Example: Sample OO Tessellation interface using polygon_data
|
|
|
|
|
|
use OpenGL qw(:all);
|
|
|
|
glutInit();
|
|
glutInitWindowSize(501, 501);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
|
|
glutCreateWindow("Tessellation");
|
|
glMatrixMode(GL_PROJECTION());
|
|
glLoadIdentity();
|
|
glOrtho(-250,250,-250,250,-1.0,1.0);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
my $view_triangles = 0;
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) if $view_triangles;
|
|
|
|
glutDisplayFunc(sub {
|
|
glColor3f(1,1,1);
|
|
my $v = [[[125,0,0], [150,150,0, 0,1,0], [0,125,0], [-150,150,0, 1,0,0],
|
|
[-125,0,0], [-150,-150,0, 0,0,1], [0,-125,0], [150,-150,0, 1,1,0], [125,0,0]],
|
|
[[75,0,0], [100,100,0], [0,75,0], [-100,100,0],
|
|
[-75,0,0], [-100,-100,0], [0,-75,0], [100,-100,0], [75,0,0]]
|
|
];
|
|
|
|
OpenGL::Tess->new(do_colors => 1, no_edge_flag => $view_triangles)->draw_contours(@$v);
|
|
|
|
glutSwapBuffers();
|
|
});
|
|
|
|
glutMainLoop();
|
|
|
|
###----------------------------------------------------------------###
|
|
|
|
|
|
package OpenGL::Tess;
|
|
|
|
# Sample object oriented Tessellator
|
|
# OpenGL::Tess->new(do_colors => 1, no_edge_flag => $view_triangles)->draw_contours(@$v);
|
|
|
|
use strict;
|
|
|
|
sub new {
|
|
my $class = shift;
|
|
my $self = bless {@_}, $class;
|
|
my $tess = $self->{'_tess'} = OpenGL::gluNewTess($self->do_colors);
|
|
for my $cb (qw(begin end vertex combine error edge_flag)) {
|
|
my $enum = OpenGL->can("GLU_TESS_\U${cb}_DATA") || die "Couldn't find callback for $cb";
|
|
my $name = "_$cb";
|
|
OpenGL::gluTessCallback($tess, $enum->(), sub { $_[-1]->$name(@_) });
|
|
}
|
|
return $self;
|
|
}
|
|
|
|
sub DESTROY {
|
|
my $tess = shift->{'_tess'};
|
|
OpenGL::gluDeleteTess($tess) if $tess;
|
|
}
|
|
|
|
sub tess {
|
|
my $self = shift;
|
|
return $self->{'_tess'} || die "Missing tess";
|
|
}
|
|
|
|
sub do_colors { shift->{'do_colors'} }
|
|
|
|
sub begin_polygon {
|
|
my $self = shift;
|
|
my $tess = $self->tess;
|
|
# self will be passed as last arg ([-1]) to all callbacks as opaque polygon data
|
|
return OpenGL::gluTessBeginPolygon($tess, $self);
|
|
}
|
|
|
|
sub end_polygon { OpenGL::gluTessEndPolygon( shift->tess) }
|
|
sub begin_contour { OpenGL::gluTessBeginContour(shift->tess) }
|
|
sub end_contour { OpenGL::gluTessEndContour( shift->tess) }
|
|
|
|
sub draw_contours {
|
|
my $self = shift;
|
|
$self->begin_polygon;
|
|
foreach my $c (@_) {
|
|
$self->begin_contour;
|
|
$self->add_vertex(@$_) for @$c;
|
|
$self->end_contour;
|
|
}
|
|
$self->end_polygon;
|
|
}
|
|
|
|
sub add_vertex {
|
|
my $self = shift;
|
|
die 'Usage $self->add_vertex($x,$y,$z)' if @_ < 3;
|
|
if ($self->do_colors) {
|
|
push @_, 1 for @_ .. 6;
|
|
OpenGL::gluTessVertex_p($self->tess, @_[0..6]);
|
|
} else {
|
|
OpenGL::gluTessVertex_p($self->tess, @_[0..3]);
|
|
}
|
|
}
|
|
|
|
sub _begin {
|
|
my ($self, $enum) = @_;
|
|
OpenGL::glBegin($enum);
|
|
}
|
|
|
|
sub _end { OpenGL::glEnd() }
|
|
|
|
sub _vertex {
|
|
my ($self, $x, $y, $z, $r, $g, $b, $a) = @_;
|
|
OpenGL::glColor4f($r, $g, $b, $a) if $self->do_colors;
|
|
OpenGL::glVertex3f($x, $y, $z);
|
|
}
|
|
|
|
sub _edge_flag {
|
|
my ($self, $flag) = @_;
|
|
return if $self->{'no_edge_flag'};
|
|
OpenGL::glEdgeFlag($flag);
|
|
}
|
|
|
|
sub _error {
|
|
my ($self, $errno) = @_;
|
|
warn __PACKAGE__ ." error: ".OpenGL::gluErrorString($errno);
|
|
}
|
|
|
|
sub _combine {
|
|
my ($self, $x, $y, $z, $v0, $v1, $v2, $v3, $w0, $w1, $w2, $w3) = @_;
|
|
return ($x, $y, $z) if !$self->do_colors;
|
|
return ($x, $y, $z,
|
|
$w0*$v0->[3] + $w1*$v1->[3] + $w2*$v2->[3] + $w3*$v3->[3],
|
|
$w0*$v0->[4] + $w1*$v1->[4] + $w2*$v2->[4] + $w3*$v3->[4],
|
|
$w0*$v0->[5] + $w1*$v1->[5] + $w2*$v2->[5] + $w3*$v3->[5],
|
|
$w0*$v0->[6] + $w1*$v1->[6] + $w2*$v2->[6] + $w3*$v3->[6]);
|
|
}
|
|
|
|
1;
|
|
|
|
=head1 AUTHOR
|
|
|
|
Paul Seamons - paul AT seamons dot com - 2011
|
|
|
|
=cut
|