This commit is contained in:
David Rose 2005-04-21 18:55:55 +00:00
parent 52dd2fa615
commit 4eb9de4b8e
11 changed files with 243 additions and 21 deletions

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "qpgeom.h"
#include "qpgeomPoints.h"
#include "qpgeomVertexReader.h"
#include "qpgeomVertexRewriter.h"
#include "pStatTimer.h"
@ -232,8 +233,14 @@ make_nonindexed(bool composite_only) {
PT(qpGeomPrimitive) primitive = (*pi)->make_copy();
new_prims.push_back(primitive);
// GeomPoints are considered "composite" for the purposes of
// making nonindexed, since there's no particular advantage to
// having indexed points (as opposed to, say, indexed triangles or
// indexed lines).
if (primitive->is_indexed() &&
(primitive->is_composite() || !composite_only)) {
(primitive->is_composite() ||
primitive->is_exact_type(qpGeomPoints::get_class_type()) ||
!composite_only)) {
primitive->make_nonindexed(new_data, orig_data);
++num_changed;
} else {
@ -281,18 +288,16 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
nassertv(cdata->_primitive_type == PT_none ||
cdata->_primitive_type == primitive->get_primitive_type());
// They also should have the same fundamental shade model, but
// SM_uniform is compatible with anything.
nassertv(cdata->_shade_model == SM_uniform ||
primitive->get_shade_model() == SM_uniform ||
cdata->_shade_model == primitive->get_shade_model());
// They also should have the a compatible shade model.
CPT(qpGeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
nassertv_always(compat != (qpGeomPrimitive *)NULL);
cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
PrimitiveType new_primitive_type = primitive->get_primitive_type();
cdata->_primitives[i] = (qpGeomPrimitive *)compat.p();
PrimitiveType new_primitive_type = compat->get_primitive_type();
if (new_primitive_type != cdata->_primitive_type) {
cdata->_primitive_type = new_primitive_type;
}
ShadeModel new_shade_model = primitive->get_shade_model();
ShadeModel new_shade_model = compat->get_shade_model();
if (new_shade_model != cdata->_shade_model &&
new_shade_model != SM_uniform) {
cdata->_shade_model = new_shade_model;
@ -323,18 +328,16 @@ add_primitive(const qpGeomPrimitive *primitive) {
nassertv(cdata->_primitive_type == PT_none ||
cdata->_primitive_type == primitive->get_primitive_type());
// They also should have the same fundamental shade model, but
// SM_uniform is compatible with anything.
nassertv(cdata->_shade_model == SM_uniform ||
primitive->get_shade_model() == SM_uniform ||
cdata->_shade_model == primitive->get_shade_model());
// They also should have the a compatible shade model.
CPT(qpGeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
nassertv_always(compat != (qpGeomPrimitive *)NULL);
cdata->_primitives.push_back((qpGeomPrimitive *)primitive);
PrimitiveType new_primitive_type = primitive->get_primitive_type();
cdata->_primitives.push_back((qpGeomPrimitive *)compat.p());
PrimitiveType new_primitive_type = compat->get_primitive_type();
if (new_primitive_type != cdata->_primitive_type) {
cdata->_primitive_type = new_primitive_type;
}
ShadeModel new_shade_model = primitive->get_shade_model();
ShadeModel new_shade_model = compat->get_shade_model();
if (new_shade_model != cdata->_shade_model &&
new_shade_model != SM_uniform) {
cdata->_shade_model = new_shade_model;
@ -515,6 +518,51 @@ unify_in_place() {
reset_geom_rendering(cdata);
}
////////////////////////////////////////////////////////////////////
// Function: qpGeom::copy_primitives_from
// Access: Published
// Description: Copies the primitives from the indicated Geom into
// this one. This does require that both Geoms contain
// the same fundamental type primitives, both have a
// compatible shade model, and both use the same
// GeomVertexData.
//
// Returns true if the copy is successful, or false
// otherwise (because the Geoms were mismatched).
////////////////////////////////////////////////////////////////////
bool qpGeom::
copy_primitives_from(const qpGeom *other) {
if (get_primitive_type() != PT_none &&
other->get_primitive_type() != get_primitive_type()) {
return false;
}
if (get_vertex_data() != other->get_vertex_data()) {
return false;
}
ShadeModel this_shade_model = get_shade_model();
ShadeModel other_shade_model = other->get_shade_model();
if (this_shade_model != SM_uniform && other_shade_model != SM_uniform &&
this_shade_model != other_shade_model) {
if ((this_shade_model == SM_flat_first_vertex && other_shade_model == SM_flat_last_vertex) ||
(this_shade_model == SM_flat_last_vertex && other_shade_model == SM_flat_first_vertex)) {
// This is acceptable; we can rotate the primitives to match.
} else {
// Otherwise, we have incompatible shade models.
return false;
}
}
int num_primitives = other->get_num_primitives();
for (int i = 0; i < num_primitives; i++) {
add_primitive(other->get_primitive(i));
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeom::get_num_bytes
// Access: Published
@ -682,11 +730,13 @@ output(ostream &out) const {
CDReader cdata(_cycler);
// Get a list of the primitive types contained within this object.
int num_faces = 0;
pset<TypeHandle> types;
Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end();
++pi) {
num_faces += (*pi)->get_num_faces();
types.insert((*pi)->get_type());
}
@ -695,7 +745,7 @@ output(ostream &out) const {
for (ti = types.begin(); ti != types.end(); ++ti) {
out << " " << (*ti);
}
out << " ], " << cdata->_data->get_num_rows() << " vertices";
out << " ], " << num_faces << " faces";
}
////////////////////////////////////////////////////////////////////

View File

@ -96,6 +96,8 @@ PUBLISHED:
void rotate_in_place();
void unify_in_place();
bool copy_primitives_from(const qpGeom *other);
int get_num_bytes() const;
INLINE UpdateSeq get_modified() const;

View File

@ -675,7 +675,9 @@ decompose() const {
// Function: qpGeomPrimitive::rotate
// Access: Published
// Description: Returns a new primitive with the shade_model reversed
// (if it is flat shaded).
// (if it is flat shaded), if possible. If the
// primitive type cannot be rotated, returns the
// original primitive, unrotated.
//
// If the current shade_model indicates
// flat_vertex_last, this should bring the last vertex
@ -694,14 +696,70 @@ rotate() const {
CPT(qpGeomVertexArrayData) rotated_vertices = rotate_impl();
if (rotated_vertices == (qpGeomVertexArrayData *)NULL) {
// This primitive type can't be rotated.
return this;
}
PT(qpGeomPrimitive) new_prim = make_copy();
new_prim->set_vertices(rotated_vertices);
switch (get_shade_model()) {
case SM_flat_first_vertex:
new_prim->set_shade_model(SM_flat_last_vertex);
break;
case SM_flat_last_vertex:
new_prim->set_shade_model(SM_flat_first_vertex);
break;
default:
break;
}
return new_prim;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomPrimitive::match_shade_model
// Access: Published
// Description: Returns a new primitive that is compatible with the
// indicated shade model, if possible, or NULL if this
// is not possible.
//
// In most cases, this will return either NULL or the
// original primitive. In the case of a
// SM_flat_first_vertex vs. a SM_flat_last_vertex (or
// vice-versa), however, it will return a rotated
// primitive.
////////////////////////////////////////////////////////////////////
CPT(qpGeomPrimitive) qpGeomPrimitive::
match_shade_model(qpGeomPrimitive::ShadeModel shade_model) const {
ShadeModel this_shade_model = get_shade_model();
if (this_shade_model == shade_model) {
// Trivially compatible.
return this;
}
if (this_shade_model == SM_uniform || shade_model == SM_uniform) {
// SM_uniform is compatible with anything.
return this;
}
if ((this_shade_model == SM_flat_first_vertex && shade_model == SM_flat_last_vertex) ||
(this_shade_model == SM_flat_last_vertex && shade_model == SM_flat_first_vertex)) {
// Needs to be rotated.
CPT(qpGeomPrimitive) rotated = rotate();
if (rotated == this) {
// Oops, can't be rotated, sorry.
return NULL;
}
return rotated;
}
// Not compatible, sorry.
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomPrimitive::get_num_bytes
// Access: Published

View File

@ -122,6 +122,7 @@ PUBLISHED:
CPT(qpGeomPrimitive) decompose() const;
CPT(qpGeomPrimitive) rotate() const;
CPT(qpGeomPrimitive) match_shade_model(ShadeModel shade_model) const;
int get_num_bytes() const;
INLINE int get_data_size_bytes() const;

View File

@ -449,6 +449,75 @@ add_geoms_from(const GeomNode *other) {
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomNode::unify
// Access: Published
// Description: Attempts to unify all of the Geoms contained within
// this node into a single Geom, or at least as few
// Geoms as possible. In turn, the individual
// GeomPrimitives contained within each resulting Geom
// are also unified. The goal is to reduce the number
// of GeomPrimitives within the node as far as possible.
// This may result in composite primitives, such as
// triangle strips and triangle fans, being decomposed
// into triangles. See also Geom::unify().
//
// In order for this to be successful, the primitives
// must reference the same GeomVertexData, have the same
// fundamental primitive type, and have compatible shade
// models.
////////////////////////////////////////////////////////////////////
void GeomNode::
unify() {
CDWriter cdata(_cycler);
Geoms new_geoms;
// Try to unify each Geom with each preceding Geom. This is an n^2
// operation, but usually there are only a handful of Geoms to
// consider, so that's not a big deal.
Geoms::iterator gi;
for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
const GeomEntry &entry = (*gi);
bool unified = false;
if (entry._geom->is_of_type(qpGeom::get_class_type())) {
Geoms::iterator gj;
for (gj = new_geoms.begin(); gj != new_geoms.end() && !unified; ++gj) {
GeomEntry &new_entry = (*gj);
if (new_entry._geom->is_of_type(qpGeom::get_class_type())) {
if (entry._state == new_entry._state) {
// Both states match, so try to combine the primitives.
if (DCAST(qpGeom, new_entry._geom)->copy_primitives_from
(DCAST(qpGeom, entry._geom))) {
// Successfully combined!
unified = true;
}
}
}
}
}
if (!unified) {
// Couldn't unify this Geom with anything, so just add it to the
// output list.
new_geoms.push_back(entry);
}
}
// Done! We'll keep whatever's left in the output list.
cdata->_geoms.swap(new_geoms);
new_geoms.clear();
// Finally, go back through and unify the resulting geom(s).
for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
const GeomEntry &entry = (*gi);
if (entry._geom->is_of_type(qpGeom::get_class_type())) {
DCAST(qpGeom, entry._geom)->unify_in_place();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomNode::write_geoms
// Access: Published

View File

@ -68,6 +68,8 @@ PUBLISHED:
INLINE void remove_geom(int n);
INLINE void remove_all_geoms();
void unify();
void write_geoms(ostream &out, int indent_level) const;
void write_verbose(ostream &out, int indent_level) const;

View File

@ -617,7 +617,7 @@ collect_vertex_data(qpGeom *geom, int collect_bits) {
// Access: Public
// Description: Collects together individual GeomVertexData
// structures that share the same format into one big
// GeomVertexData structure. This is designed to
// GeomVertexData structure. This is intended to
// minimize context switches on the graphics card.
////////////////////////////////////////////////////////////////////
int GeomTransformer::

View File

@ -133,3 +133,14 @@ INLINE int SceneGraphReducer::
make_nonindexed(PandaNode *root, int nonindexed_bits) {
return r_make_nonindexed(root, nonindexed_bits);
}
////////////////////////////////////////////////////////////////////
// Function: SceneGraphReducer::unify
// Access: Published
// Description: Calls unify() on every GeomNode at this level and
// below.
////////////////////////////////////////////////////////////////////
INLINE void SceneGraphReducer::
unify(PandaNode *root) {
r_unify(root);
}

View File

@ -674,3 +674,22 @@ r_make_nonindexed(PandaNode *node, int nonindexed_bits) {
return num_changed;
}
////////////////////////////////////////////////////////////////////
// Function: SceneGraphReducer::r_unify
// Access: Private
// Description: The recursive implementation of unify().
////////////////////////////////////////////////////////////////////
void SceneGraphReducer::
r_unify(PandaNode *node) {
if (node->is_geom_node()) {
GeomNode *geom_node = DCAST(GeomNode, node);
geom_node->unify();
}
PandaNode::Children children = node->get_children();
int num_children = children.get_num_children();
for (int i = 0; i < num_children; ++i) {
r_unify(children.get_child(i));
}
}

View File

@ -114,6 +114,7 @@ PUBLISHED:
INLINE int collect_vertex_data(PandaNode *root, int collect_bits = ~0);
INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0);
INLINE void unify(PandaNode *root);
protected:
void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
@ -143,6 +144,7 @@ protected:
int r_collect_vertex_data(PandaNode *node, int collect_bits,
GeomTransformer &transformer);
int r_make_nonindexed(PandaNode *node, int collect_bits);
void r_unify(PandaNode *node);
private:
GeomTransformer _transformer;

View File

@ -82,6 +82,12 @@ event_W(CPT_Event, void *) {
}
}
void
event_F(CPT_Event, void *) {
// shift-F: flatten the model hierarchy.
framework.get_models().flatten_strong();
}
void
event_Enter(CPT_Event, void *) {
// alt-enter: toggle between window/fullscreen in the same scene.
@ -133,8 +139,9 @@ event_0(CPT_Event event, void *) {
// 0: run hacky test.
SceneGraphReducer gr;
gr.make_nonindexed(framework.get_models().node());
gr.collect_vertex_data(framework.get_models().node());
gr.unify(framework.get_models().node());
gr.make_nonindexed(framework.get_models().node());
/*
static int count = 0;
@ -312,6 +319,7 @@ main(int argc, char *argv[]) {
framework.enable_default_keys();
framework.define_key("shift-w", "open a new window", event_W, NULL);
framework.define_key("shift-f", "flatten hierarchy", event_F, NULL);
framework.define_key("alt-enter", "toggle between window/fullscreen", event_Enter, NULL);
framework.define_key("2", "split the window", event_2, NULL);
if (pview_test_hack) {