15 #include "transformState.h" 16 #include "compose_matrix.h" 17 #include "bamReader.h" 18 #include "bamWriter.h" 19 #include "datagramIterator.h" 21 #include "compareTo.h" 22 #include "pStatTimer.h" 23 #include "config_pgraph.h" 24 #include "lightReMutexHolder.h" 25 #include "lightMutexHolder.h" 30 TransformState::States *TransformState::_states = NULL;
31 CPT(TransformState) TransformState::_identity_state;
32 CPT(TransformState) TransformState::_invalid_state;
33 UpdateSeq TransformState::_last_cycle_detect;
34 int TransformState::_garbage_index = 0;
35 bool TransformState::_uniquify_matrix =
true;
37 PStatCollector TransformState::_cache_update_pcollector(
"*:State Cache:Update");
38 PStatCollector TransformState::_garbage_collect_pcollector(
"*:State Cache:Garbage Collect");
39 PStatCollector TransformState::_transform_compose_pcollector(
"*:State Cache:Compose Transform");
40 PStatCollector TransformState::_transform_invert_pcollector(
"*:State Cache:Invert Transform");
41 PStatCollector TransformState::_transform_calc_pcollector(
"*:State Cache:Calc Components");
42 PStatCollector TransformState::_transform_break_cycles_pcollector(
"*:State Cache:Break Cycles");
43 PStatCollector TransformState::_transform_new_pcollector(
"*:State Cache:New");
44 PStatCollector TransformState::_transform_validate_pcollector(
"*:State Cache:Validate");
45 PStatCollector TransformState::_transform_hash_pcollector(
"*:State Cache:Calc Hash");
46 PStatCollector TransformState::_node_counter(
"TransformStates:On nodes");
47 PStatCollector TransformState::_cache_counter(
"TransformStates:Cached");
61 TransformState() : _lock(
"TransformState") {
62 if (_states == (States *)NULL) {
66 _flags = F_is_identity | F_singular_known | F_is_2d;
68 _cache_stats.add_num_states(1);
77 TransformState(
const TransformState &) {
87 operator = (
const TransformState &) {
100 nassertv(!is_destructing());
112 nassertv(_saved_entry == -1);
113 nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
117 nassertv(get_ref_count() == 0);
118 _cache_stats.add_num_states(-1);
139 compare_to(
const TransformState &other,
bool uniquify_matrix)
const {
140 static const int significant_flags =
141 (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_quat_given | F_is_2d);
143 int flags = (_flags & significant_flags);
144 int other_flags = (other._flags & significant_flags);
145 if (flags != other_flags) {
146 return flags < other_flags ? -1 : 1;
149 if ((_flags & (F_is_invalid | F_is_identity)) != 0) {
155 if ((_flags & F_components_given) != 0) {
158 int c = _pos.compare_to(other._pos);
163 if ((_flags & F_hpr_given) != 0) {
164 c = _hpr.compare_to(other._hpr);
168 }
else if ((_flags & F_quat_given) != 0) {
169 c = _quat.compare_to(other._quat);
175 c = _scale.compare_to(other._scale);
180 c = _shear.compare_to(other._shear);
185 if (uniquify_matrix) {
188 return get_mat().compare_to(other.get_mat());
192 if (
this != &other) {
193 return (
this < &other) ? -1 : 1;
213 bool TransformState::
214 operator == (
const TransformState &other)
const {
215 static const int significant_flags =
216 (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_quat_given | F_is_2d);
218 int flags = (_flags & significant_flags);
219 int other_flags = (other._flags & significant_flags);
220 if (flags != other_flags) {
224 if ((_flags & (F_is_invalid | F_is_identity)) != 0) {
230 if ((_flags & F_components_given) != 0) {
233 if (_pos != other._pos) {
237 if ((_flags & F_hpr_given) != 0) {
238 if (_hpr != other._hpr) {
241 }
else if ((_flags & F_quat_given) != 0) {
242 if (_quat != other._quat) {
247 if (_scale != other._scale) {
251 return (_shear == other._shear);
255 if (_uniquify_matrix) {
258 return get_mat().almost_equal(other.get_mat());
262 return (
this == &other);
271 CPT(TransformState) TransformState::
275 if (_identity_state == (TransformState *)NULL) {
276 TransformState *state =
new TransformState;
277 _identity_state = return_unique(state);
280 return _identity_state;
289 CPT(TransformState) TransformState::
291 if (_invalid_state == (TransformState *)NULL) {
292 TransformState *state =
new TransformState;
293 state->_flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
294 _invalid_state = return_unique(state);
297 return _invalid_state;
306 CPT(TransformState) TransformState::
311 if (pos ==
LVecBase3(0.0f, 0.0f, 0.0f) &&
315 return make_identity();
318 TransformState *state =
new TransformState;
321 state->_scale = scale;
322 state->_shear = shear;
323 state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components;
324 state->check_uniform_scale();
325 return return_new(state);
334 CPT(TransformState) TransformState::
339 if (pos ==
LVecBase3(0.0f, 0.0f, 0.0f) &&
343 return make_identity();
346 TransformState *state =
new TransformState;
349 state->_scale = scale;
350 state->_shear = shear;
351 state->_flags = F_components_given | F_quat_given | F_components_known | F_quat_known | F_has_components;
352 state->check_uniform_scale();
353 return return_new(state);
362 CPT(TransformState) TransformState::
364 nassertr(!mat.
is_nan(), make_invalid());
367 return make_identity();
370 TransformState *state =
new TransformState;
372 state->_flags = F_mat_known;
373 return return_new(state);
382 CPT(TransformState) TransformState::
383 make_pos_rotate_scale_shear2d(
const LVecBase2 &pos, PN_stdfloat rotate,
386 nassertr(!(pos.
is_nan() || cnan(rotate) || scale.
is_nan() || cnan(shear)) , make_invalid());
392 return make_identity();
395 TransformState *state =
new TransformState;
396 state->_pos.set(pos[0], pos[1], 0.0f);
397 switch (get_default_coordinate_system()) {
400 state->_hpr.set(rotate, 0.0f, 0.0f);
403 state->_hpr.set(-rotate, 0.0f, 0.0f);
406 state->_hpr.set(0.0f, 0.0f, -rotate);
409 state->_hpr.set(0.0, 0.0f, rotate);
412 state->_scale.set(scale[0], scale[1], 1.0f);
413 state->_shear.set(shear, 0.0f, 0.0f);
414 state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components | F_is_2d;
415 state->check_uniform_scale2d();
416 return return_new(state);
426 CPT(TransformState) TransformState::
428 nassertr(!mat.
is_nan(), make_invalid());
431 return make_identity();
434 TransformState *state =
new TransformState;
435 state->_mat.set(mat(0, 0), mat(0, 1), 0.0f, mat(0, 2),
436 mat(1, 0), mat(1, 1), 0.0f, mat(1, 2),
437 0.0f, 0.0f, 1.0f, 0.0f,
438 mat(2, 0), mat(2, 1), 0.0f, mat(2, 2));
439 state->_flags = F_mat_known | F_is_2d;
440 return return_new(state);
450 CPT(TransformState) TransformState::
452 nassertr(!pos.
is_nan(),
this);
453 nassertr(!is_invalid(),
this);
454 if (is_identity() || components_given()) {
458 return make_pos_quat_scale_shear(pos, get_quat(), get_scale(), get_shear());
460 return make_pos_hpr_scale_shear(pos, get_hpr(), get_scale(), get_shear());
467 return make_mat(mat);
478 CPT(TransformState) TransformState::
480 nassertr(!hpr.
is_nan(),
this);
481 nassertr(!is_invalid(),
this);
483 return make_pos_hpr_scale_shear(get_pos(), hpr, get_scale(), get_shear());
493 CPT(TransformState) TransformState::
495 nassertr(!quat.
is_nan(),
this);
496 nassertr(!is_invalid(),
this);
498 return make_pos_quat_scale_shear(get_pos(), quat, get_scale(), get_shear());
508 CPT(TransformState) TransformState::
509 set_scale(
const LVecBase3 &scale)
const {
510 nassertr(!scale.
is_nan(),
this);
511 nassertr(!is_invalid(),
this);
513 if (is_2d() && scale[0] == scale[1] && scale[1] == scale[2]) {
516 return make_pos_rotate_scale_shear2d(get_pos2d(), get_rotate2d(),
523 return make_pos_quat_scale_shear(get_pos(), get_quat(), scale, get_shear());
525 return make_pos_hpr_scale_shear(get_pos(), get_hpr(), scale, get_shear());
536 CPT(TransformState) TransformState::
537 set_shear(
const LVecBase3 &shear)
const {
538 nassertr(!shear.
is_nan(),
this);
539 nassertr(!is_invalid(),
this);
542 return make_pos_quat_scale_shear(get_pos(), get_quat(), get_scale(), shear);
544 return make_pos_hpr_scale_shear(get_pos(), get_hpr(), get_scale(), shear);
555 CPT(TransformState) TransformState::
557 nassertr(!pos.
is_nan(),
this);
558 nassertr(!is_invalid(),
this);
560 return set_pos(
LVecBase3(pos[0], pos[1], 0.0f));
563 if (is_identity() || components_given()) {
566 return make_pos_rotate_scale_shear2d(pos, get_rotate2d(), get_scale2d(),
573 return make_mat3(mat);
584 CPT(TransformState) TransformState::
585 set_rotate2d(PN_stdfloat rotate)
const {
586 nassertr(!cnan(rotate),
this);
587 nassertr(!is_invalid(),
this);
590 switch (get_default_coordinate_system()) {
593 return set_hpr(
LVecBase3(rotate, 0.0f, 0.0f));
595 return set_hpr(
LVecBase3(-rotate, 0.0f, 0.0f));
597 return set_hpr(
LVecBase3(0.0f, 0.0f, -rotate));
599 return set_hpr(
LVecBase3(0.0f, 0.0f, rotate));
603 return make_pos_rotate_scale_shear2d(get_pos2d(), rotate, get_scale2d(),
614 CPT(TransformState) TransformState::
615 set_scale2d(
const LVecBase2 &scale)
const {
616 nassertr(!scale.
is_nan(),
this);
617 nassertr(!is_invalid(),
this);
620 return set_scale(
LVecBase3(scale[0], scale[1], 1.0f));
622 return make_pos_rotate_scale_shear2d(get_pos2d(), get_rotate2d(),
623 scale, get_shear2d());
633 CPT(TransformState) TransformState::
634 set_shear2d(PN_stdfloat shear)
const {
635 nassertr(!cnan(shear),
this);
636 nassertr(!is_invalid(),
this);
638 return set_shear(
LVecBase3(shear, 0.0f, 0.0f));
640 return make_pos_rotate_scale_shear2d(get_pos2d(), get_rotate2d(),
641 get_scale2d(), shear);
657 CPT(TransformState) TransformState::
658 compose(
const TransformState *other)
const {
663 if (other->is_identity()) {
671 if (other->is_invalid()) {
675 if (!transform_cache) {
676 return do_compose(other);
680 CPT(TransformState) result;
683 int index = _composition_cache.find(other);
685 const Composition &comp = _composition_cache.get_data(index);
686 result = comp._result;
688 if (result != (TransformState *)NULL) {
689 _cache_stats.inc_hits();
693 if (result != (TransformState *)NULL) {
701 result = do_compose(other);
705 return ((TransformState *)
this)->store_compose(other, result);
719 CPT(TransformState) TransformState::
720 invert_compose(
const TransformState *other)
const {
736 if (other->is_invalid()) {
742 return make_identity();
745 if (!transform_cache) {
746 return do_invert_compose(other);
751 CPT(TransformState) result;
754 int index = _invert_composition_cache.find(other);
756 const Composition &comp = _invert_composition_cache.get_data(index);
757 result = comp._result;
759 if (result != (TransformState *)NULL) {
760 _cache_stats.inc_hits();
764 if (result != (TransformState *)NULL) {
772 result = do_invert_compose(other);
776 return ((TransformState *)
this)->store_invert_compose(other, result);
789 bool TransformState::
791 if (!transform_cache || garbage_collect_states) {
807 if (auto_break_cycles && uniquify_transforms) {
808 if (get_cache_ref_count() > 0 &&
809 get_ref_count() == get_cache_ref_count() + 1) {
814 ((TransformState *)
this)->detect_and_break_cycles();
826 ((TransformState *)
this)->release_new();
827 ((TransformState *)
this)->remove_cache_pointers();
839 bool TransformState::
840 validate_composition_cache()
const {
843 int size = _composition_cache.get_size();
844 for (
int i = 0; i <
size; ++i) {
845 if (!_composition_cache.has_element(i)) {
848 const TransformState *source = _composition_cache.get_key(i);
849 if (source != (TransformState *)NULL) {
852 int ri = source->_composition_cache.find(
this);
856 <<
"TransformState::composition cache is inconsistent!\n";
857 pgraph_cat.error(
false)
858 << *
this <<
" compose " << *source <<
"\n";
859 pgraph_cat.error(
false)
860 <<
"but no reverse\n";
866 size = _invert_composition_cache.get_size();
867 for (
int i = 0; i <
size; ++i) {
868 if (!_invert_composition_cache.has_element(i)) {
871 const TransformState *source = _invert_composition_cache.get_key(i);
872 if (source != (TransformState *)NULL) {
875 int ri = source->_invert_composition_cache.find(
this);
879 <<
"TransformState::invert composition cache is inconsistent!\n";
880 pgraph_cat.error(
false)
881 << *
this <<
" invert compose " << *source <<
"\n";
882 pgraph_cat.error(
false)
883 <<
"but no reverse\n";
897 void TransformState::
898 output(ostream &out)
const {
903 }
else if (is_identity()) {
906 }
else if (has_components()) {
907 bool output_hpr = !get_hpr().almost_equal(
LVecBase3(0.0f, 0.0f, 0.0f));
909 if (!components_given()) {
915 }
else if (output_hpr && quat_given()) {
925 if (!get_pos2d().almost_equal(
LVecBase2(0.0f, 0.0f))) {
926 out << lead <<
"pos " << get_pos2d();
930 out << lead <<
"rotate " << get_rotate2d();
933 if (!get_scale2d().almost_equal(
LVecBase2(1.0f, 1.0f))) {
934 if (has_uniform_scale()) {
935 out << lead <<
"scale " << get_uniform_scale();
938 out << lead <<
"scale " << get_scale2d();
942 if (has_nonzero_shear()) {
943 out << lead <<
"shear " << get_shear2d();
947 if (!get_pos().almost_equal(
LVecBase3(0.0f, 0.0f, 0.0f))) {
948 out << lead <<
"pos " << get_pos();
952 out << lead <<
"hpr " << get_hpr();
955 if (!get_scale().almost_equal(
LVecBase3(1.0f, 1.0f, 1.0f))) {
956 if (has_uniform_scale()) {
957 out << lead <<
"scale " << get_uniform_scale();
960 out << lead <<
"scale " << get_scale();
964 if (has_nonzero_shear()) {
965 out << lead <<
"shear " << get_shear();
970 out <<
"(almost identity)";
989 void TransformState::
990 write(ostream &out,
int indent_level)
const {
991 indent(out, indent_level) << *
this <<
"\n";
1002 void TransformState::
1003 write_composition_cache(ostream &out,
int indent_level)
const {
1004 indent(out, indent_level + 2) << _composition_cache <<
"\n";
1005 indent(out, indent_level + 2) << _invert_composition_cache <<
"\n";
1015 int TransformState::
1017 if (_states == (States *)NULL) {
1021 return _states->get_num_entries();
1042 int TransformState::
1043 get_num_unused_states() {
1044 if (_states == (States *)NULL) {
1053 StateCount state_count;
1055 int size = _states->get_size();
1056 for (
int si = 0; si <
size; ++si) {
1057 if (!_states->has_element(si)) {
1060 const TransformState *state = _states->get_key(si);
1063 int cache_size = state->_composition_cache.get_size();
1064 for (i = 0; i < cache_size; ++i) {
1065 if (state->_composition_cache.has_element(i)) {
1066 const TransformState *result = state->_composition_cache.get_data(i)._result;
1067 if (result != (
const TransformState *)NULL && result != state) {
1070 pair<StateCount::iterator, bool> ir =
1071 state_count.insert(StateCount::value_type(result, 1));
1075 (*(ir.first)).second++;
1080 cache_size = state->_invert_composition_cache.get_size();
1081 for (i = 0; i < cache_size; ++i) {
1082 if (state->_invert_composition_cache.has_element(i)) {
1083 const TransformState *result = state->_invert_composition_cache.get_data(i)._result;
1084 if (result != (
const TransformState *)NULL && result != state) {
1085 pair<StateCount::iterator, bool> ir =
1086 state_count.insert(StateCount::value_type(result, 1));
1088 (*(ir.first)).second++;
1100 StateCount::iterator sci;
1101 for (sci = state_count.begin(); sci != state_count.end(); ++sci) {
1102 const TransformState *state = (*sci).first;
1103 int count = (*sci).second;
1104 nassertr(count == state->get_cache_ref_count(), num_unused);
1105 nassertr(count <= state->get_ref_count(), num_unused);
1106 if (count == state->get_ref_count()) {
1109 if (pgraph_cat.is_debug()) {
1111 <<
"Unused state: " << (
void *)state <<
":" 1112 << state->get_ref_count() <<
" =\n";
1113 state->write(pgraph_cat.debug(
false), 2);
1141 int TransformState::
1143 if (_states == (States *)NULL) {
1149 int orig_size = _states->get_num_entries();
1157 TempStates temp_states;
1158 temp_states.reserve(orig_size);
1160 int size = _states->get_size();
1161 for (
int si = 0; si <
size; ++si) {
1162 if (!_states->has_element(si)) {
1165 const TransformState *state = _states->get_key(si);
1166 temp_states.push_back(state);
1172 TempStates::iterator ti;
1173 for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
1174 TransformState *state = (TransformState *)(*ti).p();
1177 int cache_size = state->_composition_cache.get_size();
1178 for (i = 0; i < cache_size; ++i) {
1179 if (state->_composition_cache.has_element(i)) {
1180 const TransformState *result = state->_composition_cache.get_data(i)._result;
1181 if (result != (
const TransformState *)NULL && result != state) {
1182 result->cache_unref();
1183 nassertr(result->get_ref_count() > 0, 0);
1187 _cache_stats.add_total_size(-state->_composition_cache.get_num_entries());
1188 state->_composition_cache.clear();
1190 cache_size = state->_invert_composition_cache.get_size();
1191 for (i = 0; i < cache_size; ++i) {
1192 if (state->_invert_composition_cache.has_element(i)) {
1193 const TransformState *result = state->_invert_composition_cache.get_data(i)._result;
1194 if (result != (
const TransformState *)NULL && result != state) {
1195 result->cache_unref();
1196 nassertr(result->get_ref_count() > 0, 0);
1200 _cache_stats.add_total_size(-state->_invert_composition_cache.get_num_entries());
1201 state->_invert_composition_cache.clear();
1209 int new_size = _states->get_num_entries();
1210 return orig_size - new_size;
1223 int TransformState::
1225 if (_states == (States *)NULL || !garbage_collect_states) {
1230 PStatTimer timer(_garbage_collect_pcollector);
1231 int orig_size = _states->get_num_entries();
1234 int size = _states->get_size();
1235 int num_this_pass = int(size * garbage_collect_states_rate);
1236 if (num_this_pass <= 0) {
1239 num_this_pass = min(num_this_pass, size);
1240 int stop_at_element = (_garbage_index + num_this_pass) % size;
1242 int num_elements = 0;
1243 int si = _garbage_index;
1245 if (_states->has_element(si)) {
1247 TransformState *state = (TransformState *)_states->get_key(si);
1248 if (auto_break_cycles && uniquify_transforms) {
1249 if (state->get_cache_ref_count() > 0 &&
1250 state->get_ref_count() == state->get_cache_ref_count()) {
1255 state->detect_and_break_cycles();
1259 if (state->get_ref_count() == 1) {
1266 state->release_new();
1267 state->remove_cache_pointers();
1268 state->cache_unref();
1273 si = (si + 1) % size;
1274 }
while (si != stop_at_element);
1275 _garbage_index = si;
1276 nassertr(_states->validate(), 0);
1278 int new_size = _states->get_num_entries();
1279 return orig_size - new_size;
1300 void TransformState::
1301 list_cycles(ostream &out) {
1302 if (_states == (States *)NULL) {
1308 VisitedStates visited;
1309 CompositionCycleDesc cycle_desc;
1311 int size = _states->get_size();
1312 for (
int si = 0; si <
size; ++si) {
1313 if (!_states->has_element(si)) {
1316 const TransformState *state = _states->get_key(si);
1318 bool inserted = visited.insert(state).second;
1320 ++_last_cycle_detect;
1321 if (r_detect_cycles(state, state, 1, _last_cycle_detect, &cycle_desc)) {
1323 CompositionCycleDesc::reverse_iterator csi;
1325 out <<
"\nCycle detected of length " << cycle_desc.size() + 1 <<
":\n" 1326 <<
"state " << (
void *)state <<
":" << state->get_ref_count()
1328 state->write(out, 2);
1329 for (csi = cycle_desc.rbegin(); csi != cycle_desc.rend(); ++csi) {
1330 const CompositionCycleDescEntry &entry = (*csi);
1331 if (entry._inverted) {
1332 out <<
"invert composed with ";
1334 out <<
"composed with ";
1336 out << (
const void *)entry._obj <<
":" << entry._obj->get_ref_count()
1337 <<
" " << *entry._obj <<
"\n" 1338 <<
"produces " << (
const void *)entry._result <<
":" 1339 << entry._result->get_ref_count() <<
" =\n";
1340 entry._result->write(out, 2);
1341 visited.insert(entry._result);
1346 ++_last_cycle_detect;
1347 if (r_detect_reverse_cycles(state, state, 1, _last_cycle_detect, &cycle_desc)) {
1349 CompositionCycleDesc::iterator csi;
1351 out <<
"\nReverse cycle detected of length " << cycle_desc.size() + 1 <<
":\n" 1353 for (csi = cycle_desc.begin(); csi != cycle_desc.end(); ++csi) {
1354 const CompositionCycleDescEntry &entry = (*csi);
1355 out << (
const void *)entry._result <<
":" 1356 << entry._result->get_ref_count() <<
" =\n";
1357 entry._result->write(out, 2);
1358 out << (
const void *)entry._obj <<
":" 1359 << entry._obj->get_ref_count() <<
" =\n";
1360 entry._obj->write(out, 2);
1361 visited.insert(entry._result);
1363 out << (
void *)state <<
":" 1364 << state->get_ref_count() <<
" =\n";
1365 state->write(out, 2);
1382 void TransformState::
1383 list_states(ostream &out) {
1384 if (_states == (States *)NULL) {
1385 out <<
"0 states:\n";
1390 out << _states->get_num_entries() <<
" states:\n";
1392 int size = _states->get_size();
1393 for (
int si = 0; si <
size; ++si) {
1394 if (!_states->has_element(si)) {
1397 const TransformState *state = _states->get_key(si);
1398 state->write(out, 2);
1412 bool TransformState::
1414 if (_states == (States *)NULL) {
1418 PStatTimer timer(_transform_validate_pcollector);
1421 if (_states->is_empty()) {
1425 if (!_states->validate()) {
1427 <<
"TransformState::_states cache is invalid!\n";
1431 int size = _states->get_size();
1433 while (si < size && !_states->has_element(si)) {
1436 nassertr(si < size,
false);
1437 nassertr(_states->get_key(si)->get_ref_count() >= 0,
false);
1440 while (snext < size && !_states->has_element(snext)) {
1443 while (snext < size) {
1444 nassertr(_states->get_key(snext)->get_ref_count() >= 0,
false);
1445 const TransformState *ssi = _states->get_key(si);
1446 if (!ssi->validate_composition_cache()) {
1449 const TransformState *ssnext = _states->get_key(snext);
1450 bool c = (*ssi) == (*ssnext);
1451 bool ci = (*ssnext) == (*ssi);
1454 <<
"TransformState::operator == () not defined properly!\n";
1455 pgraph_cat.error(
false)
1456 <<
"(a, b): " << c <<
"\n";
1457 pgraph_cat.error(
false)
1458 <<
"(b, a): " << ci <<
"\n";
1459 ssi->write(pgraph_cat.error(
false), 2);
1460 ssnext->write(pgraph_cat.error(
false), 2);
1465 while (snext < size && !_states->has_element(snext)) {
1483 void TransformState::
1485 _states =
new States;
1488 (
"uniquify-matrix",
true,
1489 PRC_DESC(
"Set this true to look up arbitrary 4x4 transform matrices in " 1490 "the cache, to ensure that two differently-computed transforms " 1491 "that happen to encode the same matrix will be collapsed into " 1492 "a single pointer. Nowadays, with the transforms stored in a " 1493 "hashtable, we're generally better off with this set true."));
1497 _uniquify_matrix = uniquify_matrix;
1504 _states_lock =
new LightReMutex(
"TransformState::_states_lock");
1505 _cache_stats.init();
1519 CPT(TransformState) TransformState::
1520 return_new(TransformState *state) {
1521 nassertr(state != (TransformState *)NULL, state);
1522 if (!uniquify_transforms && !state->is_identity()) {
1526 return return_unique(state);
1541 CPT(TransformState) TransformState::
1542 return_unique(TransformState *state) {
1543 nassertr(state != (TransformState *)NULL, state);
1545 if (!transform_cache) {
1550 if (paranoid_const) {
1551 nassertr(validate_states(), state);
1559 if (state->_saved_entry != -1) {
1567 CPT(TransformState) pt_state = state;
1569 int si = _states->find(state);
1572 return _states->get_key(si);
1576 if (garbage_collect_states) {
1582 si = _states->store(state, Empty());
1585 state->_saved_entry = si;
1596 CPT(TransformState) TransformState::
1597 do_compose(
const TransformState *other)
const {
1598 PStatTimer timer(_transform_compose_pcollector);
1600 nassertr((_flags & F_is_invalid) == 0,
this);
1601 nassertr((other->_flags & F_is_invalid) == 0, other);
1603 if (compose_componentwise &&
1604 has_uniform_scale() &&
1605 !has_nonzero_shear() && !other->has_nonzero_shear() &&
1606 ((components_given() && other->has_components()) ||
1607 (other->components_given() && has_components()))) {
1612 CPT(TransformState) result;
1613 if (is_2d() && other->is_2d()) {
1616 PN_stdfloat rotate = get_rotate2d();
1618 PN_stdfloat scale = get_uniform_scale();
1623 rotate += other->get_rotate2d();
1624 LVecBase2 new_scale = other->get_scale2d() * scale;
1626 result = make_pos_rotate_scale2d(pos, rotate, new_scale);
1632 PN_stdfloat scale = get_uniform_scale();
1634 pos += quat.
xform(other->get_pos()) * scale;
1635 quat = other->get_norm_quat() * quat;
1636 LVecBase3 new_scale = other->get_scale() * scale;
1638 result = make_pos_quat_scale(pos, quat, new_scale);
1642 if (paranoid_compose) {
1645 new_mat.multiply(other->get_mat(), get_mat());
1647 CPT(TransformState) correct = make_mat(new_mat);
1648 pgraph_cat.warning()
1649 <<
"Componentwise composition of " << *
this <<
" and " << *other
1651 << *result <<
"\n instead of:\n" << *correct <<
"\n";
1661 if (is_2d() && other->is_2d()) {
1662 LMatrix3 new_mat = other->get_mat3() * get_mat3();
1663 return make_mat3(new_mat);
1666 new_mat.multiply(other->get_mat(), get_mat());
1667 return make_mat(new_mat);
1679 CPT(TransformState) TransformState::
1680 store_compose(
const TransformState *other,
const TransformState *result) {
1682 nassertr(!is_identity(), other);
1683 nassertr(!other->is_identity(),
this);
1686 nassertr(!is_invalid(),
this);
1687 nassertr(!other->is_invalid(), other);
1692 int index = _composition_cache.find(other);
1694 Composition &comp = _composition_cache.modify_data(index);
1695 if (comp._result == (
const TransformState *)NULL) {
1699 comp._result = result;
1701 if (result != (
const TransformState *)
this) {
1704 result->cache_ref();
1708 _cache_stats.inc_hits();
1709 return comp._result;
1711 _cache_stats.inc_misses();
1720 _cache_stats.add_total_size(1);
1721 _cache_stats.inc_adds(_composition_cache.get_size() == 0);
1723 _composition_cache[other]._result = result;
1725 if (other !=
this) {
1726 _cache_stats.add_total_size(1);
1727 _cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
1728 ((TransformState *)other)->_composition_cache[
this]._result = NULL;
1731 if (result != (TransformState *)
this) {
1736 result->cache_ref();
1743 _cache_stats.maybe_report(
"TransformState");
1756 CPT(TransformState) TransformState::
1757 store_invert_compose(
const TransformState *other,
const TransformState *result) {
1759 nassertr(!is_identity(), other);
1762 nassertr(!is_invalid(),
this);
1763 nassertr(!other->is_invalid(), other);
1765 nassertr(other !=
this, make_identity());
1770 int index = _invert_composition_cache.find(other);
1772 Composition &comp = ((TransformState *)
this)->_invert_composition_cache.modify_data(index);
1773 if (comp._result == (
const TransformState *)NULL) {
1777 comp._result = result;
1779 if (result != (
const TransformState *)
this) {
1782 result->cache_ref();
1786 _cache_stats.inc_hits();
1787 return comp._result;
1789 _cache_stats.inc_misses();
1798 _cache_stats.add_total_size(1);
1799 _cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
1800 _invert_composition_cache[other]._result = result;
1802 if (other !=
this) {
1803 _cache_stats.add_total_size(1);
1804 _cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
1805 ((TransformState *)other)->_invert_composition_cache[
this]._result = NULL;
1808 if (result != (TransformState *)
this) {
1813 result->cache_ref();
1828 CPT(TransformState) TransformState::
1829 do_invert_compose(
const TransformState *other)
const {
1830 PStatTimer timer(_transform_invert_pcollector);
1832 nassertr((_flags & F_is_invalid) == 0,
this);
1833 nassertr((other->_flags & F_is_invalid) == 0, other);
1835 if (compose_componentwise &&
1836 has_uniform_scale() &&
1837 !has_nonzero_shear() && !other->has_nonzero_shear() &&
1838 ((components_given() && other->has_components()) ||
1839 (other->components_given() && has_components()))) {
1844 CPT(TransformState) result;
1845 if (is_2d() && other->is_2d()) {
1848 PN_stdfloat rotate = get_rotate2d();
1850 PN_stdfloat scale = get_uniform_scale();
1853 if (scale == 0.0f) {
1854 ((TransformState *)
this)->_flags |= F_is_singular | F_singular_known;
1855 return make_invalid();
1857 scale = 1.0f / scale;
1865 if (!other->is_identity()) {
1869 rotate += other->get_rotate2d();
1870 new_scale = other->get_scale2d() * scale;
1873 result = make_pos_rotate_scale2d(pos, rotate, new_scale);
1879 PN_stdfloat scale = get_uniform_scale();
1882 if (scale == 0.0f) {
1883 ((TransformState *)
this)->_flags |= F_is_singular | F_singular_known;
1884 return make_invalid();
1886 scale = 1.0f / scale;
1888 pos = quat.
xform(-pos) * scale;
1889 LVecBase3 new_scale(scale, scale, scale);
1892 if (!other->is_identity()) {
1893 pos += quat.
xform(other->get_pos()) * scale;
1894 quat = other->get_norm_quat() * quat;
1895 new_scale = other->get_scale() * scale;
1898 result = make_pos_quat_scale(pos, quat, new_scale);
1902 if (paranoid_compose) {
1904 if (is_singular()) {
1905 pgraph_cat.warning()
1906 <<
"Unexpected singular matrix found for " << *
this <<
"\n";
1908 nassertr(_inv_mat != (
LMatrix4 *)NULL, make_invalid());
1910 new_mat.multiply(other->get_mat(), *_inv_mat);
1912 CPT(TransformState) correct = make_mat(new_mat);
1913 pgraph_cat.warning()
1914 <<
"Componentwise invert-composition of " << *
this <<
" and " << *other
1916 << *result <<
"\n instead of:\n" << *correct <<
"\n";
1926 if (is_singular()) {
1927 return make_invalid();
1932 nassertr(_inv_mat != (
LMatrix4 *)NULL, make_invalid());
1934 if (is_2d() && other->is_2d()) {
1936 LMatrix3 inv3(i(0, 0), i(0, 1), i(0, 3),
1937 i(1, 0), i(1, 1), i(1, 3),
1938 i(3, 0), i(3, 1), i(3, 3));
1939 if (other->is_identity()) {
1940 return make_mat3(inv3);
1942 return make_mat3(other->get_mat3() * inv3);
1945 if (other->is_identity()) {
1946 return make_mat(*_inv_mat);
1948 return make_mat(other->get_mat() * (*_inv_mat));
1960 void TransformState::
1961 detect_and_break_cycles() {
1962 PStatTimer timer(_transform_break_cycles_pcollector);
1964 ++_last_cycle_detect;
1965 if (r_detect_cycles(
this,
this, 1, _last_cycle_detect, NULL)) {
1968 if (pgraph_cat.is_debug()) {
1970 <<
"Breaking cycle involving " << (*this) <<
"\n";
1973 remove_cache_pointers();
1975 ++_last_cycle_detect;
1976 if (r_detect_reverse_cycles(
this,
this, 1, _last_cycle_detect, NULL)) {
1977 if (pgraph_cat.is_debug()) {
1979 <<
"Breaking cycle involving " << (*this) <<
"\n";
1982 remove_cache_pointers();
1998 bool TransformState::
1999 r_detect_cycles(
const TransformState *start_state,
2000 const TransformState *current_state,
2002 TransformState::CompositionCycleDesc *cycle_desc) {
2003 if (current_state->_cycle_detect == this_seq) {
2010 return (current_state == start_state && length > 2);
2012 ((TransformState *)current_state)->_cycle_detect = this_seq;
2015 int cache_size = current_state->_composition_cache.get_size();
2016 for (i = 0; i < cache_size; ++i) {
2017 if (current_state->_composition_cache.has_element(i)) {
2018 const TransformState *result = current_state->_composition_cache.get_data(i)._result;
2019 if (result != (
const TransformState *)NULL) {
2020 if (r_detect_cycles(start_state, result, length + 1,
2021 this_seq, cycle_desc)) {
2023 if (cycle_desc != (CompositionCycleDesc *)NULL) {
2024 const TransformState *other = current_state->_composition_cache.get_key(i);
2025 CompositionCycleDescEntry entry(other, result,
false);
2026 cycle_desc->push_back(entry);
2034 cache_size = current_state->_invert_composition_cache.get_size();
2035 for (i = 0; i < cache_size; ++i) {
2036 if (current_state->_invert_composition_cache.has_element(i)) {
2037 const TransformState *result = current_state->_invert_composition_cache.get_data(i)._result;
2038 if (result != (
const TransformState *)NULL) {
2039 if (r_detect_cycles(start_state, result, length + 1,
2040 this_seq, cycle_desc)) {
2042 if (cycle_desc != (CompositionCycleDesc *)NULL) {
2043 const TransformState *other = current_state->_invert_composition_cache.get_key(i);
2044 CompositionCycleDescEntry entry(other, result,
true);
2045 cycle_desc->push_back(entry);
2065 bool TransformState::
2066 r_detect_reverse_cycles(
const TransformState *start_state,
2067 const TransformState *current_state,
2069 TransformState::CompositionCycleDesc *cycle_desc) {
2070 if (current_state->_cycle_detect == this_seq) {
2077 return (current_state == start_state && length > 2);
2079 ((TransformState *)current_state)->_cycle_detect = this_seq;
2082 int cache_size = current_state->_composition_cache.get_size();
2083 for (i = 0; i < cache_size; ++i) {
2084 if (current_state->_composition_cache.has_element(i)) {
2085 const TransformState *other = current_state->_composition_cache.get_key(i);
2086 if (other != current_state) {
2087 int oi = other->_composition_cache.find(current_state);
2088 nassertr(oi != -1,
false);
2090 const TransformState *result = other->_composition_cache.get_data(oi)._result;
2091 if (result != (
const TransformState *)NULL) {
2092 if (r_detect_reverse_cycles(start_state, result, length + 1,
2093 this_seq, cycle_desc)) {
2095 if (cycle_desc != (CompositionCycleDesc *)NULL) {
2096 const TransformState *other = current_state->_composition_cache.get_key(i);
2097 CompositionCycleDescEntry entry(other, result,
false);
2098 cycle_desc->push_back(entry);
2107 cache_size = current_state->_invert_composition_cache.get_size();
2108 for (i = 0; i < cache_size; ++i) {
2109 if (current_state->_invert_composition_cache.has_element(i)) {
2110 const TransformState *other = current_state->_invert_composition_cache.get_key(i);
2111 if (other != current_state) {
2112 int oi = other->_invert_composition_cache.find(current_state);
2113 nassertr(oi != -1,
false);
2115 const TransformState *result = other->_invert_composition_cache.get_data(oi)._result;
2116 if (result != (
const TransformState *)NULL) {
2117 if (r_detect_reverse_cycles(start_state, result, length + 1,
2118 this_seq, cycle_desc)) {
2120 if (cycle_desc != (CompositionCycleDesc *)NULL) {
2121 const TransformState *other = current_state->_invert_composition_cache.get_key(i);
2122 CompositionCycleDescEntry entry(other, result,
false);
2123 cycle_desc->push_back(entry);
2146 void TransformState::
2148 nassertv(_states_lock->debug_is_locked());
2150 if (_saved_entry != -1) {
2152 _saved_entry = _states->find(
this);
2153 _states->remove_element(_saved_entry);
2169 void TransformState::
2170 remove_cache_pointers() {
2171 nassertv(_states_lock->debug_is_locked());
2189 if (_composition_cache.is_empty() && _invert_composition_cache.is_empty()) {
2198 while (!_composition_cache.is_empty()) {
2200 while (!_composition_cache.has_element(i)) {
2211 TransformState *other = (TransformState *)_composition_cache.get_key(i);
2215 Composition comp = _composition_cache.get_data(i);
2221 _composition_cache.remove_element(i);
2222 _cache_stats.add_total_size(-1);
2223 _cache_stats.inc_dels();
2225 if (other !=
this) {
2226 int oi = other->_composition_cache.find(
this);
2233 Composition ocomp = other->_composition_cache.get_data(oi);
2235 other->_composition_cache.remove_element(oi);
2236 _cache_stats.add_total_size(-1);
2237 _cache_stats.inc_dels();
2243 if (ocomp._result != (
const TransformState *)NULL && ocomp._result != other) {
2244 cache_unref_delete(ocomp._result);
2251 if (comp._result != (
const TransformState *)NULL && comp._result !=
this) {
2252 cache_unref_delete(comp._result);
2258 while (!_invert_composition_cache.is_empty()) {
2259 while (!_invert_composition_cache.has_element(i)) {
2263 TransformState *other = (TransformState *)_invert_composition_cache.get_key(i);
2264 nassertv(other !=
this);
2265 Composition comp = _invert_composition_cache.get_data(i);
2266 _invert_composition_cache.remove_element(i);
2267 _cache_stats.add_total_size(-1);
2268 _cache_stats.inc_dels();
2269 if (other !=
this) {
2270 int oi = other->_invert_composition_cache.find(
this);
2272 Composition ocomp = other->_invert_composition_cache.get_data(oi);
2273 other->_invert_composition_cache.remove_element(oi);
2274 _cache_stats.add_total_size(-1);
2275 _cache_stats.inc_dels();
2276 if (ocomp._result != (
const TransformState *)NULL && ocomp._result != other) {
2277 cache_unref_delete(ocomp._result);
2281 if (comp._result != (
const TransformState *)NULL && comp._result !=
this) {
2282 cache_unref_delete(comp._result);
2292 void TransformState::
2294 PStatTimer timer(_transform_hash_pcollector);
2297 static const int significant_flags =
2298 (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_is_2d);
2300 int flags = (_flags & significant_flags);
2303 if ((_flags & (F_is_invalid | F_is_identity)) == 0) {
2307 if ((_flags & F_components_given) != 0) {
2310 _hash = _pos.add_hash(_hash);
2311 if ((_flags & F_hpr_given) != 0) {
2312 _hash = _hpr.add_hash(_hash);
2314 }
else if ((_flags & F_quat_given) != 0) {
2315 _hash = _quat.add_hash(_hash);
2318 _hash = _scale.add_hash(_hash);
2319 _hash = _shear.add_hash(_hash);
2323 if (_uniquify_matrix) {
2325 if ((_flags & F_mat_known) == 0) {
2329 _hash = _mat.add_hash(_hash);
2341 _flags |= F_hash_known;
2350 void TransformState::
2353 if ((_flags & F_singular_known) != 0) {
2358 PStatTimer timer(_transform_calc_pcollector);
2360 nassertv((_flags & F_is_invalid) == 0);
2368 nassertv(_inv_mat == (
LMatrix4 *)NULL);
2371 if ((_flags & F_mat_known) == 0) {
2374 bool inverted = _inv_mat->invert_from(_mat);
2377 _flags |= F_is_singular;
2381 _flags |= F_singular_known;
2390 void TransformState::
2391 do_calc_components() {
2392 if ((_flags & F_components_known) != 0) {
2397 PStatTimer timer(_transform_calc_pcollector);
2399 nassertv((_flags & F_is_invalid) == 0);
2400 if ((_flags & F_is_identity) != 0) {
2401 _scale.set(1.0f, 1.0f, 1.0f);
2402 _shear.set(0.0f, 0.0f, 0.0f);
2403 _hpr.set(0.0f, 0.0f, 0.0f);
2405 _pos.set(0.0f, 0.0f, 0.0f);
2406 _flags |= F_has_components | F_components_known | F_hpr_known | F_quat_known | F_uniform_scale | F_identity_scale;
2411 nassertv((_flags & F_mat_known) != 0);
2413 if ((_flags & F_mat_known) == 0) {
2416 bool possible = decompose_matrix(_mat, _scale, _shear, _hpr, _pos);
2421 _flags |= F_components_known | F_hpr_known;
2425 _flags |= F_has_components | F_components_known | F_hpr_known;
2426 check_uniform_scale();
2430 _mat.get_row3(_pos, 3);
2440 void TransformState::
2442 if ((_flags & F_hpr_known) != 0) {
2447 PStatTimer timer(_transform_calc_pcollector);
2449 nassertv((_flags & F_is_invalid) == 0);
2450 if ((_flags & F_components_known) == 0) {
2451 do_calc_components();
2453 if ((_flags & F_hpr_known) == 0) {
2456 nassertv((_flags & F_quat_known) != 0);
2457 _hpr = _quat.get_hpr();
2458 _flags |= F_hpr_known;
2467 void TransformState::
2470 if ((_flags & F_quat_known) != 0) {
2475 PStatTimer timer(_transform_calc_pcollector);
2477 nassertv((_flags & F_is_invalid) == 0);
2478 if ((_flags & F_components_known) == 0) {
2479 do_calc_components();
2481 if ((_flags & F_quat_known) == 0) {
2484 nassertv((_flags & F_hpr_known) != 0);
2485 _quat.set_hpr(_hpr);
2486 _flags |= F_quat_known;
2495 void TransformState::
2497 PStatTimer timer(_transform_calc_pcollector);
2502 _norm_quat.normalize();
2503 _flags |= F_norm_quat_known;
2512 void TransformState::
2514 if ((_flags & F_mat_known) != 0) {
2519 PStatTimer timer(_transform_calc_pcollector);
2521 nassertv((_flags & F_is_invalid) == 0);
2522 if ((_flags & F_is_identity) != 0) {
2528 nassertv((_flags & F_components_known) != 0);
2529 if ((_flags & F_hpr_known) == 0) {
2533 compose_matrix(_mat, _scale, _shear, get_hpr(), _pos);
2535 _flags |= F_mat_known;
2546 void TransformState::
2547 update_pstats(
int old_referenced_bits,
int new_referenced_bits) {
2549 if ((old_referenced_bits & R_node) != 0) {
2550 _node_counter.sub_level(1);
2551 }
else if ((old_referenced_bits & R_cache) != 0) {
2552 _cache_counter.sub_level(1);
2554 if ((new_referenced_bits & R_node) != 0) {
2555 _node_counter.add_level(1);
2556 }
else if ((new_referenced_bits & R_cache) != 0) {
2557 _cache_counter.add_level(1);
2568 void TransformState::
2569 register_with_read_factory() {
2579 void TransformState::
2583 if ((_flags & F_is_identity) != 0) {
2585 int flags = F_is_identity | F_singular_known | F_is_2d;
2588 }
else if ((_flags & F_is_invalid) != 0) {
2590 int flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
2593 }
else if ((_flags & F_components_given) != 0) {
2595 int flags = F_components_given | F_components_known | F_has_components;
2596 flags |= (_flags & F_is_2d);
2597 if ((_flags & F_quat_given) != 0) {
2598 flags |= (F_quat_given | F_quat_known);
2599 }
else if ((_flags & F_hpr_given) != 0) {
2600 flags |= (F_hpr_given | F_hpr_known);
2605 _pos.write_datagram(dg);
2606 if ((_flags & F_quat_given) != 0) {
2607 _quat.write_datagram(dg);
2609 get_hpr().write_datagram(dg);
2611 _scale.write_datagram(dg);
2612 _shear.write_datagram(dg);
2616 nassertv((_flags & F_mat_known) != 0);
2617 int flags = F_mat_known;
2618 flags |= (_flags & F_is_2d);
2620 _mat.write_datagram(dg);
2638 TransformState *state = DCAST(TransformState, old_ptr);
2639 CPT(TransformState) pointer = return_unique(state);
2643 return (TransformState *)pointer.p();
2656 TransformState *state =
new TransformState;
2660 parse_params(params, scan, manager);
2661 state->fillin(scan, manager);
2674 void TransformState::
2679 if ((_flags & F_components_given) != 0) {
2681 _pos.read_datagram(scan);
2682 if ((_flags & F_quat_given) != 0) {
2683 _quat.read_datagram(scan);
2685 _hpr.read_datagram(scan);
2687 _scale.read_datagram(scan);
2688 _shear.read_datagram(scan);
2690 check_uniform_scale();
2693 if ((_flags & F_mat_known) != 0) {
2695 _mat.read_datagram(scan);
static const LQuaternionf & ident_quat()
Returns an identity quaternion.
static const LMatrix4f & ident_mat()
Returns an identity matrix.
static size_t add_hash(size_t start, const void *key)
Adds the indicated key into a running hash.
This is the base class for all three-component vectors and points.
This is our own Panda specialization on the default STL map.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
bool almost_equal(const LMatrix4f &other, float threshold) const
Returns true if two matrices are memberwise equal within a specified tolerance.
This is a convenience class to specialize ConfigVariable as a boolean type.
Base class for objects that can be written to and read from Bam files.
A lightweight reentrant mutex.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
void set_row(int row, const LVecBase3f &v)
Replaces the indicated row of the matrix from a three-component vector.
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
bool is_nan() const
Returns true if any component of the matrix is not-a-number, false otherwise.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
void register_change_this(ChangeThisFunc func, TypedWritable *whom)
Called by an object reading itself from the bam file to indicate that the object pointer that will be...
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is our own Panda specialization on the default STL vector.
static size_t add_hash(size_t start, const Key &key)
Adds the indicated key into a running hash.
static Thread * get_main_thread()
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process...
bool invert_in_place()
Inverts the current quat.
A lightweight class that represents a single element that may be timed and/or counted via stats...
This is a 4-by-4 transform matrix.
Similar to MutexHolder, but for a light mutex.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
This is the base class for all two-component vectors and points.
A base class for things which need to inherit from both TypedWritable and from ReferenceCount.
LVecBase3f xform(const LVecBase3f &v) const
Transforms a 3-d vector by the indicated rotation.
bool is_nan() const
Returns true if any component of the matrix is not-a-number, false otherwise.
Similar to MutexHolder, but for a light reentrant mutex.
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
This is used to track the utilization of the TransformState and RenderState caches, for low-level performance tuning information.
This is the base quaternion class.
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
bool is_identity() const
Returns true if this is (close enough to) the identity matrix, false otherwise.
bool is_identity() const
Returns true if this is (close enough to) the identity matrix, false otherwise.
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
void set_row(int row, const LVecBase4f &v)
Replaces the indicated row of the matrix.
This is our own Panda specialization on the default STL set.
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
This is a 3-by-3 transform matrix.
This is a sequence number that increments monotonically.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
virtual bool unref() const
Explicitly decrements the reference count.
int size() const
Returns the number of texture stages in the collection.