Panda3D
 All Classes Functions Variables Enumerations
driveInterface.cxx
00001 // Filename: driveInterface.cxx
00002 // Created by:  drose (12Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "driveInterface.h"
00016 #include "config_tform.h"
00017 
00018 #include "compose_matrix.h"
00019 #include "mouseAndKeyboard.h"
00020 #include "mouseData.h"
00021 #include "clockObject.h"
00022 #include "modifierButtons.h"
00023 #include "keyboardButton.h"
00024 #include "mouseButton.h"
00025 #include "buttonEventList.h"
00026 #include "dataNodeTransmit.h"
00027 #include "dataGraphTraverser.h"
00028 
00029 TypeHandle DriveInterface::_type_handle;
00030 const PN_stdfloat DriveInterface::_hpr_quantize = 0.001;
00031 
00032 DriveInterface::KeyHeld::
00033 KeyHeld() {
00034   _down = false;
00035   _changed_time = 0.0f;
00036   _effect = 0.0f;
00037   _effect_at_change = 0.0f;
00038 }
00039 
00040 PN_stdfloat DriveInterface::KeyHeld::
00041 get_effect(PN_stdfloat ramp_up_time, PN_stdfloat ramp_down_time) {
00042   double elapsed = ClockObject::get_global_clock()->get_frame_time() - _changed_time;
00043   if (_down) {
00044     // We are currently holding down the key.  That means we base our
00045     // effect on the ramp_up_time.
00046     if (ramp_up_time == 0.0f) {
00047       _effect = 1.0f;
00048 
00049     } else {
00050       PN_stdfloat change = elapsed / ramp_up_time;
00051       _effect = min(_effect_at_change + change, (PN_stdfloat)1.0);
00052     }
00053   } else {
00054     // We are *not* currently holding down the key.  That means we
00055     // base our effect on the ramp_down_time.
00056     if (ramp_down_time == 0.0f) {
00057       _effect = 0.0f;
00058 
00059     } else {
00060       PN_stdfloat change = elapsed / ramp_down_time;
00061       _effect = max(_effect_at_change - change, (PN_stdfloat)0.0);
00062     }
00063   }
00064   return _effect;
00065 }
00066 
00067 void DriveInterface::KeyHeld::
00068 set_key(bool down) {
00069   if (_down != down) {
00070     _down = down;
00071     _changed_time = ClockObject::get_global_clock()->get_frame_time();
00072     _effect_at_change = _effect;
00073   }
00074 }
00075 
00076 void DriveInterface::KeyHeld::
00077 clear() {
00078   _down = false;
00079   _changed_time = 0.0f;
00080   _effect = 0.0f;
00081   _effect_at_change = 0.0f;
00082 }
00083 
00084 bool DriveInterface::KeyHeld::
00085 operator < (const DriveInterface::KeyHeld &other) const {
00086   if (_down != other._down) {
00087     // If one has the key held down and the other doesn't, the down
00088     // key wins.
00089     return _down;
00090   }
00091 
00092   // Otherwise, the most-recently changed key wins.
00093   return _changed_time > other._changed_time;
00094 }
00095 
00096 ////////////////////////////////////////////////////////////////////
00097 //     Function: DriveInterface::Constructor
00098 //       Access: Published
00099 //  Description:
00100 ////////////////////////////////////////////////////////////////////
00101 DriveInterface::
00102 DriveInterface(const string &name) : 
00103   MouseInterfaceNode(name) 
00104 {
00105   _xy_input = define_input("xy", EventStoreVec2::get_class_type());
00106   _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
00107 
00108   _transform_output = define_output("transform", TransformState::get_class_type());
00109   _velocity_output = define_output("velocity", EventStoreVec3::get_class_type());
00110 
00111   _transform = TransformState::make_identity();
00112   _velocity = new EventStoreVec3(LVector3::zero());
00113 
00114   _forward_speed = drive_forward_speed;
00115   _reverse_speed = drive_reverse_speed;
00116   _rotate_speed = drive_rotate_speed;
00117   _vertical_dead_zone = drive_vertical_dead_zone;
00118   _horizontal_dead_zone = drive_horizontal_dead_zone;
00119   _vertical_center = drive_vertical_center;
00120   _horizontal_center = drive_horizontal_center;
00121 
00122   _vertical_ramp_up_time = drive_vertical_ramp_up_time;
00123   _vertical_ramp_down_time = drive_vertical_ramp_down_time;
00124   _horizontal_ramp_up_time = drive_horizontal_ramp_up_time;
00125   _horizontal_ramp_down_time = drive_horizontal_ramp_down_time;
00126 
00127   _speed = 0.0f;
00128   _rot_speed = 0.0f;
00129 
00130   _xyz.set(0.0f, 0.0f, 0.0f);
00131   _hpr.set(0.0f, 0.0f, 0.0f);
00132 
00133   _ignore_mouse = false;
00134   _force_mouse = false;
00135   _stop_this_frame = false;
00136 
00137   watch_button(MouseButton::one());
00138 }
00139 
00140 
00141 
00142 ////////////////////////////////////////////////////////////////////
00143 //     Function: DriveInterface::Destructor
00144 //       Access: Published
00145 //  Description:
00146 ////////////////////////////////////////////////////////////////////
00147 DriveInterface::
00148 ~DriveInterface() {
00149 }
00150 
00151 ////////////////////////////////////////////////////////////////////
00152 //     Function: DriveInterface::reset
00153 //       Access: Published
00154 //  Description: Reinitializes the driver to the origin and resets any
00155 //               knowledge about buttons being held down.
00156 ////////////////////////////////////////////////////////////////////
00157 void DriveInterface::
00158 reset() {
00159   _xyz.set(0.0f, 0.0f, 0.0f);
00160   _hpr.set(0.0f, 0.0f, 0.0f);
00161   _up_arrow.clear();
00162   _down_arrow.clear();
00163   _left_arrow.clear();
00164   _right_arrow.clear();
00165 }
00166 
00167 
00168 ////////////////////////////////////////////////////////////////////
00169 //     Function: DriveInterface::set_force_roll
00170 //       Access: Published
00171 //  Description: This function is no longer used and does nothing.  It
00172 //               will be removed soon.
00173 ////////////////////////////////////////////////////////////////////
00174 void DriveInterface::
00175 set_force_roll(PN_stdfloat) {
00176 }
00177 
00178 ////////////////////////////////////////////////////////////////////
00179 //     Function: DriveInterface::set_mat
00180 //       Access: Published
00181 //  Description: Stores the indicated transform in the DriveInterface.
00182 ////////////////////////////////////////////////////////////////////
00183 void DriveInterface::
00184 set_mat(const LMatrix4 &mat) {
00185   LVecBase3 scale, shear;
00186   decompose_matrix(mat, scale, shear, _hpr, _xyz);
00187 }
00188 
00189 ////////////////////////////////////////////////////////////////////
00190 //     Function: DriveInterface::get_mat
00191 //       Access: Published
00192 //  Description: Returns the current transform.
00193 ////////////////////////////////////////////////////////////////////
00194 const LMatrix4 &DriveInterface::
00195 get_mat() {
00196   compose_matrix(_mat, 
00197                  LVecBase3(1.0f, 1.0f, 1.0f), 
00198                  LVecBase3(0.0f, 0.0f, 0.0f),
00199                  _hpr, _xyz);
00200   return _mat;
00201 }
00202 
00203 ////////////////////////////////////////////////////////////////////
00204 //     Function: DriveInterface::force_dgraph
00205 //       Access: Public
00206 //  Description: This is a special kludge for DriveInterface to allow
00207 //               us to avoid the one-frame latency after a collision.
00208 //               It forces an immediate partial data flow for all data
00209 //               graph nodes below this node, causing all data nodes
00210 //               that depend on this matrix to be updated immediately.
00211 ////////////////////////////////////////////////////////////////////
00212 void DriveInterface::
00213 force_dgraph() {
00214   _transform = TransformState::make_pos_hpr(_xyz, _hpr);
00215   _velocity->set_value(_vel);
00216 
00217   DataNodeTransmit output;
00218   output.reserve(get_num_outputs());
00219   output.set_data(_transform_output, EventParameter(_transform));
00220   output.set_data(_velocity_output, EventParameter(_velocity));
00221 
00222   DataGraphTraverser dg_trav(Thread::get_current_thread());
00223   dg_trav.traverse_below(this, output);
00224   dg_trav.collect_leftovers();
00225 }
00226 
00227 
00228 ////////////////////////////////////////////////////////////////////
00229 //     Function: DriveInterface::apply
00230 //       Access: Private
00231 //  Description: Applies the operation indicated by the user's mouse
00232 //               motion to the current state.  Returns the matrix
00233 //               indicating the new state.
00234 ////////////////////////////////////////////////////////////////////
00235 void DriveInterface::
00236 apply(double x, double y, bool any_button) {
00237   // First reset the speeds
00238   _speed = 0.0f;
00239   _rot_speed = 0.0f;
00240 
00241   if (any_button || _force_mouse) {
00242     // If we're holding down any of the mouse buttons, do this
00243     // computation based on the mouse position.
00244 
00245     // Determine, based on the mouse's position and the amount of time
00246     // elapsed since last frame, how far forward/backward we should
00247     // move and how much we should rotate.
00248 
00249     // First, how fast are we moving?  This is based on the mouse's
00250     // vertical position.
00251 
00252     PN_stdfloat dead_zone_top = _vertical_center + _vertical_dead_zone;
00253     PN_stdfloat dead_zone_bottom = _vertical_center - _vertical_dead_zone;
00254 
00255     if (y >= dead_zone_top) {
00256       // Motion is forward.  Compute the throttle value: the ratio of
00257       // the mouse pointer within the range of vertical movement.
00258       PN_stdfloat throttle =
00259         // double 1.0, not 1.0f, is required here to satisfy min()
00260         (min(y, 1.0) - dead_zone_top) /
00261         (1.0f - dead_zone_top);
00262       _speed = throttle * _forward_speed;
00263 
00264     } else if (y <= dead_zone_bottom) {
00265       // Motion is backward.
00266       PN_stdfloat throttle =
00267         (max(y, -1.0) - dead_zone_bottom) /
00268         (-1.0f - dead_zone_bottom);
00269       _speed = -throttle * _reverse_speed;
00270     }
00271 
00272     // Now, what's our rotational velocity?  This is based on the
00273     // mouse's horizontal position.
00274     PN_stdfloat dead_zone_right = _horizontal_center + _horizontal_dead_zone;
00275     PN_stdfloat dead_zone_left = _horizontal_center - _horizontal_dead_zone;
00276 
00277     if (x >= dead_zone_right) {
00278       // Rotation is to the right.  Compute the throttle value: the
00279       // ratio of the mouse pointer within the range of horizontal
00280       // movement.
00281       PN_stdfloat throttle =
00282         (min(x, 1.0) - dead_zone_right) /
00283         (1.0f - dead_zone_right);
00284       _rot_speed = throttle * _rotate_speed;
00285 
00286     } else if (x <= dead_zone_left) {
00287       // Rotation is to the left.
00288       PN_stdfloat throttle =
00289         (max(x, -1.0) - dead_zone_left) /
00290         (-1.0f - dead_zone_left);
00291       _rot_speed = -throttle * _rotate_speed;
00292     }
00293 
00294   } else {
00295     // If we're not holding down any of the mouse buttons, do this
00296     // computation based on the arrow keys.
00297 
00298     // Which vertical arrow key changed state more recently?
00299     PN_stdfloat throttle;
00300 
00301     if (_up_arrow < _down_arrow) {
00302       throttle = _up_arrow.get_effect(_vertical_ramp_up_time,
00303                                       _vertical_ramp_down_time);
00304       _speed = throttle * _forward_speed;
00305       _down_arrow._effect = 0.0f;
00306 
00307     } else {
00308       throttle = _down_arrow.get_effect(_vertical_ramp_up_time,
00309                                         _vertical_ramp_down_time);
00310       _speed = -throttle * _reverse_speed;
00311       _up_arrow._effect = 0.0f;
00312     }
00313 
00314     // Which horizontal arrow key changed state more recently?
00315     if (_right_arrow < _left_arrow) {
00316       throttle = _right_arrow.get_effect(_horizontal_ramp_up_time,
00317                                          _horizontal_ramp_down_time);
00318       _rot_speed = throttle * _rotate_speed;
00319       _left_arrow._effect = 0.0f;
00320 
00321     } else {
00322       throttle = _left_arrow.get_effect(_horizontal_ramp_up_time,
00323                                         _horizontal_ramp_down_time);
00324       _rot_speed = -throttle * _rotate_speed;
00325       _right_arrow._effect = 0.0f;
00326     }
00327     _right_arrow._effect = throttle;
00328     _left_arrow._effect = throttle;
00329   }
00330 
00331   if (_speed == 0.0f && _rot_speed == 0.0f) {
00332     _vel.set(0.0f, 0.0f, 0.0f);
00333     return;
00334   }
00335 
00336   // Now how far did we move based on the amount of time elapsed?
00337   PN_stdfloat distance = ClockObject::get_global_clock()->get_dt() * _speed;
00338   PN_stdfloat rotation = ClockObject::get_global_clock()->get_dt() * _rot_speed;
00339   if (_stop_this_frame) {
00340     distance = 0.0f;
00341     rotation = 0.0f;
00342     _stop_this_frame = false;
00343   }
00344 
00345   // Now apply the vectors.
00346 
00347   // rot_mat is the rotation matrix corresponding to our previous
00348   // heading.
00349   LMatrix3 rot_mat;
00350   rot_mat.set_rotate_mat_normaxis(_hpr[0], LVector3::up());
00351 
00352   // Take a step in the direction of our previous heading.
00353   _vel = LVector3::forward() * distance;
00354   LVector3 step = (_vel * rot_mat);
00355 
00356   // To prevent upward drift due to numerical errors, force the
00357   // vertical component of our step to zero (it should be pretty near
00358   // zero anyway).
00359   switch (get_default_coordinate_system()) {
00360   case CS_zup_right:
00361   case CS_zup_left:
00362     step[2] = 0.0f;
00363     break;
00364 
00365   case CS_yup_right:
00366   case CS_yup_left:
00367     step[1] = 0.0f;
00368     break;
00369 
00370   default:
00371     break;
00372   }
00373 
00374   _xyz += step;
00375   _hpr[0] -= rotation;
00376 }
00377 
00378 ////////////////////////////////////////////////////////////////////
00379 //     Function: DriveInterface::do_transmit_data
00380 //       Access: Protected, Virtual
00381 //  Description: The virtual implementation of transmit_data().  This
00382 //               function receives an array of input parameters and
00383 //               should generate an array of output parameters.  The
00384 //               input parameters may be accessed with the index
00385 //               numbers returned by the define_input() calls that
00386 //               were made earlier (presumably in the constructor);
00387 //               likewise, the output parameters should be set with
00388 //               the index numbers returned by the define_output()
00389 //               calls.
00390 ////////////////////////////////////////////////////////////////////
00391 void DriveInterface::
00392 do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
00393                  DataNodeTransmit &output) {
00394   // First, update our modifier buttons.
00395   bool required_buttons_match;
00396   const ButtonEventList *button_events = check_button_events(input, required_buttons_match);
00397 
00398   // Look for mouse activity.
00399   double x = 0.0f;
00400   double y = 0.0f;
00401 
00402   //bool got_mouse = false;
00403 
00404   if (required_buttons_match && input.has_data(_xy_input)) {
00405     const EventStoreVec2 *xy;
00406     DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
00407     const LVecBase2 &p = xy->get_value();
00408     x = p[0];
00409     y = p[1];
00410 
00411     //got_mouse = true;
00412   }
00413 
00414   // Look for keyboard events.
00415   if (required_buttons_match && button_events != (const ButtonEventList *)NULL) {
00416 
00417     int num_events = button_events->get_num_events();
00418     for (int i = 0; i < num_events; i++) {
00419       const ButtonEvent &be = button_events->get_event(i);
00420       if (be._type != ButtonEvent::T_keystroke) {
00421         bool down = (be._type != ButtonEvent::T_up);
00422         
00423         if (be._button == KeyboardButton::up()) {
00424           _up_arrow.set_key(down);
00425         } else if (be._button == KeyboardButton::down()) {
00426           _down_arrow.set_key(down);
00427         } else if (be._button == KeyboardButton::left()) {
00428           _left_arrow.set_key(down);
00429         } else if (be._button == KeyboardButton::right()) {
00430           _right_arrow.set_key(down);
00431         }
00432       }
00433     }
00434   }
00435 
00436   apply(x, y, !_ignore_mouse && is_down(MouseButton::one()));
00437   _transform = TransformState::make_pos_hpr(_xyz, _hpr);
00438   _velocity->set_value(_vel);
00439   output.set_data(_transform_output, EventParameter(_transform));
00440   output.set_data(_velocity_output, EventParameter(_velocity));
00441 }
 All Classes Functions Variables Enumerations