Panda3D
Loading...
Searching...
No Matches
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
28using std::max;
29using std::min;
30
31TypeHandle DriveInterface::_type_handle;
32const PN_stdfloat DriveInterface::_hpr_quantize = 0.001;
33
34DriveInterface::KeyHeld::
35KeyHeld() {
36 _down = false;
37 _changed_time = 0.0f;
38 _effect = 0.0f;
39 _effect_at_change = 0.0f;
40}
41
42PN_stdfloat DriveInterface::KeyHeld::
43get_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
69void DriveInterface::KeyHeld::
70set_key(bool down) {
71 if (_down != down) {
72 _down = down;
74 _effect_at_change = _effect;
75 }
76}
77
78void DriveInterface::KeyHeld::
79clear() {
80 _down = false;
81 _changed_time = 0.0f;
82 _effect = 0.0f;
83 _effect_at_change = 0.0f;
84}
85
86bool DriveInterface::KeyHeld::
87operator < (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 */
100DriveInterface::
101DriveInterface(const std::string &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 */
144DriveInterface::
145~DriveInterface() {
146}
147
148/**
149 * Reinitializes the driver to the origin and resets any knowledge about
150 * buttons being held down.
151 */
153reset() {
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 */
167set_force_roll(PN_stdfloat) {
168}
169
170/**
171 * Stores the indicated transform in the DriveInterface.
172 */
174set_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 */
182const LMatrix4 &DriveInterface::
183get_mat() {
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 */
198force_dgraph() {
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
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 */
217void DriveInterface::
218apply(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 */
365void DriveInterface::
366do_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}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Records a set of button events that happened recently.
get_num_events
Returns the number of events in the list.
get_event
Returns the nth event in the list.
Records a button event of some kind.
Definition buttonEvent.h:49
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
get_dt
Returns the elapsed time for the previous frame: the number of seconds elapsed between the last two c...
Definition clockObject.h:99
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
This object supervises the traversal of the data graph and the moving of data from one DataNode to it...
void traverse_below(PandaNode *node, const DataNodeTransmit &output)
Continues the traversal to all the children of the indicated node, passing in the given data,...
void collect_leftovers()
Pick up any nodes that didn't get completely traversed.
Encapsulates the data generated from (or sent into) any particular DataNode.
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...
void set_data(int index, const EventParameter &data)
Sets the data for the indicated parameter.
bool has_data(int index) const
Returns true if the indicated parameter has been stored, false otherwise.
void reserve(int num_wires)
Tells the DataNodeTransmit object how many wires it is expected to store data for.
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 set_mat(const LMatrix4 &mat)
Stores the indicated transform in the DriveInterface.
void set_force_roll(PN_stdfloat force_roll)
This function is no longer used and does nothing.
void reset()
Reinitializes the driver to the origin and resets any knowledge about buttons being held down.
void force_dgraph()
This is a special kludge for DriveInterface to allow us to avoid the one- frame latency after a colli...
An optional parameter associated with an event.
TypedWritableReferenceCount * get_ptr() const
Retrieves a pointer to the actual value stored in the parameter.
static ButtonHandle one()
Returns the ButtonHandle associated with the first mouse button.
This is the base class for some classes that monitor the mouse and keyboard input and perform some ac...
A handy class object for storing simple values (like integers or strings) passed along with an Event ...
Definition paramValue.h:103
get_value
Retrieves the value stored in the parameter.
Definition paramValue.h:115
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.