Panda3D
driveInterface.cxx
1 // Filename: driveInterface.cxx
2 // Created by: drose (12Mar02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "driveInterface.h"
16 #include "config_tform.h"
17 
18 #include "compose_matrix.h"
19 #include "mouseAndKeyboard.h"
20 #include "mouseData.h"
21 #include "clockObject.h"
22 #include "modifierButtons.h"
23 #include "keyboardButton.h"
24 #include "mouseButton.h"
25 #include "buttonEventList.h"
26 #include "dataNodeTransmit.h"
27 #include "dataGraphTraverser.h"
28 
29 TypeHandle DriveInterface::_type_handle;
30 const PN_stdfloat DriveInterface::_hpr_quantize = 0.001;
31 
32 DriveInterface::KeyHeld::
33 KeyHeld() {
34  _down = false;
35  _changed_time = 0.0f;
36  _effect = 0.0f;
37  _effect_at_change = 0.0f;
38 }
39 
40 PN_stdfloat DriveInterface::KeyHeld::
41 get_effect(PN_stdfloat ramp_up_time, PN_stdfloat ramp_down_time) {
42  double elapsed = ClockObject::get_global_clock()->get_frame_time() - _changed_time;
43  if (_down) {
44  // We are currently holding down the key. That means we base our
45  // effect on the ramp_up_time.
46  if (ramp_up_time == 0.0f) {
47  _effect = 1.0f;
48 
49  } else {
50  PN_stdfloat change = elapsed / ramp_up_time;
51  _effect = min(_effect_at_change + change, (PN_stdfloat)1.0);
52  }
53  } else {
54  // We are *not* currently holding down the key. That means we
55  // base our effect on the ramp_down_time.
56  if (ramp_down_time == 0.0f) {
57  _effect = 0.0f;
58 
59  } else {
60  PN_stdfloat change = elapsed / ramp_down_time;
61  _effect = max(_effect_at_change - change, (PN_stdfloat)0.0);
62  }
63  }
64  return _effect;
65 }
66 
67 void DriveInterface::KeyHeld::
68 set_key(bool down) {
69  if (_down != down) {
70  _down = down;
71  _changed_time = ClockObject::get_global_clock()->get_frame_time();
72  _effect_at_change = _effect;
73  }
74 }
75 
76 void DriveInterface::KeyHeld::
77 clear() {
78  _down = false;
79  _changed_time = 0.0f;
80  _effect = 0.0f;
81  _effect_at_change = 0.0f;
82 }
83 
84 bool DriveInterface::KeyHeld::
85 operator < (const DriveInterface::KeyHeld &other) const {
86  if (_down != other._down) {
87  // If one has the key held down and the other doesn't, the down
88  // key wins.
89  return _down;
90  }
91 
92  // Otherwise, the most-recently changed key wins.
93  return _changed_time > other._changed_time;
94 }
95 
96 ////////////////////////////////////////////////////////////////////
97 // Function: DriveInterface::Constructor
98 // Access: Published
99 // Description:
100 ////////////////////////////////////////////////////////////////////
101 DriveInterface::
102 DriveInterface(const string &name) :
103  MouseInterfaceNode(name)
104 {
105  _xy_input = define_input("xy", EventStoreVec2::get_class_type());
106  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
107 
108  _transform_output = define_output("transform", TransformState::get_class_type());
109  _velocity_output = define_output("velocity", EventStoreVec3::get_class_type());
110 
111  _transform = TransformState::make_identity();
112  _velocity = new EventStoreVec3(LVector3::zero());
113 
114  _forward_speed = drive_forward_speed;
115  _reverse_speed = drive_reverse_speed;
116  _rotate_speed = drive_rotate_speed;
117  _vertical_dead_zone = drive_vertical_dead_zone;
118  _horizontal_dead_zone = drive_horizontal_dead_zone;
119  _vertical_center = drive_vertical_center;
120  _horizontal_center = drive_horizontal_center;
121 
122  _vertical_ramp_up_time = drive_vertical_ramp_up_time;
123  _vertical_ramp_down_time = drive_vertical_ramp_down_time;
124  _horizontal_ramp_up_time = drive_horizontal_ramp_up_time;
125  _horizontal_ramp_down_time = drive_horizontal_ramp_down_time;
126 
127  _speed = 0.0f;
128  _rot_speed = 0.0f;
129 
130  _xyz.set(0.0f, 0.0f, 0.0f);
131  _hpr.set(0.0f, 0.0f, 0.0f);
132 
133  _ignore_mouse = false;
134  _force_mouse = false;
135  _stop_this_frame = false;
136 
137  watch_button(MouseButton::one());
138 }
139 
140 
141 
142 ////////////////////////////////////////////////////////////////////
143 // Function: DriveInterface::Destructor
144 // Access: Published
145 // Description:
146 ////////////////////////////////////////////////////////////////////
147 DriveInterface::
148 ~DriveInterface() {
149 }
150 
151 ////////////////////////////////////////////////////////////////////
152 // Function: DriveInterface::reset
153 // Access: Published
154 // Description: Reinitializes the driver to the origin and resets any
155 // knowledge about buttons being held down.
156 ////////////////////////////////////////////////////////////////////
157 void DriveInterface::
158 reset() {
159  _xyz.set(0.0f, 0.0f, 0.0f);
160  _hpr.set(0.0f, 0.0f, 0.0f);
161  _up_arrow.clear();
162  _down_arrow.clear();
163  _left_arrow.clear();
164  _right_arrow.clear();
165 }
166 
167 
168 ////////////////////////////////////////////////////////////////////
169 // Function: DriveInterface::set_force_roll
170 // Access: Published
171 // Description: This function is no longer used and does nothing. It
172 // will be removed soon.
173 ////////////////////////////////////////////////////////////////////
174 void DriveInterface::
175 set_force_roll(PN_stdfloat) {
176 }
177 
178 ////////////////////////////////////////////////////////////////////
179 // Function: DriveInterface::set_mat
180 // Access: Published
181 // Description: Stores the indicated transform in the DriveInterface.
182 ////////////////////////////////////////////////////////////////////
183 void DriveInterface::
184 set_mat(const LMatrix4 &mat) {
185  LVecBase3 scale, shear;
186  decompose_matrix(mat, scale, shear, _hpr, _xyz);
187 }
188 
189 ////////////////////////////////////////////////////////////////////
190 // Function: DriveInterface::get_mat
191 // Access: Published
192 // Description: Returns the current transform.
193 ////////////////////////////////////////////////////////////////////
196  compose_matrix(_mat,
197  LVecBase3(1.0f, 1.0f, 1.0f),
198  LVecBase3(0.0f, 0.0f, 0.0f),
199  _hpr, _xyz);
200  return _mat;
201 }
202 
203 ////////////////////////////////////////////////////////////////////
204 // Function: DriveInterface::force_dgraph
205 // Access: Public
206 // Description: This is a special kludge for DriveInterface to allow
207 // us to avoid the one-frame latency after a collision.
208 // It forces an immediate partial data flow for all data
209 // graph nodes below this node, causing all data nodes
210 // that depend on this matrix to be updated immediately.
211 ////////////////////////////////////////////////////////////////////
212 void DriveInterface::
214  _transform = TransformState::make_pos_hpr(_xyz, _hpr);
215  _velocity->set_value(_vel);
216 
217  DataNodeTransmit output;
218  output.reserve(get_num_outputs());
219  output.set_data(_transform_output, EventParameter(_transform));
220  output.set_data(_velocity_output, EventParameter(_velocity));
221 
223  dg_trav.traverse_below(this, output);
224  dg_trav.collect_leftovers();
225 }
226 
227 
228 ////////////////////////////////////////////////////////////////////
229 // Function: DriveInterface::apply
230 // Access: Private
231 // Description: Applies the operation indicated by the user's mouse
232 // motion to the current state. Returns the matrix
233 // indicating the new state.
234 ////////////////////////////////////////////////////////////////////
235 void DriveInterface::
236 apply(double x, double y, bool any_button) {
237  // First reset the speeds
238  _speed = 0.0f;
239  _rot_speed = 0.0f;
240 
241  if (any_button || _force_mouse) {
242  // If we're holding down any of the mouse buttons, do this
243  // computation based on the mouse position.
244 
245  // Determine, based on the mouse's position and the amount of time
246  // elapsed since last frame, how far forward/backward we should
247  // move and how much we should rotate.
248 
249  // First, how fast are we moving? This is based on the mouse's
250  // vertical position.
251 
252  PN_stdfloat dead_zone_top = _vertical_center + _vertical_dead_zone;
253  PN_stdfloat dead_zone_bottom = _vertical_center - _vertical_dead_zone;
254 
255  if (y >= dead_zone_top) {
256  // Motion is forward. Compute the throttle value: the ratio of
257  // the mouse pointer within the range of vertical movement.
258  PN_stdfloat throttle =
259  // double 1.0, not 1.0f, is required here to satisfy min()
260  (min(y, 1.0) - dead_zone_top) /
261  (1.0f - dead_zone_top);
262  _speed = throttle * _forward_speed;
263 
264  } else if (y <= dead_zone_bottom) {
265  // Motion is backward.
266  PN_stdfloat throttle =
267  (max(y, -1.0) - dead_zone_bottom) /
268  (-1.0f - dead_zone_bottom);
269  _speed = -throttle * _reverse_speed;
270  }
271 
272  // Now, what's our rotational velocity? This is based on the
273  // mouse's horizontal position.
274  PN_stdfloat dead_zone_right = _horizontal_center + _horizontal_dead_zone;
275  PN_stdfloat dead_zone_left = _horizontal_center - _horizontal_dead_zone;
276 
277  if (x >= dead_zone_right) {
278  // Rotation is to the right. Compute the throttle value: the
279  // ratio of the mouse pointer within the range of horizontal
280  // movement.
281  PN_stdfloat throttle =
282  (min(x, 1.0) - dead_zone_right) /
283  (1.0f - dead_zone_right);
284  _rot_speed = throttle * _rotate_speed;
285 
286  } else if (x <= dead_zone_left) {
287  // Rotation is to the left.
288  PN_stdfloat throttle =
289  (max(x, -1.0) - dead_zone_left) /
290  (-1.0f - dead_zone_left);
291  _rot_speed = -throttle * _rotate_speed;
292  }
293 
294  } else {
295  // If we're not holding down any of the mouse buttons, do this
296  // computation based on the arrow keys.
297 
298  // Which vertical arrow key changed state more recently?
299  PN_stdfloat throttle;
300 
301  if (_up_arrow < _down_arrow) {
302  throttle = _up_arrow.get_effect(_vertical_ramp_up_time,
303  _vertical_ramp_down_time);
304  _speed = throttle * _forward_speed;
305  _down_arrow._effect = 0.0f;
306 
307  } else {
308  throttle = _down_arrow.get_effect(_vertical_ramp_up_time,
309  _vertical_ramp_down_time);
310  _speed = -throttle * _reverse_speed;
311  _up_arrow._effect = 0.0f;
312  }
313 
314  // Which horizontal arrow key changed state more recently?
315  if (_right_arrow < _left_arrow) {
316  throttle = _right_arrow.get_effect(_horizontal_ramp_up_time,
317  _horizontal_ramp_down_time);
318  _rot_speed = throttle * _rotate_speed;
319  _left_arrow._effect = 0.0f;
320 
321  } else {
322  throttle = _left_arrow.get_effect(_horizontal_ramp_up_time,
323  _horizontal_ramp_down_time);
324  _rot_speed = -throttle * _rotate_speed;
325  _right_arrow._effect = 0.0f;
326  }
327  _right_arrow._effect = throttle;
328  _left_arrow._effect = throttle;
329  }
330 
331  if (_speed == 0.0f && _rot_speed == 0.0f) {
332  _vel.set(0.0f, 0.0f, 0.0f);
333  return;
334  }
335 
336  // Now how far did we move based on the amount of time elapsed?
337  PN_stdfloat distance = ClockObject::get_global_clock()->get_dt() * _speed;
338  PN_stdfloat rotation = ClockObject::get_global_clock()->get_dt() * _rot_speed;
339  if (_stop_this_frame) {
340  distance = 0.0f;
341  rotation = 0.0f;
342  _stop_this_frame = false;
343  }
344 
345  // Now apply the vectors.
346 
347  // rot_mat is the rotation matrix corresponding to our previous
348  // heading.
349  LMatrix3 rot_mat;
350  rot_mat.set_rotate_mat_normaxis(_hpr[0], LVector3::up());
351 
352  // Take a step in the direction of our previous heading.
353  _vel = LVector3::forward() * distance;
354  LVector3 step = (_vel * rot_mat);
355 
356  // To prevent upward drift due to numerical errors, force the
357  // vertical component of our step to zero (it should be pretty near
358  // zero anyway).
359  switch (get_default_coordinate_system()) {
360  case CS_zup_right:
361  case CS_zup_left:
362  step[2] = 0.0f;
363  break;
364 
365  case CS_yup_right:
366  case CS_yup_left:
367  step[1] = 0.0f;
368  break;
369 
370  default:
371  break;
372  }
373 
374  _xyz += step;
375  _hpr[0] -= rotation;
376 }
377 
378 ////////////////////////////////////////////////////////////////////
379 // Function: DriveInterface::do_transmit_data
380 // Access: Protected, Virtual
381 // Description: The virtual implementation of transmit_data(). This
382 // function receives an array of input parameters and
383 // should generate an array of output parameters. The
384 // input parameters may be accessed with the index
385 // numbers returned by the define_input() calls that
386 // were made earlier (presumably in the constructor);
387 // likewise, the output parameters should be set with
388 // the index numbers returned by the define_output()
389 // calls.
390 ////////////////////////////////////////////////////////////////////
391 void DriveInterface::
392 do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
393  DataNodeTransmit &output) {
394  // First, update our modifier buttons.
395  bool required_buttons_match;
396  const ButtonEventList *button_events = check_button_events(input, required_buttons_match);
397 
398  // Look for mouse activity.
399  double x = 0.0f;
400  double y = 0.0f;
401 
402  //bool got_mouse = false;
403 
404  if (required_buttons_match && input.has_data(_xy_input)) {
405  const EventStoreVec2 *xy;
406  DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
407  const LVecBase2 &p = xy->get_value();
408  x = p[0];
409  y = p[1];
410 
411  //got_mouse = true;
412  }
413 
414  // Look for keyboard events.
415  if (required_buttons_match && button_events != (const ButtonEventList *)NULL) {
416 
417  int num_events = button_events->get_num_events();
418  for (int i = 0; i < num_events; i++) {
419  const ButtonEvent &be = button_events->get_event(i);
420  if (be._type != ButtonEvent::T_keystroke) {
421  bool down = (be._type != ButtonEvent::T_up);
422 
423  if (be._button == KeyboardButton::up()) {
424  _up_arrow.set_key(down);
425  } else if (be._button == KeyboardButton::down()) {
426  _down_arrow.set_key(down);
427  } else if (be._button == KeyboardButton::left()) {
428  _left_arrow.set_key(down);
429  } else if (be._button == KeyboardButton::right()) {
430  _right_arrow.set_key(down);
431  }
432  }
433  }
434  }
435 
436  apply(x, y, !_ignore_mouse && is_down(MouseButton::one()));
437  _transform = TransformState::make_pos_hpr(_xyz, _hpr);
438  _velocity->set_value(_vel);
439  output.set_data(_transform_output, EventParameter(_transform));
440  output.set_data(_velocity_output, EventParameter(_velocity));
441 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
void set_mat(const LMatrix4 &mat)
Stores the indicated transform in the DriveInterface.
int get_num_outputs() const
Returns the number of different outputs that have been defined for this node using define_output()...
Definition: dataNode.I:61
const LMatrix4 & get_mat()
Returns the current transform.
void collect_leftovers()
Pick up any nodes that didn&#39;t get completely traversed.
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
void reset()
Reinitializes the driver to the origin and resets any knowledge about buttons being held down...
An optional parameter associated with an event.
int get_num_events() const
Returns the number of events in the list.
static const LVector3f & zero()
Returns a zero-length vector.
Definition: lvector3.h:270
static ButtonHandle one()
Returns the ButtonHandle associated with the first mouse button.
Definition: mouseButton.cxx:50
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.I:48
Records a button event of some kind.
Definition: buttonEvent.h:53
bool has_data(int index) const
Returns true if the indicated parameter has been stored, false otherwise.
A handy class object for storing simple values (like integers or strings) passed along with an Event ...
Definition: paramValue.h:109
void traverse_below(PandaNode *node, const DataNodeTransmit &output)
Continues the traversal to all the children of the indicated node, passing in the given data...
Records a set of button events that happened recently.
void reserve(int num_wires)
Tells the DataNodeTransmit object how many wires it is expected to store data for.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
static LVector3f forward(CoordinateSystem cs=CS_default)
Returns the forward vector for the given coordinate system.
Definition: lvector3.h:579
void set_force_roll(PN_stdfloat force_roll)
This function is no longer used and does nothing.
void force_dgraph()
This is a special kludge for DriveInterface to allow us to avoid the one-frame latency after a collis...
void set_data(int index, const EventParameter &data)
Sets the data for the indicated parameter.
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
const Type & get_value() const
Retrieves the value stored in the parameter.
Definition: paramValue.I:136
This is the base class for some classes that monitor the mouse and keyboard input and perform some ac...
TypedWritableReferenceCount * get_ptr() const
Retrieves a pointer to the actual value stored in the parameter.
const EventParameter & get_data(int index) const
Extracts the data for the indicated index, if it has been stored, or the empty parameter if it has no...
const ButtonEvent & get_event(int n) const
Returns the nth event in the list.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:110
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:541
double get_dt(Thread *current_thread=Thread::get_current_thread()) const
Returns the elapsed time for the previous frame: the number of seconds elapsed between the last two c...
Definition: clockObject.I:141
Encapsulates the data generated from (or sent into) any particular DataNode.
void set_rotate_mat_normaxis(float angle, const LVecBase3f &axis, CoordinateSystem cs=CS_default)
Fills mat with a matrix that rotates by the given angle in degrees counterclockwise about the indicat...
Definition: lmatrix.cxx:368
This object supervises the traversal of the data graph and the moving of data from one DataNode to it...