some animation optimizations

This commit is contained in:
David Rose 2011-12-15 00:33:03 +00:00
parent 5e5d8f7e17
commit 7e8737dd3d
16 changed files with 494 additions and 164 deletions

View File

@ -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<EggVertex *> 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

View File

@ -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;

View File

@ -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();
}

View File

@ -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();

View File

@ -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

View File

@ -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,

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;