Panda3D
|
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 }