Panda3D
smoothMover.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file smoothMover.cxx
10  * @author drose
11  * @date 2001-10-19
12  */
13 
14 #include "smoothMover.h"
15 #include "pnotify.h"
16 #include "config_deadrec.h"
17 
18 /**
19  *
20  */
21 SmoothMover::
22 SmoothMover() {
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;
26 
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;
34 
35  _smooth_forward_velocity = 0.0;
36  _smooth_lateral_velocity = 0.0;
37  _smooth_rotational_velocity = 0.0;
38 
39  _has_most_recent_timestamp = false;
40 
41  _last_point_before = -1;
42  _last_point_after = -1;
43 
44  _net_timestamp_delay = 0;
45  // Record one delay of 0 on the top of the delays array, just to guarantee
46  // that the array is never completely empty.
47  _timestamp_delays.push_back(0);
48 
49  _last_heard_from = 0.0;
50 
51  _smooth_mode = SM_off;
52  _prediction_mode = PM_off;
53  _delay = 0.2;
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;
60 }
61 
62 /**
63  *
64  */
65 SmoothMover::
66 ~SmoothMover() {
67 }
68 
69 /**
70  * Stores the position, orientation, and timestamp (if relevant) indicated by
71  * previous calls to set_pos(), set_hpr(), and set_timestamp() in a new
72  * position report.
73  *
74  * When compute_smooth_position() is called, it uses these stored position
75  * reports to base its computation of the known position.
76  */
77 void SmoothMover::
79  /*
80  if (deadrec_cat.is_debug()) {
81  deadrec_cat.debug() << "mark_position\n";
82  }
83  */
84  if (_smooth_mode == SM_off) {
85  // With smoothing disabled, mark_position() simply stores its current
86  // position in the smooth_position members.
87 
88  // In this mode, we also ignore the supplied timestamp, and just use the
89  // current frame time--there's no need to risk trusting the timestamp from
90  // another client.
91  double timestamp = ClockObject::get_global_clock()->get_frame_time();
92 
93  // We also need to compute the velocity here.
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);
99 
100  set_smooth_pos(_sample._pos, _sample._hpr, timestamp);
101  if (age != 0.0) {
102  compute_velocity(pos_delta, hpr_delta, age);
103  }
104 
105  } else {
106  // No velocity is possible, just position and orientation.
107  set_smooth_pos(_sample._pos, _sample._hpr, timestamp);
108  }
109 
110  } else {
111  // Otherwise, smoothing is in effect and we store a true position report.
112 
113  if (!_points.empty() && _points.back()._timestamp > _sample._timestamp) {
114  if (deadrec_cat.is_debug()) {
115  deadrec_cat.debug()
116  << "*** timestamp out of order " << _points.back()._timestamp << " "
117  << _sample._timestamp << "\n";
118  }
119 
120  // If we get a timestamp out of order, one of us must have just reset
121  // our clock. Flush the sequence and start again.
122  _points.clear();
123 
124  // That invalidates the index numbers.
125  _last_point_before = -1;
126  _last_point_after = -1;
127 
128  _points.push_back(_sample);
129 
130  } else if (!_points.empty() && _points.back()._timestamp == _sample._timestamp) {
131  if (deadrec_cat.is_debug()) {
132  deadrec_cat.debug()
133  << "*** same timestamp\n";
134  }
135  // If the new timestamp is the same as the last timestamp, the value
136  // simply replaces the previous value.
137  _points.back() = _sample;
138 
139  } else if ((int)_points.size() >= max_position_reports) {
140  if (deadrec_cat.is_debug()) {
141  deadrec_cat.debug()
142  << "*** dropped oldest position report\n";
143  }
144  // If we have too many position reports, throw away the oldest one.
145  _points.pop_front();
146 
147  --_last_point_before;
148  --_last_point_after;
149 
150  _points.push_back(_sample);
151 
152  } else {
153  // In the ordinary case, just add another sample.
154  _points.push_back(_sample);
155  }
156  }
157  // cout << "mark_position: " << _points.back()._pos << endl;
158 }
159 
160 /**
161  * Erases all the old position reports. This should be done, for instance,
162  * prior to teleporting the avatar to a new position; otherwise, the smoother
163  * might try to lerp the avatar there. If reset_velocity is true, the
164  * velocity is also reset to 0.
165  */
166 void SmoothMover::
167 clear_positions(bool reset_velocity) {
168  if (deadrec_cat.is_debug()) {
169  deadrec_cat.debug()
170  << "clear_positions " << reset_velocity << "\n";
171  }
172 
173  _points.clear();
174  _last_point_before = -1;
175  _last_point_after = -1;
176  _smooth_position_known = false;
177  _has_most_recent_timestamp = false;
178 
179  if (reset_velocity) {
180  _smooth_forward_velocity = 0.0;
181  _smooth_lateral_velocity = 0.0;
182  _smooth_rotational_velocity = 0.0;
183  }
184 }
185 
186 /**
187  * Computes the smoothed position (and orientation) of the mover at the
188  * indicated point in time, based on the previous position reports. After
189  * this call has been made, get_smooth_pos() etc. may be called to retrieve
190  * the smoothed position.
191  *
192  * The return value is true if the value has changed (or might have changed)
193  * since the last call to compute_smooth_position(), or false if it remains
194  * the same.
195  */
196 bool SmoothMover::
197 compute_smooth_position(double timestamp) {
198  if (deadrec_cat.is_spam()) {
199  deadrec_cat.spam()
200  << _points.size() << " points\n";
201  }
202 
203  if (_points.empty()) {
204  // With no position reports available, this function does nothing, except
205  // to make sure that our velocity gets reset to zero after a period of
206  // time.
207 
208  if (_smooth_position_known) {
209  double age = timestamp - _smooth_timestamp;
210  if (age > _reset_velocity_age) {
211  if (deadrec_cat.is_debug()) {
212  deadrec_cat.debug()
213  << "points empty; reset velocity, age = " << age << "\n";
214  }
215  _smooth_forward_velocity = 0.0;
216  _smooth_lateral_velocity = 0.0;
217  _smooth_rotational_velocity = 0.0;
218  }
219  }
220  bool result = _smooth_position_changed;
221  _smooth_position_changed = false;
222 
223  if (deadrec_cat.is_spam()) {
224  deadrec_cat.spam()
225  << " no points: " << result << "\n";
226  }
227  return result;
228  }
229  if (_smooth_mode == SM_off) {
230  // With smoothing disabled, this function also does nothing, except to
231  // ensure that any old bogus position reports are cleared.
232  clear_positions(false);
233  bool result = _smooth_position_changed;
234  _smooth_position_changed = false;
235 
236  if (deadrec_cat.is_spam()) {
237  deadrec_cat.spam()
238  << " disabled: " << result << "\n";
239  }
240  return result;
241  }
242 
243  // First, back up in time by the specified delay factor.
244  double orig_timestamp = timestamp;
245  timestamp -= _delay;
246  if (_accept_clock_skew) {
247  timestamp -= get_avg_timestamp_delay();
248  }
249 
250  if (deadrec_cat.is_spam()) {
251  deadrec_cat.spam()
252  << "time = " << timestamp << ", " << _points.size()
253  << " points, last = " << _last_point_before << ", "
254  << _last_point_after << "\n";
255  deadrec_cat.spam(false)
256  << " ";
257  for (int pi = 0; pi < (int)_points.size(); pi++) {
258  deadrec_cat.spam(false) << _points[pi]._timestamp << " ";
259  }
260  deadrec_cat.spam(false) << "\n";
261  }
262 
263  // Now look for the two bracketing position reports.
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;
269 
270  int num_points = _points.size();
271  int i;
272 
273  // Find the newest of the points before the indicated time. Assume that
274  // this will be no older than _last_point_before.
275  i = std::max(0, _last_point_before);
276  while (i < num_points && _points[i]._timestamp < timestamp) {
277  point_before = i;
278  timestamp_before = _points[i]._timestamp;
279  ++i;
280  }
281  point_way_before = std::max(point_before - 1, -1);
282 
283  // Now the next point is presumably the oldest point after the indicated
284  // time.
285  if (i < num_points) {
286  point_after = i;
287  timestamp_after = _points[i]._timestamp;
288  }
289 
290  if (deadrec_cat.is_spam()) {
291  deadrec_cat.spam()
292  << " found points (" << point_way_before << ") " << point_before
293  << ", " << point_after << "\n";
294  }
295 
296  if (point_before < 0) {
297  nassertr(point_after >= 0, false);
298  // If we only have an after point, we have to start there.
299  bool result = !(_last_point_before == point_before &&
300  _last_point_after == point_after);
301  const SamplePoint &point = _points[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()) {
309  deadrec_cat.spam()
310  << " only an after point: " << _last_point_before << ", "
311  << _last_point_after << "\n";
312  }
313  return result;
314  }
315 
316  bool result = true;
317 
318  if (point_after < 0 && _prediction_mode != PM_off) {
319  // With prediction in effect, we're allowed to anticipate where the avatar
320  // is going by a tiny bit, if we don't have current enough data. This
321  // works only if we have at least two points of old data.
322  if (point_way_before >= 0) {
323  // To implement simple prediction, we simply back up in time to the
324  // previous two timestamps, and base our linear interpolation off of
325  // those two, extending into the future.
326  SamplePoint &point = _points[point_way_before];
327  point_after = point_before;
328  timestamp_after = timestamp_before;
329  point_before = point_way_before;
330  timestamp_before = point._timestamp;
331 
332  if (timestamp > timestamp_after + _max_position_age) {
333  // Don't allow the prediction to get too far into the future.
334  timestamp = timestamp_after + _max_position_age;
335  }
336  }
337  }
338 
339  if (point_after < 0) {
340  // If we only have a before point even after we've checked for the
341  // possibility of using prediction, then we have to stop there.
342  if (point_way_before >= 0) {
343  // Use the previous two points, if we've got 'em, so we can still
344  // reflect the avatar's velocity.
345  if (deadrec_cat.is_spam()) {
346  deadrec_cat.spam()
347  << " previous two\n";
348  }
349  linear_interpolate(point_way_before, point_before, timestamp_before);
350 
351  } else {
352  if (deadrec_cat.is_spam()) {
353  deadrec_cat.spam()
354  << " one point\n";
355  }
356  // If we really only have one point, use it.
357  const SamplePoint &point = _points[point_before];
358  set_smooth_pos(point._pos, point._hpr, timestamp);
359  }
360 
361  double age = timestamp - timestamp_before;
362  if (age > _reset_velocity_age) {
363  if (deadrec_cat.is_spam()) {
364  deadrec_cat.spam()
365  << " reset_velocity, age = " << age << "\n";
366  }
367  _smooth_forward_velocity = 0.0;
368  _smooth_lateral_velocity = 0.0;
369  _smooth_rotational_velocity = 0.0;
370  }
371 
372  result = !(_last_point_before == point_before &&
373  _last_point_after == point_after);
374  } else {
375  // If we have two points, we can linearly interpolate between them.
376  if (deadrec_cat.is_spam()) {
377  deadrec_cat.spam()
378  << " normal interpolate\n";
379  }
380  SamplePoint &point_b = _points[point_before];
381  const SamplePoint &point_a = _points[point_after];
382 
383  if (point_b._pos == point_a._pos && point_b._hpr == point_a._hpr) {
384  // The points are equivalent, so just return that.
385  if (deadrec_cat.is_spam()) {
386  deadrec_cat.spam()
387  << "Points are equivalent\n";
388  }
389  set_smooth_pos(point_b._pos, point_b._hpr, timestamp);
390 
391  // This implies that velocity is 0.
392  _smooth_forward_velocity = 0.0;
393  _smooth_lateral_velocity = 0.0;
394  _smooth_rotational_velocity = 0.0;
395 
396  } else {
397  // The points are different, so we have to do some work.
398  double age = (point_a._timestamp - point_b._timestamp);
399 
400  if (_default_to_standing_still && (age > _max_position_age)) {
401  // If the first point is too old, assume there were a lot of implicit
402  // standing still messages that weren't sent. Insert a new sample
403  // point to reflect this.
404  if (deadrec_cat.is_spam()) {
405  deadrec_cat.spam()
406  << " first point too old: age = " << age << "\n";
407  }
408  SamplePoint new_point = point_b;
409  new_point._timestamp = point_a._timestamp - _expected_broadcast_period;
410  if (deadrec_cat.is_spam()) {
411  deadrec_cat.spam()
412  << " constructed new timestamp at " << new_point._timestamp
413  << "\n";
414  }
415  if (new_point._timestamp > point_b._timestamp) {
416  _points.insert(_points.begin() + point_after, new_point);
417 
418  // Now we've monkeyed with the sequence. Start over.
419  if (deadrec_cat.is_spam()) {
420  deadrec_cat.spam()
421  << " recursing after time adjustment.\n";
422  }
423  return compute_smooth_position(orig_timestamp);
424  }
425  }
426 
427  linear_interpolate(point_before, point_after, timestamp);
428  }
429  }
430 
431  if (deadrec_cat.is_spam()) {
432  deadrec_cat.spam()
433  << " changing " << _last_point_before << ", " << _last_point_after
434  << " to " << point_before << ", " << point_after << "\n";
435  }
436  _last_point_before = point_before;
437  _last_point_after = point_after;
438 
439 
440  // Assume we'll never get another compute_smooth_position() request for an
441  // older time than this, and remove all the timestamps at the head of the
442  // queue up to but not including point_way_before.
443  while (point_way_before > 0) {
444  nassertr(!_points.empty(), result);
445  _points.pop_front();
446 
447  --point_way_before;
448  --_last_point_before;
449  --_last_point_after;
450  if (deadrec_cat.is_spam()) {
451  deadrec_cat.spam()
452  << " popping old point: " << _last_point_before << ", "
453  << _last_point_after << "\n";
454  }
455  }
456 
457  // If we are not using prediction mode, we can also remove point_way_before.
458  if (_prediction_mode == PM_off) {
459  if (point_way_before == 0) {
460  nassertr(!_points.empty(), result);
461  _points.pop_front();
462 
463  --point_way_before;
464  --_last_point_before;
465  --_last_point_after;
466  if (deadrec_cat.is_spam()) {
467  deadrec_cat.spam()
468  << " popping way_before point: " << _last_point_before << ", "
469  << _last_point_after << "\n";
470  }
471  }
472 
473  // And if there's only one point left, remove even that one after a while.
474  /* jbutler: commented this out, seems to cause the smoothing pop that occurs
475  when this object is stopped for a while then starts moving again
476  if (_points.size() == 1) {
477  double age = timestamp - _points.back()._timestamp;
478  if (deadrec_cat.is_spam()) {
479  deadrec_cat.spam()
480  << "considering clearing all points, age = " << age << "\n";
481  }
482  if (age > _reset_velocity_age) {
483  if (deadrec_cat.is_spam()) {
484  deadrec_cat.spam()
485  << "clearing all points.\n";
486  }
487  _points.clear();
488  }
489  }
490  */
491  }
492 
493  if (deadrec_cat.is_spam()) {
494  deadrec_cat.spam()
495  << " result = " << result << "\n";
496  }
497 
498  return result;
499 }
500 
501 /**
502  * Updates the smooth_pos (and smooth_hpr, etc.) members to reflect the
503  * absolute latest position known for this avatar. This may result in a pop
504  * to the most recent position.
505  *
506  * Returns true if the latest position is known, false otherwise.
507  */
508 bool SmoothMover::
510  if (deadrec_cat.is_debug()) {
511  deadrec_cat.debug() << "get_latest_position\n";
512  }
513  if (_points.empty()) {
514  // Nothing to do if there are no points.
515  return _smooth_position_known;
516  }
517 
518  const SamplePoint &point = _points.back();
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;
523  return true;
524 }
525 
526 /**
527  *
528  */
529 void SmoothMover::
530 output(std::ostream &out) const {
531  out << "SmoothMover, " << _points.size() << " sample points.";
532 }
533 
534 /**
535  *
536  */
537 void SmoothMover::
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";
545  }
546 }
547 
548 /**
549  * Sets the computed smooth position and orientation for the indicated
550  * timestamp.
551  */
552 void SmoothMover::
553 set_smooth_pos(const LPoint3 &pos, const LVecBase3 &hpr,
554  double timestamp) {
555  if (deadrec_cat.is_spam()) {
556  deadrec_cat.spam()
557  << "set_smooth_pos(" << pos << ", " << hpr << ", "
558  << timestamp << ")\n";
559  }
560 
561  if (_smooth_pos != pos) {
562  _smooth_pos = pos;
563  _smooth_position_changed = true;
564  }
565  if (_smooth_hpr != hpr) {
566  _smooth_hpr = hpr;
567  _smooth_position_changed = true;
568  _computed_forward_axis = false;
569  }
570 
571  _smooth_timestamp = timestamp;
572  _smooth_position_known = true;
573 }
574 
575 /**
576  * Interpolates the smooth position linearly between the two bracketing
577  * position reports.
578  */
579 void SmoothMover::
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];
583 
584  double age = (point_a._timestamp - point_b._timestamp);
585 
586  /*
587  Points::const_iterator pi;
588  cout << "linear_interpolate: ";
589  for (pi = _points.begin(); pi != _points.end(); ++pi) {
590  cout << "(" << (*pi)._pos << "), ";
591  }
592  cout << endl;
593  */
594 
595  if (point_before == _last_point_before &&
596  point_after == _last_point_after) {
597  if (deadrec_cat.is_spam()) {
598  deadrec_cat.spam()
599  << " same two points\n";
600  }
601 
602  // If these are the same two points we found last time (which is likely),
603  // we can save a bit of work.
604  double t = (timestamp - point_b._timestamp) / age;
605 
606  if (deadrec_cat.is_spam()) {
607  deadrec_cat.spam()
608  << " interp " << t << ": " << point_b._pos << " to " << point_a._pos
609  << "\n";
610  }
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),
613  timestamp);
614 
615  // The velocity remains the same as last time.
616 
617  } else {
618  // To interpolate the hpr's, we must first make sure that both angles are
619  // on the same side of the discontinuity.
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;
625  }
626  }
627 
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;
631 
632  if (deadrec_cat.is_spam()) {
633  deadrec_cat.spam()
634  << " interp " << t << ": " << point_b._pos << " to " << point_a._pos
635  << "\n";
636  }
637  set_smooth_pos(point_b._pos + t * pos_delta,
638  point_b._hpr + t * hpr_delta,
639  timestamp);
640  compute_velocity(pos_delta, hpr_delta, age);
641  }
642 }
643 
644 /**
645  * Computes the forward and rotational velocities of the moving object.
646  */
647 void SmoothMover::
648 compute_velocity(const LVector3 &pos_delta, const LVecBase3 &hpr_delta,
649  double age) {
650  _smooth_rotational_velocity = hpr_delta[0] / age;
651 
652  if (_directional_velocity) {
653  // To get just the forward component of velocity, we need to project the
654  // velocity vector onto the y axis, as rotated by the current hpr.
655  if (!_computed_forward_axis) {
656  LMatrix3 rot_mat;
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;
659 
660  if (deadrec_cat.is_spam()) {
661  deadrec_cat.spam()
662  << " compute forward_axis = " << _forward_axis << "\n";
663  }
664  }
665 
666  LVector3 lateral_axis = _forward_axis.cross(LVector3(0.0,0.0,1.0));
667 
668  PN_stdfloat forward_distance = pos_delta.dot(_forward_axis);
669  PN_stdfloat lateral_distance = pos_delta.dot(lateral_axis);
670 
671  _smooth_forward_velocity = forward_distance / age;
672  _smooth_lateral_velocity = lateral_distance / age;
673 
674  } else {
675  _smooth_forward_velocity = pos_delta.length();
676  _smooth_lateral_velocity = 0.0f;
677  }
678 
679  if (deadrec_cat.is_spam()) {
680  deadrec_cat.spam()
681  << " compute_velocity = " << _smooth_forward_velocity << "\n";
682  }
683 }
684 
685 /**
686  * Records the delay measured in receiving this particular timestamp. The
687  * average delay of the last n timestamps will be used to smooth the motion
688  * properly.
689  */
690 void SmoothMover::
691 record_timestamp_delay(double timestamp) {
693 
694  // Convert the delay to an integer number of milliseconds. Integers are
695  // better than doubles because they don't accumulate errors over time.
696  int delay = (int)((now - timestamp) * 1000.0);
697  if (_timestamp_delays.full()) {
698  _net_timestamp_delay -= _timestamp_delays.front();
699  _timestamp_delays.pop_front();
700  }
701  _net_timestamp_delay += delay;
702  _timestamp_delays.push_back(delay);
703 
704  _last_heard_from = now;
705 }
706 
707 /**
708  * Node is being wrtReparented, update recorded sample positions to reflect
709  * new parent
710  */
711 void SmoothMover::
712 handle_wrt_reparent(NodePath &old_parent, NodePath &new_parent) {
713  Points::iterator pi;
714  NodePath np = old_parent.attach_new_node("smoothMoverWrtReparent");
715 
716  // cout << "handle_wrt_reparent: ";
717  for (pi = _points.begin(); pi != _points.end(); pi++) {
718  np.set_pos_hpr((*pi)._pos, (*pi)._hpr);
719  (*pi)._pos = np.get_pos(new_parent);
720  (*pi)._hpr = np.get_hpr(new_parent);
721  // cout << "(" << (*pi)._pos << "), ";
722  }
723  // cout << endl;
724 
725  np.set_pos_hpr(_sample._pos, _sample._hpr);
726  _sample._pos = np.get_pos(new_parent);
727  _sample._hpr = np.get_hpr(new_parent);
728 
729  np.set_pos_hpr(_smooth_pos, _smooth_hpr);
730  _smooth_pos = np.get_pos(new_parent);
731  _smooth_hpr = np.get_hpr(new_parent);
732 
733  _computed_forward_axis = false;
734 
735  np.detach_node();
736 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
void push_back(const Thing &t)
Adds an item to the end of the buffer.
Definition: circBuffer.I:169
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.
Definition: nodePath.I:728
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(...
Definition: smoothMover.cxx:78
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,...
Definition: smoothMover.I:285
bool full() const
Returns true if the buffer is full; if this is true, push_back() will fail.
Definition: circBuffer.I:66
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 ...
Definition: clockObject.h:91
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...
Definition: nodePath.cxx:563
void pop_front()
Removes the first item from the buffer.
Definition: circBuffer.I:125
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LVecBase3 get_hpr() const
Retrieves the rotation component of the transform.
Definition: nodePath.cxx:1058
LPoint3 get_pos() const
Retrieves the translation component of the transform.
Definition: nodePath.cxx:992
const Thing & front() const
Returns a reference to the first item in the queue.
Definition: circBuffer.I:78
void detach_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from its parent, but does not immediately delete it.
Definition: nodePath.cxx:631
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161