diff --git a/panda/src/egg/eggVertexPool.cxx b/panda/src/egg/eggVertexPool.cxx index 84869fa27e..a3d35cb1d9 100644 --- a/panda/src/egg/eggVertexPool.cxx +++ b/panda/src/egg/eggVertexPool.cxx @@ -800,6 +800,53 @@ transform(const LMatrix4d &mat) { } +// A function object for sort_by_external_index(), below. +class SortByExternalIndex { +public: + bool operator () (EggVertex *a, EggVertex *b) const { + int ai = a->get_external_index(); + int bi = b->get_external_index(); + if (ai != bi) { + return ai < bi; + } + return a->get_index() < b->get_index(); + } +}; + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::sort_by_external_index +// Access: Published +// Description: Re-orders (and re-numbers) the vertices in this +// vertex pool so that they appear in increasing order +// by the optional external_index that has been assigned +// to each vertex. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +sort_by_external_index() { + // Copy the vertices into a vector for sorting. + typedef pvector SortedVertices; + SortedVertices sorted_vertices; + sorted_vertices.reserve(size()); + iterator i; + for (i = begin(); i != end(); ++i) { + sorted_vertices.push_back(*i); + } + + ::sort(sorted_vertices.begin(), sorted_vertices.end(), SortByExternalIndex()); + + // Now reassign the indices, and copy them into a new index map. + IndexVertices new_index_vertices; + int vi; + for (vi = 0; vi < (int)sorted_vertices.size(); ++vi) { + EggVertex *vertex = sorted_vertices[vi]; + vertex->_index = vi; + new_index_vertices[vi] = vertex; + } + + // Finally, assign the new index map. + _index_vertices.swap(new_index_vertices); +} + //////////////////////////////////////////////////////////////////// // Function: EggVertexPool::write // Access: Public diff --git a/panda/src/egg/eggVertexPool.h b/panda/src/egg/eggVertexPool.h index 7a5c6b1f17..8fc34f83fb 100644 --- a/panda/src/egg/eggVertexPool.h +++ b/panda/src/egg/eggVertexPool.h @@ -133,6 +133,7 @@ PUBLISHED: void add_unused_vertices_to_prim(EggPrimitive *prim); void transform(const LMatrix4d &mat); + void sort_by_external_index(); void write(ostream &out, int indent_level) const; diff --git a/panda/src/egg2pg/config_egg2pg.cxx b/panda/src/egg2pg/config_egg2pg.cxx index a134333f70..5cf3fa03d1 100644 --- a/panda/src/egg2pg/config_egg2pg.cxx +++ b/panda/src/egg2pg/config_egg2pg.cxx @@ -170,13 +170,20 @@ ConfigVariableBool egg_preload_simple_textures "either this or preload-simple-textures is true.")); ConfigVariableDouble egg_vertex_membership_quantize -("egg-vertex-membership-quantize", 0.01, +("egg-vertex-membership-quantize", 0.1, PRC_DESC("Specifies the nearest amount to round each vertex joint " "membership value when loading an egg file. This affects animated " "egg files only. There is a substantial runtime " "performance advantage for reducing trivial differences in joint " "membership. Set this to 0 to leave joint membership as it is.")); +ConfigVariableInt egg_vertex_max_num_joints +("egg-vertex-max-num-joints", 4, + PRC_DESC("Specifies the maximum number of distinct joints that are allowed " + "to control any one vertex. If a vertex requests assignment to " + "more than this number of joints, the joints with the lesser membership " + "value are ignored. Set this to -1 to allow any number of joints.")); + ConfigureFn(config_egg2pg) { init_libegg2pg(); } diff --git a/panda/src/egg2pg/config_egg2pg.h b/panda/src/egg2pg/config_egg2pg.h index 8f3358a04a..62fe95ce22 100644 --- a/panda/src/egg2pg/config_egg2pg.h +++ b/panda/src/egg2pg/config_egg2pg.h @@ -53,6 +53,7 @@ extern EXPCL_PANDAEGG ConfigVariableInt egg_max_indices; extern EXPCL_PANDAEGG ConfigVariableBool egg_emulate_bface; extern EXPCL_PANDAEGG ConfigVariableBool egg_preload_simple_textures; extern EXPCL_PANDAEGG ConfigVariableDouble egg_vertex_membership_quantize; +extern EXPCL_PANDAEGG ConfigVariableInt egg_vertex_max_num_joints; extern EXPCL_PANDAEGG void init_libegg2pg(); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 75ad3dca7e..d3be30fe03 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -379,6 +379,20 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform, // pools as if they contain a combination of multiple colors. has_overall_color = false; } + + PT(TransformBlendTable) blend_table; + if (is_dynamic) { + // Dynamic vertex pools will require a TransformBlendTable to + // indicate how the vertices are to be animated. + blend_table = make_blend_table(vertex_pool, egg_bin, character_maker); + + // Now that we've created the blend table, we can re-order the + // vertices in the pool to efficiently group vertices together + // that will share the same transform matrix. (We have to + // re-order them before we create primitives, below, because + // this will change the vertex index numbers.) + vertex_pool->sort_by_external_index(); + } // Create a handful of GeomPrimitives corresponding to the various // types of primitives that reference this vertex pool. @@ -400,10 +414,10 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform, } else { mat = egg_bin->get_vertex_to_node(); } - + // Now convert this vertex pool to a GeomVertexData. PT(GeomVertexData) vertex_data = - make_vertex_data(render_state, vertex_pool, egg_bin, mat, + make_vertex_data(render_state, vertex_pool, egg_bin, mat, blend_table, is_dynamic, character_maker, has_overall_color); nassertv(vertex_data != (GeomVertexData *)NULL); @@ -2185,7 +2199,7 @@ check_for_polysets(EggGroup *egg_group, bool &all_polysets, bool &any_hidden) { PT(GeomVertexData) EggLoader:: make_vertex_data(const EggRenderState *render_state, EggVertexPool *vertex_pool, EggNode *primitive_home, - const LMatrix4d &transform, + const LMatrix4d &transform, TransformBlendTable *blend_table, bool is_dynamic, CharacterMaker *character_maker, bool ignore_color) { VertexPoolTransform vpt; @@ -2255,7 +2269,6 @@ make_vertex_data(const EggRenderState *render_state, PT(GeomVertexFormat) temp_format = new GeomVertexFormat(array_format); - PT(TransformBlendTable) blend_table; PT(SliderTable) slider_table; string name = _data->get_egg_filename().get_basename_wo_extension(); @@ -2271,9 +2284,6 @@ make_vertex_data(const EggRenderState *render_state, animation.set_panda(); temp_format->set_animation(animation); - blend_table = new TransformBlendTable; - blend_table->set_rows(SparseArray::lower_on(vertex_pool->size())); - PT(GeomVertexArrayFormat) anim_array_format = new GeomVertexArrayFormat; anim_array_format->add_column (InternalName::get_transform_blend(), 1, @@ -2477,34 +2487,7 @@ make_vertex_data(const EggRenderState *render_state, } if (is_dynamic) { - // Figure out the transforms affecting this particular vertex. - TransformBlend blend; - if (vertex->gref_size() == 0) { - // If the vertex has no explicit membership, it belongs right - // where it is. - PT(VertexTransform) vt = character_maker->egg_to_transform(primitive_home); - nassertr(vt != (VertexTransform *)NULL, vertex_data); - blend.add_transform(vt, 1.0f); - } else { - // If the vertex does have an explicit membership, ignore its - // parentage and assign it where it wants to be. - double quantize = egg_vertex_membership_quantize; - EggVertex::GroupRef::const_iterator gri; - for (gri = vertex->gref_begin(); gri != vertex->gref_end(); ++gri) { - EggGroup *egg_joint = (*gri); - double membership = egg_joint->get_vertex_membership(vertex); - if (quantize != 0.0) { - membership = cfloor(membership / quantize + 0.5) * quantize; - } - - PT(VertexTransform) vt = character_maker->egg_to_transform(egg_joint); - nassertr(vt != (VertexTransform *)NULL, vertex_data); - blend.add_transform(vt, membership); - } - } - blend.normalize_weights(); - - int table_index = blend_table->add_blend(blend); + int table_index = vertex->get_external_index(); gvw.set_column(InternalName::get_transform_blend()); gvw.set_data1i(table_index); } @@ -2518,6 +2501,62 @@ make_vertex_data(const EggRenderState *render_state, return vertex_data; } +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_blend_table +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PT(TransformBlendTable) EggLoader:: +make_blend_table(EggVertexPool *vertex_pool, EggNode *primitive_home, + CharacterMaker *character_maker) { + PT(TransformBlendTable) blend_table; + blend_table = new TransformBlendTable; + blend_table->set_rows(SparseArray::lower_on(vertex_pool->size())); + + EggVertexPool::const_iterator vi; + for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) { + EggVertex *vertex = (*vi); + + // Figure out the transforms affecting this particular vertex. + TransformBlend blend; + if (vertex->gref_size() == 0) { + // If the vertex has no explicit membership, it belongs right + // where it is. + PT(VertexTransform) vt = character_maker->egg_to_transform(primitive_home); + nassertr(vt != (VertexTransform *)NULL, NULL); + blend.add_transform(vt, 1.0f); + } else { + // If the vertex does have an explicit membership, ignore its + // parentage and assign it where it wants to be. + double quantize = egg_vertex_membership_quantize; + EggVertex::GroupRef::const_iterator gri; + for (gri = vertex->gref_begin(); gri != vertex->gref_end(); ++gri) { + EggGroup *egg_joint = (*gri); + double membership = egg_joint->get_vertex_membership(vertex); + if (quantize != 0.0) { + membership = cfloor(membership / quantize + 0.5) * quantize; + } + + PT(VertexTransform) vt = character_maker->egg_to_transform(egg_joint); + nassertr(vt != (VertexTransform *)NULL, NULL); + blend.add_transform(vt, membership); + } + } + if (egg_vertex_max_num_joints >= 0) { + blend.limit_transforms(egg_vertex_max_num_joints); + } + blend.normalize_weights(); + + int table_index = blend_table->add_blend(blend); + + // We take advantage of the "external index" field of the + // EggVertex to temporarily store the transform blend index. + vertex->set_external_index(table_index); + } + + return blend_table; +} + //////////////////////////////////////////////////////////////////// // Function: EggLoader::record_morph // Access: Private diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index 2908f8a62f..b4c91a2e89 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -143,8 +143,11 @@ private: bool &any_hidden); PT(GeomVertexData) make_vertex_data (const EggRenderState *render_state, EggVertexPool *vertex_pool, - EggNode *primitive_home, const LMatrix4d &transform, bool is_dynamic, - CharacterMaker *character_maker, bool ignore_color); + EggNode *primitive_home, const LMatrix4d &transform, TransformBlendTable *blend_table, + bool is_dynamic, CharacterMaker *character_maker, bool ignore_color); + PT(TransformBlendTable) make_blend_table + (EggVertexPool *vertex_bool, EggNode *primitive_home, + CharacterMaker *character_maker); void record_morph (GeomVertexArrayFormat *array_format, CharacterMaker *character_maker, const string &morph_name, diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 26cfd033d1..1df109959a 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -35,6 +35,7 @@ PStatCollector GeomVertexData::_scale_color_pcollector("*:Munge:Scale color"); PStatCollector GeomVertexData::_set_color_pcollector("*:Munge:Set color"); PStatCollector GeomVertexData::_animation_pcollector("*:Animation"); + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::Default Constructor // Access: Private @@ -1100,6 +1101,49 @@ clear_animated_vertices() { cdata->_animated_vertices.clear(); } + +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::transform_vertices +// Access: Published +// Description: Applies the indicated transform matrix to all of the +// vertices in the GeomVertexData. The transform is +// applied to all "point" and "vector" type columns +// described in the format. +//////////////////////////////////////////////////////////////////// +void GeomVertexData:: +transform_vertices(const LMatrix4 &mat) { + transform_vertices(mat, 0, get_num_rows()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::transform_vertices +// Access: Published +// Description: Applies the indicated transform matrix to all of the +// vertices from begin_row up to but not including +// end_row. The transform is applied to all "point" and +// "vector" type columns described in the format. +//////////////////////////////////////////////////////////////////// +void GeomVertexData:: +transform_vertices(const LMatrix4 &mat, int begin_row, int end_row) { + if (end_row <= begin_row) { + // Trivial no-op. + return; + } + + const GeomVertexFormat *format = get_format(); + + int ci; + for (ci = 0; ci < format->get_num_points(); ci++) { + GeomVertexRewriter data(this, format->get_point(ci)); + do_transform_point_column(format, data, mat, begin_row, end_row); + } + + for (ci = 0; ci < format->get_num_vectors(); ci++) { + GeomVertexRewriter data(this, format->get_vector(ci)); + do_transform_vector_column(format, data, mat, begin_row, end_row); + } +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::bytewise_copy // Access: Private, Static @@ -1552,17 +1596,18 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) { // Then apply the transforms. CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table.get_read_pointer(); if (tb_table != (TransformBlendTable *)NULL) { - PStatTimer timer3(_skinning_pcollector); - // Recompute all the blends up front, so we don't have to test // each one for staleness at each vertex. - int num_blends = tb_table->get_num_blends(); - int bi; - for (bi = 0; bi < num_blends; bi++) { - tb_table->get_blend(bi).update_blend(current_thread); + { + PStatTimer timer4(_blends_pcollector); + int num_blends = tb_table->get_num_blends(); + for (int bi = 0; bi < num_blends; bi++) { + tb_table->get_blend(bi).update_blend(current_thread); + } } // Now go through and apply the transforms. + PStatTimer timer3(_skinning_pcollector); const SparseArray &rows = tb_table->get_rows(); int num_subranges = rows.get_num_subranges(); @@ -1577,8 +1622,7 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) { CPT(GeomVertexArrayFormat) blend_array_format = orig_format->get_array(blend_array_index); - if (blend_array_format->get_num_columns() == 1 && - blend_array_format->get_stride() == 2 && + if (blend_array_format->get_stride() == 2 && blend_array_format->get_column(0)->get_component_bytes() == 2) { // The blend indices are a table of ushorts. Optimize this // common case. @@ -1588,98 +1632,80 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) { int ci; for (ci = 0; ci < new_format->get_num_points(); ci++) { GeomVertexRewriter data(new_data, new_format->get_point(ci)); - const GeomVertexColumn *data_column = data.get_column(); - if (data_column->get_num_values() == 3 && - data_column->get_numeric_type() == NT_float32) { - // Table of points is a table of LPoint3f's. Optimize this - // common case. - int data_index = new_format->get_array_with(new_format->get_point(ci)); - PT(GeomVertexArrayData) data_array = new_data->modify_array(data_index); - PT(GeomVertexArrayDataHandle) data_handle = data_array->modify_handle(); - unsigned char *datat = data_handle->get_write_pointer(); - datat += data_column->get_start(); - size_t stride = data_array->get_array_format()->get_stride(); - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - for (int j = begin; j < end; ++j) { - LPoint3f &vertex = *(LPoint3f *)(&datat[j * stride]); - int bi = blendt[j]; - tb_table->get_blend(bi).transform_point(vertex, current_thread); - } - } + for (int i = 0; i < num_subranges; ++i) { + int begin = rows.get_subrange_begin(i); + int end = rows.get_subrange_end(i); + nassertv(begin < end); - } else if (data_column->get_num_values() == 4) { - // Use the GeomVertexRewriter to adjust the 4-component - // points. - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - data.set_row_unsafe(begin); - for (int j = begin; j < end; ++j) { - LPoint4 vertex = data.get_data4(); - int bi = blendt[j]; - tb_table->get_blend(bi).transform_point(vertex, current_thread); - data.set_data4(vertex); - } - } + int first_vertex = begin; + int first_bi = blendt[first_vertex]; - } else { - // Use the GeomVertexRewriter to adjust the 3-component - // points. - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - data.set_row_unsafe(begin); - for (int j = begin; j < end; ++j) { - LPoint3 vertex = data.get_data3(); - int bi = blendt[j]; - tb_table->get_blend(bi).transform_point(vertex, current_thread); - data.set_data3(vertex); + while (first_vertex < end) { + // At this point, first_vertex is the first of a series of + // vertices that shares the blend index first_bi. + + // Scan for the end of this series of vertices--we're + // looking for the next vertex with a different blend index. + int next_vertex = first_vertex; + int next_bi = first_bi; + ++next_vertex; + while (next_vertex < end) { + next_bi = blendt[next_vertex]; + if (next_bi != first_bi) { + break; + } + ++next_vertex; } + + // We've just reached the end of the vertices with a matching + // blend index. Transform all those vertices as a block. + LMatrix4 mat; + tb_table->get_blend(first_bi).get_blend(mat, current_thread); + new_data->do_transform_point_column(new_format, data, mat, first_vertex, next_vertex); + + first_vertex = next_vertex; + first_bi = next_bi; } } } - // Also process vectors: normals, etc. for (ci = 0; ci < new_format->get_num_vectors(); ci++) { GeomVertexRewriter data(new_data, new_format->get_vector(ci)); - const GeomVertexColumn *data_column = data.get_column(); - if (data_column->get_num_values() == 3 && - data_column->get_numeric_type() == NT_float32) { - // Table of vectors is a table of LVector3f's. Optimize this - // common case. - int data_index = new_format->get_array_with(new_format->get_vector(ci)); - PT(GeomVertexArrayData) data_array = new_data->modify_array(data_index); - PT(GeomVertexArrayDataHandle) data_handle = data_array->modify_handle(); - unsigned char *datat = data_handle->get_write_pointer(); - datat += data_column->get_start(); - size_t stride = data_array->get_array_format()->get_stride(); - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - for (int j = begin; j < end; ++j) { - LVector3f &vertex = *(LVector3f *)(&datat[j * stride]); - int bi = blendt[j]; - tb_table->get_blend(bi).transform_vector(vertex, current_thread); - } - } + for (int i = 0; i < num_subranges; ++i) { + int begin = rows.get_subrange_begin(i); + int end = rows.get_subrange_end(i); + nassertv(begin < end); + + int first_vertex = begin; + int first_bi = blendt[first_vertex]; - } else { - // Use the GeomVertexRewriter to adjust the vectors. - - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - data.set_row_unsafe(begin); - for (int j = begin; j < end; ++j) { - LVector3 vertex = data.get_data3(); - int bi = blendt[j]; - tb_table->get_blend(bi).transform_vector(vertex, current_thread); - data.set_data3(vertex); + while (first_vertex < end) { + // At this point, first_vertex is the first of a series of + // vertices that shares the blend index first_bi. + + // Scan for the end of this series of vertices--we're + // looking for the next vertex with a different blend index. + int next_vertex = first_vertex; + int next_bi = first_bi; + ++next_vertex; + while (next_vertex < end) { + next_bi = blendt[next_vertex]; + if (next_bi != first_bi) { + break; + } + ++next_vertex; } + + // We've just reached the end of the vertices with a matching + // blend index. Transform all those vertices as a block. + LMatrix4 mat; + tb_table->get_blend(first_bi).get_blend(mat, current_thread); + new_data->do_transform_vector_column(new_format, data, mat, first_vertex, next_vertex); + + first_vertex = next_vertex; + first_bi = next_bi; } } } @@ -1693,51 +1719,174 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) { int ci; for (ci = 0; ci < new_format->get_num_points(); ci++) { GeomVertexRewriter data(new_data, new_format->get_point(ci)); - const GeomVertexColumn *data_column = data.get_column(); - - if (data_column->get_num_values() == 4) { - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - data.set_row_unsafe(begin); - blendi.set_row_unsafe(begin); - for (int j = begin; j < end; ++j) { - LPoint4 vertex = data.get_data4(); - int bi = blendi.get_data1i(); - tb_table->get_blend(bi).transform_point(vertex, current_thread); - data.set_data4(vertex); - } - } - } else { - for (int i = 0; i < num_subranges; ++i) { - int begin = rows.get_subrange_begin(i); - int end = rows.get_subrange_end(i); - data.set_row_unsafe(begin); - blendi.set_row_unsafe(begin); - for (int j = begin; j < end; ++j) { - LPoint3 vertex = data.get_data3(); - int bi = blendi.get_data1i(); - tb_table->get_blend(bi).transform_point(vertex, current_thread); - data.set_data3(vertex); - } - } - } - } - for (ci = 0; ci < new_format->get_num_vectors(); ci++) { - GeomVertexRewriter data(new_data, new_format->get_vector(ci)); + for (int i = 0; i < num_subranges; ++i) { int begin = rows.get_subrange_begin(i); int end = rows.get_subrange_end(i); - data.set_row_unsafe(begin); + nassertv(begin < end); blendi.set_row_unsafe(begin); - for (int j = begin; j < end; ++j) { - LVector3 vertex = data.get_data3(); - int bi = blendi.get_data1i(); - tb_table->get_blend(bi).transform_vector(vertex, current_thread); - data.set_data3(vertex); + + int first_vertex = begin; + int first_bi = blendi.get_data1i(); + + while (first_vertex < end) { + // At this point, first_vertex is the first of a series of + // vertices that shares the blend index first_bi. + + // Scan for the end of this series of vertices--we're + // looking for the next vertex with a different blend index. + int next_vertex = first_vertex; + int next_bi = first_bi; + ++next_vertex; + while (next_vertex < end) { + next_bi = blendi.get_data1i(); + if (next_bi != first_bi) { + break; + } + ++next_vertex; + } + + // We've just reached the end of the vertices with a matching + // blend index. Transform all those vertices as a block. + LMatrix4 mat; + tb_table->get_blend(first_bi).get_blend(mat, current_thread); + new_data->do_transform_point_column(new_format, data, mat, first_vertex, next_vertex); + + first_vertex = next_vertex; + first_bi = next_bi; } } } + + for (ci = 0; ci < new_format->get_num_vectors(); ci++) { + GeomVertexRewriter data(new_data, new_format->get_vector(ci)); + + for (int i = 0; i < num_subranges; ++i) { + int begin = rows.get_subrange_begin(i); + int end = rows.get_subrange_end(i); + nassertv(begin != end); + blendi.set_row_unsafe(begin); + + int first_vertex = begin; + int first_bi = blendi.get_data1i(); + + while (first_vertex < end) { + // At this point, first_vertex is the first of a series of + // vertices that shares the blend index first_bi. + + // Scan for the end of this series of vertices--we're + // looking for the next vertex with a different blend index. + int next_vertex = first_vertex; + int next_bi = first_bi; + ++next_vertex; + while (next_vertex < end) { + next_bi = blendi.get_data1i(); + if (next_bi != first_bi) { + break; + } + ++next_vertex; + } + + // We've just reached the end of the vertices with a matching + // blend index. Transform all those vertices as a block. + LMatrix4 mat; + tb_table->get_blend(first_bi).get_blend(mat, current_thread); + new_data->do_transform_vector_column(new_format, data, mat, first_vertex, next_vertex); + + first_vertex = next_vertex; + first_bi = next_bi; + } + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::do_transform_point_column +// Access: Private +// Description: Transforms a range of vertices for one particular +// column, as a point. +//////////////////////////////////////////////////////////////////// +void GeomVertexData:: +do_transform_point_column(const GeomVertexFormat *format, GeomVertexRewriter &data, + const LMatrix4 &mat, int begin_row, int end_row) { + const GeomVertexColumn *data_column = data.get_column(); + + if (data_column->get_num_values() == 3 && + data_column->get_numeric_type() == NT_float32) { + // The table of points is a table of LPoint3f's. Optimize this + // common case. + PT(GeomVertexArrayDataHandle) data_handle = data.get_array_handle(); + PT(GeomVertexArrayData) data_array = data_handle->get_object(); + + unsigned char *datat = data_handle->get_write_pointer(); + datat += data_column->get_start(); + size_t stride = data_array->get_array_format()->get_stride(); + + LMatrix4f matf = LCAST(float, mat); + for (int j = begin_row; j < end_row; ++j) { + LPoint3f &vertex = *(LPoint3f *)(&datat[j * stride]); + vertex = vertex * matf; + } + + } else if (data_column->get_num_values() == 4) { + // Use the GeomVertexRewriter to adjust the 4-component + // points. + + data.set_row_unsafe(begin_row); + for (int j = begin_row; j < end_row; ++j) { + LPoint4 vertex = data.get_data4(); + data.set_data4(vertex * mat); + } + + } else { + // Use the GeomVertexRewriter to adjust the 3-component + // points. + + data.set_row_unsafe(begin_row); + for (int j = begin_row; j < end_row; ++j) { + LPoint3 vertex = data.get_data3(); + data.set_data3(vertex * mat); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::do_transform_vector_column +// Access: Private +// Description: Transforms a range of vertices for one particular +// column, as a vector. +//////////////////////////////////////////////////////////////////// +void GeomVertexData:: +do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &data, + const LMatrix4 &mat, int begin_row, int end_row) { + const GeomVertexColumn *data_column = data.get_column(); + + if (data_column->get_num_values() == 3 && + data_column->get_numeric_type() == NT_float32) { + // The table of vectors is a table of LVector3f's. Optimize this + // common case. + PT(GeomVertexArrayDataHandle) data_handle = data.get_array_handle(); + PT(GeomVertexArrayData) data_array = data_handle->get_object(); + + unsigned char *datat = data_handle->get_write_pointer(); + datat += data_column->get_start(); + size_t stride = data_array->get_array_format()->get_stride(); + + LMatrix4f matf = LCAST(float, mat); + for (int j = begin_row; j < end_row; ++j) { + LVector3f &vector = *(LVector3f *)(&datat[j * stride]); + vector = vector * matf; + } + + } else { + // Use the GeomVertexRewriter to transform the vectors. + + data.set_row_unsafe(begin_row); + for (int j = begin_row; j < end_row; ++j) { + LVector3 vertex = data.get_data3(); + data.set_data3(vertex * mat); } } } diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 5ab570ad52..e6f0bf8ba3 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -42,6 +42,7 @@ class FactoryParams; class GeomVertexColumn; +class GeomVertexRewriter; //////////////////////////////////////////////////////////////////// // Class : GeomVertexData @@ -152,6 +153,8 @@ PUBLISHED: CPT(GeomVertexData) animate_vertices(bool force, Thread *current_thread) const; void clear_animated_vertices(); + void transform_vertices(const LMatrix4 &mat); + void transform_vertices(const LMatrix4 &mat, int begin_row, int end_row); PT(GeomVertexData) replace_column(InternalName *name, int num_components, @@ -315,6 +318,10 @@ private: private: void update_animated_vertices(CData *cdata, Thread *current_thread); + void do_transform_point_column(const GeomVertexFormat *format, GeomVertexRewriter &data, + const LMatrix4 &mat, int begin_row, int end_row); + void do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &data, + const LMatrix4 &mat, int begin_row, int end_row); static PStatCollector _convert_pcollector; static PStatCollector _scale_color_pcollector; diff --git a/panda/src/gobj/geomVertexReader.I b/panda/src/gobj/geomVertexReader.I index b8abb6eabb..20c0ff420d 100644 --- a/panda/src/gobj/geomVertexReader.I +++ b/panda/src/gobj/geomVertexReader.I @@ -206,6 +206,18 @@ get_array_data() const { return _array_data; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexReader::get_array_handle +// Access: Published +// Description: Returns the read handle to the array object that the +// read is currently processing. This low-level call +// should be used with caution. +//////////////////////////////////////////////////////////////////// +INLINE const GeomVertexArrayDataHandle *GeomVertexReader:: +get_array_handle() const { + return _handle; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexReader::get_current_thread // Access: Published diff --git a/panda/src/gobj/geomVertexReader.h b/panda/src/gobj/geomVertexReader.h index 71f34898df..884e6179f3 100644 --- a/panda/src/gobj/geomVertexReader.h +++ b/panda/src/gobj/geomVertexReader.h @@ -82,6 +82,7 @@ PUBLISHED: INLINE const GeomVertexData *get_vertex_data() const; INLINE const GeomVertexArrayData *get_array_data() const; + INLINE const GeomVertexArrayDataHandle *get_array_handle() const; INLINE Thread *get_current_thread() const; INLINE void set_force(bool force); diff --git a/panda/src/gobj/geomVertexRewriter.I b/panda/src/gobj/geomVertexRewriter.I index 3350883308..41a48b085a 100644 --- a/panda/src/gobj/geomVertexRewriter.I +++ b/panda/src/gobj/geomVertexRewriter.I @@ -159,6 +159,20 @@ get_array_data() const { return GeomVertexWriter::get_array_data(); } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexRewriter::get_array_handle +// Access: Published +// Description: Returns the write handle to the array object that the +// rewriter is currently processing. This low-level call +// should be used with caution; be careful with +// modifying the data in the handle out from under the +// GeomVertexRewriter. +//////////////////////////////////////////////////////////////////// +INLINE GeomVertexArrayDataHandle *GeomVertexRewriter:: +get_array_handle() const { + return GeomVertexWriter::get_array_handle(); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexRewriter::set_column // Access: Published diff --git a/panda/src/gobj/geomVertexRewriter.h b/panda/src/gobj/geomVertexRewriter.h index dc7ac4f313..e19b799261 100644 --- a/panda/src/gobj/geomVertexRewriter.h +++ b/panda/src/gobj/geomVertexRewriter.h @@ -53,6 +53,7 @@ PUBLISHED: INLINE GeomVertexData *get_vertex_data() const; INLINE GeomVertexArrayData *get_array_data() const; + INLINE GeomVertexArrayDataHandle *get_array_handle() const; INLINE bool set_column(int column); INLINE bool set_column(const string &name); diff --git a/panda/src/gobj/geomVertexWriter.I b/panda/src/gobj/geomVertexWriter.I index bac37ae9d2..fbdb806436 100644 --- a/panda/src/gobj/geomVertexWriter.I +++ b/panda/src/gobj/geomVertexWriter.I @@ -201,6 +201,20 @@ get_array_data() const { return _array_data; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexWriter::get_array_handle +// Access: Published +// Description: Returns the write handle to the array object that the +// writer is currently processing. This low-level call +// should be used with caution; be careful with +// modifying the data in the handle out from under the +// GeomVertexWriter. +//////////////////////////////////////////////////////////////////// +INLINE GeomVertexArrayDataHandle *GeomVertexWriter:: +get_array_handle() const { + return _handle; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexWriter::get_current_thread // Access: Published diff --git a/panda/src/gobj/geomVertexWriter.h b/panda/src/gobj/geomVertexWriter.h index 374f89852d..0f0b480fbc 100644 --- a/panda/src/gobj/geomVertexWriter.h +++ b/panda/src/gobj/geomVertexWriter.h @@ -94,6 +94,7 @@ PUBLISHED: INLINE GeomVertexData *get_vertex_data() const; INLINE GeomVertexArrayData *get_array_data() const; + INLINE GeomVertexArrayDataHandle *get_array_handle() const; INLINE Thread *get_current_thread() const; INLINE bool set_column(int column); diff --git a/panda/src/gobj/transformBlend.cxx b/panda/src/gobj/transformBlend.cxx index 2fd2a72d5d..fe5f06611a 100644 --- a/panda/src/gobj/transformBlend.cxx +++ b/panda/src/gobj/transformBlend.cxx @@ -79,7 +79,7 @@ add_transform(const VertexTransform *transform, PN_stdfloat weight) { //////////////////////////////////////////////////////////////////// // Function: TransformBlend::remove_transform // Access: Published -// Description: Removes the indicated transform to the blend. +// Description: Removes the indicated transform from the blend. //////////////////////////////////////////////////////////////////// void TransformBlend:: remove_transform(const VertexTransform *transform) { @@ -94,6 +94,38 @@ remove_transform(const VertexTransform *transform) { clear_result(current_thread); } +//////////////////////////////////////////////////////////////////// +// Function: TransformBlend::limit_transforms +// Access: Published +// Description: If the total number of transforms in the blend +// exceeds max_transforms, removes the n least-important +// transforms as needed to reduce the number of +// transforms to max_transforms. +//////////////////////////////////////////////////////////////////// +void TransformBlend:: +limit_transforms(int max_transforms) { + if (max_transforms <= 0) { + _entries.clear(); + return; + } + + while (_entries.size() > max_transforms) { + // Repeatedly find and remove the least-important transform. + nassertv(!_entries.empty()); + Entries::iterator ei_least = _entries.begin(); + Entries::iterator ei = ei_least; + ++ei; + while (ei != _entries.end()) { + if ((*ei)._weight < (*ei_least)._weight) { + ei_least = ei; + } + ++ei; + } + + _entries.erase(ei_least); + } +} + //////////////////////////////////////////////////////////////////// // Function: TransformBlend::normalize_weights // Access: Published diff --git a/panda/src/gobj/transformBlend.h b/panda/src/gobj/transformBlend.h index 26b00cf372..eb0a6c4c49 100644 --- a/panda/src/gobj/transformBlend.h +++ b/panda/src/gobj/transformBlend.h @@ -57,6 +57,7 @@ PUBLISHED: void add_transform(const VertexTransform *transform, PN_stdfloat weight); void remove_transform(const VertexTransform *transform); + void limit_transforms(int max_transforms); void normalize_weights(); bool has_transform(const VertexTransform *transform) const; PN_stdfloat get_weight(const VertexTransform *transform) const;