Panda3D
Loading...
Searching...
No Matches
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 */
21SmoothMover::
22SmoothMover() {
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 */
65SmoothMover::
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 */
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 */
167clear_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 */
197compute_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 */
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 */
529void SmoothMover::
530output(std::ostream &out) const {
531 out << "SmoothMover, " << _points.size() << " sample points.";
532}
533
534/**
535 *
536 */
537void SmoothMover::
538write(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 */
552void SmoothMover::
553set_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 */
579void SmoothMover::
580linear_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 */
647void SmoothMover::
648compute_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 */
690void SmoothMover::
691record_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 */
712handle_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}
bool full() const
Returns true if the buffer is full; if this is true, push_back() will fail.
Definition circBuffer.I:66
const Thing & front() const
Returns a reference to the first item in the queue.
Definition circBuffer.I:78
void pop_front()
Removes the first item from the buffer.
Definition circBuffer.I:125
void push_back(const Thing &t)
Adds an item to the end of the buffer.
Definition circBuffer.I:169
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
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
LVecBase3 get_hpr() const
Retrieves the rotation component of the transform.
LPoint3 get_pos() const
Retrieves the translation component of the transform.
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:599
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 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:667
bool compute_smooth_position()
Computes the smoothed position (and orientation) of the mover at the indicated point in time,...
bool get_latest_position()
Updates the smooth_pos (and smooth_hpr, etc.) members to reflect the absolute latest position known f...
void handle_wrt_reparent(NodePath &old_parent, NodePath &new_parent)
Node is being wrtReparented, update recorded sample positions to reflect new parent.
void clear_positions(bool reset_velocity)
Erases all the old position reports.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.