30TransformState::States *TransformState::_states =
nullptr;
33UpdateSeq TransformState::_last_cycle_detect;
34size_t TransformState::_garbage_index = 0;
35bool TransformState::_uniquify_matrix =
true;
37PStatCollector TransformState::_cache_update_pcollector(
"*:State Cache:Update");
38PStatCollector TransformState::_garbage_collect_pcollector(
"*:State Cache:Garbage Collect");
39PStatCollector TransformState::_transform_compose_pcollector(
"*:State Cache:Compose Transform");
40PStatCollector TransformState::_transform_invert_pcollector(
"*:State Cache:Invert Transform");
41PStatCollector TransformState::_transform_calc_pcollector(
"*:State Cache:Calc Components");
42PStatCollector TransformState::_transform_break_cycles_pcollector(
"*:State Cache:Break Cycles");
43PStatCollector TransformState::_transform_new_pcollector(
"*:State Cache:New");
44PStatCollector TransformState::_transform_validate_pcollector(
"*:State Cache:Validate");
45PStatCollector TransformState::_transform_hash_pcollector(
"*:State Cache:Calc Hash");
46PStatCollector TransformState::_node_counter(
"TransformStates:On nodes");
47PStatCollector TransformState::_cache_counter(
"TransformStates:Cached");
59TransformState() : _lock(
"TransformState") {
60 if (_states ==
nullptr) {
64 _flags = F_is_identity | F_singular_known | F_is_2d;
66 _cache_stats.add_num_states(1);
80 nassertv(!is_destructing());
84 if (_inv_mat !=
nullptr) {
92 nassertv(_saved_entry == -1);
93 nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
98 _cache_stats.add_num_states(-1);
101 _flags = F_is_invalid | F_is_destructing;
117compare_to(
const TransformState &other,
bool uniquify_matrix)
const {
118 static const int significant_flags =
119 (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_quat_given | F_is_2d);
121 int flags = (_flags & significant_flags);
122 int other_flags = (other._flags & significant_flags);
123 if (flags != other_flags) {
124 return flags < other_flags ? -1 : 1;
127 if ((_flags & (F_is_invalid | F_is_identity)) != 0) {
133 if ((_flags & F_components_given) != 0) {
136 int c = _pos.compare_to(other._pos);
141 if ((_flags & F_hpr_given) != 0) {
142 c = _hpr.compare_to(other._hpr);
146 }
else if ((_flags & F_quat_given) != 0) {
147 c = _quat.compare_to(other._quat);
153 c = _scale.compare_to(other._scale);
158 c = _shear.compare_to(other._shear);
163 if (uniquify_matrix) {
169 if (
this != &other) {
170 return (
this < &other) ? -1 : 1;
187 static const int significant_flags =
188 (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_quat_given | F_is_2d);
190 int flags = (_flags & significant_flags);
191 int other_flags = (other._flags & significant_flags);
192 if (flags != other_flags) {
196 if ((_flags & (F_is_invalid | F_is_identity)) != 0) {
202 if ((_flags & F_components_given) != 0) {
205 if (_pos != other._pos) {
209 if ((_flags & F_hpr_given) != 0) {
210 if (_hpr != other._hpr) {
213 }
else if ((_flags & F_quat_given) != 0) {
214 if (_quat != other._quat) {
219 if (_scale != other._scale) {
223 return (_shear == other._shear);
227 if (_uniquify_matrix) {
233 return (
this == &other);
244 if (_identity_state ==
nullptr) {
246 _identity_state = return_unique(state);
249 return _identity_state;
258 if (_invalid_state ==
nullptr) {
259 TransformState *state =
new TransformState;
260 state->_flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
261 _invalid_state = return_unique(state);
264 return _invalid_state;
271make_pos_hpr_scale_shear(
const LVecBase3 &pos,
const LVecBase3 &hpr,
272 const LVecBase3 &scale,
const LVecBase3 &shear) {
273 nassertr(!(pos.is_nan() || hpr.is_nan() || scale.is_nan() || shear.is_nan()) , make_invalid());
275 if (pos == LVecBase3(0.0f, 0.0f, 0.0f) &&
276 hpr == LVecBase3(0.0f, 0.0f, 0.0f) &&
277 scale == LVecBase3(1.0f, 1.0f, 1.0f) &&
278 shear == LVecBase3(0.0f, 0.0f, 0.0f)) {
279 return make_identity();
282 TransformState *state =
new TransformState;
285 state->_scale = scale;
286 state->_shear = shear;
287 state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components;
288 state->check_uniform_scale();
289 return return_new(state);
296make_pos_quat_scale_shear(
const LVecBase3 &pos,
const LQuaternion &quat,
297 const LVecBase3 &scale,
const LVecBase3 &shear) {
298 nassertr(!(pos.is_nan() || quat.is_nan() || scale.is_nan() || shear.is_nan()) , make_invalid());
300 if (pos == LVecBase3(0.0f, 0.0f, 0.0f) &&
301 quat == LQuaternion::ident_quat() &&
302 scale == LVecBase3(1.0f, 1.0f, 1.0f) &&
303 shear == LVecBase3(0.0f, 0.0f, 0.0f)) {
304 return make_identity();
307 TransformState *state =
new TransformState;
310 state->_scale = scale;
311 state->_shear = shear;
312 state->_flags = F_components_given | F_quat_given | F_components_known | F_quat_known | F_has_components;
313 state->check_uniform_scale();
314 return return_new(state);
321make_mat(
const LMatrix4 &mat) {
322 nassertr(!mat.is_nan(), make_invalid());
324 if (mat.is_identity()) {
325 return make_identity();
328 TransformState *state =
new TransformState;
330 state->_flags = F_mat_known;
331 return return_new(state);
338make_pos_rotate_scale_shear2d(
const LVecBase2 &pos, PN_stdfloat rotate,
339 const LVecBase2 &scale,
341 nassertr(!(pos.is_nan() || cnan(rotate) || scale.is_nan() || cnan(shear)) , make_invalid());
343 if (pos == LVecBase2(0.0f, 0.0f) &&
345 scale == LVecBase2(1.0f, 1.0f) &&
347 return make_identity();
350 TransformState *state =
new TransformState;
351 state->_pos.set(pos[0], pos[1], 0.0f);
352 switch (get_default_coordinate_system()) {
355 state->_hpr.set(rotate, 0.0f, 0.0f);
358 state->_hpr.set(-rotate, 0.0f, 0.0f);
361 state->_hpr.set(0.0f, 0.0f, -rotate);
364 state->_hpr.set(0.0, 0.0f, rotate);
367 state->_scale.set(scale[0], scale[1], 1.0f);
368 state->_shear.set(shear, 0.0f, 0.0f);
369 state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components | F_is_2d;
370 state->check_uniform_scale2d();
371 return return_new(state);
380make_mat3(
const LMatrix3 &mat) {
381 nassertr(!mat.is_nan(), make_invalid());
383 if (mat.is_identity()) {
384 return make_identity();
387 TransformState *state =
new TransformState;
388 state->_mat.set(mat(0, 0), mat(0, 1), 0.0f, mat(0, 2),
389 mat(1, 0), mat(1, 1), 0.0f, mat(1, 2),
390 0.0f, 0.0f, 1.0f, 0.0f,
391 mat(2, 0), mat(2, 1), 0.0f, mat(2, 2));
392 state->_flags = F_mat_known | F_is_2d;
393 return return_new(state);
401set_pos(
const LVecBase3 &pos)
const {
402 nassertr(!pos.is_nan(),
this);
416 return make_mat(mat);
426set_hpr(
const LVecBase3 &hpr)
const {
427 nassertr(!hpr.is_nan(),
this);
439set_quat(
const LQuaternion &quat)
const {
440 nassertr(!quat.is_nan(),
this);
452set_scale(
const LVecBase3 &scale)
const {
453 nassertr(!scale.is_nan(),
this);
456 if (
is_2d() && scale[0] == scale[1] && scale[1] == scale[2]) {
459 LVecBase2(scale[0], scale[0]),
477set_shear(
const LVecBase3 &shear)
const {
478 nassertr(!shear.is_nan(),
this);
493set_pos2d(
const LVecBase2 &pos)
const {
494 nassertr(!pos.is_nan(),
this);
497 return set_pos(LVecBase3(pos[0], pos[1], 0.0f));
509 return make_mat3(mat);
519set_rotate2d(PN_stdfloat rotate)
const {
520 nassertr(!cnan(rotate),
this);
524 switch (get_default_coordinate_system()) {
527 return set_hpr(LVecBase3(rotate, 0.0f, 0.0f));
529 return set_hpr(LVecBase3(-rotate, 0.0f, 0.0f));
531 return set_hpr(LVecBase3(0.0f, 0.0f, -rotate));
533 return set_hpr(LVecBase3(0.0f, 0.0f, rotate));
547set_scale2d(
const LVecBase2 &scale)
const {
548 nassertr(!scale.is_nan(),
this);
552 return set_scale(LVecBase3(scale[0], scale[1], 1.0f));
564set_shear2d(PN_stdfloat shear)
const {
565 nassertr(!cnan(shear),
this);
568 return set_shear(LVecBase3(shear, 0.0f, 0.0f));
601 if (!transform_cache) {
602 return do_compose(other);
605 LightReMutexHolder holder(*_states_lock);
608 int index = _composition_cache.find(other);
610 const Composition &comp = _composition_cache.get_data(index);
611 if (comp._result !=
nullptr) {
613 _cache_stats.inc_hits();
621 CPT(TransformState) result = do_compose(other);
624 Composition &comp = _composition_cache.modify_data(index);
628 comp._result = result;
630 if (result != (
const TransformState *)
this) {
636 _cache_stats.inc_hits();
639 _cache_stats.inc_misses();
648 _cache_stats.add_total_size(1);
649 _cache_stats.inc_adds(_composition_cache.is_empty());
651 _composition_cache[other]._result = result;
654 _cache_stats.add_total_size(1);
655 _cache_stats.inc_adds(other->_composition_cache.
is_empty());
656 other->_composition_cache[
this]._result =
nullptr;
659 if (result != (TransformState *)
this) {
670 _cache_stats.maybe_report(
"TransformState");
705 return make_identity();
708 if (!transform_cache) {
709 return do_invert_compose(other);
712 LightReMutexHolder holder(*_states_lock);
714 int index = _invert_composition_cache.find(other);
716 const Composition &comp = _invert_composition_cache.get_data(index);
717 if (comp._result !=
nullptr) {
719 _cache_stats.inc_hits();
727 CPT(TransformState) result = do_invert_compose(other);
731 Composition &comp = _invert_composition_cache.modify_data(index);
735 comp._result = result;
737 if (result != (
const TransformState *)
this) {
743 _cache_stats.inc_hits();
746 _cache_stats.inc_misses();
755 _cache_stats.add_total_size(1);
756 _cache_stats.inc_adds(_invert_composition_cache.is_empty());
757 _invert_composition_cache[other]._result = result;
760 _cache_stats.add_total_size(1);
761 _cache_stats.inc_adds(other->_invert_composition_cache.
is_empty());
762 other->_invert_composition_cache[
this]._result =
nullptr;
765 if (result != (TransformState *)
this) {
787 if (garbage_collect_states || !transform_cache) {
803 if (auto_break_cycles && uniquify_transforms) {
809 ((TransformState *)
this)->detect_and_break_cycles();
821 ((TransformState *)
this)->release_new();
822 ((TransformState *)
this)->remove_cache_pointers();
835 size_t size = _composition_cache.get_num_entries();
836 for (
size_t i = 0; i < size; ++i) {
837 const TransformState *source = _composition_cache.get_key(i);
838 if (source !=
nullptr) {
841 int ri = source->_composition_cache.
find(
this);
845 <<
"TransformState::composition cache is inconsistent!\n";
846 pgraph_cat.error(
false)
847 << *
this <<
" compose " << *source <<
"\n";
848 pgraph_cat.error(
false)
849 <<
"but no reverse\n";
855 size = _invert_composition_cache.get_num_entries();
856 for (
size_t i = 0; i < size; ++i) {
857 const TransformState *source = _invert_composition_cache.get_key(i);
858 if (source !=
nullptr) {
861 int ri = source->_invert_composition_cache.
find(
this);
865 <<
"TransformState::invert composition cache is inconsistent!\n";
866 pgraph_cat.error(
false)
867 << *
this <<
" invert compose " << *source <<
"\n";
868 pgraph_cat.error(
false)
869 <<
"but no reverse\n";
882output(ostream &out)
const {
891 bool output_hpr = !
get_hpr().almost_equal(LVecBase3(0.0f, 0.0f, 0.0f));
907 if (!
get_pos2d().almost_equal(LVecBase2(0.0f, 0.0f))) {
915 if (!
get_scale2d().almost_equal(LVecBase2(1.0f, 1.0f))) {
929 if (!
get_pos().almost_equal(LVecBase3(0.0f, 0.0f, 0.0f))) {
930 out << lead <<
"pos " <<
get_pos();
934 out << lead <<
"hpr " <<
get_hpr();
937 if (!
get_scale().almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) {
952 out <<
"(almost identity)";
970write(ostream &out,
int indent_level)
const {
971 indent(out, indent_level) << *
this <<
"\n";
981 indent(out, indent_level + 2) << _composition_cache <<
"\n";
982 indent(out, indent_level + 2) << _invert_composition_cache <<
"\n";
991 if (_states ==
nullptr) {
995 return _states->get_num_entries();
1013 if (_states ==
nullptr) {
1021 typedef pmap<const TransformState *, int> StateCount;
1022 StateCount state_count;
1024 size_t size = _states->get_num_entries();
1025 for (
size_t si = 0; si < size; ++si) {
1026 const TransformState *state = _states->get_key(si);
1028 std::pair<StateCount::iterator, bool> ir =
1029 state_count.insert(StateCount::value_type(state, 1));
1033 (*(ir.first)).second++;
1038 for (i = 0; i < cache_size; ++i) {
1039 const TransformState *result = state->_composition_cache.
get_data(i)._result;
1040 if (result !=
nullptr && result != state) {
1042 std::pair<StateCount::iterator, bool> ir =
1043 state_count.insert(StateCount::value_type(result, 1));
1047 (*(ir.first)).second++;
1052 for (i = 0; i < cache_size; ++i) {
1053 const TransformState *result = state->_invert_composition_cache.
get_data(i)._result;
1054 if (result !=
nullptr && result != state) {
1055 std::pair<StateCount::iterator, bool> ir =
1056 state_count.insert(StateCount::value_type(result, 1));
1058 (*(ir.first)).second++;
1069 StateCount::iterator sci;
1070 for (sci = state_count.begin(); sci != state_count.end(); ++sci) {
1071 const TransformState *state = (*sci).first;
1072 int count = (*sci).second;
1078 if (pgraph_cat.is_debug()) {
1080 <<
"Unused state: " << (
void *)state <<
":"
1082 state->write(pgraph_cat.debug(
false), 2);
1106 if (_states ==
nullptr) {
1112 int orig_size = _states->get_num_entries();
1119 typedef pvector< CPT(TransformState) > TempStates;
1120 TempStates temp_states;
1121 temp_states.reserve(orig_size);
1123 size_t size = _states->get_num_entries();
1124 for (
size_t si = 0; si < size; ++si) {
1125 const TransformState *state = _states->get_key(si);
1126 temp_states.push_back(state);
1131 TempStates::iterator ti;
1132 for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
1133 TransformState *state = (TransformState *)(*ti).p();
1137 for (i = 0; i < cache_size; ++i) {
1138 const TransformState *result = state->_composition_cache.
get_data(i)._result;
1139 if (result !=
nullptr && result != state) {
1140 result->cache_unref();
1144 _cache_stats.add_total_size(-(
int)state->_composition_cache.
get_num_entries());
1145 state->_composition_cache.
clear();
1148 for (i = 0; i < cache_size; ++i) {
1149 const TransformState *result = state->_invert_composition_cache.
get_data(i)._result;
1150 if (result !=
nullptr && result != state) {
1151 result->cache_unref();
1155 _cache_stats.add_total_size(-(
int)state->_invert_composition_cache.
get_num_entries());
1156 state->_invert_composition_cache.
clear();
1164 int new_size = _states->get_num_entries();
1165 return orig_size - new_size;
1176 if (_states ==
nullptr || !garbage_collect_states) {
1182 PStatTimer timer(_garbage_collect_pcollector);
1183 size_t orig_size = _states->get_num_entries();
1186 size_t size = orig_size;
1187 size_t num_this_pass = std::max(0,
int(size * garbage_collect_states_rate));
1188 if (num_this_pass <= 0) {
1192 bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
1194 size_t si = _garbage_index;
1199 num_this_pass = std::min(num_this_pass, size);
1200 size_t stop_at_element = (si + num_this_pass) % size;
1203 TransformState *state = (TransformState *)_states->get_key(si);
1204 if (break_and_uniquify) {
1211 state->detect_and_break_cycles();
1222 state->release_new();
1223 state->remove_cache_pointers();
1224 state->cache_unref_only();
1232 if (stop_at_element > 0) {
1237 si = (si + 1) % size;
1238 }
while (si != stop_at_element);
1239 _garbage_index = si;
1241 nassertr(_states->get_num_entries() == size, 0);
1244 nassertr(_states->validate(), 0);
1249 _states->consider_shrink_table();
1251 return (
int)orig_size - (int)size;
1269 if (_states ==
nullptr) {
1274 typedef pset<const TransformState *> VisitedStates;
1275 VisitedStates visited;
1276 CompositionCycleDesc cycle_desc;
1278 size_t size = _states->get_num_entries();
1279 for (
size_t si = 0; si < size; ++si) {
1280 const TransformState *state = _states->get_key(si);
1282 bool inserted = visited.insert(state).second;
1284 ++_last_cycle_detect;
1285 if (r_detect_cycles(state, state, 1, _last_cycle_detect, &cycle_desc)) {
1287 CompositionCycleDesc::reverse_iterator csi;
1289 out <<
"\nCycle detected of length " << cycle_desc.size() + 1 <<
":\n"
1290 <<
"state " << (
void *)state <<
":" << state->
get_ref_count()
1292 state->write(out, 2);
1293 for (csi = cycle_desc.rbegin(); csi != cycle_desc.rend(); ++csi) {
1294 const CompositionCycleDescEntry &entry = (*csi);
1295 if (entry._inverted) {
1296 out <<
"invert composed with ";
1298 out <<
"composed with ";
1300 out << (
const void *)entry._obj <<
":" << entry._obj->
get_ref_count()
1301 <<
" " << *entry._obj <<
"\n"
1302 <<
"produces " << (
const void *)entry._result <<
":"
1304 entry._result->write(out, 2);
1305 visited.insert(entry._result);
1310 ++_last_cycle_detect;
1311 if (r_detect_reverse_cycles(state, state, 1, _last_cycle_detect, &cycle_desc)) {
1313 CompositionCycleDesc::iterator csi;
1315 out <<
"\nReverse cycle detected of length " << cycle_desc.size() + 1 <<
":\n"
1317 for (csi = cycle_desc.begin(); csi != cycle_desc.end(); ++csi) {
1318 const CompositionCycleDescEntry &entry = (*csi);
1319 out << (
const void *)entry._result <<
":"
1321 entry._result->write(out, 2);
1322 out << (
const void *)entry._obj <<
":"
1324 entry._obj->write(out, 2);
1325 visited.insert(entry._result);
1327 out << (
void *)state <<
":"
1329 state->write(out, 2);
1346 if (_states ==
nullptr) {
1347 out <<
"0 states:\n";
1352 size_t size = _states->get_num_entries();
1353 out << size <<
" states:\n";
1354 for (
size_t si = 0; si < size; ++si) {
1355 const TransformState *state = _states->get_key(si);
1356 state->write(out, 2);
1368 if (_states ==
nullptr) {
1372 PStatTimer timer(_transform_validate_pcollector);
1375 if (_states->is_empty()) {
1379 if (!_states->validate()) {
1381 <<
"TransformState::_states cache is invalid!\n";
1385 size_t size = _states->get_num_entries();
1387 nassertr(si < size,
false);
1388 nassertr(_states->get_key(si)->get_ref_count() >= 0,
false);
1391 while (snext < size) {
1392 nassertr(_states->get_key(snext)->get_ref_count() >= 0,
false);
1393 const TransformState *ssi = _states->get_key(si);
1397 const TransformState *ssnext = _states->get_key(snext);
1398 bool c = (*ssi) == (*ssnext);
1399 bool ci = (*ssnext) == (*ssi);
1402 <<
"TransformState::operator == () not defined properly!\n";
1403 pgraph_cat.error(
false)
1404 <<
"(a, b): " << c <<
"\n";
1405 pgraph_cat.error(
false)
1406 <<
"(b, a): " << ci <<
"\n";
1407 ssi->write(pgraph_cat.error(
false), 2);
1408 ssnext->write(pgraph_cat.error(
false), 2);
1427 _states =
new States;
1430 (
"uniquify-matrix",
true,
1431 PRC_DESC(
"Set this true to look up arbitrary 4x4 transform matrices in "
1432 "the cache, to ensure that two differently-computed transforms "
1433 "that happen to encode the same matrix will be collapsed into "
1434 "a single pointer. Nowadays, with the transforms stored in a "
1435 "hashtable, we're generally better off with this set true."));
1439 _uniquify_matrix = uniquify_matrix;
1445 _states_lock =
new LightReMutex(
"TransformState::_states_lock");
1446 _cache_stats.init();
1459 nassertr(state !=
nullptr, state);
1460 if (!uniquify_transforms && !state->
is_identity()) {
1464 return return_unique(state);
1478 nassertr(state !=
nullptr, state);
1480 if (!transform_cache) {
1485 if (paranoid_const) {
1490 PStatTimer timer(_transform_new_pcollector);
1492 LightReMutexHolder holder(*_states_lock);
1494 if (state->_saved_entry != -1) {
1502 CPT(TransformState) pt_state = state;
1504 int si = _states->find(state);
1507 return _states->get_key(si);
1511 if (garbage_collect_states) {
1517 si = _states->store(state,
nullptr);
1520 state->_saved_entry = si;
1530 PStatTimer timer(_transform_compose_pcollector);
1532 nassertr((_flags & F_is_invalid) == 0,
this);
1533 nassertr((other->_flags & F_is_invalid) == 0, other);
1535 if (compose_componentwise &&
1543 CPT(TransformState) result;
1551 LPoint3 op = quat.xform(other->
get_pos());
1552 pos += LVecBase2(op[0], op[1]) * scale;
1555 LVecBase2 new_scale = other->
get_scale2d() * scale;
1557 result = make_pos_rotate_scale2d(pos, rotate, new_scale);
1565 pos += quat.xform(other->
get_pos()) * scale;
1567 LVecBase3 new_scale = other->
get_scale() * scale;
1569 result = make_pos_quat_scale(pos, quat, new_scale);
1573 if (paranoid_compose) {
1577 if (!new_mat.almost_equal(result->get_mat(), 0.1)) {
1578 CPT(TransformState) correct = make_mat(new_mat);
1579 pgraph_cat.warning()
1580 <<
"Componentwise composition of " << *
this <<
" and " << *other
1582 << *result <<
"\n instead of:\n" << *correct <<
"\n";
1594 return make_mat3(new_mat);
1598 return make_mat(new_mat);
1607 PStatTimer timer(_transform_invert_pcollector);
1609 nassertr((_flags & F_is_invalid) == 0,
this);
1610 nassertr((other->_flags & F_is_invalid) == 0, other);
1612 if (compose_componentwise &&
1620 CPT(TransformState) result;
1629 if (scale == 0.0f) {
1630 ((TransformState *)
this)->_flags |= F_is_singular | F_singular_known;
1631 return make_invalid();
1633 scale = 1.0f / scale;
1634 quat.invert_in_place();
1636 LVecBase3 mp = quat.xform(-LVecBase3(pos[0], pos[1], 0.0f));
1637 pos = LVecBase2(mp[0], mp[1]) * scale;
1638 LVecBase2 new_scale(scale, scale);
1642 LPoint3 op = quat.xform(other->
get_pos());
1643 pos += LVecBase2(op[0], op[1]) * scale;
1649 result = make_pos_rotate_scale2d(pos, rotate, new_scale);
1658 if (scale == 0.0f) {
1659 ((TransformState *)
this)->_flags |= F_is_singular | F_singular_known;
1660 return make_invalid();
1662 scale = 1.0f / scale;
1663 quat.invert_in_place();
1664 pos = quat.xform(-pos) * scale;
1665 LVecBase3 new_scale(scale, scale, scale);
1669 pos += quat.xform(other->
get_pos()) * scale;
1674 result = make_pos_quat_scale(pos, quat, new_scale);
1678 if (paranoid_compose) {
1681 pgraph_cat.warning()
1682 <<
"Unexpected singular matrix found for " << *
this <<
"\n";
1684 nassertr(_inv_mat !=
nullptr, make_invalid());
1686 new_mat.multiply(other->
get_mat(), *_inv_mat);
1687 if (!new_mat.almost_equal(result->get_mat(), 0.1)) {
1688 CPT(TransformState) correct = make_mat(new_mat);
1689 pgraph_cat.warning()
1690 <<
"Componentwise invert-composition of " << *
this <<
" and " << *other
1692 << *result <<
"\n instead of:\n" << *correct <<
"\n";
1703 return make_invalid();
1708 nassertr(_inv_mat !=
nullptr, make_invalid());
1711 const LMatrix4 &i = *_inv_mat;
1712 LMatrix3 inv3(i(0, 0), i(0, 1), i(0, 3),
1713 i(1, 0), i(1, 1), i(1, 3),
1714 i(3, 0), i(3, 1), i(3, 3));
1716 return make_mat3(inv3);
1718 return make_mat3(other->
get_mat3() * inv3);
1722 return make_mat(*_inv_mat);
1724 return make_mat(other->
get_mat() * (*_inv_mat));
1733void TransformState::
1734detect_and_break_cycles() {
1735 PStatTimer timer(_transform_break_cycles_pcollector);
1737 ++_last_cycle_detect;
1738 if (r_detect_cycles(
this,
this, 1, _last_cycle_detect,
nullptr)) {
1741 if (pgraph_cat.is_debug()) {
1743 <<
"Breaking cycle involving " << (*this) <<
"\n";
1746 remove_cache_pointers();
1748 ++_last_cycle_detect;
1749 if (r_detect_reverse_cycles(
this,
this, 1, _last_cycle_detect,
nullptr)) {
1750 if (pgraph_cat.is_debug()) {
1752 <<
"Breaking cycle involving " << (*this) <<
"\n";
1755 remove_cache_pointers();
1767bool TransformState::
1771 TransformState::CompositionCycleDesc *cycle_desc) {
1772 if (current_state->_cycle_detect == this_seq) {
1778 return (current_state == start_state && length > 2);
1780 ((TransformState *)current_state)->_cycle_detect = this_seq;
1783 size_t cache_size = current_state->_composition_cache.
get_num_entries();
1784 for (i = 0; i < cache_size; ++i) {
1785 const TransformState *result = current_state->_composition_cache.
get_data(i)._result;
1786 if (result !=
nullptr) {
1787 if (r_detect_cycles(start_state, result, length + 1,
1788 this_seq, cycle_desc)) {
1790 if (cycle_desc !=
nullptr) {
1791 const TransformState *other = current_state->_composition_cache.
get_key(i);
1792 CompositionCycleDescEntry entry(other, result,
false);
1793 cycle_desc->push_back(entry);
1800 cache_size = current_state->_invert_composition_cache.
get_num_entries();
1801 for (i = 0; i < cache_size; ++i) {
1802 const TransformState *result = current_state->_invert_composition_cache.
get_data(i)._result;
1803 if (result !=
nullptr) {
1804 if (r_detect_cycles(start_state, result, length + 1,
1805 this_seq, cycle_desc)) {
1807 if (cycle_desc !=
nullptr) {
1808 const TransformState *other = current_state->_invert_composition_cache.
get_key(i);
1809 CompositionCycleDescEntry entry(other, result,
true);
1810 cycle_desc->push_back(entry);
1826bool TransformState::
1830 TransformState::CompositionCycleDesc *cycle_desc) {
1831 if (current_state->_cycle_detect == this_seq) {
1837 return (current_state == start_state && length > 2);
1839 ((TransformState *)current_state)->_cycle_detect = this_seq;
1842 size_t cache_size = current_state->_composition_cache.
get_num_entries();
1843 for (i = 0; i < cache_size; ++i) {
1844 const TransformState *other = current_state->_composition_cache.
get_key(i);
1845 if (other != current_state) {
1846 int oi = other->_composition_cache.
find(current_state);
1847 nassertr(oi != -1,
false);
1849 const TransformState *result = other->_composition_cache.
get_data(oi)._result;
1850 if (result !=
nullptr) {
1851 if (r_detect_reverse_cycles(start_state, result, length + 1,
1852 this_seq, cycle_desc)) {
1854 if (cycle_desc !=
nullptr) {
1855 const TransformState *other = current_state->_composition_cache.
get_key(i);
1856 CompositionCycleDescEntry entry(other, result,
false);
1857 cycle_desc->push_back(entry);
1865 cache_size = current_state->_invert_composition_cache.
get_num_entries();
1866 for (i = 0; i < cache_size; ++i) {
1867 const TransformState *other = current_state->_invert_composition_cache.
get_key(i);
1868 if (other != current_state) {
1869 int oi = other->_invert_composition_cache.
find(current_state);
1870 nassertr(oi != -1,
false);
1872 const TransformState *result = other->_invert_composition_cache.
get_data(oi)._result;
1873 if (result !=
nullptr) {
1874 if (r_detect_reverse_cycles(start_state, result, length + 1,
1875 this_seq, cycle_desc)) {
1877 if (cycle_desc !=
nullptr) {
1878 const TransformState *other = current_state->_invert_composition_cache.
get_key(i);
1879 CompositionCycleDescEntry entry(other, result,
false);
1880 cycle_desc->push_back(entry);
1899void TransformState::
1901 nassertv(_states_lock->debug_is_locked());
1903 if (_saved_entry != -1) {
1905 nassertv_always(_states->remove(
this));
1916void TransformState::
1917remove_cache_pointers() {
1918 nassertv(_states_lock->debug_is_locked());
1936 if (_composition_cache.is_empty() && _invert_composition_cache.is_empty()) {
1939 PStatTimer timer(_cache_update_pcollector);
1945 while (!_composition_cache.is_empty()) {
1953 TransformState *other = (TransformState *)_composition_cache.get_key(i);
1957 Composition comp = _composition_cache.get_data(i);
1962 _composition_cache.remove_element(i);
1963 _cache_stats.add_total_size(-1);
1964 _cache_stats.inc_dels();
1966 if (other !=
this) {
1967 int oi = other->_composition_cache.
find(
this);
1973 Composition ocomp = other->_composition_cache.
get_data(oi);
1976 _cache_stats.add_total_size(-1);
1977 _cache_stats.inc_dels();
1982 if (ocomp._result !=
nullptr && ocomp._result != other) {
1990 if (comp._result !=
nullptr && comp._result !=
this) {
1997 while (!_invert_composition_cache.is_empty()) {
1998 TransformState *other = (TransformState *)_invert_composition_cache.get_key(i);
1999 nassertv(other !=
this);
2000 Composition comp = _invert_composition_cache.get_data(i);
2001 _invert_composition_cache.remove_element(i);
2002 _cache_stats.add_total_size(-1);
2003 _cache_stats.inc_dels();
2004 if (other !=
this) {
2005 int oi = other->_invert_composition_cache.
find(
this);
2007 Composition ocomp = other->_invert_composition_cache.
get_data(oi);
2009 _cache_stats.add_total_size(-1);
2010 _cache_stats.inc_dels();
2011 if (ocomp._result !=
nullptr && ocomp._result != other) {
2016 if (comp._result !=
nullptr && comp._result !=
this) {
2025void TransformState::
2027 PStatTimer timer(_transform_hash_pcollector);
2030 static const int significant_flags =
2031 (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_is_2d);
2033 int flags = (_flags & significant_flags);
2036 if ((_flags & (F_is_invalid | F_is_identity)) == 0) {
2040 if ((_flags & F_components_given) != 0) {
2042 _hash = _pos.add_hash(_hash);
2043 if ((_flags & F_hpr_given) != 0) {
2044 _hash = _hpr.add_hash(_hash);
2046 }
else if ((_flags & F_quat_given) != 0) {
2047 _hash = _quat.add_hash(_hash);
2050 _hash = _scale.add_hash(_hash);
2051 _hash = _shear.add_hash(_hash);
2055 if (_uniquify_matrix) {
2057 if ((_flags & F_mat_known) == 0) {
2061 _hash = _mat.add_hash(_hash);
2073 _flags |= F_hash_known;
2080void TransformState::
2082 LightMutexHolder holder(_lock);
2083 if ((_flags & F_singular_known) != 0) {
2088 PStatTimer timer(_transform_calc_pcollector);
2090 nassertv((_flags & F_is_invalid) == 0);
2098 nassertv(_inv_mat ==
nullptr);
2099 _inv_mat =
new LMatrix4;
2101 if ((_flags & F_mat_known) == 0) {
2104 bool inverted = _inv_mat->invert_from(_mat);
2107 _flags |= F_is_singular;
2111 _flags |= F_singular_known;
2118void TransformState::
2119do_calc_components() {
2120 if ((_flags & F_components_known) != 0) {
2125 PStatTimer timer(_transform_calc_pcollector);
2127 nassertv((_flags & F_is_invalid) == 0);
2128 if ((_flags & F_is_identity) != 0) {
2129 _scale.set(1.0f, 1.0f, 1.0f);
2130 _shear.set(0.0f, 0.0f, 0.0f);
2131 _hpr.set(0.0f, 0.0f, 0.0f);
2132 _quat = LQuaternion::ident_quat();
2133 _pos.set(0.0f, 0.0f, 0.0f);
2134 _flags |= F_has_components | F_components_known | F_hpr_known | F_quat_known | F_uniform_scale | F_identity_scale;
2139 nassertv((_flags & F_mat_known) != 0);
2141 if ((_flags & F_mat_known) == 0) {
2144 bool possible = decompose_matrix(_mat, _scale, _shear, _hpr, _pos);
2149 _flags |= F_components_known | F_hpr_known;
2153 _flags |= F_has_components | F_components_known | F_hpr_known;
2154 check_uniform_scale();
2158 _mat.get_row3(_pos, 3);
2166void TransformState::
2168 if ((_flags & F_hpr_known) != 0) {
2173 PStatTimer timer(_transform_calc_pcollector);
2175 nassertv((_flags & F_is_invalid) == 0);
2176 if ((_flags & F_components_known) == 0) {
2177 do_calc_components();
2179 if ((_flags & F_hpr_known) == 0) {
2182 nassertv((_flags & F_quat_known) != 0);
2183 _hpr = _quat.get_hpr();
2184 _flags |= F_hpr_known;
2191void TransformState::
2193 LightMutexHolder holder(_lock);
2194 if ((_flags & F_quat_known) != 0) {
2199 PStatTimer timer(_transform_calc_pcollector);
2201 nassertv((_flags & F_is_invalid) == 0);
2202 if ((_flags & F_components_known) == 0) {
2203 do_calc_components();
2205 if ((_flags & F_quat_known) == 0) {
2208 nassertv((_flags & F_hpr_known) != 0);
2209 _quat.set_hpr(_hpr);
2210 _flags |= F_quat_known;
2217void TransformState::
2219 PStatTimer timer(_transform_calc_pcollector);
2222 LightMutexHolder holder(_lock);
2224 _norm_quat.normalize();
2225 _flags |= F_norm_quat_known;
2232void TransformState::
2234 if ((_flags & F_mat_known) != 0) {
2239 PStatTimer timer(_transform_calc_pcollector);
2241 nassertv((_flags & F_is_invalid) == 0);
2242 if ((_flags & F_is_identity) != 0) {
2243 _mat = LMatrix4::ident_mat();
2248 nassertv((_flags & F_components_known) != 0);
2249 if ((_flags & F_hpr_known) == 0) {
2253 compose_matrix(_mat, _scale, _shear,
get_hpr(), _pos);
2255 _flags |= F_mat_known;
2263void TransformState::
2264update_pstats(
int old_referenced_bits,
int new_referenced_bits) {
2266 if ((old_referenced_bits & R_node) != 0) {
2267 _node_counter.sub_level(1);
2268 }
else if ((old_referenced_bits & R_cache) != 0) {
2269 _cache_counter.sub_level(1);
2271 if ((new_referenced_bits & R_node) != 0) {
2272 _node_counter.add_level(1);
2273 }
else if ((new_referenced_bits & R_cache) != 0) {
2274 _cache_counter.add_level(1);
2295 if ((_flags & F_is_identity) != 0) {
2297 int flags = F_is_identity | F_singular_known | F_is_2d;
2300 }
else if ((_flags & F_is_invalid) != 0) {
2302 int flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
2305 }
else if ((_flags & F_components_given) != 0) {
2307 int flags = F_components_given | F_components_known | F_has_components;
2308 flags |= (_flags & F_is_2d);
2309 if ((_flags & F_quat_given) != 0) {
2310 flags |= (F_quat_given | F_quat_known);
2311 }
else if ((_flags & F_hpr_given) != 0) {
2312 flags |= (F_hpr_given | F_hpr_known);
2317 _pos.write_datagram(dg);
2318 if ((_flags & F_quat_given) != 0) {
2319 _quat.write_datagram(dg);
2323 _scale.write_datagram(dg);
2324 _shear.write_datagram(dg);
2328 nassertv((_flags & F_mat_known) != 0);
2329 int flags = F_mat_known;
2330 flags |= (_flags & F_is_2d);
2332 _mat.write_datagram(dg);
2362 TransformState *state =
new TransformState;
2363 DatagramIterator scan;
2367 state->fillin(scan, manager);
2368 manager->register_change_this(change_this, state);
2377void TransformState::
2382 if ((_flags & F_components_given) != 0) {
2384 _pos.read_datagram(scan);
2385 if ((_flags & F_quat_given) != 0) {
2386 _quat.read_datagram(scan);
2388 _hpr.read_datagram(scan);
2390 _scale.read_datagram(scan);
2391 _shear.read_datagram(scan);
2393 check_uniform_scale();
2396 if ((_flags & F_mat_known) != 0) {
2398 _mat.read_datagram(scan);
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams ¶ms, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void cache_unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
This is used to track the utilization of the TransformState and RenderState caches,...
get_cache_ref_count
Returns the current reference count.
This is a convenience class to specialize ConfigVariable as a boolean type.
A class to retrieve the individual data elements previously stored in a Datagram.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Similar to MutexHolder, but for a light reentrant mutex.
A lightweight reentrant mutex.
static void update_type(ReferenceCount *ptr, TypeHandle type)
Associates the indicated type with the given pointer.
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
bool unref_if_one() const
Atomically decreases the reference count of this object if it is one.
get_ref_count
Returns the current reference count.
virtual bool unref() const
Explicitly decrements the reference count.
const Key & get_key(size_t n) const
Returns the key in the nth entry of the table.
void clear()
Completely empties the table.
const Value & get_data(size_t n) const
Returns the data in the nth entry of the table.
int find(const Key &key) const
Searches for the indicated key in the table.
void remove_element(size_t n)
Removes the nth entry from the table.
bool is_empty() const
Returns true if the table is empty; i.e.
size_t get_num_entries() const
Returns the number of active entries in the table.
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
get_current_thread
Returns a pointer to the currently-executing Thread object.
TypeHandle is the identifier used to differentiate C++ class types.
A base class for things which need to inherit from both TypedWritable and from ReferenceCount.
Base class for objects that can be written to and read from Bam files.
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...
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 a sequence number that increments monotonically.
static size_t add_hash(size_t start, const int &key)
static size_t add_hash(size_t start, const void *key)
Adds the indicated key into a running hash.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.