Browse Source

tform: A partially working trackball; much is still unimplemented

Donny Lawrence 6 years ago
parent
commit
d6d91d1b1c

+ 2 - 0
panda/src/tform/CMakeLists.txt

@@ -10,6 +10,7 @@ set(P3TFORM_HEADERS
   mouseWatcherGroup.h
   mouseWatcherGroup.h
   mouseWatcherParameter.I mouseWatcherParameter.h
   mouseWatcherParameter.I mouseWatcherParameter.h
   mouseWatcherRegion.I mouseWatcherRegion.h
   mouseWatcherRegion.I mouseWatcherRegion.h
+  touchTrackball.h touchTrackball.I
   trackball.h
   trackball.h
   transform2sg.h
   transform2sg.h
 )
 )
@@ -26,6 +27,7 @@ set(P3TFORM_SOURCES
   mouseWatcherGroup.cxx
   mouseWatcherGroup.cxx
   mouseWatcherParameter.cxx
   mouseWatcherParameter.cxx
   mouseWatcherRegion.cxx
   mouseWatcherRegion.cxx
+  touchTrackball.cxx
   trackball.cxx
   trackball.cxx
   transform2sg.cxx
   transform2sg.cxx
 )
 )

+ 3 - 0
panda/src/tform/config_tform.cxx

@@ -16,10 +16,12 @@
 #include "driveInterface.h"
 #include "driveInterface.h"
 #include "buttonThrower.h"
 #include "buttonThrower.h"
 #include "mouseSubregion.h"
 #include "mouseSubregion.h"
+#include "mouseTrackball.h"
 #include "mouseWatcher.h"
 #include "mouseWatcher.h"
 #include "mouseWatcherBase.h"
 #include "mouseWatcherBase.h"
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherRegion.h"
 #include "mouseWatcherRegion.h"
+#include "touchTrackball.h"
 #include "trackball.h"
 #include "trackball.h"
 #include "transform2sg.h"
 #include "transform2sg.h"
 
 
@@ -75,6 +77,7 @@ ConfigureFn(config_tform) {
   MouseWatcherBase::init_type();
   MouseWatcherBase::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherRegion::init_type();
   MouseWatcherRegion::init_type();
+  TouchTrackball::init_type();
   Trackball::init_type();
   Trackball::init_type();
   Transform2SG::init_type();
   Transform2SG::init_type();
 }
 }

+ 1 - 0
panda/src/tform/mouseTrackball.cxx

@@ -30,6 +30,7 @@ MouseTrackball(const std::string &name) :
   _lastx = _lasty = 0.5f;
   _lastx = _lasty = 0.5f;
 
 
   // We want to track the state of these buttons.
   // We want to track the state of these buttons.
+  watch_button(MouseButton::one());
   watch_button(MouseButton::two());
   watch_button(MouseButton::two());
   watch_button(MouseButton::three());
   watch_button(MouseButton::three());
 
 

+ 22 - 1
panda/src/tform/mouseWatcher.cxx

@@ -58,11 +58,13 @@ MouseWatcher(const string &name) :
   _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type());
   _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type());
   _xy_output = define_output("xy", EventStoreVec2::get_class_type());
   _xy_output = define_output("xy", EventStoreVec2::get_class_type());
   _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
   _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
+  _pointer_events_output = define_output("pointer_events", PointerEventList::get_class_type());
 
 
   _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _button_events = new ButtonEventList;
   _button_events = new ButtonEventList;
+  _pointer_events = new PointerEventList;
 
 
   _has_mouse = false;
   _has_mouse = false;
   _internal_suppress = 0;
   _internal_suppress = 0;
@@ -1454,9 +1456,11 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
         pointer_up(event._data, pos);
         pointer_up(event._data, pos);
         _active_regions.erase(it);
         _active_regions.erase(it);
       }
       }
+
+      _recent_pointer_events[event._data.get_id()] = PointerEvent(event);
     }
     }
 
 
-     // Code for recording the mouse trail.
+    // Code for recording the mouse trail.
     _num_trail_recent = 0;
     _num_trail_recent = 0;
     if (_trail_log_duration > 0.0) {
     if (_trail_log_duration > 0.0) {
       _num_trail_recent = this_pointer_events->get_num_events();
       _num_trail_recent = this_pointer_events->get_num_events();
@@ -1663,6 +1667,23 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
   if (_button_events->get_num_events() != 0) {
   if (_button_events->get_num_events() != 0) {
     output.set_data(_button_events_output, EventParameter(_button_events));
     output.set_data(_button_events_output, EventParameter(_button_events));
   }
   }
+
+
+  if (!_recent_pointer_events.empty()) {
+    _pointer_events->clear();
+
+    pmap<int, PointerEvent> next_pointer_events;
+    for (auto id_event_pair : _recent_pointer_events) {
+      _pointer_events->add_event(id_event_pair.second);
+      
+      if (id_event_pair.second._data.get_phase() != PointerPhase::ended) {
+        next_pointer_events[id_event_pair.first] = id_event_pair.second;
+      }
+    }
+
+    _recent_pointer_events = next_pointer_events;
+    output.set_data(_pointer_events_output, EventParameter(_pointer_events));
+  }
 }
 }
 
 
 /**
 /**

+ 4 - 0
panda/src/tform/mouseWatcher.h

@@ -304,11 +304,15 @@ private:
   int _pixel_size_output;
   int _pixel_size_output;
   int _xy_output;
   int _xy_output;
   int _button_events_output;
   int _button_events_output;
+  int _pointer_events_output;
 
 
   PT(EventStoreVec2) _pixel_xy;
   PT(EventStoreVec2) _pixel_xy;
   PT(EventStoreVec2) _xy;
   PT(EventStoreVec2) _xy;
   PT(EventStoreVec2) _pixel_size;
   PT(EventStoreVec2) _pixel_size;
   PT(ButtonEventList) _button_events;
   PT(ButtonEventList) _button_events;
+  PT(PointerEventList) _pointer_events;
+
+  pmap<int, PointerEvent> _recent_pointer_events;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 17 - 0
panda/src/tform/touchTrackball.I

@@ -0,0 +1,17 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file touchTrackball.I
+ * @author D. Lawrence
+ * @date 2019-08-16
+ */
+
+GestureState TouchTrackball::
+get_gesture_state() {
+  return _gesture_state;
+}

+ 151 - 0
panda/src/tform/touchTrackball.cxx

@@ -0,0 +1,151 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file touchTrackball.cxx
+ * @author D. Lawrence
+ * @date 2019-08-16
+ */
+
+#include "touchTrackball.h"
+
+TypeHandle TouchTrackball::_type_handle;
+
+/**
+ *
+ */
+TouchTrackball::
+TouchTrackball(const std::string &name) :
+  Trackball(name)
+{
+  _pointer_input = define_input("pointer_events", PointerEventList::get_class_type());
+
+  // Primary touch events trigger MouseButton::one(), so we can use this to tell
+  // if we need to start tracking the gestures or not.
+  watch_button(MouseButton::one());
+}
+
+GestureState TouchTrackball::
+determine_gesture_state(const PointerEventList *event_list) {
+
+  // Sure do wish we could use optionals right about now.
+  PT(PointerEvent) primary;
+  PT(PointerEvent) secondary;
+
+  for (int i = 0; i < event_list->get_num_events(); i++) {
+    const PointerEvent &event = event_list->get_event(i);
+
+    if (event._sequence == 0) {
+      primary = new PointerEvent(event);
+      if (event._data.get_phase() == PointerPhase::began) {
+        _initial_touches.first = primary;
+      }
+    } else {
+      secondary = new PointerEvent(event);
+      if (event._data.get_phase() == PointerPhase::began) {
+        _initial_touches.second = secondary;
+      }
+    }
+  }
+
+  GestureState next_state = _gesture_state;
+
+  // If we don't have a primary pointer, then we know we're not doing a gesture.
+  if (!primary || primary->_data.get_phase() == PointerPhase::ended) {
+    // Make sure it's cleared out.
+    
+    primary = nullptr;
+    _initial_touches.first = nullptr;
+    next_state = GestureState::none;
+
+  } else if (primary->_data.get_phase() == PointerPhase::began
+             && _gesture_state == GestureState::none) {
+    // The primary finger just started to touch the screen.
+    next_state = GestureState::one_drag;
+
+  } else if (_gesture_state == GestureState::one_drag
+             && secondary
+             && secondary->_data.get_phase() != PointerPhase::ended) {
+    // We're doing a single-finger drag right now, but that could change under
+    // the right circumstances.
+    double primary_duration = ClockObject::get_global_clock()->get_frame_time() - _initial_touches.first->_time;
+    if (primary_duration < _gesture_detect_time) {
+      // The primary touch started recently enough that we can potentially
+      // transition to another gesture.
+      double initial_diff_x = _initial_touches.first->_data.get_x() - _initial_touches.second->_data.get_x();
+      double initial_diff_y = _initial_touches.first->_data.get_y() - _initial_touches.second->_data.get_y();
+      double initial_dist = sqrt(initial_diff_x * initial_diff_x
+                                 + initial_diff_y * initial_diff_y);
+
+      double curr_diff_x = primary->_data.get_x() - secondary->_data.get_x();
+      double curr_diff_y = primary->_data.get_y() - secondary->_data.get_y();
+      double curr_dist = sqrt(curr_diff_x * curr_diff_x
+                                 + curr_diff_y * curr_diff_y);
+
+      if (curr_dist - initial_dist < _pinch_touch_thresh) {
+        next_state = GestureState::two_drag;
+      } else {
+        next_state = GestureState::pinch;
+      }
+      printf("%f\n", curr_dist - initial_dist);
+    }
+  }
+
+  if (!secondary || secondary->_data.get_phase() == PointerPhase::ended) {
+    secondary = nullptr;
+    _initial_touches.second = nullptr;
+  }
+
+  _prev_touches.first = _current_touches.first;
+  _prev_touches.second = _current_touches.second;
+  _current_touches.first = primary;
+  _current_touches.second = secondary;
+
+  return next_state;
+}
+
+/**
+ *
+ */
+void TouchTrackball::
+do_transmit_data(DataGraphTraverser *trav,
+                 const DataNodeTransmit &input,
+                 DataNodeTransmit &output) {
+
+  if (input.has_data(_pointer_input)) {
+    const PointerEventList *event_list;
+    DCAST_INTO_V(event_list, input.get_data(_pointer_input).get_ptr());
+
+    _gesture_state = determine_gesture_state(event_list);
+
+    if (_gesture_state == GestureState::none || !_prev_touches.first || !_current_touches.first) {
+      return;
+    }
+
+    if (!_rel_to.is_empty()) {
+      // If we have a rel_to node, we must first adjust our rotation and
+      // translation to be in those local coordinates.
+      reextract();
+    }
+
+    double dx = _current_touches.first->_data.get_x() - _prev_touches.first->_data.get_x();
+    double dy = _current_touches.first->_data.get_y() - _prev_touches.first->_data.get_y();
+
+    switch (_gesture_state) {
+    case GestureState::one_drag:
+      // Rotation.
+      _rotation *=
+        LMatrix4::rotate_mat_normaxis(dx * _rotscale, LVector3::up(_cs), _cs) *
+        LMatrix4::rotate_mat_normaxis(dy * _rotscale, LVector3::right(_cs), _cs);
+      break;
+    }
+
+    recompute();
+  }
+
+  Trackball::do_transmit_data(trav, input, output);
+}

+ 84 - 0
panda/src/tform/touchTrackball.h

@@ -0,0 +1,84 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file touchTrackball.h
+ * @author D. Lawrence
+ * @date 2019-08-16
+ */
+
+#ifndef TOUCH_TRACKBALL_H
+#define TOUCH_TRACKBALL_H
+
+#include "pandabase.h"
+
+#include "mouseInterfaceNode.h"
+#include "nodePath.h"
+#include "modifierButtons.h"
+#include "luse.h"
+#include "transformState.h"
+
+BEGIN_PUBLISH
+// At the time of writing this is the only class in all of Panda that utilizes
+// multitouch gestures. We'll want to come up with a more generalized way of
+// detecting them that applications can utilize.
+enum class GestureState {
+  none,
+  one_drag,
+  two_drag,
+  pinch
+};
+END_PUBLISH
+
+/**
+ * This functions similarly to Trackball, but uses touch controls instead.
+ */
+class EXPCL_PANDA_TFORM TouchTrackball : public Trackball {
+PUBLISHED:
+  explicit TouchTrackball(const std::string &name);
+
+  INLINE GestureState get_gesture_state();
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+  GestureState determine_gesture_state(const PointerEventList *event_list);
+  
+private:
+  int _pointer_input;
+
+  GestureState _gesture_state;
+
+  const double _gesture_detect_time = 0.5;
+  const double _pinch_touch_thresh = 100.0;
+
+  std::pair<PT(PointerEvent), PT(PointerEvent)> _current_touches, _prev_touches, _initial_touches;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MouseInterfaceNode::init_type();
+    register_type(_type_handle, "TouchTrackball",
+                  MouseInterfaceNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "touchTrackball.I"
+
+#endif

+ 0 - 2
panda/src/tform/trackball.cxx

@@ -46,8 +46,6 @@ Trackball(const std::string &name) :
   _invert = true;
   _invert = true;
   _cs = get_default_coordinate_system();
   _cs = get_default_coordinate_system();
   _control_mode = CM_default;
   _control_mode = CM_default;
-
-  watch_button(MouseButton::one());
 }
 }
 
 
 /**
 /**