Panda3D
 All Classes Functions Variables Enumerations
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.
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.
static const LVector3f & zero()
Returns a zero-length vector.
Definition: lvector3.h:269
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
Records a button event of some kind.
Definition: buttonEvent.h:53
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:565
const ButtonEvent & get_event(int n) const
Returns the nth event in the list.
void set_force_roll(PN_stdfloat force_roll)
This function is no longer used and does nothing.
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
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
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
int get_num_events() const
Returns the number of events in the list.
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...
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
This is the base class for some classes that monitor the mouse and keyboard input and perform some ac...
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 Type & get_value() const
Retrieves the value stored in the parameter.
Definition: paramValue.I:136
TypedWritableReferenceCount * get_ptr() const
Retrieves a pointer to the actual value stored in the parameter.
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
bool has_data(int index) const
Returns true if the indicated parameter has been stored, false otherwise.
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:527
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...