23 _sample._pos.set(0.0, 0.0, 0.0);
24 _sample._hpr.set(0.0, 0.0, 0.0);
25 _sample._timestamp = 0.0;
27 _smooth_pos.set(0.0, 0.0, 0.0);
28 _smooth_hpr.set(0.0, 0.0, 0.0);
29 _forward_axis.set(0.0, 1.0, 0.0);
30 _smooth_timestamp = 0.0;
31 _smooth_position_known =
false;
32 _smooth_position_changed =
true;
33 _computed_forward_axis =
true;
35 _smooth_forward_velocity = 0.0;
36 _smooth_lateral_velocity = 0.0;
37 _smooth_rotational_velocity = 0.0;
39 _has_most_recent_timestamp =
false;
41 _last_point_before = -1;
42 _last_point_after = -1;
44 _net_timestamp_delay = 0;
49 _last_heard_from = 0.0;
51 _smooth_mode = SM_off;
52 _prediction_mode = PM_off;
54 _accept_clock_skew = accept_clock_skew;
55 _directional_velocity =
true;
56 _default_to_standing_still =
true;
57 _max_position_age = 0.25;
58 _expected_broadcast_period = 0.2;
59 _reset_velocity_age = 0.3;
84 if (_smooth_mode == SM_off) {
94 if (_smooth_position_known) {
95 LVector3 pos_delta = _sample._pos - _smooth_pos;
96 LVecBase3 hpr_delta = _sample._hpr - _smooth_hpr;
97 double age = timestamp - _smooth_timestamp;
98 age = std::min(age, _max_position_age);
100 set_smooth_pos(_sample._pos, _sample._hpr, timestamp);
102 compute_velocity(pos_delta, hpr_delta, age);
107 set_smooth_pos(_sample._pos, _sample._hpr, timestamp);
113 if (!_points.empty() && _points.back()._timestamp > _sample._timestamp) {
114 if (deadrec_cat.is_debug()) {
116 <<
"*** timestamp out of order " << _points.back()._timestamp <<
" " 117 << _sample._timestamp <<
"\n";
125 _last_point_before = -1;
126 _last_point_after = -1;
128 _points.push_back(_sample);
130 }
else if (!_points.empty() && _points.back()._timestamp == _sample._timestamp) {
131 if (deadrec_cat.is_debug()) {
133 <<
"*** same timestamp\n";
137 _points.back() = _sample;
139 }
else if ((
int)_points.size() >= max_position_reports) {
140 if (deadrec_cat.is_debug()) {
142 <<
"*** dropped oldest position report\n";
147 --_last_point_before;
150 _points.push_back(_sample);
154 _points.push_back(_sample);
168 if (deadrec_cat.is_debug()) {
170 <<
"clear_positions " << reset_velocity <<
"\n";
174 _last_point_before = -1;
175 _last_point_after = -1;
176 _smooth_position_known =
false;
177 _has_most_recent_timestamp =
false;
179 if (reset_velocity) {
180 _smooth_forward_velocity = 0.0;
181 _smooth_lateral_velocity = 0.0;
182 _smooth_rotational_velocity = 0.0;
198 if (deadrec_cat.is_spam()) {
200 << _points.size() <<
" points\n";
203 if (_points.empty()) {
208 if (_smooth_position_known) {
209 double age = timestamp - _smooth_timestamp;
210 if (age > _reset_velocity_age) {
211 if (deadrec_cat.is_debug()) {
213 <<
"points empty; reset velocity, age = " << age <<
"\n";
215 _smooth_forward_velocity = 0.0;
216 _smooth_lateral_velocity = 0.0;
217 _smooth_rotational_velocity = 0.0;
220 bool result = _smooth_position_changed;
221 _smooth_position_changed =
false;
223 if (deadrec_cat.is_spam()) {
225 <<
" no points: " << result <<
"\n";
229 if (_smooth_mode == SM_off) {
233 bool result = _smooth_position_changed;
234 _smooth_position_changed =
false;
236 if (deadrec_cat.is_spam()) {
238 <<
" disabled: " << result <<
"\n";
244 double orig_timestamp = timestamp;
246 if (_accept_clock_skew) {
247 timestamp -= get_avg_timestamp_delay();
250 if (deadrec_cat.is_spam()) {
252 <<
"time = " << timestamp <<
", " << _points.size()
253 <<
" points, last = " << _last_point_before <<
", " 254 << _last_point_after <<
"\n";
255 deadrec_cat.spam(
false)
257 for (
int pi = 0; pi < (int)_points.size(); pi++) {
258 deadrec_cat.spam(
false) << _points[pi]._timestamp <<
" ";
260 deadrec_cat.spam(
false) <<
"\n";
264 int point_way_before = -1;
265 int point_before = -1;
266 double timestamp_before = 0.0;
267 int point_after = -1;
268 double timestamp_after = 0.0;
270 int num_points = _points.size();
275 i = std::max(0, _last_point_before);
276 while (i < num_points && _points[i]._timestamp < timestamp) {
278 timestamp_before = _points[i]._timestamp;
281 point_way_before = std::max(point_before - 1, -1);
285 if (i < num_points) {
287 timestamp_after = _points[i]._timestamp;
290 if (deadrec_cat.is_spam()) {
292 <<
" found points (" << point_way_before <<
") " << point_before
293 <<
", " << point_after <<
"\n";
296 if (point_before < 0) {
297 nassertr(point_after >= 0,
false);
299 bool result = !(_last_point_before == point_before &&
300 _last_point_after == point_after);
302 set_smooth_pos(point._pos, point._hpr, timestamp);
303 _smooth_forward_velocity = 0.0;
304 _smooth_lateral_velocity = 0.0;
305 _smooth_rotational_velocity = 0.0;
306 _last_point_before = point_before;
307 _last_point_after = point_after;
308 if (deadrec_cat.is_spam()) {
310 <<
" only an after point: " << _last_point_before <<
", " 311 << _last_point_after <<
"\n";
318 if (point_after < 0 && _prediction_mode != PM_off) {
322 if (point_way_before >= 0) {
327 point_after = point_before;
328 timestamp_after = timestamp_before;
329 point_before = point_way_before;
330 timestamp_before = point._timestamp;
332 if (timestamp > timestamp_after + _max_position_age) {
334 timestamp = timestamp_after + _max_position_age;
339 if (point_after < 0) {
342 if (point_way_before >= 0) {
345 if (deadrec_cat.is_spam()) {
347 <<
" previous two\n";
349 linear_interpolate(point_way_before, point_before, timestamp_before);
352 if (deadrec_cat.is_spam()) {
358 set_smooth_pos(point._pos, point._hpr, timestamp);
361 double age = timestamp - timestamp_before;
362 if (age > _reset_velocity_age) {
363 if (deadrec_cat.is_spam()) {
365 <<
" reset_velocity, age = " << age <<
"\n";
367 _smooth_forward_velocity = 0.0;
368 _smooth_lateral_velocity = 0.0;
369 _smooth_rotational_velocity = 0.0;
372 result = !(_last_point_before == point_before &&
373 _last_point_after == point_after);
376 if (deadrec_cat.is_spam()) {
378 <<
" normal interpolate\n";
383 if (point_b._pos == point_a._pos && point_b._hpr == point_a._hpr) {
385 if (deadrec_cat.is_spam()) {
387 <<
"Points are equivalent\n";
389 set_smooth_pos(point_b._pos, point_b._hpr, timestamp);
392 _smooth_forward_velocity = 0.0;
393 _smooth_lateral_velocity = 0.0;
394 _smooth_rotational_velocity = 0.0;
398 double age = (point_a._timestamp - point_b._timestamp);
400 if (_default_to_standing_still && (age > _max_position_age)) {
404 if (deadrec_cat.is_spam()) {
406 <<
" first point too old: age = " << age <<
"\n";
409 new_point._timestamp = point_a._timestamp - _expected_broadcast_period;
410 if (deadrec_cat.is_spam()) {
412 <<
" constructed new timestamp at " << new_point._timestamp
415 if (new_point._timestamp > point_b._timestamp) {
416 _points.insert(_points.begin() + point_after, new_point);
419 if (deadrec_cat.is_spam()) {
421 <<
" recursing after time adjustment.\n";
427 linear_interpolate(point_before, point_after, timestamp);
431 if (deadrec_cat.is_spam()) {
433 <<
" changing " << _last_point_before <<
", " << _last_point_after
434 <<
" to " << point_before <<
", " << point_after <<
"\n";
436 _last_point_before = point_before;
437 _last_point_after = point_after;
443 while (point_way_before > 0) {
444 nassertr(!_points.empty(), result);
448 --_last_point_before;
450 if (deadrec_cat.is_spam()) {
452 <<
" popping old point: " << _last_point_before <<
", " 453 << _last_point_after <<
"\n";
458 if (_prediction_mode == PM_off) {
459 if (point_way_before == 0) {
460 nassertr(!_points.empty(), result);
464 --_last_point_before;
466 if (deadrec_cat.is_spam()) {
468 <<
" popping way_before point: " << _last_point_before <<
", " 469 << _last_point_after <<
"\n";
493 if (deadrec_cat.is_spam()) {
495 <<
" result = " << result <<
"\n";
510 if (deadrec_cat.is_debug()) {
511 deadrec_cat.debug() <<
"get_latest_position\n";
513 if (_points.empty()) {
515 return _smooth_position_known;
519 set_smooth_pos(point._pos, point._hpr, point._timestamp);
520 _smooth_forward_velocity = 0.0;
521 _smooth_lateral_velocity = 0.0;
522 _smooth_rotational_velocity = 0.0;
530 output(std::ostream &out)
const {
531 out <<
"SmoothMover, " << _points.size() <<
" sample points.";
538 write(std::ostream &out)
const {
539 out <<
"SmoothMover, " << _points.size() <<
" sample points:\n";
540 int num_points = _points.size();
541 for (
int i = 0; i < num_points; i++) {
542 const SamplePoint &point = _points[i];
543 out <<
" " << i <<
". time = " << point._timestamp <<
" pos = " 544 << point._pos <<
" hpr = " << point._hpr <<
"\n";
553 set_smooth_pos(
const LPoint3 &pos,
const LVecBase3 &hpr,
555 if (deadrec_cat.is_spam()) {
557 <<
"set_smooth_pos(" << pos <<
", " << hpr <<
", " 558 << timestamp <<
")\n";
561 if (_smooth_pos != pos) {
563 _smooth_position_changed =
true;
565 if (_smooth_hpr != hpr) {
567 _smooth_position_changed =
true;
568 _computed_forward_axis =
false;
571 _smooth_timestamp = timestamp;
572 _smooth_position_known =
true;
580 linear_interpolate(
int point_before,
int point_after,
double timestamp) {
581 SamplePoint &point_b = _points[point_before];
582 const SamplePoint &point_a = _points[point_after];
584 double age = (point_a._timestamp - point_b._timestamp);
595 if (point_before == _last_point_before &&
596 point_after == _last_point_after) {
597 if (deadrec_cat.is_spam()) {
599 <<
" same two points\n";
604 double t = (timestamp - point_b._timestamp) / age;
606 if (deadrec_cat.is_spam()) {
608 <<
" interp " << t <<
": " << point_b._pos <<
" to " << point_a._pos
611 set_smooth_pos(point_b._pos + t * (point_a._pos - point_b._pos),
612 point_b._hpr + t * (point_a._hpr - point_b._hpr),
620 for (
int j = 0; j < 3; j++) {
621 if ((point_b._hpr[j] - point_a._hpr[j]) > 180.0) {
622 point_b._hpr[j] -= 360.0;
623 }
else if ((point_b._hpr[j] - point_a._hpr[j]) < -180.0) {
624 point_b._hpr[j] += 360.0;
628 double t = (timestamp - point_b._timestamp) / age;
629 LVector3 pos_delta = point_a._pos - point_b._pos;
630 LVecBase3 hpr_delta = point_a._hpr - point_b._hpr;
632 if (deadrec_cat.is_spam()) {
634 <<
" interp " << t <<
": " << point_b._pos <<
" to " << point_a._pos
637 set_smooth_pos(point_b._pos + t * pos_delta,
638 point_b._hpr + t * hpr_delta,
640 compute_velocity(pos_delta, hpr_delta, age);
648 compute_velocity(
const LVector3 &pos_delta,
const LVecBase3 &hpr_delta,
650 _smooth_rotational_velocity = hpr_delta[0] / age;
652 if (_directional_velocity) {
655 if (!_computed_forward_axis) {
657 compose_matrix(rot_mat, LVecBase3(1.0, 1.0, 1.0), _smooth_hpr);
658 _forward_axis = LVector3(0.0, 1.0, 0.0) * rot_mat;
660 if (deadrec_cat.is_spam()) {
662 <<
" compute forward_axis = " << _forward_axis <<
"\n";
666 LVector3 lateral_axis = _forward_axis.cross(LVector3(0.0,0.0,1.0));
668 PN_stdfloat forward_distance = pos_delta.dot(_forward_axis);
669 PN_stdfloat lateral_distance = pos_delta.dot(lateral_axis);
671 _smooth_forward_velocity = forward_distance / age;
672 _smooth_lateral_velocity = lateral_distance / age;
675 _smooth_forward_velocity = pos_delta.length();
676 _smooth_lateral_velocity = 0.0f;
679 if (deadrec_cat.is_spam()) {
681 <<
" compute_velocity = " << _smooth_forward_velocity <<
"\n";
691 record_timestamp_delay(
double timestamp) {
696 int delay = (int)((now - timestamp) * 1000.0);
697 if (_timestamp_delays.
full()) {
698 _net_timestamp_delay -= _timestamp_delays.
front();
701 _net_timestamp_delay += delay;
704 _last_heard_from = now;
717 for (pi = _points.begin(); pi != _points.end(); pi++) {
719 (*pi)._pos = np.
get_pos(new_parent);
720 (*pi)._hpr = np.
get_hpr(new_parent);
726 _sample._pos = np.
get_pos(new_parent);
727 _sample._hpr = np.
get_hpr(new_parent);
730 _smooth_pos = np.
get_pos(new_parent);
731 _smooth_hpr = np.
get_hpr(new_parent);
733 _computed_forward_axis =
false;
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
void push_back(const Thing &t)
Adds an item to the end of the buffer.
void set_pos_hpr(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the translation and rotation component of the transform, leaving scale untouched.
void clear_positions(bool reset_velocity)
Erases all the old position reports.
bool get_latest_position()
Updates the smooth_pos (and smooth_hpr, etc.) members to reflect the absolute latest position known f...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void mark_position()
Stores the position, orientation, and timestamp (if relevant) indicated by previous calls to set_pos(...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool compute_smooth_position()
Computes the smoothed position (and orientation) of the mover at the indicated point in time,...
bool full() const
Returns true if the buffer is full; if this is true, push_back() will fail.
void handle_wrt_reparent(NodePath &old_parent, NodePath &new_parent)
Node is being wrtReparented, update recorded sample positions to reflect new parent.
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
void pop_front()
Removes the first item from the buffer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LVecBase3 get_hpr() const
Retrieves the rotation component of the transform.
LPoint3 get_pos() const
Retrieves the translation component of the transform.
const Thing & front() const
Returns a reference to the first item in the queue.
void detach_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from its parent, but does not immediately delete it.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...