Support strip cut indices and (using those) support direct rendering of linestrips in OpenGL

This commit is contained in:
rdb 2014-09-25 00:16:44 +00:00
parent db0fd516a0
commit a25a9e655e
12 changed files with 423 additions and 77 deletions

View File

@ -178,7 +178,10 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data,
// Even beyond munging the vertex format, we have to convert the
// Geom itself into a new primitive type the GSG can render
// directly.
if ((unsupported_bits & Geom::GR_composite_bits) != 0) {
// If we don't support a strip cut index, it might be faster to
// just decompose it rather than draw them one by one.
if ((unsupported_bits & Geom::GR_composite_bits) != 0 ||
(unsupported_bits & Geom::GR_strip_cut_index) != 0) {
// This decomposes everything in the primitive, so that if (for
// instance) the primitive contained both strips and fans, but
// the GSG didn't support fans, it would decompose the strips
@ -224,7 +227,10 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
// Even beyond munging the vertex format, we have to convert the
// Geom itself into a new primitive type the GSG can render
// directly.
if ((unsupported_bits & Geom::GR_composite_bits) != 0) {
// If we don't support a strip cut index, it might be faster to
// just decompose it rather than draw them one by one.
if ((unsupported_bits & Geom::GR_composite_bits) != 0 ||
(unsupported_bits & Geom::GR_strip_cut_index) != 0) {
// This decomposes everything in the primitive, so that if (for
// instance) the primitive contained both strips and fans, but
// the GSG didn't support fans, it would decompose the strips

View File

@ -515,6 +515,7 @@ reset() {
Geom::GR_point | Geom::GR_point_uniform_size |
Geom::GR_indexed_other |
Geom::GR_triangle_strip | Geom::GR_triangle_fan |
Geom::GR_line_strip |
Geom::GR_flat_last_vertex;
_supports_point_parameters = false;
@ -551,6 +552,30 @@ reset() {
_supported_geom_rendering |= Geom::GR_point_sprite;
}
_glPrimitiveRestartIndex = NULL;
if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_ES3_compatibility")) {
// As long as we enable this, OpenGL will always use the highest possible index
// for a numeric type as strip cut index, which coincides with our convention.
// This saves us a call to glPrimitiveRestartIndex.
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
_supported_geom_rendering |= Geom::GR_strip_cut_index;
} else if (is_at_least_gl_version(3, 1)) {
glEnable(GL_PRIMITIVE_RESTART);
_supported_geom_rendering |= Geom::GR_strip_cut_index;
_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)
get_extension_func("glPrimitiveRestartIndex");
} else if (has_extension("GL_NV_primitive_restart")) {
glEnable(GL_PRIMITIVE_RESTART_NV);
_supported_geom_rendering |= Geom::GR_strip_cut_index;
_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)
get_extension_func("glPrimitiveRestartIndexNV");
}
_supports_vertex_blend = has_extension("GL_ARB_vertex_blend");
if (_supports_vertex_blend) {
@ -3800,7 +3825,113 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) {
return false;
PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread());
report_my_gl_errors();
#ifndef NDEBUG
if (GLCAT.is_spam()) {
GLCAT.spam() << "draw_linestrips: " << *(reader->get_object()) << "\n";
}
#endif // NDEBUG
#ifdef SUPPORT_IMMEDIATE_MODE
if (_use_sender) {
draw_immediate_composite_primitives(reader, GL_LINE_STRIP);
} else
#endif // SUPPORT_IMMEDIATE_MODE
{
if (reader->is_indexed() &&
(_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) {
// One long triangle strip, connected by strip cut indices.
if (_glPrimitiveRestartIndex != NULL) {
_glPrimitiveRestartIndex(reader->get_strip_cut_index());
}
int num_vertices = reader->get_num_vertices();
_vertices_other_pcollector.add_level(num_vertices);
_primitive_batches_other_pcollector.add_level(1);
const unsigned char *client_pointer;
if (!setup_primitive(client_pointer, reader, force)) {
return false;
}
#ifndef OPENGLES
if (_supports_geometry_instancing && _instance_count > 0) {
_glDrawElementsInstanced(GL_LINE_STRIP, num_vertices,
get_numeric_type(reader->get_index_type()),
client_pointer, _instance_count);
} else
#endif
{
_glDrawRangeElements(GL_LINE_STRIP,
reader->get_min_vertex(),
reader->get_max_vertex(),
num_vertices,
get_numeric_type(reader->get_index_type()),
client_pointer);
}
} else {
// Send the individual line strips, stepping over the
// strip-cut indices.
CPTA_int ends = reader->get_ends();
_primitive_batches_other_pcollector.add_level(ends.size());
if (reader->is_indexed()) {
const unsigned char *client_pointer;
if (!setup_primitive(client_pointer, reader, force)) {
return false;
}
int index_stride = reader->get_index_stride();
GeomVertexReader mins(reader->get_mins(), 0);
GeomVertexReader maxs(reader->get_maxs(), 0);
nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
unsigned int start = 0;
for (size_t i = 0; i < ends.size(); i++) {
_vertices_other_pcollector.add_level(ends[i] - start);
#ifndef OPENGLES
if (_supports_geometry_instancing && _instance_count > 0) {
_glDrawElementsInstanced(GL_LINE_STRIP, ends[i] - start,
get_numeric_type(reader->get_index_type()),
client_pointer + start * index_stride,
_instance_count);
} else
#endif
{
_glDrawRangeElements(GL_LINE_STRIP,
mins.get_data1i(), maxs.get_data1i(),
ends[i] - start,
get_numeric_type(reader->get_index_type()),
client_pointer + start * index_stride);
}
start = ends[i] + 1;
}
} else {
unsigned int start = 0;
int first_vertex = reader->get_first_vertex();
for (size_t i = 0; i < ends.size(); i++) {
_vertices_other_pcollector.add_level(ends[i] - start);
#ifndef OPENGLES
if (_supports_geometry_instancing && _instance_count > 0) {
_glDrawArraysInstanced(GL_LINE_STRIP, first_vertex + start,
ends[i] - start, _instance_count);
} else
#endif
{
glDrawArrays(GL_LINE_STRIP, first_vertex + start,
ends[i] - start);
}
start = ends[i] + 1;
}
}
}
}
report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////

View File

@ -598,6 +598,8 @@ public:
PFNGLPOINTPARAMETERFVPROC _glPointParameterfv;
bool _supports_point_sprite;
PFNGLPRIMITIVERESTARTINDEXPROC _glPrimitiveRestartIndex;
bool _supports_vertex_blend;
PFNGLWEIGHTPOINTERARBPROC _glWeightPointer;
PFNGLVERTEXBLENDARBPROC _glVertexBlend;

View File

@ -1163,6 +1163,11 @@ extern "C" {
/* reuse GL_TEXTURE_IMMUTABLE_FORMAT */
#endif
#ifndef GL_VERSION_4_3
#define GL_VERSION_4_3 1
#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
#endif
#ifndef GL_ARB_multitexture
#define GL_TEXTURE0_ARB 0x84C0
#define GL_TEXTURE1_ARB 0x84C1

View File

@ -125,6 +125,9 @@ PUBLISHED:
// The union of all of the above composite types.
GR_composite_bits = 0x01c00,
// If strip-cut indices are used to restart a composite primitive.
GR_strip_cut_index = 0x20000,
// If the shade model requires a particular vertex for flat shading.
GR_flat_first_vertex = 0x02000,
GR_flat_last_vertex = 0x04000,
@ -216,4 +219,3 @@ EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::NumericType nume
EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::Contents contents);
#endif

View File

@ -89,7 +89,11 @@ get_primitive_type() const {
int GeomLinestrips::
get_geom_rendering() const {
if (is_indexed()) {
return GR_line_strip | GR_indexed_other;
if (get_num_primitives() > 1) {
return GR_line_strip | GR_indexed_other | GR_strip_cut_index;
} else {
return GR_line_strip | GR_indexed_other;
}
} else {
return GR_line_strip;
}
@ -106,6 +110,20 @@ get_min_num_vertices_per_primitive() const {
return 2;
}
////////////////////////////////////////////////////////////////////
// Function: GeomLinestrips::get_num_unused_vertices_per_primitive
// Access: Public, Virtual
// Description: Returns the number of vertices that are added between
// primitives that aren't, strictly speaking, part of
// the primitives themselves. This is used, for
// instance, to define degenerate triangles to connect
// otherwise disconnected triangle strips.
////////////////////////////////////////////////////////////////////
int GeomLinestrips::
get_num_unused_vertices_per_primitive() const {
return 1;
}
////////////////////////////////////////////////////////////////////
// Function: GeomLinestrips::draw
// Access: Public, Virtual
@ -138,9 +156,13 @@ decompose_impl() const {
lines->set_shade_model(get_shade_model());
CPTA_int ends = get_ends();
int vi = 0;
int num_unused = get_num_unused_vertices_per_primitive();
int vi = -num_unused;
int li = 0;
while (li < (int)ends.size()) {
// Skip unused vertices between tristrips.
vi += num_unused;
int end = ends[li];
nassertr(vi + 1 <= end, lines.p());
int v0 = get_vertex(vi);
@ -210,6 +232,33 @@ rotate_impl() const {
return new_vertices;
}
////////////////////////////////////////////////////////////////////
// Function: GeomLinestrips::requires_unused_vertices
// Access: Protected, Virtual
// Description: Should be redefined to return true in any primitive
// that implements append_unused_vertices().
////////////////////////////////////////////////////////////////////
bool GeomLinestrips::
requires_unused_vertices() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GeomLinestrips::append_unused_vertices
// Access: Protected, Virtual
// Description: Called when a new primitive is begun (other than the
// first primitive), this should add some degenerate
// vertices between primitives, if the primitive type
// requires that. The second parameter is the first
// vertex that begins the new primitive.
////////////////////////////////////////////////////////////////////
void GeomLinestrips::
append_unused_vertices(GeomVertexArrayData *vertices, int vertex) {
GeomVertexWriter to(vertices, 0);
to.set_row_unsafe(vertices->get_num_rows());
to.add_data1i(get_strip_cut_index());
}
////////////////////////////////////////////////////////////////////
// Function: GeomLinestrips::register_with_read_factory
// Access: Public, Static

View File

@ -34,6 +34,7 @@ public:
virtual PrimitiveType get_primitive_type() const;
virtual int get_geom_rendering() const;
virtual int get_min_num_vertices_per_primitive() const;
virtual int get_num_unused_vertices_per_primitive() const;
public:
virtual bool draw(GraphicsStateGuardianBase *gsg,
@ -43,6 +44,9 @@ public:
protected:
virtual CPT(GeomPrimitive) decompose_impl() const;
virtual CPT(GeomVertexArrayData) rotate_impl() const;
virtual bool requires_unused_vertices() const;
virtual void append_unused_vertices(GeomVertexArrayData *vertices,
int vertex);
public:
static void register_with_read_factory();

View File

@ -129,7 +129,7 @@ get_first_vertex() const {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_num_vertices
// Access: Published
// Description: Returns the number of vertices used by all the
// Description: Returns the number of indices used by all the
// primitives in this object.
////////////////////////////////////////////////////////////////////
INLINE int GeomPrimitive::
@ -310,6 +310,20 @@ get_index_stride() const {
return reader.get_index_stride();
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_strip_cut_index
// Access: Published
// Description: If relevant, returns the index value that may be
// used in some cases to signify the end of a
// primitive. This is typically the highest value
// that the numeric type can store.
////////////////////////////////////////////////////////////////////
INLINE int GeomPrimitive::
get_strip_cut_index() const {
CDReader cdata(_cycler);
return get_strip_cut_index(cdata->_index_type);
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_ends
// Access: Published
@ -679,6 +693,16 @@ get_read_pointer(bool force) const {
return _vertices_reader->get_read_pointer(force);
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitivePipelineReader::get_strip_cut_index
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE int GeomPrimitivePipelineReader::
get_strip_cut_index() const {
return GeomPrimitive::get_strip_cut_index(_cdata->_index_type);
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitivePipelineReader::get_ends
// Access: Public

View File

@ -61,18 +61,18 @@ make_cow_copy() {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::Constructor
// Access: Published
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomPrimitive::
GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
CDWriter cdata(_cycler, true);
cdata->_usage_hint = usage_hint;
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::Copy Constructor
// Access: Published
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomPrimitive::
GeomPrimitive(const GeomPrimitive &copy) :
@ -80,7 +80,7 @@ GeomPrimitive(const GeomPrimitive &copy) :
_cycler(copy._cycler)
{
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::Copy Assignment Operator
// Access: Published
@ -98,7 +98,7 @@ operator = (const GeomPrimitive &copy) {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::Destructor
// Access: Published, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomPrimitive::
~GeomPrimitive() {
@ -197,7 +197,7 @@ add_vertex(int vertex) {
int num_primitives = get_num_primitives();
if (num_primitives > 0 &&
requires_unused_vertices() &&
requires_unused_vertices() &&
get_num_vertices() == get_primitive_end(num_primitives - 1)) {
// If we are beginning a new primitive, give the derived class a
// chance to insert some degenerate vertices.
@ -224,7 +224,7 @@ add_vertex(int vertex) {
cdata->_got_minmax = false;
return;
}
// Otherwise, we need to suddenly become an indexed primitive.
do_make_indexed(cdata);
}
@ -288,7 +288,7 @@ add_consecutive_vertices(int start, int num_vertices) {
cdata->_got_minmax = false;
return;
}
// Otherwise, we need to suddenly become an indexed primitive.
do_make_indexed(cdata);
}
@ -468,12 +468,18 @@ offset_vertices(int offset) {
recompute_minmax(cdata);
nassertv(cdata->_got_minmax);
}
consider_elevate_index_type(cdata, cdata->_max_vertex + offset);
int strip_cut_index = get_strip_cut_index(cdata->_index_type);
GeomVertexRewriter index(do_modify_vertices(cdata), 0);
while (!index.is_at_end()) {
index.set_data1i(index.get_data1i() + offset);
int vertex = index.get_data1i();
if (vertex != strip_cut_index) {
index.set_data1i(vertex + offset);
}
}
} else {
@ -483,7 +489,7 @@ offset_vertices(int offset) {
cdata->_modified = Geom::get_next_modified();
cdata->_got_minmax = false;
consider_elevate_index_type(cdata,
consider_elevate_index_type(cdata,
cdata->_first_vertex + cdata->_num_vertices - 1);
}
}
@ -516,14 +522,19 @@ offset_vertices(int offset, int begin_row, int end_row) {
if (is_indexed()) {
CDWriter cdata(_cycler, true);
int strip_cut_index = get_strip_cut_index(cdata->_index_type);
// Calculate the maximum vertex over our range.
int max_vertex = 0;
{
GeomVertexReader index_r(cdata->_vertices.get_read_pointer(), 0);
index_r.set_row_unsafe(begin_row);
for (int j = begin_row; j < end_row; ++j) {
max_vertex = max(max_vertex, index_r.get_data1i());
int vertex = index_r.get_data1i();
if (vertex != strip_cut_index) {
max_vertex = max(max_vertex, vertex);
}
}
}
@ -532,7 +543,10 @@ offset_vertices(int offset, int begin_row, int end_row) {
GeomVertexRewriter index(do_modify_vertices(cdata), 0);
index.set_row_unsafe(begin_row);
for (int j = begin_row; j < end_row; ++j) {
index.set_data1i(index.get_data1i() + offset);
int vertex = index.get_data1i();
if (vertex != strip_cut_index) {
index.set_data1i(vertex + offset);
}
}
} else {
@ -544,7 +558,7 @@ offset_vertices(int offset, int begin_row, int end_row) {
cdata->_modified = Geom::get_next_modified();
cdata->_got_minmax = false;
consider_elevate_index_type(cdata,
consider_elevate_index_type(cdata,
cdata->_first_vertex + cdata->_num_vertices - 1);
}
}
@ -554,17 +568,20 @@ offset_vertices(int offset, int begin_row, int end_row) {
// Access: Published
// Description: Converts the primitive from indexed to nonindexed by
// duplicating vertices as necessary into the indicated
// dest GeomVertexData.
// dest GeomVertexData. Note: does not support
// primitives with strip cut indices.
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
make_nonindexed(GeomVertexData *dest, const GeomVertexData *source) {
Thread *current_thread = Thread::get_current_thread();
int num_vertices = get_num_vertices();
int dest_start = dest->get_num_rows();
int strip_cut_index = get_strip_cut_index();
dest->set_num_rows(dest_start + num_vertices);
for (int i = 0; i < num_vertices; ++i) {
int v = get_vertex(i);
nassertd(v != strip_cut_index) continue;
dest->copy_row_from(dest_start + i, source, v, current_thread);
}
@ -596,14 +613,18 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) {
int num_vertices = get_num_vertices();
int dest_start = dest->get_num_rows();
int strip_cut_index = get_strip_cut_index();
for (int i = 0; i < num_vertices; ++i) {
int v = get_vertex(i);
if (v == strip_cut_index) {
continue;
}
// Try to add the relation { v : size() }. If that succeeds,
// great; if it doesn't, look up whatever we previously added
// for v.
pair<CopiedIndices::iterator, bool> result =
pair<CopiedIndices::iterator, bool> result =
copied_indices.insert(CopiedIndices::value_type(v, (int)copied_indices.size()));
int v2 = (*result.first).second + dest_start;
index.add_data1i(v2);
@ -613,7 +634,7 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) {
dest->copy_row_from(v2, source, v, current_thread);
}
}
set_vertices(new_vertices);
}
}
@ -643,7 +664,7 @@ make_indexed() {
// Function: GeomPrimitive::get_primitive_start
// Access: Published
// Description: Returns the element within the _vertices list at which
// the nth primitive starts.
// the nth primitive starts.
//
// If i is one more than the highest valid primitive
// vertex, the return value will be one more than the
@ -723,7 +744,7 @@ get_primitive_num_vertices(int n) const {
} else {
int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
return cdata->_ends[n] - cdata->_ends[n - 1] - num_unused_vertices_per_primitive;
}
}
} else {
// This is a simple primitive type like a triangle: each primitive
@ -732,6 +753,28 @@ get_primitive_num_vertices(int n) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_num_used_vertices
// Access: Published
// Description: Returns the number of vertices used by all of the
// primitives. This is the same as summing
// get_primitive_num_vertices(n) for n in
// get_num_primitives(). It is like get_num_vertices
// except that it excludes all of the degenerate
// vertices and strip-cut indices.
////////////////////////////////////////////////////////////////////
int GeomPrimitive::
get_num_used_vertices() const {
int num_primitives = get_num_primitives();
if (num_primitives > 0) {
return get_num_vertices() - ((num_primitives - 1) *
get_num_unused_vertices_per_primitive());
} else {
return 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_primitive_min_vertex
// Access: Published
@ -959,10 +1002,14 @@ make_points() const {
int num_vertices = get_num_vertices();
if (is_indexed()) {
CPT(GeomVertexArrayData) vertices = get_vertices();
int strip_cut_index = get_strip_cut_index();
GeomVertexReader index(vertices, 0);
for (int vi = 0; vi < num_vertices; ++vi) {
nassertr(!index.is_at_end(), NULL);
bits.set_bit(index.get_data1i());
int vertex = index.get_data1i();
if (vertex != strip_cut_index) {
bits.set_bit(vertex);
}
}
} else {
int first_vertex = get_first_vertex();
@ -996,14 +1043,13 @@ make_points() const {
// Access: Published
// Description: Decomposes a complex primitive type into a simpler
// primitive type, for instance triangle strips to
// triangles, puts these in a new GeomPatches objectand returns a pointer to the new primitive
// triangles, puts these in a new GeomPatches object
// and returns a pointer to the new primitive
// definition. If the decomposition cannot be
// performed, this might return the original object.
//
// This method is useful for application code that wants
// to iterate through the set of triangles on the
// primitive without having to write handlers for each
// possible kind of primitive type.
// to use tesselation shaders on arbitrary geometry.
////////////////////////////////////////////////////////////////////
CPT(GeomPrimitive) GeomPrimitive::
make_patches() const {
@ -1079,7 +1125,7 @@ request_resident() const {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::output
// Access: Published, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
output(ostream &out) const {
@ -1090,7 +1136,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::write
// Access: Published, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
write(ostream &out, int indent_level) const {
@ -1448,7 +1494,7 @@ is_prepared(PreparedGraphicsObjects *prepared_objects) const {
// rendered.
////////////////////////////////////////////////////////////////////
IndexBufferContext *GeomPrimitive::
prepare_now(PreparedGraphicsObjects *prepared_objects,
prepare_now(PreparedGraphicsObjects *prepared_objects,
GraphicsStateGuardianBase *gsg) {
nassertr(is_indexed(), NULL);
@ -1558,11 +1604,44 @@ clear_prepared(PreparedGraphicsObjects *prepared_objects) {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_highest_index_value
// Access: Private, Static
// Description: Returns the largest index value that can be stored in
// an index of the indicated type.
// Description: Returns the largest index value that can be stored
// in an index of the indicated type, minus one (to
// leave room for a potential strip cut index)
////////////////////////////////////////////////////////////////////
int GeomPrimitive::
get_highest_index_value(NumericType index_type) {
// Reserve the highest possible index because implementations use
// this as a strip-cut index.
switch (index_type) {
case NT_uint8:
return 0xff - 1;
case NT_uint16:
return 0xffff - 1;
case NT_uint32:
// We don't actually allow use of the sign bit, since all of our
// functions receive an "int" instead of an "unsigned int".
return 0x7fffffff - 1;
default:
return 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::get_strip_cut_index
// Access: Private, Static
// Description: Returns the index of the indicated type that is
// reserved for use as a strip cut index, if enabled
// for the primitive. When the renderer encounters
// this index, it will restart the primitive. This
// is guaranteed not to point to an actual vertex.
////////////////////////////////////////////////////////////////////
int GeomPrimitive::
get_strip_cut_index(NumericType index_type) {
// Reserve the highest possible index because implementations use
// this as a strip-cut index.
switch (index_type) {
case NT_uint8:
return 0xff;
@ -1571,12 +1650,8 @@ get_highest_index_value(NumericType index_type) {
return 0xffff;
case NT_uint32:
// We don't actually allow use of the sign bit, since all of our
// functions receive an "int" instead of an "unsigned int".
return 0x7fffffff;
default:
return 0;
return -1;
}
}
@ -1593,7 +1668,7 @@ get_highest_index_value(NumericType index_type) {
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any,
bool &found_any,
const GeomVertexData *vertex_data,
bool got_mat, const LMatrix4 &mat,
const InternalName *column_name,
@ -1613,7 +1688,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
for (int i = 0; i < cdata->_num_vertices; i++) {
reader.set_row_unsafe(cdata->_first_vertex + i);
LPoint3 vertex = mat.xform_point(reader.get_data3());
if (found_any) {
min_point.set(min(min_point[0], vertex[0]),
min(min_point[1], vertex[1]),
@ -1631,7 +1706,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
for (int i = 0; i < cdata->_num_vertices; i++) {
reader.set_row_unsafe(cdata->_first_vertex + i);
const LVecBase3 &vertex = reader.get_data3();
if (found_any) {
min_point.set(min(min_point[0], vertex[0]),
min(min_point[1], vertex[1]),
@ -1650,13 +1725,17 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
} else {
// Indexed case.
GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
int strip_cut_index = get_strip_cut_index(cdata->_index_type);
if (got_mat) {
while (!index.is_at_end()) {
int ii = index.get_data1i();
if (ii == strip_cut_index) {
continue;
}
reader.set_row_unsafe(ii);
LPoint3 vertex = mat.xform_point(reader.get_data3());
if (found_any) {
min_point.set(min(min_point[0], vertex[0]),
min(min_point[1], vertex[1]),
@ -1673,9 +1752,12 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
} else {
while (!index.is_at_end()) {
int ii = index.get_data1i();
if (ii == strip_cut_index) {
continue;
}
reader.set_row_unsafe(ii);
const LVecBase3 &vertex = reader.get_data3();
if (found_any) {
min_point.set(min(min_point[0], vertex[0]),
min(min_point[1], vertex[1]),
@ -1797,63 +1879,77 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
cdata->_max_vertex = 0;
cdata->_mins.clear();
cdata->_maxs.clear();
} else if (get_num_vertices_per_primitive() == 0) {
// This is a complex primitive type like a triangle strip; compute
// the minmax of each primitive (as well as the overall minmax).
GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
cdata->_mins = make_index_data();
cdata->_maxs = make_index_data();
GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0);
mins.reserve_num_rows(cdata->_ends.size());
GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0);
maxs.reserve_num_rows(cdata->_ends.size());
int pi = 0;
unsigned int vertex = index.get_data1i();
cdata->_min_vertex = vertex;
cdata->_max_vertex = vertex;
unsigned int min_prim = vertex;
unsigned int max_prim = vertex;
int num_unused_vertices = get_num_unused_vertices_per_primitive();
for (int vi = 1; vi < num_vertices; ++vi) {
nassertv(!index.is_at_end());
unsigned int vertex = index.get_data1i();
cdata->_min_vertex = min(cdata->_min_vertex, vertex);
cdata->_max_vertex = max(cdata->_max_vertex, vertex);
nassertv(pi < (int)cdata->_ends.size());
unsigned int vertex;
if (vi == cdata->_ends[pi]) {
// Skip unused vertices, since they won't be very relevant and
// may contain a strip-cut index, which would distort the result.
if (num_unused_vertices > 0) {
vi += num_unused_vertices;
index.set_row_unsafe(vi);
}
vertex = index.get_data1i();
mins.add_data1i(min_prim);
maxs.add_data1i(max_prim);
min_prim = vertex;
max_prim = vertex;
++pi;
} else {
vertex = index.get_data1i();
min_prim = min(min_prim, vertex);
max_prim = max(max_prim, vertex);
}
cdata->_min_vertex = min(cdata->_min_vertex, vertex);
cdata->_max_vertex = max(cdata->_max_vertex, vertex);
}
mins.add_data1i(min_prim);
maxs.add_data1i(max_prim);
nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size());
} else {
// This is a simple primitive type like a triangle; just compute
// the overall minmax.
GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
cdata->_mins.clear();
cdata->_maxs.clear();
unsigned int vertex = index.get_data1i();
cdata->_min_vertex = vertex;
cdata->_max_vertex = vertex;
for (int vi = 1; vi < num_vertices; ++vi) {
nassertv(!index.is_at_end());
unsigned int vertex = index.get_data1i();
@ -1862,7 +1958,7 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
}
}
}
cdata->_got_minmax = true;
}
@ -1899,22 +1995,25 @@ do_make_indexed(CData *cdata) {
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
consider_elevate_index_type(CData *cdata, int vertex) {
// Note that we reserve the highest possible index of a particular
// index type (ie. -1) because this is commonly used as a strip-cut
// (also known as primitive restart) index.
switch (cdata->_index_type) {
case NT_uint8:
if (vertex > 0xff) {
if (vertex >= 0xff) {
do_set_index_type(cdata, NT_uint16);
}
break;
case NT_uint16:
if (vertex > 0xffff) {
if (vertex >= 0xffff) {
do_set_index_type(cdata, NT_uint32);
}
break;
case NT_uint32:
// Not much we can do here.
nassertv(vertex <= 0x7fffffff);
nassertv(vertex < 0x7fffffff);
break;
default:
@ -1929,6 +2028,9 @@ consider_elevate_index_type(CData *cdata, int vertex) {
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
int old_strip_cut_index = get_strip_cut_index(cdata->_index_type);
int new_strip_cut_index = get_strip_cut_index(index_type);
cdata->_index_type = index_type;
if (gobj_cat.is_debug()) {
@ -1938,7 +2040,7 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
if (!cdata->_vertices.is_null()) {
CPT(GeomVertexArrayFormat) new_format = get_index_format();
CPT(GeomVertexArrayData) array_obj = cdata->_vertices.get_read_pointer();
if (array_obj->get_array_format() != new_format) {
PT(GeomVertexArrayData) new_vertices = make_index_data();
@ -1946,9 +2048,13 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
GeomVertexReader from(array_obj, 0);
GeomVertexWriter to(new_vertices, 0);
while (!from.is_at_end()) {
to.set_data1i(from.get_data1i());
int index = from.get_data1i();
if (index == old_strip_cut_index) {
index = new_strip_cut_index;
}
to.set_data1i(index);
}
cdata->_vertices = new_vertices;
cdata->_got_minmax = false;
@ -2056,7 +2162,7 @@ int GeomPrimitive::CData::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
int pi = CycleData::complete_pointers(p_list, manager);
_vertices = DCAST(GeomVertexArrayData, p_list[pi++]);
_vertices = DCAST(GeomVertexArrayData, p_list[pi++]);
if (manager->get_file_minor_ver() < 6 && !_vertices.is_null()) {
// Older bam files might have a meaningless number in
@ -2106,7 +2212,7 @@ check_minmax() const {
#ifdef DO_PIPELINING
unref_delete((CycleData *)_cdata);
#endif
GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler,
GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler,
false, _current_thread);
((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING
@ -2132,7 +2238,7 @@ check_minmax() const {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitivePipelineReader::get_first_vertex
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
int GeomPrimitivePipelineReader::
get_first_vertex() const {
@ -2170,7 +2276,7 @@ get_vertex(int i) const {
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitivePipelineReader::get_num_primitives
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
int GeomPrimitivePipelineReader::
get_num_primitives() const {

View File

@ -118,6 +118,7 @@ PUBLISHED:
int get_primitive_start(int n) const;
int get_primitive_end(int n) const;
int get_primitive_num_vertices(int n) const;
int get_num_used_vertices() const;
INLINE int get_num_faces() const;
INLINE int get_primitive_num_faces(int n) const;
@ -163,6 +164,7 @@ PUBLISHED:
void set_nonindexed_vertices(int first_vertex, int num_vertices);
INLINE int get_index_stride() const;
INLINE int get_strip_cut_index() const;
INLINE CPTA_int get_ends() const;
PTA_int modify_ends();
@ -194,6 +196,7 @@ public:
private:
void clear_prepared(PreparedGraphicsObjects *prepared_objects);
static int get_highest_index_value(NumericType index_type);
static int get_strip_cut_index(NumericType index_type);
public:
virtual bool draw(GraphicsStateGuardianBase *gsg,
@ -358,6 +361,7 @@ public:
INLINE int get_index_stride() const;
INLINE const GeomVertexArrayDataHandle *get_vertices_reader() const;
INLINE const unsigned char *get_read_pointer(bool force) const;
INLINE int get_strip_cut_index() const;
INLINE CPTA_int get_ends() const;
INLINE CPT(GeomVertexArrayData) get_mins() const;
INLINE CPT(GeomVertexArrayData) get_maxs() const;

View File

@ -95,6 +95,17 @@ get_geom_rendering() const {
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomTristrips::get_min_num_vertices_per_primitive
// Access: Public, Virtual
// Description: Returns the minimum number of vertices that must be
// added before close_primitive() may legally be called.
////////////////////////////////////////////////////////////////////
int GeomTristrips::
get_min_num_vertices_per_primitive() const {
return 3;
}
////////////////////////////////////////////////////////////////////
// Function: GeomTristrips::get_num_unused_vertices_per_primitive
// Access: Public, Virtual
@ -142,6 +153,7 @@ decompose_impl() const {
CPTA_int ends = get_ends();
int num_vertices = get_num_vertices();
int num_unused = get_num_unused_vertices_per_primitive();
// We need a slightly different algorithm for SM_flat_first_vertex
// than for SM_flat_last_vertex, to preserve the key vertex in the
@ -150,11 +162,11 @@ decompose_impl() const {
if (get_shade_model() == SM_flat_first_vertex) {
// Preserve the first vertex of each component triangle as the
// first vertex of each generated triangle.
int vi = -2;
int vi = -num_unused;
int li = 0;
while (li < (int)ends.size()) {
// Skip unused vertices between tristrips.
vi += 2;
vi += num_unused;
int end = ends[li];
nassertr(vi + 2 <= end, NULL);
int v0 = get_vertex(vi);
@ -192,11 +204,11 @@ decompose_impl() const {
} else {
// Preserve the last vertex of each component triangle as the
// last vertex of each generated triangle.
int vi = -2;
int vi = -num_unused;
int li = 0;
while (li < (int)ends.size()) {
// Skip unused vertices between tristrips.
vi += 2;
vi += num_unused;
int end = ends[li];
nassertr(vi + 2 <= end, NULL);
int v0 = get_vertex(vi);

View File

@ -33,6 +33,7 @@ public:
virtual PT(GeomPrimitive) make_copy() const;
virtual PrimitiveType get_primitive_type() const;
virtual int get_geom_rendering() const;
virtual int get_min_num_vertices_per_primitive() const;
virtual int get_num_unused_vertices_per_primitive() const;
public: