Panda3D
trackball.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 trackball.cxx
10  * @author drose
11  * @date 2002-03-12
12  */
13 
14 #include "trackball.h"
15 #include "buttonEvent.h"
16 #include "buttonEventList.h"
17 #include "dataNodeTransmit.h"
18 #include "compose_matrix.h"
19 #include "mouseData.h"
20 #include "modifierButtons.h"
21 #include "linmath_events.h"
22 #include "mouseButton.h"
23 #include "keyboardButton.h"
24 #include "config_tform.h"
25 
26 TypeHandle Trackball::_type_handle;
27 
28 // These are used internally.
29 #define B1_MASK 0x01
30 #define B2_MASK 0x02
31 #define B3_MASK 0x04
32 
33 /**
34  *
35  */
36 Trackball::
37 Trackball(const std::string &name) :
38  MouseInterfaceNode(name)
39 {
40  _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
41 
42  _transform_output = define_output("transform", TransformState::get_class_type());
43 
44  _transform = TransformState::make_identity();
45 
46  _rotscale = 0.3;
47  _fwdscale = 0.3;
48 
49  _last_button = 0;
50  _lastx = _lasty = 0.5f;
51 
52  _rotation = LMatrix4::ident_mat();
53  _translation.set(0.0f, 0.0f, 0.0f);
54  _mat = LMatrix4::ident_mat();
55  _orig = LMatrix4::ident_mat();
56  _invert = true;
57  _cs = get_default_coordinate_system();
58  _control_mode = CM_default;
59 
60  // We want to track the state of these buttons.
61  watch_button(MouseButton::one());
62  watch_button(MouseButton::two());
63  watch_button(MouseButton::three());
64 
65  if (trackball_use_alt_keys) {
66  // In OSX mode, we need to use the command and option key in conjunction
67  // with the (one) mouse button.
68  watch_button(KeyboardButton::control());
69  watch_button(KeyboardButton::meta());
70  watch_button(KeyboardButton::alt());
71  }
72 }
73 
74 /**
75  *
76  */
77 Trackball::
78 ~Trackball() {
79 }
80 
81 /**
82  * Reinitializes all transforms to identity.
83  */
84 void Trackball::
85 reset() {
86  _rotation = LMatrix4::ident_mat();
87  _translation.set(0.0f, 0.0f, 0.0f);
88  _orig = LMatrix4::ident_mat();
89  _mat = LMatrix4::ident_mat();
90 }
91 
92 /**
93  * Returns the scale factor applied to forward and backward motion. See
94  * set_forward_scale().
95  */
96 PN_stdfloat Trackball::
98  return _fwdscale;
99 }
100 
101 /**
102  * Changes the scale factor applied to forward and backward motion. The
103  * larger this number, the faster the model will move in response to dollying
104  * in and out.
105  */
106 void Trackball::
107 set_forward_scale(PN_stdfloat fwdscale) {
108  _fwdscale = fwdscale;
109 }
110 
111 
112 /**
113  * Return the offset from the center of rotation.
114  */
115 const LPoint3 &Trackball::
116 get_pos() const {
117  return _translation;
118 }
119 
120 PN_stdfloat Trackball::
121 get_x() const {
122  return _translation[0];
123 }
124 
125 PN_stdfloat Trackball::
126 get_y() const {
127  return _translation[1];
128 }
129 
130 PN_stdfloat Trackball::
131 get_z() const {
132  return _translation[2];
133 }
134 
135 
136 /**
137  * Directly set the offset from the rotational origin.
138  */
139 void Trackball::
140 set_pos(const LVecBase3 &vec) {
141  _translation = vec;
142  recompute();
143 }
144 
145 void Trackball::
146 set_pos(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
147  _translation.set(x, y, z);
148  recompute();
149 }
150 
151 void Trackball::
152 set_x(PN_stdfloat x) {
153  _translation[0] = x;
154  recompute();
155 }
156 
157 void Trackball::
158 set_y(PN_stdfloat y) {
159  _translation[1] = y;
160  recompute();
161 }
162 
163 void Trackball::
164 set_z(PN_stdfloat z) {
165  _translation[2] = z;
166  recompute();
167 }
168 
169 
170 /**
171  * Return the trackball's orientation.
172  */
173 LVecBase3 Trackball::
174 get_hpr() const {
175  LVecBase3 scale, shear, hpr, translate;
176  decompose_matrix(_rotation, scale, shear, hpr, translate);
177  return hpr;
178 }
179 
180 PN_stdfloat Trackball::
181 get_h() const {
182  LVecBase3 scale, shear, hpr, translate;
183  decompose_matrix(_rotation, scale, shear, hpr, translate);
184  return hpr[0];
185 }
186 
187 PN_stdfloat Trackball::
188 get_p() const {
189  LVecBase3 scale, shear, hpr, translate;
190  decompose_matrix(_rotation, scale, shear, hpr, translate);
191  return hpr[1];
192 }
193 
194 PN_stdfloat Trackball::
195 get_r() const {
196  LVecBase3 scale, shear, hpr, translate;
197  decompose_matrix(_rotation, scale, shear, hpr, translate);
198  return hpr[2];
199 }
200 
201 
202 /**
203  * Directly set the mover's orientation.
204  */
205 void Trackball::
206 set_hpr(const LVecBase3 &hpr) {
207  LVecBase3 scale, shear, old_hpr, translate;
208  decompose_matrix(_rotation, scale, shear, old_hpr, translate);
209  compose_matrix(_rotation, scale, shear, hpr, translate);
210  recompute();
211 }
212 
213 void Trackball::
214 set_hpr(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r) {
215  LVecBase3 scale, shear, hpr, translate;
216  decompose_matrix(_rotation, scale, shear, hpr, translate);
217  hpr.set(h, p, r);
218  compose_matrix(_rotation, scale, shear, hpr, translate);
219  recompute();
220 }
221 
222 void Trackball::
223 set_h(PN_stdfloat h) {
224  LVecBase3 scale, shear, hpr, translate;
225  decompose_matrix(_rotation, scale, shear, hpr, translate);
226  hpr[0] = h;
227  compose_matrix(_rotation, scale, shear, hpr, translate);
228  recompute();
229 }
230 
231 void Trackball::
232 set_p(PN_stdfloat p) {
233  LVecBase3 scale, shear, hpr, translate;
234  decompose_matrix(_rotation, scale, shear, hpr, translate);
235  hpr[1] = p;
236  compose_matrix(_rotation, scale, shear, hpr, translate);
237  recompute();
238 }
239 
240 void Trackball::
241 set_r(PN_stdfloat r) {
242  LVecBase3 scale, shear, hpr, translate;
243  decompose_matrix(_rotation, scale, shear, hpr, translate);
244  hpr[2] = r;
245  compose_matrix(_rotation, scale, shear, hpr, translate);
246  recompute();
247 }
248 
249 
250 /**
251  * Reposition the center of rotation to coincide with the current translation
252  * offset. Future rotations will be about the current origin.
253  */
254 void Trackball::
256  recompute();
257  _rotation = _orig;
258  _translation.set(0.0f, 0.0f, 0.0f);
259 }
260 
261 
262 /**
263  * Moves the center of rotation by the given amount.
264  */
265 void Trackball::
266 move_origin(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
267  _rotation = LMatrix4::translate_mat(LVecBase3(x, y, z)) * _rotation;
268 }
269 
270 /**
271  * Returns the current center of rotation.
272  */
273 LPoint3 Trackball::
274 get_origin() const {
275  return _rotation.get_row3(3);
276 }
277 
278 /**
279  * Directly sets the center of rotation.
280  */
281 void Trackball::
282 set_origin(const LVecBase3 &origin) {
283  _rotation.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
284  _rotation = LMatrix4::translate_mat(-origin) * _rotation;
285 }
286 
287 
288 /**
289  * Sets the invert flag. When this is set, the inverse matrix is generated,
290  * suitable for joining to a camera, instead of parenting the scene under it.
291  */
292 void Trackball::
293 set_invert(bool flag) {
294  _invert = flag;
295 }
296 
297 /**
298  * Returns the invert flag. When this is set, the inverse matrix is
299  * generated, suitable for joining to a camera, instead of parenting the scene
300  * under it.
301  */
302 bool Trackball::
303 get_invert() const {
304  return _invert;
305 }
306 
307 /**
308  * Sets the control mode. Normally this is CM_default, which means each mouse
309  * button serves its normal function. When it is CM_truck, CM_pan, CM_dolly,
310  * or CM_roll, all of the mouse buttons serve the indicated function instead
311  * of their normal function. This can be used in conjunction with some
312  * external way of changing modes.
313  */
314 void Trackball::
315 set_control_mode(ControlMode control_mode) {
316  _control_mode = control_mode;
317 }
318 
319 /**
320  * Returns the control mode. See set_control_mode().
321  */
322 Trackball::ControlMode Trackball::
324  return _control_mode;
325 }
326 
327 /**
328  * Sets the NodePath that all trackball manipulations are to be assumed to be
329  * relative to. For instance, set your camera node here to make the trackball
330  * motion camera relative. The default is the empty path, which means
331  * trackball motion is in global space.
332  */
333 void Trackball::
334 set_rel_to(const NodePath &rel_to) {
335  _rel_to = rel_to;
336 }
337 
338 /**
339  * Returns the NodePath that all trackball manipulations are relative to, or
340  * the empty path.
341  */
342 const NodePath &Trackball::
343 get_rel_to() const {
344  return _rel_to;
345 }
346 
347 
348 /**
349  * Sets the coordinate system of the Trackball. Normally, this is the default
350  * coordinate system. This changes the axes the Trackball manipulates so that
351  * the user interface remains consistent across different coordinate systems.
352  */
353 void Trackball::
354 set_coordinate_system(CoordinateSystem cs) {
355  _cs = cs;
356 }
357 
358 /**
359  * Returns the coordinate system of the Trackball. See
360  * set_coordinate_system().
361  */
362 CoordinateSystem Trackball::
364  return _cs;
365 }
366 
367 /**
368  * Stores the indicated transform in the trackball. This is a transform in
369  * global space, regardless of the rel_to node.
370  */
371 void Trackball::
372 set_mat(const LMatrix4 &mat) {
373  _orig = mat;
374  if (_invert) {
375  _mat = invert(_orig);
376  } else {
377  _mat = _orig;
378  }
379 
380  reextract();
381 }
382 
383 
384 /**
385  * Returns the matrix represented by the trackball rotation.
386  */
387 const LMatrix4 &Trackball::
388 get_mat() const {
389  return _orig;
390 }
391 
392 /**
393  * Returns the actual transform that will be applied to the scene graph. This
394  * is the same as get_mat(), unless invert is in effect.
395  */
396 const LMatrix4 &Trackball::
397 get_trans_mat() const {
398  return _mat;
399 }
400 
401 
402 /**
403  * Applies the operation indicated by the user's mouse motion to the current
404  * state. Returns the matrix indicating the new state.
405  */
406 void Trackball::
407 apply(double x, double y, int button) {
408  if (button && !_rel_to.is_empty()) {
409  // If we have a rel_to node, we must first adjust our rotation and
410  // translation to be in those local coordinates.
411  reextract();
412  }
413 
414  if (button == B1_MASK && _control_mode != CM_default) {
415  // We have a control mode set; this may change the meaning of button 1.
416  // Remap button to match the current control mode setting.
417  switch (_control_mode) {
418  case CM_truck:
419  button = B1_MASK;
420  break;
421 
422  case CM_pan:
423  button = B2_MASK;
424  break;
425 
426  case CM_dolly:
427  button = B3_MASK;
428  break;
429 
430  case CM_roll:
431  button = B2_MASK | B3_MASK;
432  break;
433 
434  case CM_default:
435  // Not possible due to above logic.
436  nassertv(false);
437  }
438  }
439 
440  if (button == B1_MASK) {
441  // Button 1: translate in plane parallel to screen.
442 
443  _translation +=
444  x * _fwdscale * LVector3::right(_cs) +
445  y * _fwdscale * LVector3::down(_cs);
446 
447  } else if (button == (B2_MASK | B3_MASK)) {
448  // Buttons 2 + 3: rotate about the vector perpendicular to the screen.
449 
450  _rotation *=
451  LMatrix4::rotate_mat_normaxis((x - y) * _rotscale,
452  LVector3::forward(_cs), _cs);
453 
454  } else if ((button == B2_MASK) || (button == (B1_MASK | B3_MASK))) {
455  // Button 2, or buttons 1 + 3: rotate about the right and up vectors. (We
456  // alternately define this as buttons 1 + 3, to support two-button mice.)
457 
458  _rotation *=
459  LMatrix4::rotate_mat_normaxis(x * _rotscale, LVector3::up(_cs), _cs) *
460  LMatrix4::rotate_mat_normaxis(y * _rotscale, LVector3::right(_cs), _cs);
461 
462  } else if ((button == B3_MASK) || (button == (B1_MASK | B2_MASK))) {
463  // Button 3, or buttons 1 + 2: dolly in and out along the forward vector.
464  // (We alternately define this as buttons 1 + 2, to support two-button
465  // mice.)
466  _translation -= y * _fwdscale * LVector3::forward(_cs);
467  }
468 
469  if (button) {
470  recompute();
471  }
472 }
473 
474 
475 /**
476  * Given a correctly computed _orig matrix, rederive the translation and
477  * rotation elements.
478  */
479 void Trackball::
480 reextract() {
481  LMatrix4 m = _orig;
482  if (!_rel_to.is_empty()) {
483  NodePath root;
484  m = _orig * root.get_transform(_rel_to)->get_mat();
485  }
486 
487  m.get_row3(_translation,3);
488  _rotation = m;
489  _rotation.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
490 }
491 
492 /**
493  * Rebuilds the matrix according to the stored rotation and translation
494  * components.
495  */
496 void Trackball::
497 recompute() {
498  _orig = _rotation * LMatrix4::translate_mat(_translation);
499 
500  if (!_rel_to.is_empty()) {
501  NodePath root;
502  _orig = _orig * _rel_to.get_transform(root)->get_mat();
503  }
504 
505  if (_invert) {
506  _mat = invert(_orig);
507  } else {
508  _mat = _orig;
509  }
510 }
511 
512 
513 /**
514  * The virtual implementation of transmit_data(). This function receives an
515  * array of input parameters and should generate an array of output
516  * parameters. The input parameters may be accessed with the index numbers
517  * returned by the define_input() calls that were made earlier (presumably in
518  * the constructor); likewise, the output parameters should be set with the
519  * index numbers returned by the define_output() calls.
520  */
521 void Trackball::
522 do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
523  DataNodeTransmit &output) {
524  // First, update our modifier buttons.
525  bool required_buttons_match;
526  check_button_events(input, required_buttons_match);
527 
528  // Now, check for mouse motion.
529  if (required_buttons_match && input.has_data(_pixel_xy_input)) {
530  const EventStoreVec2 *pixel_xy;
531  DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
532  const LVecBase2 &p = pixel_xy->get_value();
533  PN_stdfloat this_x = p[0];
534  PN_stdfloat this_y = p[1];
535  int this_button = 0;
536 
537  if (is_down(MouseButton::one())) {
538  if (is_down(KeyboardButton::alt())) {
539  // B1 + alt (option) = B2.
540  this_button |= B2_MASK;
541  if (is_down(KeyboardButton::meta()) || is_down(KeyboardButton::control())) {
542  this_button |= B3_MASK;
543  }
544 
545  } else if (is_down(KeyboardButton::meta()) || is_down(KeyboardButton::control())) {
546  // B1 + meta (command) = B3.
547  this_button |= B3_MASK;
548 
549  } else {
550  // Without a special key, B1 is B1.
551  this_button |= B1_MASK;
552  }
553  }
554  if (is_down(MouseButton::two())) {
555  this_button |= B2_MASK;
556  }
557  if (is_down(MouseButton::three())) {
558  this_button |= B3_MASK;
559  }
560 
561  PN_stdfloat x = this_x - _lastx;
562  PN_stdfloat y = this_y - _lasty;
563 
564  if (this_button == _last_button) {
565  apply(x, y, this_button);
566  }
567 
568  _last_button = this_button;
569  _lastx = this_x;
570  _lasty = this_y;
571  } else {
572  _last_button = 0;
573  }
574 
575  // Now send our matrix down the pipe.
576  _transform = TransformState::make_mat(_mat);
577  output.set_data(_transform_output, EventParameter(_transform));
578 }
const LMatrix4 & get_trans_mat() const
Returns the actual transform that will be applied to the scene graph.
Definition: trackball.cxx:397
void set_mat(const LMatrix4 &mat)
Stores the indicated transform in the trackball.
Definition: trackball.cxx:372
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static ButtonHandle three()
Returns the ButtonHandle associated with the third mouse button.
Definition: mouseButton.cxx:59
static ButtonHandle two()
Returns the ButtonHandle associated with the second mouse button.
Definition: mouseButton.cxx:51
void set_control_mode(ControlMode control_mode)
Sets the control mode.
Definition: trackball.cxx:315
An optional parameter associated with an event.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
get_value
Retrieves the value stored in the parameter.
Definition: paramValue.h:115
void set_invert(bool flag)
Sets the invert flag.
Definition: trackball.cxx:293
static ButtonHandle one()
Returns the ButtonHandle associated with the first mouse button.
Definition: mouseButton.cxx:43
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
CoordinateSystem get_coordinate_system() const
Returns the coordinate system of the Trackball.
Definition: trackball.cxx:363
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_mat
Returns the matrix that describes the transform.
void set_coordinate_system(CoordinateSystem cs)
Sets the coordinate system of the Trackball.
Definition: trackball.cxx:354
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const LPoint3 & get_pos() const
Return the offset from the center of rotation.
Definition: trackball.cxx:116
PN_stdfloat get_forward_scale() const
Returns the scale factor applied to forward and backward motion.
Definition: trackball.cxx:97
void set_data(int index, const EventParameter &data)
Sets the data for the indicated parameter.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ControlMode get_control_mode() const
Returns the control mode.
Definition: trackball.cxx:323
LPoint3 get_origin() const
Returns the current center of rotation.
Definition: trackball.cxx:274
const NodePath & get_rel_to() const
Returns the NodePath that all trackball manipulations are relative to, or the empty path.
Definition: trackball.cxx:343
void reset()
Reinitializes all transforms to identity.
Definition: trackball.cxx:85
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void move_origin(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Moves the center of rotation by the given amount.
Definition: trackball.cxx:266
LVecBase3 get_hpr() const
Return the trackball's orientation.
Definition: trackball.cxx:174
const LMatrix4 & get_mat() const
Returns the matrix represented by the trackball rotation.
Definition: trackball.cxx:388
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...
bool get_invert() const
Returns the invert flag.
Definition: trackball.cxx:303
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_origin(const LVecBase3 &origin)
Directly sets the center of rotation.
Definition: trackball.cxx:282
void set_rel_to(const NodePath &rel_to)
Sets the NodePath that all trackball manipulations are to be assumed to be relative to.
Definition: trackball.cxx:334
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.
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:758
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_hpr(const LVecBase3 &hpr)
Directly set the mover's orientation.
Definition: trackball.cxx:206
void set_forward_scale(PN_stdfloat fwdscale)
Changes the scale factor applied to forward and backward motion.
Definition: trackball.cxx:107
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
void set_pos(const LVecBase3 &vec)
Directly set the offset from the rotational origin.
Definition: trackball.cxx:140
Encapsulates the data generated from (or sent into) any particular DataNode.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void reset_origin_here()
Reposition the center of rotation to coincide with the current translation offset.
Definition: trackball.cxx:255
This object supervises the traversal of the data graph and the moving of data from one DataNode to it...