Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

direct/src/deadrec/smoothMover.cxx

Go to the documentation of this file.
00001 // Filename: smoothMover.cxx
00002 // Created by:  drose (19Oct01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "smoothMover.h"
00020 #include "notify.h"
00021 #include "compose_matrix.h"
00022 
00023 SmoothMover::SmoothMode SmoothMover::_smooth_mode = SmoothMover::SM_off;
00024 SmoothMover::PredictionMode SmoothMover::_prediction_mode = SmoothMover::PM_off;
00025 double SmoothMover::_delay = 0.2;
00026 bool SmoothMover::_accept_clock_skew = true;
00027 double SmoothMover::_max_position_age = 0.25;
00028 double SmoothMover::_reset_velocity_age = 0.3;
00029 
00030 ////////////////////////////////////////////////////////////////////
00031 //     Function: SmoothMover::Constructor
00032 //       Access: Published
00033 //  Description: 
00034 ////////////////////////////////////////////////////////////////////
00035 SmoothMover::
00036 SmoothMover() {
00037   _scale.set(1.0, 1.0, 1.0);
00038   _sample._pos.set(0.0, 0.0, 0.0);
00039   _sample._hpr.set(0.0, 0.0, 0.0);
00040   _sample._timestamp = 0.0;
00041 
00042   _smooth_pos.set(0.0, 0.0, 0.0);
00043   _smooth_hpr.set(0.0, 0.0, 0.0);
00044   _smooth_mat = LMatrix4f::ident_mat();
00045   _smooth_timestamp = 0.0;
00046   _smooth_position_known = false;
00047   _smooth_position_changed = true;
00048   _computed_smooth_mat = true;
00049 
00050   _smooth_forward_velocity = 0.0;
00051   _smooth_rotational_velocity = 0.0;
00052 
00053   _last_point_before = -1;
00054   _last_point_after = -1;
00055 
00056   _net_timestamp_delay = 0;
00057   // Record one delay of 0 on the top of the delays array, just to
00058   // guarantee that the array is never completely empty.
00059   _timestamp_delays.push_back(0);
00060 
00061   _last_heard_from = 0.0;
00062 }
00063 
00064 ////////////////////////////////////////////////////////////////////
00065 //     Function: SmoothMover::Destructor
00066 //       Access: Published
00067 //  Description: 
00068 ////////////////////////////////////////////////////////////////////
00069 SmoothMover::
00070 ~SmoothMover() {
00071 }
00072 
00073 ////////////////////////////////////////////////////////////////////
00074 //     Function: SmoothMover::set_mat
00075 //       Access: Published
00076 //  Description: Specifies the scale, hpr, and pos for the SmoothMover
00077 //               at some particular point, based on the matrix.
00078 ////////////////////////////////////////////////////////////////////
00079 bool SmoothMover::
00080 set_mat(const LMatrix4f &mat) {
00081   bool result = false;
00082 
00083   LVecBase3f scale, hpr, pos;
00084   if (decompose_matrix(mat, scale, hpr, pos)) {
00085     result = set_scale(scale) | set_hpr(hpr) | set_pos(pos);
00086   }
00087   return result;
00088 }
00089 
00090 ////////////////////////////////////////////////////////////////////
00091 //     Function: SmoothMover::mark_position
00092 //       Access: Published
00093 //  Description: Stores the position, orientation, and timestamp (if
00094 //               relevant) indicated by previous calls to set_pos(),
00095 //               set_hpr(), and set_timestamp() in a new position
00096 //               report.
00097 //
00098 //               When compute_smooth_position() is called, it uses
00099 //               these stored position reports to base its computation
00100 //               of the known position.
00101 ////////////////////////////////////////////////////////////////////
00102 void SmoothMover::
00103 mark_position() {
00104   if (_smooth_mode == SM_off) {
00105     // With smoothing disabled, mark_position() simply stores its
00106     // current position in the smooth_position members.
00107 
00108     // In this mode, we also ignore the supplied timestamp, and just
00109     // use the current frame time--there's no need to risk trusting
00110     // the timestamp from another client.
00111     double timestamp = ClockObject::get_global_clock()->get_frame_time();
00112 
00113     // We also need to compute the velocity here.
00114     if (_smooth_position_known) {
00115       LVector3f pos_delta = _sample._pos - _smooth_pos;
00116       LVecBase3f hpr_delta = _sample._hpr - _smooth_hpr;
00117       double age = timestamp - _smooth_timestamp;
00118       age = min(age, _max_position_age);
00119 
00120       set_smooth_pos(_sample._pos, _sample._hpr, timestamp);
00121       if (age != 0.0) {
00122         compute_velocity(pos_delta, hpr_delta, age);
00123       }
00124 
00125     } else {
00126       // No velocity is possible, just position and orientation.
00127       set_smooth_pos(_sample._pos, _sample._hpr, timestamp);
00128     }
00129 
00130   } else {
00131     // Otherwise, smoothing is in effect and we store a true position
00132     // report.
00133     
00134     if (_points.full()) {
00135       // If we have too many position reports, throw away the oldest
00136       // one.
00137       _points.pop_front();
00138 
00139       // That invalidates the index numbers.
00140       _last_point_before = -1;
00141       _last_point_after = -1;
00142     }
00143     
00144     _points.push_back(_sample);
00145   }
00146 }
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: SmoothMover::clear_positions
00150 //       Access: Published
00151 //  Description: Erases all the old position reports.  This should be
00152 //               done, for instance, prior to teleporting the avatar
00153 //               to a new position; otherwise, the smoother might try
00154 //               to lerp the avatar there.  If reset_velocity is true,
00155 //               the velocity is also reset to 0.
00156 ////////////////////////////////////////////////////////////////////
00157 void SmoothMover::
00158 clear_positions(bool reset_velocity) {
00159   while (!_points.empty()) {
00160     _points.pop_front();
00161   }
00162   _last_point_before = -1;
00163   _last_point_after = -1;
00164   _smooth_position_known = false;
00165 
00166   if (reset_velocity) {
00167     _smooth_forward_velocity = 0.0;
00168     _smooth_rotational_velocity = 0.0;
00169   }
00170 }
00171 
00172 ////////////////////////////////////////////////////////////////////
00173 //     Function: SmoothMover::compute_smooth_position
00174 //       Access: Published
00175 //  Description: Computes the smoothed position (and orientation) of
00176 //               the mover at the indicated point in time, based on
00177 //               the previous position reports.  After this call has
00178 //               been made, get_smooth_pos() etc. may be called to
00179 //               retrieve the smoothed position.
00180 //
00181 //               The return value is true if the value has changed (or
00182 //               might have changed) since the last call to
00183 //               compute_smooth_position(), or false if it remains the
00184 //               same.
00185 ////////////////////////////////////////////////////////////////////
00186 bool SmoothMover::
00187 compute_smooth_position(double timestamp) {
00188   if (_points.empty()) {
00189     // With no position reports available, this function does nothing,
00190     // except to make sure that our velocity gets reset to zero after
00191     // a period of time.
00192 
00193     if (_smooth_position_known) {
00194       double age = timestamp - _smooth_timestamp;
00195       if (age > _reset_velocity_age) {
00196         _smooth_forward_velocity = 0.0;
00197         _smooth_rotational_velocity = 0.0;
00198       }
00199     }
00200     bool result = _smooth_position_changed;
00201     _smooth_position_changed = false;
00202     return result;
00203   }
00204   if (_smooth_mode == SM_off) {
00205     // With smoothing disabled, this function also does nothing,
00206     // except to ensure that any old bogus position reports are
00207     // cleared.
00208     clear_positions(false);
00209     bool result = _smooth_position_changed;
00210     _smooth_position_changed = false;
00211     return result;
00212   }
00213 
00214   // First, back up in time by the specified delay factor.
00215   double orig_timestamp = timestamp;
00216   timestamp -= _delay;
00217   if (_accept_clock_skew) {
00218     timestamp -= get_avg_timestamp_delay();
00219   }
00220 
00221   // Now look for the two bracketing position reports.
00222   int point_way_before = -1;
00223   int point_before = -1;
00224   double timestamp_before = 0.0;
00225   int point_after = -1;
00226   double timestamp_after = 0.0;
00227 
00228   // This loop doesn't make any assumptions about the ordering of
00229   // timestamps in the queue.  We could probably make it faster by
00230   // assuming timestamps are ordered from oldest to newest (as they
00231   // are generally guaranteed to be, except for the minor detail of
00232   // clients occasionally resetting their clocks).
00233   int num_points = _points.size();
00234   for (int i = 0; i < num_points; i++) {
00235     const SamplePoint &point = _points[i];
00236     if (point._timestamp < timestamp) {
00237       // This point is before the desired time.  Find the newest of
00238       // these.
00239       if (point_before == -1 || point._timestamp > timestamp_before) {
00240         point_way_before = point_before;
00241         point_before = i;
00242         timestamp_before = point._timestamp;
00243       }
00244     }
00245     if (point._timestamp >= timestamp) {
00246       // This point is after the desired time.  Find the oldest of
00247       // these.
00248       if (point_after == -1 || point._timestamp < timestamp_after) {
00249         point_after = i;
00250         timestamp_after = point._timestamp;
00251       }
00252     }
00253   }
00254 
00255   if (point_before == -1) {
00256     nassertr(point_after != -1, false);
00257     // If we only have an after point, we have to start there.
00258     bool result = !(_last_point_before == point_before && 
00259                     _last_point_after == point_after);
00260     const SamplePoint &point = _points[point_after];
00261     set_smooth_pos(point._pos, point._hpr, timestamp);
00262     _smooth_forward_velocity = 0.0;
00263     _smooth_rotational_velocity = 0.0;
00264     _last_point_before = point_before;
00265     _last_point_after = point_after;
00266     return result;
00267   }
00268 
00269   bool result = true;
00270 
00271   if (point_after == -1 && _prediction_mode != PM_off) {
00272     // With prediction in effect, we're allowed to anticipate where
00273     // the avatar is going by a tiny bit, if we don't have current
00274     // enough data.  This works only if we have at least two points of
00275     // old data.
00276     if (point_way_before != -1) {
00277       // To implement simple prediction, we simply back up in time to
00278       // the previous two timestamps, and base our linear
00279       // interpolation off of those two, extending into the future.
00280       SamplePoint &point = _points[point_way_before];
00281       point_after = point_before;
00282       timestamp_after = timestamp_before;
00283       point_before = point_way_before;
00284       timestamp_before = point._timestamp;
00285       
00286       if (timestamp > timestamp_after + _max_position_age) {
00287         // Don't allow the prediction to get too far into the
00288         // future.
00289         timestamp = timestamp_after + _max_position_age;
00290       }
00291     }
00292   }
00293 
00294   if (point_after == -1) {
00295     // If we only have a before point even after we've checked for the
00296     // possibility of using prediction, then we have to stop there.
00297     if (point_way_before != -1) {
00298       // Use the previous two points, if we've got 'em, so we can
00299       // still reflect the avatar's velocity.
00300       linear_interpolate(point_way_before, point_before, timestamp_before);
00301 
00302     } else {
00303       // If we really only have one point, use it.
00304       const SamplePoint &point = _points[point_before];
00305       set_smooth_pos(point._pos, point._hpr, timestamp);
00306     }
00307 
00308     if (orig_timestamp - _last_heard_from > _reset_velocity_age) {
00309       // Furthermore, if we haven't heard from this client in a while,
00310       // reset the velocity.  This decision is based entirely on our
00311       // local timestamps, not on the other client's reported
00312       // timestamps.
00313       _smooth_forward_velocity = 0.0;
00314       _smooth_rotational_velocity = 0.0;
00315     }
00316 
00317     result = !(_last_point_before == point_before && 
00318                _last_point_after == point_after);
00319     _last_point_before = point_before;
00320     _last_point_after = point_after;
00321 
00322   } else {
00323     // If we have two points, we can linearly interpolate between them.
00324     linear_interpolate(point_before, point_after, timestamp);
00325   }
00326 
00327   // Assume we'll never get another compute_smooth_position() request
00328   // for an older time than this, and remove all the timestamps at the
00329   // head of the queue before point_way_before.
00330   double timestamp_way_before = _points[point_way_before]._timestamp;
00331   while (!_points.empty() && _points.front()._timestamp < timestamp_way_before) {
00332     _points.pop_front();
00333 
00334     // This invalidates the index numbers.
00335     _last_point_before = -1;
00336     _last_point_after = -1;
00337   }
00338 
00339   return result;
00340 }
00341 
00342 ////////////////////////////////////////////////////////////////////
00343 //     Function: SmoothMover::get_latest_position
00344 //       Access: Published
00345 //  Description: Updates the smooth_pos (and smooth_hpr, etc.) members
00346 //               to reflect the absolute latest position known for
00347 //               this avatar.  This may result in a pop to the most
00348 //               recent position.
00349 //
00350 //               Returns true if the latest position is known, false
00351 //               otherwise.
00352 ////////////////////////////////////////////////////////////////////
00353 bool SmoothMover::
00354 get_latest_position() {
00355   if (_points.empty()) {
00356     // Nothing to do if there are no points.
00357     return _smooth_position_known;
00358   }
00359 
00360   const SamplePoint &point = _points.back();
00361   set_smooth_pos(point._pos, point._hpr, point._timestamp);
00362   _smooth_forward_velocity = 0.0;
00363   _smooth_rotational_velocity = 0.0;
00364   return true;
00365 }
00366 
00367 ////////////////////////////////////////////////////////////////////
00368 //     Function: SmoothMover::output
00369 //       Access: Published
00370 //  Description: 
00371 ////////////////////////////////////////////////////////////////////
00372 void SmoothMover::
00373 output(ostream &out) const {
00374   out << "SmoothMover, " << _points.size() << " sample points.";
00375 }
00376 
00377 ////////////////////////////////////////////////////////////////////
00378 //     Function: SmoothMover::write
00379 //       Access: Published
00380 //  Description: 
00381 ////////////////////////////////////////////////////////////////////
00382 void SmoothMover::
00383 write(ostream &out) const {
00384   out << "SmoothMover, " << _points.size() << " sample points:\n";
00385   int num_points = _points.size();
00386   for (int i = 0; i < num_points; i++) {
00387     const SamplePoint &point = _points[i];
00388     out << "  " << i << ". time = " << point._timestamp << " pos = " 
00389         << point._pos << " hpr = " << point._hpr << "\n";
00390   }
00391 }
00392 
00393 ////////////////////////////////////////////////////////////////////
00394 //     Function: SmoothMover::set_smooth_pos
00395 //       Access: Private
00396 //  Description: Sets the computed smooth position and orientation for
00397 //               the indicated timestamp.
00398 ////////////////////////////////////////////////////////////////////
00399 void SmoothMover::
00400 set_smooth_pos(const LPoint3f &pos, const LVecBase3f &hpr,
00401                double timestamp) {
00402   _smooth_pos = pos;
00403   _smooth_hpr = hpr;
00404   _smooth_timestamp = timestamp;
00405   _smooth_position_known = true;
00406   _smooth_position_changed = true;
00407   _computed_smooth_mat = false;
00408 }
00409 
00410 ////////////////////////////////////////////////////////////////////
00411 //     Function: SmoothMover::compose_smooth_mat
00412 //       Access: Private
00413 //  Description: Computes the smooth_mat based on smooth_pos and
00414 //               smooth_hpr.
00415 ////////////////////////////////////////////////////////////////////
00416 void SmoothMover::
00417 compose_smooth_mat() {
00418   compose_matrix(_smooth_mat, _scale, _smooth_hpr, _smooth_pos);
00419   _computed_smooth_mat = true;
00420 }
00421 
00422 ////////////////////////////////////////////////////////////////////
00423 //     Function: SmoothMover::linear_interpolate
00424 //       Access: Private
00425 //  Description: Interpolates the smooth position linearly between the
00426 //               two bracketing position reports.
00427 ////////////////////////////////////////////////////////////////////
00428 void SmoothMover::
00429 linear_interpolate(int point_before, int point_after, double timestamp) {
00430   SamplePoint &point_b = _points[point_before];
00431   const SamplePoint &point_a = _points[point_after];
00432 
00433   double age = (point_a._timestamp - point_b._timestamp);
00434 
00435   if (point_before == _last_point_before && 
00436       point_after == _last_point_after) {
00437     // If these are the same two points we found last time (which is
00438     // likely), we can save a bit of work.
00439     double t = (timestamp - point_b._timestamp) / age;
00440     set_smooth_pos(point_b._pos + t * (point_a._pos - point_b._pos),
00441                    point_b._hpr + t * (point_a._hpr - point_b._hpr),
00442                    timestamp);
00443 
00444     // The velocity remains the same as last time.
00445 
00446   } else {
00447     _last_point_before = point_before;
00448     _last_point_after = point_after;
00449     
00450     if (age > _max_position_age) {
00451       // If the first point is too old, assume there were a lot of
00452       // implicit standing still messages that weren't sent.  Reset
00453       // the first point's timestamp to reflect this.
00454       point_b._timestamp = min(timestamp, point_a._timestamp - _max_position_age);
00455       age = (point_a._timestamp - point_b._timestamp);
00456     }
00457     
00458     // To interpolate the hpr's, we must first make sure that both
00459     // angles are on the same side of the discontinuity.
00460     for (int j = 0; j < 3; j++) {
00461       if ((point_b._hpr[j] - point_a._hpr[j]) > 180.0) {
00462         point_b._hpr[j] -= 360.0;
00463       } else if ((point_b._hpr[j] - point_a._hpr[j]) < -180.0) {
00464         point_b._hpr[j] += 360.0;
00465       }
00466     }
00467     
00468     double t = (timestamp - point_b._timestamp) / age;
00469     LVector3f pos_delta = point_a._pos - point_b._pos;
00470     LVecBase3f hpr_delta = point_a._hpr - point_b._hpr;
00471 
00472     set_smooth_pos(point_b._pos + t * pos_delta, 
00473                    point_b._hpr + t * hpr_delta, 
00474                    timestamp);
00475     compute_velocity(pos_delta, hpr_delta, age);
00476   }
00477 }
00478 
00479 ////////////////////////////////////////////////////////////////////
00480 //     Function: SmoothMover::compute_velocity
00481 //       Access: Private
00482 //  Description: Computes the forward and rotational velocities of the
00483 //               moving object.
00484 ////////////////////////////////////////////////////////////////////
00485 void SmoothMover::
00486 compute_velocity(const LVector3f &pos_delta, const LVecBase3f &hpr_delta,
00487                  double age) {
00488   // Also compute the velocity.  To get just the forward component
00489   // of velocity, we need to project the velocity vector onto the y
00490   // axis, as rotated by the current hpr.
00491   LMatrix3f rot_mat;
00492   compose_matrix(rot_mat, LVecBase3f(1.0, 1.0, 1.0), _smooth_hpr);
00493   LVector3f y_axis = LVector3f(0.0, 1.0, 0.0) * rot_mat;
00494   float forward_distance = pos_delta.dot(y_axis);
00495   
00496   _smooth_forward_velocity = forward_distance / age;
00497   _smooth_rotational_velocity = hpr_delta[0] / age;
00498 }
00499 
00500 ////////////////////////////////////////////////////////////////////
00501 //     Function: SmoothMover::record_timestamp_delay
00502 //       Access: Private
00503 //  Description: Records the delay measured in receiving this
00504 //               particular timestamp.  The average delay of the last
00505 //               n timestamps will be used to smooth the motion
00506 //               properly.
00507 ////////////////////////////////////////////////////////////////////
00508 void SmoothMover::
00509 record_timestamp_delay(double timestamp) {
00510   double now = ClockObject::get_global_clock()->get_frame_time();
00511 
00512   // Convert the delay to an integer number of milliseconds.  Integers
00513   // are better than doubles because they don't accumulate errors over
00514   // time.
00515   int delay = (int)((now - timestamp) * 1000.0);
00516   if (_timestamp_delays.full()) {
00517     _net_timestamp_delay -= _timestamp_delays.front();
00518     _timestamp_delays.pop_front();
00519   }
00520   _net_timestamp_delay += delay;
00521   _timestamp_delays.push_back(delay);
00522 
00523   _last_heard_from = now;
00524 }

Generated on Fri May 2 01:37:18 2003 for Direct by doxygen1.3