diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 0a9774581d..c6ad07d83e 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -49,6 +49,7 @@ #include "fog.h" #include "fogAttrib.h" #include "geomNode.h" +#include "geomTransformer.h" #include "lensNode.h" #include "light.h" #include "lightAttrib.h" @@ -397,6 +398,7 @@ init_libpgraph() { Fog::init_type(); FogAttrib::init_type(); GeomNode::init_type(); + GeomTransformer::init_type(); LensNode::init_type(); Light::init_type(); LightAttrib::init_type(); diff --git a/panda/src/pgraph/geomTransformer.cxx b/panda/src/pgraph/geomTransformer.cxx index 28174294d1..8441b038cd 100644 --- a/panda/src/pgraph/geomTransformer.cxx +++ b/panda/src/pgraph/geomTransformer.cxx @@ -38,6 +38,8 @@ PStatCollector GeomTransformer::_apply_set_color_collector("*:Flatten:apply:set PStatCollector GeomTransformer::_apply_scale_color_collector("*:Flatten:apply:scale color"); PStatCollector GeomTransformer::_apply_set_format_collector("*:Flatten:apply:set format"); +TypeHandle GeomTransformer::NewCollectedData::_type_handle; + //////////////////////////////////////////////////////////////////// // Function: GeomTransformer::Constructor // Access: Public @@ -68,6 +70,7 @@ GeomTransformer(const GeomTransformer ©) : //////////////////////////////////////////////////////////////////// GeomTransformer:: ~GeomTransformer() { + finish_collect(false); } //////////////////////////////////////////////////////////////////// @@ -580,9 +583,18 @@ reverse(GeomNode *node) { // Description: Collects together GeomVertexDatas from different // geoms into one big (or several big) GeomVertexDatas. // Returns the number of unique GeomVertexDatas created. +// +// If format_only is true, this only makes +// GeomVertexFormats compatible; it does not otherwise +// combine vertices. +// +// You should follow this up with a call to +// finish_collect(), but you probably don't want to call +// this method directly anyway. Call +// SceneGraphReducer::collect_vertex_data() instead. //////////////////////////////////////////////////////////////////// int GeomTransformer:: -collect_vertex_data(Geom *geom, int collect_bits) { +collect_vertex_data(Geom *geom, int collect_bits, bool format_only) { CPT(GeomVertexData) vdata = geom->get_vertex_data(); if (vdata->get_num_rows() > _max_collect_vertices) { // Don't even bother. @@ -609,245 +621,67 @@ collect_vertex_data(Geom *geom, int collect_bits) { key._animation_type = Geom::AT_none; } - AlreadyCollected::const_iterator ai; - ai = _already_collected.find(vdata); - if (ai != _already_collected.end()) { + AlreadyCollectedMap::const_iterator ai; + ai = _already_collected_map.find(vdata); + if (ai != _already_collected_map.end()) { // We've previously collected this vertex data; reuse it. const AlreadyCollectedData &acd = (*ai).second; - geom->offset_vertices(acd._data, acd._offset); + SourceGeom source_geom; + source_geom._geom = geom; + source_geom._vertex_offset = acd._vertex_offset; + acd._ncd->_source_geoms.push_back(source_geom); return 0; } - // We haven't collected this vertex data yet; append the vertices - // onto the new data. - int num_created = 0; - - NewCollectedData::iterator ni = _new_collected_data.find(key); - PT(GeomVertexData) new_data; - if (ni != _new_collected_data.end()) { - new_data = (*ni).second; + // We haven't collected this vertex data yet; associate it with a + // new data. + NewCollectedMap::iterator ni = _new_collected_map.find(key); + NewCollectedData *ncd; + if (ni != _new_collected_map.end()) { + ncd = (*ni).second; } else { // We haven't encountered a compatible GeomVertexData before. - // Create a new one. - new_data = new GeomVertexData(vdata->get_name(), format, - vdata->get_usage_hint()); - _new_collected_data[key] = new_data; - ++num_created; + // Create a new entry. + ncd = new NewCollectedData(vdata); + _new_collected_list.push_back(ncd); + _new_collected_map[key] = ncd; } - int offset = new_data->get_num_rows(); - int new_num_vertices = offset + vdata->get_num_rows(); - if (new_num_vertices > _max_collect_vertices) { + if (ncd->_new_format != format) { + ncd->_new_format = format->get_union_format(ncd->_new_format); + } + + int this_num_vertices = vdata->get_num_rows(); + + if (!format_only && + ncd->_num_vertices + this_num_vertices > _max_collect_vertices) { // Whoa, hold the phone! Too many vertices going into this one // GeomVertexData object; we'd better start over. - new_data = new GeomVertexData(vdata->get_name(), format, - vdata->get_usage_hint()); - _new_collected_data[key] = new_data; - offset = 0; - new_num_vertices = vdata->get_num_rows(); - ++num_created; + ncd = new NewCollectedData(vdata); + _new_collected_list.push_back(ncd); + _new_collected_map[key] = ncd; } - if (new_data->get_format() != format) { - // We're combining two GeomVertexDatas of different formats. - // Therefore, we need to expand the format to include the union of - // both sets of columns. - CPT(GeomVertexFormat) new_format = format->get_union_format(new_data->get_format()); - new_data->set_format(new_format); - nassertr(offset == new_data->get_num_rows(), 0); + int vertex_offset = ncd->_num_vertices; - // Also, convert (non-destructively) the current Geom's vertex - // data to the new format, so we can just blindly append the - // vertices to new_data, in the lines below. - vdata = vdata->convert_to(new_format); - format = new_format; - } + AlreadyCollectedData &acd = _already_collected_map[vdata]; + acd._ncd = ncd; + acd._vertex_offset = vertex_offset; - new_data->set_num_rows(new_num_vertices); + SourceGeom source_geom; + source_geom._geom = geom; + source_geom._vertex_offset = vertex_offset; + ncd->_source_geoms.push_back(source_geom); + + SourceData source_data; + source_data._vdata = vdata; + source_data._num_vertices = this_num_vertices; - for (int i = 0; i < vdata->get_num_arrays(); ++i) { - PT(GeomVertexArrayData) new_array = new_data->modify_array(i); - CPT(GeomVertexArrayData) old_array = vdata->get_array(i); - int stride = format->get_array(i)->get_stride(); - int start_byte = offset * stride; - int copy_bytes = old_array->get_data_size_bytes(); - nassertr(start_byte + copy_bytes == new_array->get_data_size_bytes(), 0); + ncd->_source_datas.push_back(source_data); + ncd->_num_vertices += this_num_vertices; - new_array->modify_handle()->copy_subdata_from - (start_byte, copy_bytes, - old_array->get_handle(), 0, copy_bytes); - } - - // Also, copy the animation data (if any). This means combining - // transform and/or slider tables, and might therefore mean - // remapping transform indices in the vertices. Each of these has a - // slightly different way to handle the remapping, because they have - // slightly different kinds of data. - typedef vector_int IndexMap; - - if (vdata->get_transform_table() != (TransformTable *)NULL || - new_data->get_transform_table() != (TransformTable *)NULL) { - // The TransformTable. - CPT(TransformTable) old_table; - if (vdata->get_transform_table() != (TransformTable *)NULL) { - old_table = vdata->get_transform_table(); - } else { - PT(TransformTable) temp_table = new TransformTable; - // There's an implicit identity transform for all nodes. - PT(VertexTransform) identity_transform = new UserVertexTransform("identity"); - temp_table->add_transform(identity_transform); - old_table = TransformTable::register_table(temp_table); - } - - // First, build a mapping of the transforms we already have in the - // current table. We must do this because the TransformTable - // doesn't automatically unquify index numbers for us (it doesn't - // store an index). - typedef pmap AddedTransforms; - AddedTransforms added_transforms; - - int num_old_transforms = old_table->get_num_transforms(); - for (int i = 0; i < num_old_transforms; i++) { - added_transforms[old_table->get_transform(i)] = i; - } - - // Now create a new table. We have to create a new table instead - // of modifying the existing one, since a registered - // TransformTable cannot be modified. - PT(TransformTable) new_table; - if (new_data->get_transform_table() != (TransformTable *)NULL) { - new_table = new TransformTable(*new_data->get_transform_table()); - } else { - new_table = new TransformTable; - } - - // Now walk through the old table and copy over its transforms. - // We will build up an IndexMap of old index numbers to new index - // numbers while we go, which we can use to modify the vertices. - IndexMap transform_map; - - int num_transforms = old_table->get_num_transforms(); - transform_map.reserve(num_transforms); - for (int ti = 0; ti < num_transforms; ++ti) { - const VertexTransform *transform = old_table->get_transform(ti); - AddedTransforms::iterator ai = added_transforms.find(transform); - if (ai != added_transforms.end()) { - // Already got this one in the table. - transform_map.push_back((*ai).second); - } else { - // This is a new one. - int tj = new_table->add_transform(transform); - transform_map.push_back(tj); - added_transforms[transform] = tj; - } - } - new_data->set_transform_table(TransformTable::register_table(new_table)); - - // And now modify the vertices to update the indices to their new - // values in the new table. This requires a nested loop, since - // each column of transform_index might define multiple index - // values. - GeomVertexRewriter index(new_data, InternalName::get_transform_index()); - if (index.has_column()) { - int num_values = index.get_column()->get_num_values(); - int new_index[4]; - - index.set_row(offset); - while (!index.is_at_end()) { - const int *orig_index = index.get_data4i(); - for (int i = 0; i < num_values; i++) { - nassertr(orig_index[i] >= 0 && orig_index[i] < (int)transform_map.size(), 0); - new_index[i] = transform_map[orig_index[i]]; - } - index.set_data4i(new_index); - } - } - } - - if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL || - new_data->get_transform_blend_table() != (TransformBlendTable *)NULL) { - // The TransformBlendTable. This one is the easiest, because we - // can modify it directly, and it will uniquify blend objects for - // us. - - CPT(TransformBlendTable) old_btable; - if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL) { - old_btable = vdata->get_transform_blend_table(); - } else { - PT(TransformBlendTable) temp_btable; - temp_btable = new TransformBlendTable; - temp_btable->add_blend(TransformBlend()); - old_btable = temp_btable; - } - - PT(TransformBlendTable) new_btable; - if (new_data->get_transform_blend_table() != (TransformBlendTable *)NULL) { - new_btable = new_data->modify_transform_blend_table(); - } else { - new_btable = new TransformBlendTable; - new_btable->add_blend(TransformBlend()); - new_data->set_transform_blend_table(new_btable); - } - - SparseArray new_rows = old_btable->get_rows(); - new_rows <<= offset; - new_rows |= new_btable->get_rows(); - new_btable->set_rows(new_rows); - - // We still need to build up the IndexMap. - IndexMap blend_map; - - int num_blends = old_btable->get_num_blends(); - blend_map.reserve(num_blends); - for (int bi = 0; bi < num_blends; ++bi) { - int bj = new_btable->add_blend(old_btable->get_blend(bi)); - blend_map.push_back(bj); - } - - // Modify the indices. This is simpler than the transform_index, - // above, because each column of transform_blend may only define - // one index value. - GeomVertexRewriter index(new_data, InternalName::get_transform_blend()); - if (index.has_column()) { - index.set_row(offset); - while (!index.is_at_end()) { - int orig_index = index.get_data1i(); - nassertr(orig_index >= 0 && orig_index < (int)blend_map.size(), 0); - int new_index = blend_map[orig_index]; - index.set_data1i(new_index); - } - } - } - - if (vdata->get_slider_table() != (SliderTable *)NULL) { - // The SliderTable. This one requires making a copy, like the - // TransformTable (since it can't be modified once registered - // either), but at least it uniquifies sliders added to it. Also, - // it doesn't require indexing into it, so we don't have to build - // an IndexMap to modify the vertices with. - const SliderTable *old_sliders = vdata->get_slider_table(); - PT(SliderTable) new_sliders; - if (new_data->get_slider_table() != (SliderTable *)NULL) { - new_sliders = new SliderTable(*new_data->get_slider_table()); - } else { - new_sliders = new SliderTable; - } - int num_sliders = old_sliders->get_num_sliders(); - for (int si = 0; si < num_sliders; ++si) { - SparseArray new_rows = old_sliders->get_slider_rows(si); - new_rows <<= offset; - new_sliders->add_slider(old_sliders->get_slider(si), new_rows); - } - new_data->set_slider_table(SliderTable::register_table(new_sliders)); - } - - AlreadyCollectedData &acd = _already_collected[vdata]; - acd._data = new_data; - acd._offset = offset; - geom->offset_vertices(new_data, offset); - - return num_created; + return 0; } @@ -858,11 +692,19 @@ collect_vertex_data(Geom *geom, int collect_bits) { // structures that share the same format into one big // GeomVertexData structure. This is intended to // minimize context switches on the graphics card. +// +// If format_only is true, this only makes +// GeomVertexFormats compatible; it does not otherwise +// combine vertices. +// +// You should follow this up with a call to +// finish_collect(), but you probably don't want to call +// this method directly anyway. Call +// SceneGraphReducer::collect_vertex_data() instead. //////////////////////////////////////////////////////////////////// int GeomTransformer:: -collect_vertex_data(GeomNode *node, int collect_bits) { - int num_created = 0; - +collect_vertex_data(GeomNode *node, int collect_bits, bool format_only) { + int num_adjusted = 0; GeomTransformer *dynamic = NULL; GeomNode::CDWriter cdata(node->_cycler); @@ -880,18 +722,57 @@ collect_vertex_data(GeomNode *node, int collect_bits) { if (dynamic == (GeomTransformer *)NULL) { dynamic = new GeomTransformer(*this); } - num_created += dynamic->collect_vertex_data(new_geom, collect_bits); + num_adjusted += dynamic->collect_vertex_data(new_geom, collect_bits, format_only); } else { - num_created += collect_vertex_data(new_geom, collect_bits); + num_adjusted += collect_vertex_data(new_geom, collect_bits, format_only); } } if (dynamic != (GeomTransformer *)NULL) { + num_adjusted += dynamic->finish_collect(format_only); delete dynamic; } - return num_created; + return num_adjusted; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::finish_collect +// Access: Public +// Description: This should be called after a call to +// collect_vertex_data() to finalize the changes and +// apply them to the vertices in the graph. If this is +// not called, it will be called automatically by the +// GeomTransformer destructor. +// +// If format_only is true, this returns the number of +// GeomVertexDatas modified to use a new format. If +// false, it returns the number of GeomVertexDatas +// created. +//////////////////////////////////////////////////////////////////// +int GeomTransformer:: +finish_collect(bool format_only) { + int num_adjusted = 0; + + NewCollectedList::iterator nci; + for (nci = _new_collected_list.begin(); + nci != _new_collected_list.end(); + ++nci) { + NewCollectedData *ncd = (*nci); + if (format_only) { + num_adjusted += ncd->apply_format_only_changes(); + } else { + num_adjusted += ncd->apply_collect_changes(); + } + delete ncd; + } + + _new_collected_list.clear(); + _new_collected_map.clear(); + _already_collected_map.clear(); + + return num_adjusted; } //////////////////////////////////////////////////////////////////// @@ -920,3 +801,303 @@ premunge_geom(const Geom *geom, GeomMunger *munger) { return geom_copy; } + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::NewCollectedData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomTransformer::NewCollectedData:: +NewCollectedData(const GeomVertexData *source_data) { + _new_format = source_data->get_format(); + _vdata_name = source_data->get_name(); + _usage_hint = source_data->get_usage_hint(); + _num_vertices = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::NewCollectedData::apply_format_only_changes +// Access: Public +// Description: Actually adjusts the GeomVertexDatas found in a +// collect_vertex_data() format-only call to have the +// same vertex format. Returns the number of vdatas +// modified. +//////////////////////////////////////////////////////////////////// +int GeomTransformer::NewCollectedData:: +apply_format_only_changes() { + int num_modified = 0; + + // We probably don't need to use a map, since + // GeomVertexData::convert_to() already caches its result, but we do + // it anyway just in case there's danger of overflowing the cache. + // What the heck, it's easy to do. + typedef pmap< CPT(GeomVertexData), CPT(GeomVertexData) > VDataMap; + VDataMap vdata_map; + + SourceGeoms::iterator sgi; + for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) { + SourceGeom &sg = (*sgi); + CPT(GeomVertexData) orig_data = sg._geom->get_vertex_data(); + + if (orig_data->get_format() != _new_format) { + VDataMap::iterator mi = vdata_map.find(orig_data); + if (mi != vdata_map.end()) { + // Already modified this vdata. + sg._geom->set_vertex_data((*mi).second); + + } else { + // Modify this vdata to the new format. + CPT(GeomVertexData) new_data = orig_data->convert_to(_new_format); + vdata_map[orig_data] = new_data; + ++num_modified; + + sg._geom->set_vertex_data(new_data); + } + } + } + + return num_modified; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::NewCollectedData::apply_collect_changes +// Access: Public +// Description: Actually combines all of the vertex datas found in a +// previous call to collect_vertex_data(). +//////////////////////////////////////////////////////////////////// +int GeomTransformer::NewCollectedData:: +apply_collect_changes() { + if (_num_vertices == 0) { + return 0; + } + + _new_data = + new GeomVertexData(_vdata_name, _new_format, _usage_hint); + + _new_data->unclean_set_num_rows(_num_vertices); + + // Copy each source data into the new GeomVertexData, one at a time. + int vertex_offset = 0; + SourceDatas::iterator sdi; + for (sdi = _source_datas.begin(); sdi != _source_datas.end(); ++sdi) { + SourceData &sd = (*sdi); + CPT(GeomVertexData) vdata = sd._vdata; + + if (_new_format != vdata->get_format()) { + // Convert (non-destructively) the current Geom's vertex + // data to the new format, so we can just blindly append the + // vertices to _new_data, within append_vdata(). + vdata = vdata->convert_to(_new_format); + } + + append_vdata(vdata, vertex_offset); + vertex_offset += sd._num_vertices; + } + + nassertr(vertex_offset == _num_vertices, 0); + + if (_new_btable != (TransformBlendTable *)NULL) { + _new_btable->set_rows(_new_btable_rows); + _new_data->set_transform_blend_table(_new_btable); + } + + update_geoms(); + + _new_data.clear(); + _new_btable.clear(); + _new_btable_rows.clear(); + + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::NewCollectedData::append_vdata +// Access: Public +// Description: Appends the vertices from the indicated source +// GeomVertexData to the end of the working data. +//////////////////////////////////////////////////////////////////// +void GeomTransformer::NewCollectedData:: +append_vdata(const GeomVertexData *vdata, int vertex_offset) { + for (int i = 0; i < vdata->get_num_arrays(); ++i) { + PT(GeomVertexArrayData) new_array = _new_data->modify_array(i); + CPT(GeomVertexArrayData) old_array = vdata->get_array(i); + int stride = _new_format->get_array(i)->get_stride(); + int start_byte = vertex_offset * stride; + int copy_bytes = old_array->get_data_size_bytes(); + nassertv(start_byte + copy_bytes <= new_array->get_data_size_bytes()); + + new_array->modify_handle()->copy_subdata_from + (start_byte, copy_bytes, + old_array->get_handle(), 0, copy_bytes); + } + + // Also, copy the animation data (if any). This means combining + // transform and/or slider tables, and might therefore mean + // remapping transform indices in the vertices. Each of these has a + // slightly different way to handle the remapping, because they have + // slightly different kinds of data. + + if (vdata->get_transform_table() != (TransformTable *)NULL || + _new_data->get_transform_table() != (TransformTable *)NULL) { + // The TransformTable. + CPT(TransformTable) old_table; + if (vdata->get_transform_table() != (TransformTable *)NULL) { + old_table = vdata->get_transform_table(); + } else { + PT(TransformTable) temp_table = new TransformTable; + // There's an implicit identity transform for all nodes. + PT(VertexTransform) identity_transform = new UserVertexTransform("identity"); + temp_table->add_transform(identity_transform); + old_table = TransformTable::register_table(temp_table); + } + + // First, build a mapping of the transforms we already have in the + // current table. We must do this because the TransformTable + // doesn't automatically unquify index numbers for us (it doesn't + // store an index). + typedef pmap AddedTransforms; + AddedTransforms added_transforms; + + int num_old_transforms = old_table->get_num_transforms(); + for (int i = 0; i < num_old_transforms; i++) { + added_transforms[old_table->get_transform(i)] = i; + } + + // Now create a new table. We have to create a new table instead + // of modifying the existing one, since a registered + // TransformTable cannot be modified. + PT(TransformTable) new_table; + if (_new_data->get_transform_table() != (TransformTable *)NULL) { + new_table = new TransformTable(*_new_data->get_transform_table()); + } else { + new_table = new TransformTable; + } + + // Now walk through the old table and copy over its transforms. + // We will build up an IndexMap of old index numbers to new index + // numbers while we go, which we can use to modify the vertices. + IndexMap transform_map; + + int num_transforms = old_table->get_num_transforms(); + transform_map.reserve(num_transforms); + for (int ti = 0; ti < num_transforms; ++ti) { + const VertexTransform *transform = old_table->get_transform(ti); + AddedTransforms::iterator ai = added_transforms.find(transform); + if (ai != added_transforms.end()) { + // Already got this one in the table. + transform_map.push_back((*ai).second); + } else { + // This is a new one. + int tj = new_table->add_transform(transform); + transform_map.push_back(tj); + added_transforms[transform] = tj; + } + } + _new_data->set_transform_table(TransformTable::register_table(new_table)); + + // And now modify the vertices to update the indices to their new + // values in the new table. This requires a nested loop, since + // each column of transform_index might define multiple index + // values. + GeomVertexRewriter index(_new_data, InternalName::get_transform_index()); + if (index.has_column()) { + int num_values = index.get_column()->get_num_values(); + int num_rows = vdata->get_num_rows(); + int new_index[4]; + + index.set_row(vertex_offset); + for (int ci = 0; ci < num_rows; ++ci) { + const int *orig_index = index.get_data4i(); + for (int i = 0; i < num_values; i++) { + nassertv(orig_index[i] >= 0 && orig_index[i] < (int)transform_map.size()); + new_index[i] = transform_map[orig_index[i]]; + } + index.set_data4i(new_index); + } + } + } + + if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL) { + // The TransformBlendTable. This one is the easiest, because we + // can modify it directly, and it will uniquify blend objects for + // us. + + // We have few special optimizations to handle the + // TransformBlendTable, since it's a very common case and + // therefore worth spending a bit of effort to optimize deeply. + + CPT(TransformBlendTable) old_btable = vdata->get_transform_blend_table(); + + if (_new_btable == (TransformBlendTable *)NULL) { + _new_btable = new TransformBlendTable; + _new_btable->add_blend(TransformBlend()); + } + + SparseArray new_rows = old_btable->get_rows(); + new_rows <<= vertex_offset; + _new_btable_rows |= new_rows; + + // We still need to build up the IndexMap. + IndexMap blend_map; + + int num_blends = old_btable->get_num_blends(); + blend_map.reserve(num_blends); + for (int bi = 0; bi < num_blends; ++bi) { + int bj = _new_btable->add_blend(old_btable->get_blend(bi)); + blend_map.push_back(bj); + } + + // Modify the indices. This is simpler than the transform_index, + // above, because each column of transform_blend may only define + // one index value. + GeomVertexRewriter index(_new_data, InternalName::get_transform_blend()); + if (index.has_column()) { + int num_rows = vdata->get_num_rows(); + index.set_row(vertex_offset); + + for (int ci = 0; ci < num_rows; ++ci) { + int orig_index = index.get_data1i(); + nassertv(orig_index >= 0 && orig_index < (int)blend_map.size()); + int new_index = blend_map[orig_index]; + index.set_data1i(new_index); + } + } + } + + if (vdata->get_slider_table() != (SliderTable *)NULL) { + // The SliderTable. This one requires making a copy, like the + // TransformTable (since it can't be modified once registered + // either), but at least it uniquifies sliders added to it. Also, + // it doesn't require indexing into it, so we don't have to build + // an IndexMap to modify the vertices with. + const SliderTable *old_sliders = vdata->get_slider_table(); + PT(SliderTable) new_sliders; + if (_new_data->get_slider_table() != (SliderTable *)NULL) { + new_sliders = new SliderTable(*_new_data->get_slider_table()); + } else { + new_sliders = new SliderTable; + } + int num_sliders = old_sliders->get_num_sliders(); + for (int si = 0; si < num_sliders; ++si) { + SparseArray new_rows = old_sliders->get_slider_rows(si); + new_rows <<= vertex_offset; + new_sliders->add_slider(old_sliders->get_slider(si), new_rows); + } + _new_data->set_slider_table(SliderTable::register_table(new_sliders)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::NewCollectedData::update_geoms +// Access: Public +// Description: Updates all of the source Geoms to reference the new +// vertex data. +//////////////////////////////////////////////////////////////////// +void GeomTransformer::NewCollectedData:: +update_geoms() { + SourceGeoms::iterator sgi; + for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) { + SourceGeom &sg = (*sgi); + sg._geom->offset_vertices(_new_data, sg._vertex_offset); + } +} diff --git a/panda/src/pgraph/geomTransformer.h b/panda/src/pgraph/geomTransformer.h index de3acb454f..6ae2ec02b5 100644 --- a/panda/src/pgraph/geomTransformer.h +++ b/panda/src/pgraph/geomTransformer.h @@ -79,8 +79,9 @@ public: bool doubleside(GeomNode *node); bool reverse(GeomNode *node); - int collect_vertex_data(Geom *geom, int collect_bits); - int collect_vertex_data(GeomNode *node, int collect_bits); + int collect_vertex_data(Geom *geom, int collect_bits, bool format_only); + int collect_vertex_data(GeomNode *node, int collect_bits, bool format_only); + int finish_collect(bool format_only); PT(Geom) premunge_geom(const Geom *geom, GeomMunger *munger); @@ -138,14 +139,6 @@ private: typedef pmap ReversedNormals; ReversedNormals _reversed_normals; - class AlreadyCollectedData { - public: - CPT(GeomVertexData) _data; - int _offset; - }; - typedef pmap< CPT(GeomVertexData), AlreadyCollectedData> AlreadyCollected; - AlreadyCollected _already_collected; - class NewCollectedKey { public: INLINE bool operator < (const NewCollectedKey &other) const; @@ -155,14 +148,81 @@ private: Geom::UsageHint _usage_hint; Geom::AnimationType _animation_type; }; - typedef pmap< NewCollectedKey, PT(GeomVertexData) > NewCollectedData; - NewCollectedData _new_collected_data; + + class SourceData { + public: + const GeomVertexData *_vdata; + int _num_vertices; + }; + typedef pvector SourceDatas; + class SourceGeom { + public: + Geom *_geom; + int _vertex_offset; + }; + typedef pvector SourceGeoms; + class NewCollectedData { + public: + ALLOC_DELETED_CHAIN(NewCollectedData); + + NewCollectedData(const GeomVertexData *source_data); + void add_source_data(const GeomVertexData *source_data); + int apply_format_only_changes(); + int apply_collect_changes(); + + CPT(GeomVertexFormat) _new_format; + string _vdata_name; + GeomEnums::UsageHint _usage_hint; + SourceDatas _source_datas; + SourceGeoms _source_geoms; + int _num_vertices; + + private: + // These are used just during apply_changes(). + void append_vdata(const GeomVertexData *vdata, int vertex_offset); + void update_geoms(); + + typedef vector_int IndexMap; + + PT(GeomVertexData) _new_data; + PT(TransformBlendTable) _new_btable; + SparseArray _new_btable_rows; + + // We need a TypeHandle just for ALLOC_DELETED_CHAIN. + public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "GeomTransformer::NewCollectedData"); + } + + private: + static TypeHandle _type_handle; + }; + typedef pvector NewCollectedList; + typedef pmap NewCollectedMap; + NewCollectedList _new_collected_list; + NewCollectedMap _new_collected_map; + + class AlreadyCollectedData { + public: + NewCollectedData *_ncd; + int _vertex_offset; + }; + typedef pmap AlreadyCollectedMap; + AlreadyCollectedMap _already_collected_map; static PStatCollector _apply_vertex_collector; static PStatCollector _apply_texcoord_collector; static PStatCollector _apply_set_color_collector; static PStatCollector _apply_scale_color_collector; static PStatCollector _apply_set_format_collector; + +public: + static void init_type() { + NewCollectedData::init_type(); + } }; #include "geomTransformer.I" diff --git a/panda/src/pgraph/sceneGraphAnalyzer.I b/panda/src/pgraph/sceneGraphAnalyzer.I index ed5d4a74e0..044988cc81 100644 --- a/panda/src/pgraph/sceneGraphAnalyzer.I +++ b/panda/src/pgraph/sceneGraphAnalyzer.I @@ -117,6 +117,16 @@ get_num_geom_vertex_datas() const { return _num_geom_vertex_datas; } +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::get_num_geom_vertex_formats +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +int SceneGraphAnalyzer:: +get_num_geom_vertex_formats() const { + return _num_geom_vertex_formats; +} + //////////////////////////////////////////////////////////////////// // Function: SceneGraphAnalyzer::get_vertex_data_size // Access: Published diff --git a/panda/src/pgraph/sceneGraphAnalyzer.cxx b/panda/src/pgraph/sceneGraphAnalyzer.cxx index 32e159eecd..4e0b742eac 100644 --- a/panda/src/pgraph/sceneGraphAnalyzer.cxx +++ b/panda/src/pgraph/sceneGraphAnalyzer.cxx @@ -65,6 +65,7 @@ void SceneGraphAnalyzer:: clear() { _nodes.clear(); _vdatas.clear(); + _vformats.clear(); _vadatas.clear(); _unique_vdatas.clear(); _unique_vadatas.clear(); @@ -78,6 +79,7 @@ clear() { _num_geom_nodes = 0; _num_geoms = 0; _num_geom_vertex_datas = 0; + _num_geom_vertex_formats = 0; _vertex_data_size = 0; _prim_data_size = 0; @@ -138,7 +140,9 @@ write(ostream &out, int indent_level) const { indent(out, indent_level) << _num_geoms << " Geoms, with " << _num_geom_vertex_datas - << " GeomVertexDatas, appear on " << _num_geom_nodes << " GeomNodes.\n"; + << " GeomVertexDatas and " << _num_geom_vertex_formats + << " GeomVertexFormats, appear on " << _num_geom_nodes + << " GeomNodes.\n"; indent(out, indent_level) << _num_vertices << " vertices, " << _num_normals << " normals, " @@ -373,6 +377,13 @@ collect_statistics(const Geom *geom) { if (result.second) { // This is the first time we've encountered this vertex data. ++_num_geom_vertex_datas; + + CPT(GeomVertexFormat) vformat = vdata->get_format(); + bool format_inserted = _vformats.insert(vformat).second; + if (format_inserted) { + // This is the first time we've encountered this vertex format. + ++_num_geom_vertex_formats; + } int &dup_count = (*(_unique_vdatas.insert(UniqueVDatas::value_type(vdata, 0)).first)).second; ++dup_count; diff --git a/panda/src/pgraph/sceneGraphAnalyzer.h b/panda/src/pgraph/sceneGraphAnalyzer.h index efa526e62b..29114e9c82 100644 --- a/panda/src/pgraph/sceneGraphAnalyzer.h +++ b/panda/src/pgraph/sceneGraphAnalyzer.h @@ -67,6 +67,7 @@ PUBLISHED: INLINE int get_num_geom_nodes() const; INLINE int get_num_geoms() const; INLINE int get_num_geom_vertex_datas() const; + INLINE int get_num_geom_vertex_formats() const; INLINE int get_vertex_data_size() const; INLINE int get_num_vertices() const; @@ -104,6 +105,7 @@ private: typedef pmap Nodes; typedef pmap VDatas; + typedef pset VFormats; typedef pset VADatas; typedef pmap > UniqueVDatas; typedef pmap > UniqueVADatas; @@ -113,6 +115,7 @@ private: Nodes _nodes; VDatas _vdatas; + VFormats _vformats; VADatas _vadatas; VADatas _prim_vadatas; UniqueVDatas _unique_vdatas; @@ -129,6 +132,7 @@ private: int _num_geom_nodes; int _num_geoms; int _num_geom_vertex_datas; + int _num_geom_vertex_formats; size_t _vertex_data_size; size_t _prim_data_size; diff --git a/panda/src/pgraph/sceneGraphReducer.I b/panda/src/pgraph/sceneGraphReducer.I index 49782b6171..1381639086 100644 --- a/panda/src/pgraph/sceneGraphReducer.I +++ b/panda/src/pgraph/sceneGraphReducer.I @@ -121,6 +121,37 @@ apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs, r_apply_attribs(node, attribs, attrib_types, transformer); } +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::make_compatible_format +// Access: Published +// Description: Walks through the tree at this node and below and +// unifies the GeomVertexFormat for any GeomVertexData +// objects that are found, so that all eligible vdatas +// (according to collect_bits; see collect_vertex_data) +// will share the same vertex format. +// +// This will add unused columns where necessary to match +// formats. It can result in suboptimal performance if +// used needlessly. +// +// There is usually no reason to call this explicitly, +// since collect_vertex_data() will do this anyway if it +// has not been done already. However, calling it ahead +// of time can make that future call to +// collect_vertex_data() run a little bit faster. +// +// The return value is the number of vertex datas +// modified. +//////////////////////////////////////////////////////////////////// +INLINE int SceneGraphReducer:: +make_compatible_format(PandaNode *root, int collect_bits) { + PStatTimer timer(_collect_collector); + int count = 0; + count += r_collect_vertex_data(root, collect_bits, _transformer, true); + count += _transformer.finish_collect(true); + return count; +} + //////////////////////////////////////////////////////////////////// // Function: SceneGraphReducer::collect_vertex_data // Access: Published @@ -140,7 +171,10 @@ apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs, INLINE int SceneGraphReducer:: collect_vertex_data(PandaNode *root, int collect_bits) { PStatTimer timer(_collect_collector); - return r_collect_vertex_data(root, collect_bits, _transformer); + int count = 0; + count += r_collect_vertex_data(root, collect_bits, _transformer, false); + count += _transformer.finish_collect(false); + return count; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/sceneGraphReducer.cxx b/panda/src/pgraph/sceneGraphReducer.cxx index ca13facb3d..edb6fcd0e0 100644 --- a/panda/src/pgraph/sceneGraphReducer.cxx +++ b/panda/src/pgraph/sceneGraphReducer.cxx @@ -726,8 +726,8 @@ r_remove_column(PandaNode *node, const InternalName *column, //////////////////////////////////////////////////////////////////// int SceneGraphReducer:: r_collect_vertex_data(PandaNode *node, int collect_bits, - GeomTransformer &transformer) { - int num_created = 0; + GeomTransformer &transformer, bool format_only) { + int num_adjusted = 0; int this_node_bits = 0; if (node->is_of_type(ModelNode::get_class_type())) { @@ -746,37 +746,35 @@ r_collect_vertex_data(PandaNode *node, int collect_bits, if (node->is_geom_node()) { // When we come to a geom node, collect. - if (new_transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits)) { - ++num_created; - } + num_adjusted += new_transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits, format_only); } PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - num_created += - r_collect_vertex_data(children.get_child(i), collect_bits, new_transformer); + num_adjusted += + r_collect_vertex_data(children.get_child(i), collect_bits, new_transformer, format_only); } + num_adjusted += new_transformer.finish_collect(format_only); + } else { // Keep the same collection. if (node->is_geom_node()) { - if (transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits)) { - ++num_created; - } + num_adjusted += transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits, format_only); } PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - num_created += - r_collect_vertex_data(children.get_child(i), collect_bits, transformer); + num_adjusted += + r_collect_vertex_data(children.get_child(i), collect_bits, transformer, format_only); } } Thread::consider_yield(); - return num_created; + return num_adjusted; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/sceneGraphReducer.h b/panda/src/pgraph/sceneGraphReducer.h index 2177d58371..7667083e9c 100644 --- a/panda/src/pgraph/sceneGraphReducer.h +++ b/panda/src/pgraph/sceneGraphReducer.h @@ -138,6 +138,7 @@ PUBLISHED: int remove_column(PandaNode *root, const InternalName *column); + INLINE int make_compatible_format(PandaNode *root, int collect_bits = ~0); INLINE int collect_vertex_data(PandaNode *root, int collect_bits = ~0); INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0); void unify(PandaNode *root, bool preserve_order); @@ -173,7 +174,7 @@ protected: GeomTransformer &transformer); int r_collect_vertex_data(PandaNode *node, int collect_bits, - GeomTransformer &transformer); + GeomTransformer &transformer, bool format_only); int r_make_nonindexed(PandaNode *node, int collect_bits); void r_unify(PandaNode *node, int max_indices, bool preserve_order);