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