mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 18:31:55 -04:00
unify()
This commit is contained in:
parent
52dd2fa615
commit
4eb9de4b8e
@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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::
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user