2
0
David Rose 24 жил өмнө
parent
commit
d3e2ffbda2
44 өөрчлөгдсөн 3047 нэмэгдсэн , 171 устгасан
  1. 18 13
      panda/src/device/Sources.pp
  2. 2 0
      panda/src/device/config_device.cxx
  3. 1 0
      panda/src/device/device_composite2.cxx
  4. 91 0
      panda/src/device/mouseAndKeyboard.cxx
  5. 90 0
      panda/src/device/mouseAndKeyboard.h
  6. 37 24
      panda/src/dgraph/Sources.pp
  7. 2 0
      panda/src/dgraph/config_dgraph.cxx
  8. 111 0
      panda/src/dgraph/dataNodeTransmit.I
  9. 33 0
      panda/src/dgraph/dataNodeTransmit.cxx
  10. 54 0
      panda/src/dgraph/dataNodeTransmit.h
  11. 3 0
      panda/src/dgraph/dgraph_composite2.cxx
  12. 29 0
      panda/src/dgraph/qpdataGraphTraverser.I
  13. 167 0
      panda/src/dgraph/qpdataGraphTraverser.cxx
  14. 66 0
      panda/src/dgraph/qpdataGraphTraverser.h
  15. 67 0
      panda/src/dgraph/qpdataNode.I
  16. 289 0
      panda/src/dgraph/qpdataNode.cxx
  17. 142 0
      panda/src/dgraph/qpdataNode.h
  18. 0 1
      panda/src/event/config_event.cxx
  19. 31 44
      panda/src/event/eventParameter.I
  20. 9 14
      panda/src/event/eventParameter.h
  21. 21 15
      panda/src/mathutil/Sources.pp
  22. 5 1
      panda/src/mathutil/config_mathutil.cxx
  23. 24 0
      panda/src/mathutil/linmath_events.cxx
  24. 53 0
      panda/src/mathutil/linmath_events.h
  25. 1 0
      panda/src/mathutil/mathutil_composite2.cxx
  26. 75 24
      panda/src/pgraph/pandaNode.cxx
  27. 2 0
      panda/src/pgraph/pandaNode.h
  28. 9 3
      panda/src/putil/Sources.pp
  29. 74 0
      panda/src/putil/buttonEventList.I
  30. 62 0
      panda/src/putil/buttonEventList.cxx
  31. 82 0
      panda/src/putil/buttonEventList.h
  32. 2 0
      panda/src/putil/config_util.cxx
  33. 1 0
      panda/src/putil/putil_composite1.cxx
  34. 21 26
      panda/src/testbed/pview.cxx
  35. 21 6
      panda/src/tform/Sources.pp
  36. 6 0
      panda/src/tform/config_tform.cxx
  37. 399 0
      panda/src/tform/qpbuttonThrower.cxx
  38. 106 0
      panda/src/tform/qpbuttonThrower.h
  39. 530 0
      panda/src/tform/qptrackball.cxx
  40. 152 0
      panda/src/tform/qptrackball.h
  41. 84 0
      panda/src/tform/qptransform2sg.cxx
  42. 72 0
      panda/src/tform/qptransform2sg.h
  43. 1 0
      panda/src/tform/tform_composite1.cxx
  44. 2 0
      panda/src/tform/tform_composite2.cxx

+ 18 - 13
panda/src/device/Sources.pp

@@ -9,21 +9,25 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
 
   #define SOURCES \
-     analogNode.I analogNode.h buttonNode.I buttonNode.h  \
-     clientAnalogDevice.I clientAnalogDevice.h clientBase.I  \
-     clientBase.h clientButtonDevice.I clientButtonDevice.h  \
-     clientDevice.I clientDevice.h clientDialDevice.I  \
-     clientDialDevice.h clientTrackerDevice.I  \
-     clientTrackerDevice.h config_device.h dialNode.I dialNode.h  \
-     mouse.h trackerData.I trackerData.h trackerNode.I  \
-     trackerNode.h virtualMouse.h
+    analogNode.I analogNode.h buttonNode.I buttonNode.h  \
+    clientAnalogDevice.I clientAnalogDevice.h clientBase.I  \
+    clientBase.h clientButtonDevice.I clientButtonDevice.h  \
+    clientDevice.I clientDevice.h clientDialDevice.I  \
+    clientDialDevice.h clientTrackerDevice.I  \
+    clientTrackerDevice.h config_device.h dialNode.I dialNode.h  \
+    mouse.h \
+    mouseAndKeyboard.h \
+    trackerData.I trackerData.h trackerNode.I  \
+    trackerNode.h virtualMouse.h
 
   #define INCLUDED_SOURCES \
-     analogNode.cxx buttonNode.cxx clientAnalogDevice.cxx  \
-     clientBase.cxx clientButtonDevice.cxx clientDevice.cxx  \
-     clientDialDevice.cxx clientTrackerDevice.cxx  \
-     config_device.cxx dialNode.cxx mouse.cxx trackerData.cxx  \
-     trackerNode.cxx virtualMouse.cxx
+    analogNode.cxx buttonNode.cxx clientAnalogDevice.cxx  \
+    clientBase.cxx clientButtonDevice.cxx clientDevice.cxx  \
+    clientDialDevice.cxx clientTrackerDevice.cxx  \
+    config_device.cxx dialNode.cxx mouse.cxx \
+    mouseAndKeyboard.cxx \
+    trackerData.cxx  \
+    trackerNode.cxx virtualMouse.cxx
 
   #define INSTALL_HEADERS \
     analogNode.I analogNode.h \
@@ -35,6 +39,7 @@
     clientDialDevice.I clientDialDevice.h \
     clientTrackerDevice.I clientTrackerDevice.h \
     config_device.h mouse.h \
+    mouseAndKeyboard.h \
     dialNode.I dialNode.h \
     trackerData.I trackerData.h \
     trackerNode.I trackerNode.h virtualMouse.h

+ 2 - 0
panda/src/device/config_device.cxx

@@ -28,6 +28,7 @@
 #include "clientTrackerDevice.h"
 #include "dialNode.h"
 #include "mouse.h"
+#include "mouseAndKeyboard.h"
 #include "trackerNode.h"
 #include "virtualMouse.h"
 
@@ -68,6 +69,7 @@ init_libdevice() {
   ClientTrackerDevice::init_type();
   DialNode::init_type();
   MouseAndKeyboard::init_type();
+  qpMouseAndKeyboard::init_type();
   TrackerNode::init_type();
   VirtualMouse::init_type();
 }

+ 1 - 0
panda/src/device/device_composite2.cxx

@@ -4,6 +4,7 @@
 #include "buttonNode.cxx"
 #include "dialNode.cxx"
 #include "mouse.cxx"
+#include "mouseAndKeyboard.cxx"
 #include "trackerData.cxx"
 #include "trackerNode.cxx"
 #include "virtualMouse.cxx"

+ 91 - 0
panda/src/device/mouseAndKeyboard.cxx

@@ -0,0 +1,91 @@
+// Filename: mouseAndKeyboard.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mouseAndKeyboard.h"
+#include "mouseData.h"
+#include "buttonHandle.h"
+#include "buttonEvent.h"
+#include "dataNodeTransmit.h"
+
+TypeHandle qpMouseAndKeyboard::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseAndKeyboard::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpMouseAndKeyboard::
+qpMouseAndKeyboard(GraphicsWindow *window, int device, const string &name) :
+  qpDataNode(name),
+  _window(window),
+  _device(device)
+{
+  _pixel_xy_output = define_output("PixelXY", EventStoreVec2::get_class_type());
+  _xy_output = define_output("XY", EventStoreVec2::get_class_type());
+  _button_events_output = define_output("ButtonEvents", ButtonEventList::get_class_type());
+
+  _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+  _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+  _button_events = new ButtonEventList;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseAndKeyboard::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpMouseAndKeyboard::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+  if (_window->has_button_event(_device)) {
+    // Fill up the button events.
+    _button_events->clear();
+    while (_window->has_button_event(_device)) {
+      ButtonEvent be = _window->get_button_event(_device);
+      _button_events->add_event(be);
+    }
+    output.set_data(_button_events_output, EventParameter(_button_events));
+  }
+
+  if (_window->has_pointer(_device)) {
+    const MouseData &mdata = _window->get_mouse_data(_device);
+
+    if (mdata._in_window) {
+      // Get mouse motion in pixels.
+      _pixel_xy->set_value(LPoint2f(mdata._xpos, mdata._ypos));
+      output.set_data(_pixel_xy_output, EventParameter(_pixel_xy));
+
+      int w = _window->get_width();
+      int h = _window->get_height();
+
+      // Normalize pixel motion to range [-1,1].
+      float xf = (float)(2 * mdata._xpos) / (float)w - 1.0f;
+      float yf = 1.0f - (float)(2 * mdata._ypos) / (float)h;
+
+      _xy->set_value(LPoint2f(xf, yf));
+      output.set_data(_xy_output, EventParameter(_xy));
+    }
+  }
+}

+ 90 - 0
panda/src/device/mouseAndKeyboard.h

@@ -0,0 +1,90 @@
+// Filename: mouseAndKeyboard.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MOUSEANDKEYBOARD_H
+#define MOUSEANDKEYBOARD_H
+
+#include "pandabase.h"
+
+#include "qpdataNode.h"
+#include "buttonEventList.h"
+#include "linmath_events.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : MouseAndKeyboard
+// Description : Reads the mouse and/or keyboard data sent from a
+//               GraphicsWindow, and transmits it down the data graph.
+//
+//               The mouse and keyboard devices are bundled together
+//               into one device here, because they interrelate so
+//               much.  A mouse might be constrained by the holding
+//               down of the shift key, for instance, or the clicking
+//               of the mouse button might be handled in much the same
+//               way as a keyboard key.
+//
+//               Mouse data is sent down the data graph as an x,y
+//               position as well as the set of buttons currently
+//               being held down; keyboard data is sent down as a set
+//               of keypress events in an EventDataTransition.  To
+//               throw these events to the system, you must attach an
+//               EventThrower to the MouseAndKeyboard object;
+//               otherwise, the events will be discarded.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpMouseAndKeyboard : public qpDataNode {
+PUBLISHED:
+  qpMouseAndKeyboard(GraphicsWindow *window, int device,
+                     const string &name);
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // outputs
+  int _pixel_xy_output;
+  int _xy_output;
+  int _button_events_output;
+
+  PT(EventStoreVec2) _pixel_xy;
+  PT(EventStoreVec2) _xy;
+  PT(ButtonEventList) _button_events;
+
+  PT(GraphicsWindow) _window;
+  int _device;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpMouseAndKeyboard",
+                  qpDataNode::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;
+};
+
+#endif

+ 37 - 24
panda/src/dgraph/Sources.pp

@@ -4,38 +4,52 @@
 #begin lib_target
   #define TARGET dgraph
   #define LOCAL_LIBS \
-    pstatclient sgraph graph putil sgattrib mathutil
+    pstatclient sgraph graph putil sgattrib mathutil event
 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx  
   
   #define SOURCES \
-     buttonEventDataTransition.I buttonEventDataTransition.h  \
-     config_dgraph.h dataGraphTraversal.h dataGraphTraverser.I  \
-     dataGraphTraverser.h dataNode.h dataRelation.I  \
-     dataRelation.N dataRelation.h describe_data_verbose.h  \
-     doubleDataTransition.I doubleDataTransition.h  \
-     intDataTransition.I intDataTransition.h \
-     matrixDataTransition.I matrixDataTransition.h \
-     numericDataTransition.I numericDataTransition.h \
-     vec3DataTransition.I vec3DataTransition.h \
-     vec4DataTransition.I vec4DataTransition.h  \
-     vectorDataTransition.I vectorDataTransition.h
+    buttonEventDataTransition.I buttonEventDataTransition.h  \
+    config_dgraph.h dataGraphTraversal.h dataGraphTraverser.I  \
+    dataGraphTraverser.h \
+    qpdataGraphTraverser.I qpdataGraphTraverser.h \
+    dataNode.h \
+    qpdataNode.I qpdataNode.h \
+    dataNodeTransmit.I dataNodeTransmit.h \
+    dataRelation.I  \
+    dataRelation.N dataRelation.h describe_data_verbose.h  \
+    doubleDataTransition.I doubleDataTransition.h  \
+    intDataTransition.I intDataTransition.h \
+    matrixDataTransition.I matrixDataTransition.h \
+    numericDataTransition.I numericDataTransition.h \
+    vec3DataTransition.I vec3DataTransition.h \
+    vec4DataTransition.I vec4DataTransition.h  \
+    vectorDataTransition.I vectorDataTransition.h
     
  #define INCLUDED_SOURCES \
-     buttonEventDataTransition.cxx  \
-     config_dgraph.cxx dataGraphTraversal.cxx  \
-     dataGraphTraverser.cxx dataNode.cxx dataRelation.cxx  \
-     describe_data_verbose.cxx \
-     doubleDataTransition.cxx  \
-     intDataTransition.cxx  \
-     matrixDataTransition.cxx  \
-     vec3DataTransition.cxx  \
-     vec4DataTransition.cxx  
+    buttonEventDataTransition.cxx  \
+    config_dgraph.cxx dataGraphTraversal.cxx  \
+    dataGraphTraverser.cxx \
+    qpdataGraphTraverser.cxx \
+    dataNode.cxx \
+    qpdataNode.cxx \
+    dataNodeTransmit.cxx \
+    dataRelation.cxx  \
+    describe_data_verbose.cxx \
+    doubleDataTransition.cxx  \
+    intDataTransition.cxx  \
+    matrixDataTransition.cxx  \
+    vec3DataTransition.cxx  \
+    vec4DataTransition.cxx  
 
   #define INSTALL_HEADERS \
     buttonEventDataTransition.I buttonEventDataTransition.h \
     dataGraphTraversal.h \
-    dataNode.h dataRelation.I dataRelation.h \
+    qpdataGraphTraverser.I qpdataGraphTraverser.h \
+    dataNode.h \
+    qpdataNode.I qpdataNode.h \
+    dataNodeTransmit.I dataNodeTransmit.h \
+    dataRelation.I dataRelation.h \
     describe_data_verbose.h \
     doubleDataTransition.I doubleDataTransition.h \
     intDataTransition.I intDataTransition.h \
@@ -46,8 +60,7 @@
     vectorDataTransition.I vectorDataTransition.h
 
   #define IGATESCAN \
-    dataNode.cxx dataNode.h dataRelation.cxx dataRelation.h \
-    dataGraphTraversal.cxx dataGraphTraversal.h
+    all
 
 #end lib_target
 

+ 2 - 0
panda/src/dgraph/config_dgraph.cxx

@@ -18,6 +18,7 @@
 
 #include "config_dgraph.h"
 #include "dataNode.h"
+#include "qpdataNode.h"
 #include "dataRelation.h"
 #include "intDataTransition.h"
 #include "doubleDataTransition.h"
@@ -32,6 +33,7 @@ NotifyCategoryDef(dgraph, "");
 
 ConfigureFn(config_dgraph) {
   DataNode::init_type();
+  qpDataNode::init_type();
   DataRelation::init_type();
   IntDataTransition::init_type();
   DoubleDataTransition::init_type();

+ 111 - 0
panda/src/dgraph/dataNodeTransmit.I

@@ -0,0 +1,111 @@
+// Filename: dataNodeTransmit.I
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DataNodeTransmit::
+DataNodeTransmit() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DataNodeTransmit::
+DataNodeTransmit(const DataNodeTransmit &copy) :
+  _data(copy._data)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void DataNodeTransmit::
+operator = (const DataNodeTransmit &copy) {
+  _data = copy._data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DataNodeTransmit::
+~DataNodeTransmit() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::reserve
+//       Access: Public
+//  Description: Tells the DataNodeTransmit object how many wires it
+//               is expected to store data for.
+////////////////////////////////////////////////////////////////////
+INLINE void DataNodeTransmit::
+reserve(int num_wires) {
+  _data.reserve(num_wires);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::get_data
+//       Access: Public
+//  Description: Extracts the data for the indicated index, if it has
+//               been stored, or the empty parameter if it has not.
+////////////////////////////////////////////////////////////////////
+INLINE const EventParameter &DataNodeTransmit::
+get_data(int index) const {
+  if (index >= 0 && index < (int)_data.size()) {
+    return _data[index];
+  }
+  static EventParameter empty_parameter;
+  return empty_parameter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::has_data
+//       Access: Public
+//  Description: Returns true if the indicated parameter has been
+//               stored, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool DataNodeTransmit::
+has_data(int index) const {
+  if (index >= 0 && index < (int)_data.size()) {
+    return !_data[index].is_empty();
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::set_data
+//       Access: Public
+//  Description: Sets the data for the indicated parameter.
+////////////////////////////////////////////////////////////////////
+INLINE void DataNodeTransmit::
+set_data(int index, const EventParameter &data) {
+  if (index >= (int)_data.size()) {
+    slot_data(index);
+  }
+  nassertv(index >= 0 && index < (int)_data.size());
+  _data[index] = data;
+}

+ 33 - 0
panda/src/dgraph/dataNodeTransmit.cxx

@@ -0,0 +1,33 @@
+// Filename: dataNodeTransmit.cxx
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dataNodeTransmit.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNodeTransmit::slot_data
+//       Access: Private
+//  Description: Ensures that the given index number exists in the
+//               data array.
+////////////////////////////////////////////////////////////////////
+void DataNodeTransmit::
+slot_data(int index) {
+  nassertv(index < 1000);
+  while (index >= (int)_data.size()) {
+    _data.push_back(EventParameter());
+  }
+}

+ 54 - 0
panda/src/dgraph/dataNodeTransmit.h

@@ -0,0 +1,54 @@
+// Filename: dataNodeTransmit.h
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DATANODETRANSMIT_H
+#define DATANODETRANSMIT_H
+
+#include "pandabase.h"
+#include "eventParameter.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DataNodeTransmit
+// Description : Encapsulates the data generated from (or sent into)
+//               any particular DataNode.  This is basically just an
+//               array of EventParameters, one for each registered
+//               input or output wire.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DataNodeTransmit {
+public:
+  INLINE DataNodeTransmit();
+  INLINE DataNodeTransmit(const DataNodeTransmit &copy);
+  INLINE void operator = (const DataNodeTransmit &copy);
+  INLINE ~DataNodeTransmit();
+
+  INLINE void reserve(int num_wires);
+
+  INLINE const EventParameter &get_data(int index) const;
+  INLINE bool has_data(int index) const;
+  INLINE void set_data(int index, const EventParameter &data);
+
+private:
+  void slot_data(int index);
+
+  typedef pvector<EventParameter> Data;
+  Data _data;
+};
+
+#include "dataNodeTransmit.I"
+
+#endif

+ 3 - 0
panda/src/dgraph/dgraph_composite2.cxx

@@ -1,7 +1,10 @@
 
 #include "dataGraphTraversal.cxx"
 #include "dataGraphTraverser.cxx"
+#include "qpdataGraphTraverser.cxx"
 #include "dataNode.cxx"
+#include "qpdataNode.cxx"
+#include "dataNodeTransmit.cxx"
 #include "dataRelation.cxx"
 #include "describe_data_verbose.cxx"
 #include "doubleDataTransition.cxx"

+ 29 - 0
panda/src/dgraph/qpdataGraphTraverser.I

@@ -0,0 +1,29 @@
+// Filename: qpdataGraphTraverser.I
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::CollectedData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE qpDataGraphTraverser::CollectedData::
+CollectedData() :
+  _num_parents(0)
+{
+}

+ 167 - 0
panda/src/dgraph/qpdataGraphTraverser.cxx

@@ -0,0 +1,167 @@
+// Filename: qpdataGraphTraverser.cxx
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpdataGraphTraverser.h"
+#include "qpdataNode.h"
+#include "config_dgraph.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::CollectedData::set_data
+//       Access: Public
+//  Description: Sets the data associated with the indicated parent of
+//               this CollectedData object's node.
+////////////////////////////////////////////////////////////////////
+void qpDataGraphTraverser::CollectedData::
+set_data(int parent_index, const DataNodeTransmit &data) {
+  if ((int)_data.size() <= parent_index) {
+    _data.reserve(parent_index + 1);
+    while ((int)_data.size() <= parent_index) {
+      _data.push_back(DataNodeTransmit());
+    }
+  }
+
+  nassertv(parent_index >= 0 && parent_index < (int)_data.size());
+  _data[parent_index] = data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpDataGraphTraverser::
+qpDataGraphTraverser() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpDataGraphTraverser::
+~qpDataGraphTraverser() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::traverse
+//       Access: Public
+//  Description: Starts the traversal of the data graph at the
+//               indicated root node.
+////////////////////////////////////////////////////////////////////
+void qpDataGraphTraverser::
+traverse(PandaNode *node) {
+  if (node->is_of_type(qpDataNode::get_class_type())) {
+    qpDataNode *data_node = DCAST(qpDataNode, node);
+    int num_parents = data_node->get_num_parents();
+    // We must start the traversal at the root of the graph.
+    nassertv(num_parents == 0);
+
+    r_transmit(data_node, (DataNodeTransmit *)NULL);
+
+  } else {
+    r_traverse_children(node, DataNodeTransmit());
+  }
+
+  // Now pick up any nodes that didn't get completely traversed.
+  // These must be nodes that have multiple parents, with at least one
+  // parent completely outside of the data graph.
+
+  while (!_multipass_data.empty()) {
+    MultipassData::iterator mi = _multipass_data.begin();
+    qpDataNode *data_node = (*mi).first;
+    const CollectedData &collected_data = (*mi).second;
+
+    dgraph_cat.warning()
+      << *data_node << " improperly parented partly outside of data graph.\n";
+
+    r_transmit(data_node, &collected_data._data[0]);
+    _multipass_data.erase(mi);
+  }  
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::r_transmit
+//       Access: Private
+//  Description: Part of the recursive implementation of traverse().
+//               This transmits the given data into the indicated
+//               DataNode, and then sends the output data to each of
+//               the node's children.
+////////////////////////////////////////////////////////////////////
+void qpDataGraphTraverser::
+r_transmit(qpDataNode *data_node, const DataNodeTransmit inputs[]) {
+  DataNodeTransmit output;
+  output.reserve(data_node->get_num_outputs());
+  data_node->transmit_data(inputs, output);
+
+  r_traverse_children(data_node, output);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::r_traverse_children
+//       Access: Private
+//  Description: Part of the recursive implementation of traverse().
+//               This visits each of the children of the indicated
+//               node, passing in the given data.
+////////////////////////////////////////////////////////////////////
+void qpDataGraphTraverser::
+r_traverse_children(PandaNode *node, const DataNodeTransmit &output) {
+  PandaNode::Children cr = node->get_children();
+  int num_children = cr.get_num_children();
+
+  for (int i = 0; i < num_children; i++) {
+    PandaNode *child_node = cr.get_child(i);
+    if (child_node->is_of_type(qpDataNode::get_class_type())) {
+      qpDataNode *data_node = DCAST(qpDataNode, child_node);
+      // If it's a DataNode-type child, we need to pass it the data.
+      // Maybe it has only one parent, and can accept the data
+      // immediately.
+      int num_parents = data_node->get_num_parents();
+      if (num_parents == 1) {
+        // The easy, common case: only one parent.  We make our output
+        // into a one-element array of inputs by turning it into a
+        // pointer.
+        r_transmit(data_node, &output);
+      } else {
+        // A more difficult case: multiple parents.  We must collect
+        // instances together, meaning we must hold onto this node
+        // until we have reached it through all paths.
+        CollectedData &collected_data = _multipass_data[data_node];
+        int parent_index = data_node->find_parent(node);
+        nassertv(parent_index != -1);
+
+        collected_data.set_data(parent_index, output);
+        collected_data._num_parents++;
+        nassertv(collected_data._num_parents <= num_parents);
+        if (collected_data._num_parents == num_parents) {
+          // Now we've got all the data; go into the child.
+          r_transmit(data_node, &collected_data._data[0]);
+          _multipass_data.erase(data_node);
+        }
+      }
+    } else {
+      // The child node is not a DataNode-type child.  We continue the
+      // traversal, but data does not pass through this node, and
+      // instances are not collected together.  (Although we appear to
+      // be passing the data through here, it doesn't do any good
+      // anyway, since the child nodes of this node will not know how
+      // to interpret the data from a non-DataNode parent.)
+      r_traverse_children(child_node, output);
+    }
+  }
+}

+ 66 - 0
panda/src/dgraph/qpdataGraphTraverser.h

@@ -0,0 +1,66 @@
+// Filename: qpdataGraphTraverser.h
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpDATAGRAPHTRAVERSER_H
+#define qpDATAGRAPHTRAVERSER_H
+
+#include "pandabase.h"
+
+#include "dataNodeTransmit.h"
+#include "pvector.h"
+#include "pmap.h"
+
+class qpDataNode;
+class PandaNode;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DataGraphTraverser
+// Description : This object supervises the traversal of the data
+//               graph and the moving of data from one DataNode to its
+//               children.  The data graph is used to manage data from
+//               input devices, etc.  See the overview of the data
+//               graph in dataNode.h.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpDataGraphTraverser {
+PUBLISHED:
+  qpDataGraphTraverser();
+  ~qpDataGraphTraverser();
+
+  void traverse(PandaNode *node);
+
+private:
+  void r_transmit(qpDataNode *data_node, const DataNodeTransmit inputs[]);
+  void r_traverse_children(PandaNode *node, const DataNodeTransmit &output);
+
+  typedef pvector<DataNodeTransmit> DataVector;
+
+  class CollectedData {
+  public:
+    INLINE CollectedData();
+    void set_data(int parent_index, const DataNodeTransmit &data);
+
+    int _num_parents;
+    DataVector _data;
+  };
+  typedef pmap<qpDataNode *, CollectedData> MultipassData;
+  MultipassData _multipass_data;
+};
+
+#include "qpdataGraphTraverser.I"
+
+#endif

+ 67 - 0
panda/src/dgraph/qpdataNode.I

@@ -0,0 +1,67 @@
+// Filename: qpdataNode.I
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE qpDataNode::
+qpDataNode(const string &name) :
+  PandaNode(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpDataNode::
+qpDataNode(const qpDataNode &copy) :
+  PandaNode(copy)
+{
+  // Copying a DataNode doesn't copy its inputs or outputs.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::get_num_inputs
+//       Access: Public
+//  Description: Returns the number of different inputs that have been
+//               defined for this node using define_input().  This
+//               indicates the size of the DataNodeTransmit object
+//               that should be passed to do_transmit_data().
+////////////////////////////////////////////////////////////////////
+INLINE int qpDataNode::
+get_num_inputs() const {
+  return _input_wires.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::get_num_outputs
+//       Access: Public
+//  Description: Returns the number of different outputs that have been
+//               defined for this node using define_output().  This
+//               indicates the size of the DataNodeTransmit object
+//               that should be passed to do_transmit_data().
+////////////////////////////////////////////////////////////////////
+INLINE int qpDataNode::
+get_num_outputs() const {
+  return _output_wires.size();
+}

+ 289 - 0
panda/src/dgraph/qpdataNode.cxx

@@ -0,0 +1,289 @@
+// Filename: qpdataNode.cxx
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpdataNode.h"
+#include "dataNodeTransmit.h"
+#include "config_dgraph.h"
+
+TypeHandle qpDataNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpDataNode::
+make_copy() const {
+  return new qpDataNode(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::transmit_data
+//       Access: Public
+//  Description: Collects the data from all of the parent nodes and
+//               puts it into one DataNodeTransmit object, for
+//               processing; calls do_transmit_data() to read all the
+//               inputs and put the result into the indicated output.
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+transmit_data(const DataNodeTransmit inputs[],
+              DataNodeTransmit &output) {
+  DataNodeTransmit new_input;
+  new_input.reserve(get_num_inputs());
+
+  DataConnections::const_iterator ci;
+  for (ci = _data_connections.begin(); ci != _data_connections.end(); ++ci) {
+    const DataConnection &connect = (*ci);
+    const EventParameter &data = 
+      inputs[connect._parent_index].get_data(connect._output_index);
+    new_input.set_data(connect._input_index, data);
+  }
+
+  do_transmit_data(new_input, output);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::write_inputs
+//       Access: Published
+//  Description: Writes to the indicated ostream a list of all the
+//               inputs this DataNode might expect to receive.
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+write_inputs(ostream &out) const {
+  Wires::const_iterator wi;
+  for (wi = _input_wires.begin(); wi != _input_wires.end(); ++wi) {
+    const string &name = (*wi).first;
+    const WireDef &def = (*wi).second;
+    out << name << " " << def._data_type << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::write_outputs
+//       Access: Published
+//  Description: Writes to the indicated ostream a list of all the
+//               outputs this DataNode might generate.
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+write_outputs(ostream &out) const {
+  Wires::const_iterator wi;
+  for (wi = _output_wires.begin(); wi != _output_wires.end(); ++wi) {
+    const string &name = (*wi).first;
+    const WireDef &def = (*wi).second;
+    out << name << " " << def._data_type << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::write_connections
+//       Access: Published
+//  Description: Writes to the indicated ostream a list of all the
+//               connections currently showing between this DataNode
+//               and its parent(s).
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+write_connections(ostream &out) const {
+  DataConnections::const_iterator ci;
+  for (ci = _data_connections.begin(); ci != _data_connections.end(); ++ci) {
+    const DataConnection &connect = (*ci);
+    nassertv(connect._parent_index >= 0 && connect._parent_index < get_num_parents());
+
+    // Now we have to search exhaustively for the input with the
+    // matching index number.
+    Wires::const_iterator wi;
+    bool found = false;
+    for (wi = _input_wires.begin(); wi != _input_wires.end() && !found; ++wi) {
+      const string &name = (*wi).first;
+      const WireDef &def = (*wi).second;
+      if (def._index == connect._input_index) {
+        out << name << " " << def._data_type << " from " 
+            << *get_parent(connect._parent_index) << "\n";
+        found = true;
+      }
+    }
+    nassertv(found);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::define_input
+//       Access: Protected
+//  Description: Adds a new input wire with the given name and the
+//               indicated data type.  The data type should be the
+//               TypeHandle for some type that derives from
+//               TypedReferenceCount, e.g. EventStoreInt,
+//               EventStoreDouble, or some fancier data type like
+//               Texture.
+//
+//               If there is already an input wire defined with the
+//               indicated name, its type is changed.
+//
+//               The return value is the index into the "input"
+//               parameter to do_transmit_data() that can be used to
+//               access the input data.
+////////////////////////////////////////////////////////////////////
+int qpDataNode::
+define_input(const string &name, TypeHandle data_type) {
+  // We shouldn't already be connected.
+  nassertr(_data_connections.empty(), 0);
+
+  Wires::iterator wi;
+  wi = _input_wires.find(name);
+  if (wi != _input_wires.end()) {
+    // This wire already existed; modify it and return the original
+    // index.
+    WireDef &def = (*wi).second;
+    def._data_type = data_type;
+    return def._index;
+  }
+
+  // This wire did not already exist; add it.
+  WireDef &def = _input_wires[name];
+  def._data_type = data_type;
+  def._index = _input_wires.size() - 1;
+  return def._index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::define_output
+//       Access: Protected
+//  Description: Adds a new output wire with the given name and the
+//               indicated data type.  The data type should be the
+//               TypeHandle for some type that derives from
+//               TypedReferenceCount, e.g. EventStoreInt,
+//               EventStoreDouble, or some fancier data type like
+//               Texture.
+//
+//               If there is already an output wire defined with the
+//               indicated name, its type is changed.
+//
+//               The return value is the index into the "output"
+//               parameter to do_transmit_data() where the output data
+//               should be stored.
+////////////////////////////////////////////////////////////////////
+int qpDataNode::
+define_output(const string &name, TypeHandle data_type) {
+  // We shouldn't already be connected.
+  nassertr(_data_connections.empty(), 0);
+
+  Wires::iterator wi;
+  wi = _output_wires.find(name);
+  if (wi != _output_wires.end()) {
+    // This wire already existed; modify it and return the original
+    // index.
+    WireDef &def = (*wi).second;
+    def._data_type = data_type;
+    return def._index;
+  }
+
+  // This wire did not already exist; add it.
+  WireDef &def = _output_wires[name];
+  def._data_type = data_type;
+  def._index = _output_wires.size() - 1;
+  return def._index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::parents_changed
+//       Access: Protected, Virtual
+//  Description: Called after a scene graph update that either adds or
+//               remove parents from this node, this just provides a
+//               hook for derived PandaNode objects that need to
+//               update themselves based on the set of parents the
+//               node has.
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+parents_changed() {
+  reconnect();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataNode::reconnect
+//       Access: Private
+//  Description: Establishes the input(s) that this DataNode has in
+//               common with its parents' output(s).  Builds up the
+//               _data_connections list correspondingly.
+////////////////////////////////////////////////////////////////////
+void qpDataNode::
+reconnect() {
+  int num_parents = get_num_parents();
+  _data_connections.clear();
+  // Look for each input among one of the parents.
+
+  Wires::const_iterator wi;
+  for (wi = _input_wires.begin(); wi != _input_wires.end(); ++wi) {
+    const string &name = (*wi).first;
+    const WireDef &input_def = (*wi).second;
+
+    int num_found = 0;
+    for (int i = 0; i < num_parents; i++) {
+      PandaNode *parent_node = get_parent(i);
+      if (parent_node->is_of_type(qpDataNode::get_class_type())) {
+        qpDataNode *data_node = DCAST(qpDataNode, parent_node);
+        Wires::const_iterator pi;
+        pi = data_node->_output_wires.find(name);
+        if (pi != data_node->_output_wires.end()) {
+          const WireDef &output_def = (*pi).second;
+          num_found++;
+          if (output_def._data_type != input_def._data_type) {
+            dgraph_cat.warning()
+              << "Ignoring mismatched type for connection " << name 
+              << " between " << *data_node << " and " << *this << "\n";
+          } else if (num_found == 1) {
+            DataConnection dc;
+            dc._parent_index = i;
+            dc._output_index = output_def._index;
+            dc._input_index = input_def._index;
+            _data_connections.push_back(dc);
+          }
+        }
+      }
+    }
+
+    if (num_found > 1) {
+      dgraph_cat.warning()
+        << "Multiple connections found for " << name << " into " << *this
+        << "\n";
+    }
+  }
+            
+  if (_data_connections.empty() && get_num_inputs() != 0 && num_parents != 0) {
+    dgraph_cat.warning()
+      << "No data connected to " << *this << "\n";
+  }
+}

+ 142 - 0
panda/src/dgraph/qpdataNode.h

@@ -0,0 +1,142 @@
+// Filename: qpdataNode.h
+// Created by:  drose (11Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpDATANODE_H
+#define qpDATANODE_H
+
+////////////////////////////////////////////////////////////////////
+//
+// The Data Graph.
+//
+// The data graph is intended to hook up devices and their inputs
+// and/or outputs in a clean interface.  It uses the same graph
+// relationship that is used to construct the scene graph, with the
+// same sort of nodes and NodePaths.
+//
+// In a data graph, each node may potentially produce and/or consume
+// data, and the arcs transmit data downward, from the root of the
+// graph to its leaves.  Thus, an input device such as a mouse might
+// be added to the graph near the root, and a tformer-style object
+// that interprets the mouse data as a trackball motion and outputs a
+// matrix might be the immediate child of the mouse, followed by an
+// object that accepts a matrix and sets it on some particular arc in
+// the scene graph.
+//
+// Each different kind of DataNode defines its own set of input values
+// and output values, identified by name.  When a DataNode is attached
+// to another DataNode, the inputs of the child are automatically
+// connected up to the corresponding outputs of the parent, and an
+// error message is issued if there are no matching connections.
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+
+#include "pandaNode.h"
+#include "pointerTo.h"
+
+class DataNodeTransmit;
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpDataNode
+// Description : The fundamental type of node for the data graph.  The
+//               DataNode class is itself primarily intended as an
+//               abstract class; it defines no inputs and no outputs.
+//               Most kinds of data nodes will derive from this to
+//               specify the inputs and outputs in the constructor.
+//
+//               DataNode does not attempt to cycle its data with a
+//               PipelineCycler.  The data graph is intended to be
+//               used only within a single thread.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpDataNode : public PandaNode {
+PUBLISHED:
+  INLINE qpDataNode(const string &name);
+
+protected:
+  INLINE qpDataNode(const qpDataNode &copy);
+public:
+  virtual PandaNode *make_copy() const;
+
+  void transmit_data(const DataNodeTransmit inputs[],
+                     DataNodeTransmit &output);
+
+  INLINE int get_num_inputs() const;
+  INLINE int get_num_outputs() const;
+
+PUBLISHED:
+  void write_inputs(ostream &out) const;
+  void write_outputs(ostream &out) const;
+  void write_connections(ostream &out) const;
+
+  //protected:
+  int define_input(const string &name, TypeHandle data_type);
+  int define_output(const string &name, TypeHandle data_type);
+
+protected:
+  // Inherited from PandaNode
+  virtual void parents_changed();
+
+  // Local to DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  void reconnect();
+
+  class WireDef {
+  public:
+    TypeHandle _data_type;
+    int _index;
+  };
+
+  typedef pmap<string, WireDef> Wires;
+
+  Wires _input_wires;
+  Wires _output_wires;
+
+  class DataConnection {
+  public:
+    int _parent_index;
+    int _output_index;
+    int _input_index;
+  };
+  typedef pvector<DataConnection> DataConnections;
+  DataConnections _data_connections;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "qpDataNode",
+                  PandaNode::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 "qpdataNode.I"
+
+#endif

+ 0 - 1
panda/src/event/config_event.cxx

@@ -32,6 +32,5 @@ ConfigureFn(config_event) {
   EventStoreInt::init_type("EventStoreInt");
   EventStoreDouble::init_type("EventStoreDouble");
   EventStoreString::init_type("EventStoreString");
-//  EventStoreVec3::init_type("EventStoreVec3");
 }
 

+ 31 - 44
panda/src/event/eventParameter.I

@@ -21,6 +21,16 @@
 template<class Type>
 TypeHandle EventStoreValue<Type>::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: EventParameter::Default constructor
+//       Access: Public
+//  Description: Defines an EventParameter that stores nothing: the
+//               "empty" parameter.
+////////////////////////////////////////////////////////////////////
+INLINE EventParameter::
+EventParameter() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EventParameter::Pointer constructor
 //       Access: Public
@@ -61,18 +71,6 @@ INLINE EventParameter::
 EventParameter(const string &value) : _ptr(new EventStoreString(value)) { }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: EventParameter::LVecBase3f constructor
-//       Access: Public
-//  Description: Defines an EventParameter that stores a
-//               three-component numeric value.
-////////////////////////////////////////////////////////////////////
-#if 0
-INLINE EventParameter::
-EventParameter(const LVecBase3f &value) : _ptr(new EventStoreVec3(value)) { }
-#endif
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EventParameter::Copy constructor
 //       Access: Public
@@ -93,6 +91,17 @@ operator = (const EventParameter &other) {
   return *this;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EventParameter::is_empty
+//       Access: Public
+//  Description: Returns true if the EventParameter is the empty
+//               parameter, storing nothing, or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool EventParameter::
+is_empty() const {
+  return (_ptr == (TypedReferenceCount *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EventParameter::is_int
 //       Access: Public
@@ -101,7 +110,9 @@ operator = (const EventParameter &other) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool EventParameter::
 is_int() const {
-  nassertr(_ptr != (TypedReferenceCount *)NULL, false);
+  if (is_empty()) {
+    return false;
+  }
   return _ptr->is_of_type(EventStoreInt::get_class_type());
 }
 
@@ -129,7 +140,9 @@ get_int_value() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool EventParameter::
 is_double() const {
-  nassertr(_ptr != (TypedReferenceCount *)NULL, false);
+  if (is_empty()) {
+    return false;
+  }
   return _ptr->is_of_type(EventStoreDouble::get_class_type());
 }
 
@@ -154,7 +167,9 @@ get_double_value() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool EventParameter::
 is_string() const {
-  nassertr(_ptr != (TypedReferenceCount *)NULL, false);
+  if (is_empty()) {
+    return false;
+  }
   return _ptr->is_of_type(EventStoreString::get_class_type());
 }
 
@@ -171,34 +186,6 @@ get_string_value() const {
   return ((const EventStoreString *)_ptr.p())->get_value();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EventParameter::is_vec3
-//       Access: Public
-//  Description: Returns true if the EventParameter stores a
-//               three-component vector value, false otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE bool EventParameter::
-is_vec3() const {
-  nassertr(_ptr != (TypedReferenceCount *)NULL, false);
-  return false;
-//  return _ptr->is_of_type(EventStoreVec3::get_class_type());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: EventParameter::get_vec3_value
-//       Access: Public
-//  Description: Retrieves the value stored in the EventParameter.  It
-//               is only valid to call this if is_vec3() has already
-//               returned true.
-////////////////////////////////////////////////////////////////////
-#if 0
-INLINE LVecBase3f EventParameter::
-get_vec3_value() const {
-  nassertr(is_vec3(), LVecBase3f(0.0, 0.0, 0.0));
-  return ((const EventStoreVec3 *)_ptr.p())->get_value();
-}
-#endif
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EventParameter::get_ptr
 //       Access: Public
@@ -234,7 +221,7 @@ set_value(const Type &value) {
 //  Description: Retrieves the value stored in the parameter.
 ////////////////////////////////////////////////////////////////////
 template<class Type>
-INLINE Type EventStoreValue<Type>::
+INLINE const Type &EventStoreValue<Type>::
 get_value() const {
   return _value;
 }

+ 9 - 14
panda/src/event/eventParameter.h

@@ -19,14 +19,12 @@
 #ifndef EVENTPARAMETER_H
 #define EVENTPARAMETER_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
-#include <typedef.h>
-#include <typedObject.h>
-#include <typedReferenceCount.h>
-#include <pointerTo.h>
-//#include <luse.h>
-#include <string>
+#include "typedef.h"
+#include "typedObject.h"
+#include "typedReferenceCount.h"
+#include "pointerTo.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : EventParameter
@@ -40,11 +38,11 @@
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEXPRESS EventParameter {
 PUBLISHED:
+  INLINE EventParameter();
   INLINE EventParameter(const TypedReferenceCount *ptr);
   INLINE EventParameter(int value);
   INLINE EventParameter(double value);
   INLINE EventParameter(const string &value);
-//  INLINE EventParameter(const LVecBase3f &value);
 
   INLINE EventParameter(const EventParameter &copy);
   INLINE EventParameter &operator = (const EventParameter &copy);
@@ -54,14 +52,13 @@ PUBLISHED:
   // retrieve the corresponding value.  Of course, it is possible that
   // the EventParameter is some user-defined type, and is none of
   // these.
+  INLINE bool is_empty() const;
   INLINE bool is_int() const;
   INLINE int get_int_value() const;
   INLINE bool is_double() const;
   INLINE double get_double_value() const;
   INLINE bool is_string() const;
   INLINE string get_string_value() const;
-  INLINE bool is_vec3() const;
-//  INLINE LVecBase3f get_vec3_value() const;
 
   INLINE const TypedReferenceCount *get_ptr() const;
 
@@ -85,7 +82,7 @@ public:
   EventStoreValue(const Type &value) : _value(value) { }
 
   INLINE void set_value(const Type &value);
-  INLINE Type get_value() const;
+  INLINE const Type &get_value() const;
 
   Type _value;
 
@@ -93,7 +90,7 @@ public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
-  static void init_type(const string &type_name) {
+  static void init_type(const string &type_name = "EventStoreValue") {
     TypedReferenceCount::init_type();
     register_type(_type_handle, type_name,
                   TypedReferenceCount::get_class_type());
@@ -114,12 +111,10 @@ private:
 EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<int>);
 EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<double>);
 EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<std::string>);
-//EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<LVecBase3f>);
 
 typedef EventStoreValue<int> EventStoreInt;
 typedef EventStoreValue<double> EventStoreDouble;
 typedef EventStoreValue<string> EventStoreString;
-//typedef EventStoreValue<LVecBase3f> EventStoreVec3;
 
 #include "eventParameter.I"
 

+ 21 - 15
panda/src/mathutil/Sources.pp

@@ -4,28 +4,32 @@
 #begin lib_target
   #define TARGET mathutil
   #define LOCAL_LIBS \
-    linmath putil express
+    linmath putil event express
   #define USE_FFTW yes
   #define UNIX_SYS_LIBS m
 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
   #define SOURCES  \
-     boundingHexahedron.I boundingHexahedron.h boundingLine.I  \
-     boundingLine.h boundingSphere.I boundingSphere.h  \
-     boundingVolume.I boundingVolume.h config_mathutil.h  \
-     fftCompressor.h finiteBoundingVolume.h frustum.h  \
-     frustum_src.I frustum_src.h geometricBoundingVolume.I  \
-     geometricBoundingVolume.h look_at.h look_at_src.I  \
-     look_at_src.cxx look_at_src.h omniBoundingVolume.I  \
-     omniBoundingVolume.h plane.h plane_src.I plane_src.cxx  \
-     plane_src.h rotate_to.h rotate_to_src.cxx
+    boundingHexahedron.I boundingHexahedron.h boundingLine.I  \
+    boundingLine.h boundingSphere.I boundingSphere.h  \
+    boundingVolume.I boundingVolume.h config_mathutil.h  \
+    fftCompressor.h finiteBoundingVolume.h frustum.h  \
+    frustum_src.I frustum_src.h geometricBoundingVolume.I  \
+    geometricBoundingVolume.h \
+    linmath_events.h \
+    look_at.h look_at_src.I  \
+    look_at_src.cxx look_at_src.h omniBoundingVolume.I  \
+    omniBoundingVolume.h plane.h plane_src.I plane_src.cxx  \
+    plane_src.h rotate_to.h rotate_to_src.cxx
      
   #define INCLUDED_SOURCES \
-     boundingHexahedron.cxx boundingLine.cxx boundingSphere.cxx  \
-     boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx  \
-     finiteBoundingVolume.cxx geometricBoundingVolume.cxx  \
-     look_at.cxx omniBoundingVolume.cxx plane.cxx rotate_to.cxx
+    boundingHexahedron.cxx boundingLine.cxx boundingSphere.cxx  \
+    boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx  \
+    finiteBoundingVolume.cxx geometricBoundingVolume.cxx  \
+    look_at.cxx \
+    linmath_events.cxx \
+    omniBoundingVolume.cxx plane.cxx rotate_to.cxx
 
   #define INSTALL_HEADERS \
     boundingHexahedron.I boundingHexahedron.h boundingLine.I \
@@ -33,7 +37,9 @@
     boundingVolume.h config_mathutil.h fftCompressor.h \
     finiteBoundingVolume.h frustum.h frustum_src.I frustum_src.h \
     geometricBoundingVolume.I geometricBoundingVolume.h look_at.h \
-    look_at_src.I look_at_src.h mathHelpers.I mathHelpers.h \
+    look_at_src.I look_at_src.h \
+    linmath_events.h \
+    mathHelpers.I mathHelpers.h \
     omniBoundingVolume.I omniBoundingVolume.h plane.h plane_src.I \
     plane_src.h rotate_to.h
 

+ 5 - 1
panda/src/mathutil/config_mathutil.cxx

@@ -24,7 +24,8 @@
 #include "boundingSphere.h"
 #include "boundingHexahedron.h"
 #include "boundingLine.h"
-#include <dconfig.h>
+#include "linmath_events.h"
+#include "dconfig.h"
 
 Configure(config_mathutil);
 NotifyCategoryDef(mathutil, "");
@@ -41,5 +42,8 @@ ConfigureFn(config_mathutil) {
   GeometricBoundingVolume::init_type();
   OmniBoundingVolume::init_type();
   BoundingLine::init_type();
+  EventStoreVec2::init_type("EventStoreVec2");
+  EventStoreVec3::init_type("EventStoreVec3");
+  EventStoreMat4::init_type("EventStoreMat4");
 }
 

+ 24 - 0
panda/src/mathutil/linmath_events.cxx

@@ -0,0 +1,24 @@
+// Filename: linmath_events.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "linmath_events.h"
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma implementation
+#endif

+ 53 - 0
panda/src/mathutil/linmath_events.h

@@ -0,0 +1,53 @@
+// Filename: linmath_events.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef LINMATH_EVENTS_H
+#define LINMATH_EVENTS_H
+
+#include "pandabase.h"
+
+#include "eventParameter.h"
+#include "luse.h"
+
+////////////////////////////////////////////////////////////////////
+//
+// This file defines a few more EventStore classes for storing linmath
+// objects in an EventParameter.  We can't define this with the other
+// EventStore classes, because linmath hasn't been defined yet at that
+// point.
+//
+// See eventParameter.h.
+//
+////////////////////////////////////////////////////////////////////
+
+
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<LVecBase2f>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<LVecBase3f>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue<LMatrix4f>);
+
+typedef EventStoreValue<LVecBase2f> EventStoreVec2;
+typedef EventStoreValue<LVecBase3f> EventStoreVec3;
+typedef EventStoreValue<LMatrix4f> EventStoreMat4;
+
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+#endif

+ 1 - 0
panda/src/mathutil/mathutil_composite2.cxx

@@ -3,5 +3,6 @@
 #include "plane.cxx"
 #include "rotate_to.cxx"
 #include "look_at.cxx"
+#include "linmath_events.cxx"
 #include "fftCompressor.cxx"
 

+ 75 - 24
panda/src/pgraph/pandaNode.cxx

@@ -510,6 +510,10 @@ add_child(PandaNode *child_node, int sort) {
 
   // Mark the bounding volumes stale.
   force_bound_stale();
+
+  // Call callback hooks.
+  children_changed();
+  child_node->parents_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -519,11 +523,12 @@ add_child(PandaNode *child_node, int sort) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 remove_child(int n) {
+  PT(PandaNode) child_node;
   {
     CDWriter cdata(_cycler);
     nassertv(n >= 0 && n < (int)cdata->_down.size());
     
-    PT(PandaNode) child_node = cdata->_down[n].get_child();
+    child_node = cdata->_down[n].get_child();
     CDWriter cdata_child(child_node->_cycler);
     
     cdata->_down.erase(cdata->_down.begin() + n);
@@ -562,6 +567,10 @@ remove_child(int n) {
 
   // Mark the bounding volumes stale.
   force_bound_stale();
+
+  // Call callback hooks.
+  children_changed();
+  child_node->parents_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -574,9 +583,9 @@ remove_child(int n) {
 bool PandaNode::
 remove_child(PandaNode *child_node) {
   // Ensure the child_node is not deleted while we do this.
-  {
-    PT(PandaNode) keep_child = child_node;
+  PT(PandaNode) keep_child = child_node;
     
+  {
     CDWriter cdata(_cycler);
     CDWriter cdata_child(child_node->_cycler);
     
@@ -626,6 +635,10 @@ remove_child(PandaNode *child_node) {
 
   // Mark the bounding volumes stale.
   force_bound_stale();
+
+  // Call callback hooks.
+  children_changed();
+  child_node->parents_changed();
   return true;
 }
 
@@ -641,36 +654,40 @@ remove_all_children() {
     Down::iterator ci;
     for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
       PT(PandaNode) child_node = (*ci).get_child();
-      CDWriter cdata_child(child_node->_cycler);
-      cdata_child->_up.erase(UpConnection(this));
+      {
+        CDWriter cdata_child(child_node->_cycler);
+        cdata_child->_up.erase(UpConnection(this));
       
-      // Now sever any qpNodePathComponents on the child that reference
-      // this node.  If we have multiple of these, we have to collapse
-      // them together (see above).
-      qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
-      Chains::iterator ci;
-      ci = cdata_child->_chains.begin();
-      while (ci != cdata_child->_chains.end()) {
-        Chains::iterator cnext = ci;
-        ++cnext;
-        if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
-          if (collapsed == (qpNodePathComponent *)NULL) {
-            (*ci)->set_top_node();
-            collapsed = (*ci);
-          } else {
-            (*ci)->collapse_with(collapsed);
-            cdata_child->_chains.erase(ci);
+        // Now sever any qpNodePathComponents on the child that
+        // reference this node.  If we have multiple of these, we have
+        // to collapse them together (see above).
+        qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
+        Chains::iterator ci;
+        ci = cdata_child->_chains.begin();
+        while (ci != cdata_child->_chains.end()) {
+          Chains::iterator cnext = ci;
+          ++cnext;
+          if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
+            if (collapsed == (qpNodePathComponent *)NULL) {
+              (*ci)->set_top_node();
+              collapsed = (*ci);
+            } else {
+              (*ci)->collapse_with(collapsed);
+              cdata_child->_chains.erase(ci);
+            }
           }
+          ci = cnext;
         }
-        ci = cnext;
+        
+        child_node->fix_chain_lengths(cdata_child);
       }
-      
-      child_node->fix_chain_lengths(cdata_child);
+      child_node->parents_changed();
     }
   }
 
   // Mark the bounding volumes stale.
   force_bound_stale();
+  children_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -813,6 +830,32 @@ recompute_internal_bound() {
   return _internal_bound.recompute_bound();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::parents_changed
+//       Access: Protected, Virtual
+//  Description: Called after a scene graph update that either adds or
+//               remove parents from this node, this just provides a
+//               hook for derived PandaNode objects that need to
+//               update themselves based on the set of parents the
+//               node has.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+parents_changed() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::children_changed
+//       Access: Protected, Virtual
+//  Description: Called after a scene graph update that either adds or
+//               remove children from this node, this just provides a
+//               hook for derived PandaNode objects that need to
+//               update themselves based on the set of children the
+//               node has.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+children_changed() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::attach
 //       Access: Private, Static
@@ -900,6 +943,10 @@ detach(qpNodePathComponent *child) {
 
   // Mark the bounding volumes stale.
   parent_node->force_bound_stale();
+
+  // Call callback hooks.
+  parent_node->children_changed();
+  child_node->parents_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -936,6 +983,10 @@ reparent(qpNodePathComponent *new_parent, qpNodePathComponent *child, int sort)
 
   // Mark the bounding volumes stale.
   parent_node->force_bound_stale();
+
+  // Call callback hooks.
+  parent_node->children_changed();
+  child_node->parents_changed();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
panda/src/pgraph/pandaNode.h

@@ -144,6 +144,8 @@ protected:
   // Local to PandaNode
   virtual BoundingVolume *recompute_internal_bound();
   INLINE void changed_internal_bound();
+  virtual void parents_changed();
+  virtual void children_changed();
 
   // This is the bounding volume around the contents of the node
   // itself (without including all of the node's children).

+ 9 - 3
panda/src/putil/Sources.pp

@@ -10,7 +10,9 @@
   #define SOURCES \
     bam.h bamReader.I bamReader.N bamReader.h bamReaderParam.I \
     bamReaderParam.h bamWriter.I bamWriter.h bitMask.I \
-    bitMask.h buttonEvent.I buttonEvent.h buttonHandle.I \
+    bitMask.h buttonEvent.I buttonEvent.h \
+    buttonEventList.I buttonEventList.h \
+    buttonHandle.I \
     buttonHandle.h buttonRegistry.I buttonRegistry.h \
     collideMask.h \
     compareTo.I compareTo.h \
@@ -50,7 +52,9 @@
     
  #define INCLUDED_SOURCES \
     bamReader.cxx bamReaderParam.cxx bamWriter.cxx bitMask.cxx \
-    buttonEvent.cxx buttonHandle.cxx buttonRegistry.cxx \
+    buttonEvent.cxx \
+    buttonEventList.cxx \
+    buttonHandle.cxx buttonRegistry.cxx \
     config_util.cxx configurable.cxx \
     cycleData.cxx \
     cycleDataReader.cxx \
@@ -78,7 +82,9 @@
   #define INSTALL_HEADERS \
     bam.h bamReader.I bamReader.h bamReaderParam.I bamReaderParam.h \
     bamWriter.I bamWriter.h bitMask.I bitMask.h buttonEvent.I \
-    buttonEvent.h buttonHandle.I buttonHandle.h buttonRegistry.I \
+    buttonEvent.h \
+    buttonEventList.I buttonEventList.h \
+    buttonHandle.I buttonHandle.h buttonRegistry.I \
     buttonRegistry.h collideMask.h \
     compareTo.I compareTo.h \
     config_util.h configurable.h factory.I factory.h \

+ 74 - 0
panda/src/putil/buttonEventList.I

@@ -0,0 +1,74 @@
+// Filename: buttonEventList.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ButtonEventList::
+ButtonEventList() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::add_event
+//       Access: Public
+//  Description: Adds a new event to the end of the list.
+////////////////////////////////////////////////////////////////////
+INLINE void ButtonEventList::
+add_event(ButtonEvent event) {
+  _events.push_back(event);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::get_num_events
+//       Access: Public
+//  Description: Returns the number of events in the list.
+////////////////////////////////////////////////////////////////////
+INLINE int ButtonEventList::
+get_num_events() const {
+  return _events.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::get_event
+//       Access: Public
+//  Description: Returns the nth event in the list.  This does not
+//               remove the event from the list; the only way to
+//               remove events is to empty the whole list with
+//               clear().
+////////////////////////////////////////////////////////////////////
+INLINE const ButtonEvent &ButtonEventList::
+get_event(int n) const {
+#ifndef NDEBUG
+  static ButtonEvent empty_event;
+  nassertr(n >= 0 && n < (int)_events.size(), empty_event);
+#endif  // NDEBUG
+  return _events[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::clear
+//       Access: Public
+//  Description: Empties all the events from the list.
+////////////////////////////////////////////////////////////////////
+INLINE void ButtonEventList::
+clear() {
+  _events.clear();
+}

+ 62 - 0
panda/src/putil/buttonEventList.cxx

@@ -0,0 +1,62 @@
+// Filename: buttonEventList.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "buttonEventList.h"
+#include "modifierButtons.h"
+#include "indent.h"
+
+TypeHandle ButtonEventList::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::update_mods
+//       Access: Public
+//  Description: Updates the indicated ModifierButtons object with all
+//               of the button up/down transitions indicated in the
+//               list.
+////////////////////////////////////////////////////////////////////
+void ButtonEventList::
+update_mods(ModifierButtons &mods) const {
+  Events::const_iterator ei;
+  for (ei = _events.begin(); ei != _events.end(); ++ei) {
+    mods.add_event(*ei);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ButtonEventList::
+output(ostream &out) const {
+  out << get_type() << " (" << get_num_events() << " events)";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventList::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ButtonEventList::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << ":\n";
+  Events::const_iterator ei;
+  for (ei = _events.begin(); ei != _events.end(); ++ei) {
+    indent(out, indent_level + 2) << (*ei) << "\n";
+  }
+}

+ 82 - 0
panda/src/putil/buttonEventList.h

@@ -0,0 +1,82 @@
+// Filename: buttonEventList.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUTTONEVENTLIST_H
+#define BUTTONEVENTLIST_H
+
+#include "pandabase.h"
+
+#include "buttonEvent.h"
+#include "typedReferenceCount.h"
+#include "pvector.h"
+
+class ModifierButtons;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ButtonEventList
+// Description : Records a set of button events that happened
+//               recently.  This class is usually used only in the
+//               data graph, to transmit the recent button presses,
+//               but it may be used anywhere a list of ButtonEvents
+//               is desired.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ButtonEventList : public TypedReferenceCount {
+public:
+  INLINE ButtonEventList();
+
+  INLINE void add_event(ButtonEvent event);
+  INLINE int get_num_events() const;
+  INLINE const ButtonEvent &get_event(int n) const;
+  INLINE void clear();
+
+  void update_mods(ModifierButtons &mods) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
+private:
+  typedef pvector<ButtonEvent> Events;
+  Events _events;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "ButtonEventList",
+                  TypedReferenceCount::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;
+};
+
+INLINE ostream &operator << (ostream &out, const ButtonEventList &list) {
+  list.output(out);
+  return out;
+}
+
+#include "buttonEventList.I"
+
+#endif
+

+ 2 - 0
panda/src/putil/config_util.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "config_util.h"
+#include "buttonEventList.h"
 #include "clockObject.h"
 #include "typedObject.h"
 #include "configurable.h"
@@ -43,6 +44,7 @@ NotifyCategoryDef(bam, util_cat);
 
 ConfigureFn(config_util) {
 //  ClockObject::init_ptr();
+  ButtonEventList::init_type();
   TypedObject::init_type();
   Configurable::init_type();
   Namable::init_type();

+ 1 - 0
panda/src/putil/putil_composite1.cxx

@@ -3,6 +3,7 @@
 #include "bamWriter.cxx"
 #include "bitMask.cxx"
 #include "buttonEvent.cxx"
+#include "buttonEventList.cxx"
 #include "buttonHandle.cxx"
 #include "buttonRegistry.cxx"
 #include "config_util.cxx"

+ 21 - 26
panda/src/testbed/pview.cxx

@@ -39,21 +39,16 @@
 #include "notify.h"
 #include "qpnodePath.h"
 #include "cullBinManager.h"
-
-// These are in support of legacy data graph operations.
-#include "namedNode.h"
-#include "mouse.h"
-#include "mouseWatcher.h"
-#include "trackball.h"
-#include "transform2sg.h"
-#include "dataRelation.h"
-#include "dataGraphTraversal.h"
-#include "buttonThrower.h"
+#include "mouseAndKeyboard.h"
+#include "qpbuttonThrower.h"
+#include "qptrackball.h"
+#include "qptransform2sg.h"
 #include "modifierButtons.h"
 #include "keyboardButton.h"
 #include "event.h"
 #include "eventQueue.h"
 #include "eventHandler.h"
+#include "qpdataGraphTraverser.h"
 
 // Use dconfig to read a few Configrc variables.
 Configure(config_pview);
@@ -233,32 +228,32 @@ get_models(PandaNode *parent, int argc, char *argv[]) {
   }
 }
 
-NamedNode * 
-setup_mouse(NamedNode *data_root, GraphicsWindow *window) {
-  MouseAndKeyboard *mouse = new MouseAndKeyboard(window, 0);
-  new DataRelation(data_root, mouse);
+PandaNode * 
+setup_mouse(PandaNode *data_root, GraphicsWindow *window) {
+  qpMouseAndKeyboard *mouse = new qpMouseAndKeyboard(window, 0, "mouse");
+  data_root->add_child(mouse);
 
   // Create a ButtonThrower to throw events from the keyboard.
-  PT(ButtonThrower) bt = new ButtonThrower("kb-events");
+  PT(qpButtonThrower) bt = new qpButtonThrower("kb-events");
   ModifierButtons mods;
   mods.add_button(KeyboardButton::shift());
   mods.add_button(KeyboardButton::control());
   mods.add_button(KeyboardButton::alt());
   bt->set_modifier_buttons(mods);
-  new DataRelation(mouse, bt);
+  mouse->add_child(bt);
 
   return mouse;
 }
 
 void 
-setup_trackball(NamedNode *mouse, qpCamera *camera) {
-  PT(Trackball) trackball = new Trackball("trackball");
+setup_trackball(PandaNode *mouse, qpCamera *camera) {
+  PT(qpTrackball) trackball = new qpTrackball("trackball");
   trackball->set_pos(LVector3f::forward() * 50.0);
-  new DataRelation(mouse, trackball);
+  mouse->add_child(trackball);
 
-  PT(Transform2SG) tball2cam = new Transform2SG("tball2cam");
+  PT(qpTransform2SG) tball2cam = new qpTransform2SG("tball2cam");
   tball2cam->set_node(camera);
-  new DataRelation(trackball, tball2cam);
+  trackball->add_child(tball2cam);
 }
 
 void
@@ -365,11 +360,11 @@ main(int argc, char *argv[]) {
   // sorting, above, in event_s().
   CullBinManager::get_global_ptr()->add_bin("unsorted", CullBinManager::BT_unsorted, 0);
 
-  // Set up a data graph for tracking user input.  For now, this uses
-  // the old-style graph interface.
-  PT(NamedNode) data_root = new NamedNode("data_root");
-  NamedNode *mouse = setup_mouse(data_root, window);
+  // Set up a data graph for tracking user input.
+  PT(PandaNode) data_root = new PandaNode("data_root");
+  PandaNode *mouse = setup_mouse(data_root, window);
   setup_trackball(mouse, camera);
+  qpDataGraphTraverser dg_trav;
 
   // Use an event handler to manage keyboard events.
   EventHandler event_handler(EventQueue::get_global_event_queue());
@@ -401,7 +396,7 @@ main(int argc, char *argv[]) {
   // This is our main update loop.  Loop here until someone
   // (e.g. event_esc) sets run_flag to false.
   while (run_flag) {
-    traverse_data_graph(data_root);
+    dg_trav.traverse(data_root);
     event_handler.process_events();
     engine->render_frame();
   } 

+ 21 - 6
panda/src/tform/Sources.pp

@@ -10,26 +10,41 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
   #define SOURCES  \
-    buttonThrower.h config_tform.h dataValve.I dataValve.h  \
+    buttonThrower.h \
+    qpbuttonThrower.h \
+    config_tform.h dataValve.I dataValve.h  \
     driveInterface.I driveInterface.h mouseWatcher.I  \
     mouseWatcher.h mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h  \
     mouseWatcherRegion.I mouseWatcherRegion.h  \
-    planarSlider.h trackball.h transform2sg.h  
+    planarSlider.h trackball.h \
+    qptrackball.h \
+    transform2sg.h \
+    qptransform2sg.h  
      
   #define INCLUDED_SOURCES  \
-    buttonThrower.cxx config_tform.cxx dataValve.cxx  \
+    buttonThrower.cxx \
+    qpbuttonThrower.cxx \
+    config_tform.cxx dataValve.cxx  \
     driveInterface.cxx mouseWatcher.cxx mouseWatcherGroup.cxx \
     mouseWatcherParameter.cxx mouseWatcherRegion.cxx  \
-    planarSlider.cxx trackball.cxx transform2sg.cxx 
+    planarSlider.cxx trackball.cxx \
+    qptrackball.cxx \
+    transform2sg.cxx \
+    qptransform2sg.cxx 
 
   #define INSTALL_HEADERS \
-    buttonThrower.h dataValve.I dataValve.h \
+    buttonThrower.h \
+    qpbuttonThrower.h \
+    dataValve.I dataValve.h \
     driveInterface.I driveInterface.h mouseWatcher.I mouseWatcher.h \
     mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \
     mouseWatcherRegion.I mouseWatcherRegion.h \
-    planarSlider.h trackball.h transform2sg.h
+    planarSlider.h trackball.h \
+    qptrackball.h \
+    transform2sg.h \
+    qptransform2sg.h
 
   #define IGATESCAN all
 

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

@@ -21,12 +21,15 @@
 #include "dataValve.h"
 #include "driveInterface.h"
 #include "buttonThrower.h"
+#include "qpbuttonThrower.h"
 #include "mouseWatcher.h"
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherRegion.h"
 #include "planarSlider.h"
 #include "trackball.h"
+#include "qptrackball.h"
 #include "transform2sg.h"
+#include "qptransform2sg.h"
 
 #include <dconfig.h>
 
@@ -49,10 +52,13 @@ ConfigureFn(config_tform) {
   DataValve::init_type();
   DriveInterface::init_type();
   ButtonThrower::init_type();
+  qpButtonThrower::init_type();
   MouseWatcher::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherRegion::init_type();
   PlanarSlider::init_type();
   Trackball::init_type();
+  qpTrackball::init_type();
   Transform2SG::init_type();
+  qpTransform2SG::init_type();
 }

+ 399 - 0
panda/src/tform/qpbuttonThrower.cxx

@@ -0,0 +1,399 @@
+// Filename: qpbuttonThrower.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpbuttonThrower.h"
+
+#include "buttonEvent.h"
+#include "buttonEventList.h"
+#include "dataNodeTransmit.h"
+#include "throw_event.h"
+#include "indent.h"
+
+TypeHandle qpButtonThrower::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpButtonThrower::
+qpButtonThrower(const string &name) :
+  qpDataNode(name)
+{
+  _button_events_input = define_input("ButtonEvents", ButtonEventList::get_class_type());
+  _button_events_output = define_output("ButtonEvents", ButtonEventList::get_class_type());
+
+  _button_events = new ButtonEventList;
+
+  _throw_buttons_active = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpButtonThrower::
+~qpButtonThrower() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::set_prefix
+//       Access: Published
+//  Description: Sets the prefix which is prepended to all event names
+//               thrown by this object.
+////////////////////////////////////////////////////////////////////
+void qpButtonThrower::
+set_prefix(const string &prefix) {
+  _prefix = prefix;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::has_prefix
+//       Access: Published
+//  Description: Returns true if the qpButtonThrower has a prefix set,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpButtonThrower::
+has_prefix() const {
+  return !_prefix.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::get_prefix
+//       Access: Published
+//  Description: Returns the prefix that has been set on this
+//               qpButtonThrower.  See set_prefix().
+////////////////////////////////////////////////////////////////////
+string qpButtonThrower::
+get_prefix() const {
+  return _prefix;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::get_modifier_buttons
+//       Access: Published
+//  Description: Returns the set of ModifierButtons that the
+//               qpButtonThrower will consider important enough to
+//               prepend the event name with.  Normally, this set will
+//               be empty, and the qpButtonThrower will therefore ignore
+//               all ModifierButtons attached to the key events, but
+//               if one or more buttons have been added to this set,
+//               and those modifier buttons are set on the button
+//               event, then the event name will be prepended with the
+//               names of the modifier buttons.
+////////////////////////////////////////////////////////////////////
+const ModifierButtons &qpButtonThrower::
+get_modifier_buttons() const {
+  return _mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::set_modifier_buttons
+//       Access: Published
+//  Description: Changes the set of ModifierButtons that the
+//               qpButtonThrower will consider important enough to
+//               prepend the event name with.  Normally, this set will
+//               be empty, and the qpButtonThrower will therefore ignore
+//               all ModifierButtons attached to the key events, but
+//               if one or more buttons have been added to this set,
+//               then the event name will be prepended with the names
+//               of the modifier buttons.
+//
+//               It is recommended that you change this setting by
+//               first calling get_modifier_buttons(), making
+//               adjustments, and passing the new value to
+//               set_modifier_buttons().  This way the current state
+//               of the modifier buttons will not be lost.
+////////////////////////////////////////////////////////////////////
+void qpButtonThrower::
+set_modifier_buttons(const ModifierButtons &mods) {
+  _mods = mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::set_throw_buttons_active
+//       Access: Published
+//  Description: Sets the flag that indicates whether the
+//               qpButtonThrower will only process events for the
+//               explicitly named buttons or not.  Normally this is
+//               false, meaning all buttons are processed; set it true
+//               to indicate that only some buttons should be
+//               processed.  See add_throw_button().
+////////////////////////////////////////////////////////////////////
+void qpButtonThrower::
+set_throw_buttons_active(bool flag) {
+  _throw_buttons_active = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::get_throw_buttons_active
+//       Access: Published
+//  Description: Returns the flag that indicates whether the
+//               qpButtonThrower will only process events for the
+//               explicitly named buttons or not.  See
+//               set_throw_buttons_active().
+////////////////////////////////////////////////////////////////////
+bool qpButtonThrower::
+get_throw_buttons_active() const {
+  return _throw_buttons_active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::add_throw_button
+//       Access: Published
+//  Description: Adds a new button to the set of buttons that the
+//               qpButtonThrower explicitly processes.
+//
+//               If set_throw_buttons_active is false (which is the
+//               default), the qpButtonThrower will process all buttons.
+//               Otherwise, the qpButtonThrower will only process events
+//               for the button(s) explicitly named by this function;
+//               buttons not on the list will be ignored by this
+//               object and passed on downstream to the child node(s)
+//               in the data graph.  A button that *is* on the list
+//               will be processed by the qpButtonThrower and not passed
+//               on to the child node(s).
+//
+//               The return value is true if the button is added, or
+//               false if it was already in the set.
+////////////////////////////////////////////////////////////////////
+bool qpButtonThrower::
+add_throw_button(const ModifierButtons &mods, const ButtonHandle &button) {
+  ThrowButtonDef &def = _throw_buttons[button];
+
+  // This is a vector of ModifierButtons for which the indicated
+  // button is handled.  Make sure the current ModifierButtons object
+  // is not already on the list.
+  ThrowButtonDef::iterator di;
+  for (di = def.begin(); di != def.end(); ++di) {
+    if (mods.matches(*di)) {
+      return false;
+    }
+  }
+
+  def.push_back(mods);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::remove_throw_button
+//       Access: Published
+//  Description: Removes the indicated button from the set of buttons
+//               that the qpButtonThrower explicitly processes.  See
+//               add_throw_button().
+//
+//               The return value is true if the button is removed, or
+//               false if it was not on the set.
+////////////////////////////////////////////////////////////////////
+bool qpButtonThrower::
+remove_throw_button(const ModifierButtons &mods, const ButtonHandle &button) {
+  ThrowButtons::iterator ti = _throw_buttons.find(button);
+  if (ti == _throw_buttons.end()) {
+    // No buttons of this kind are in the set.
+    return false;
+  }
+
+  ThrowButtonDef &def = (*ti).second;
+
+  // This is a vector of ModifierButtons for which the indicated
+  // button is handled.
+  ThrowButtonDef::iterator di;
+  for (di = def.begin(); di != def.end(); ++di) {
+    if (mods.matches(*di)) {
+      def.erase(di);
+      if (def.empty()) {
+        _throw_buttons.erase(ti);
+      }
+      return true;
+    }
+  }
+
+  // The indicated ModifierButtons are not applied to this button in
+  // the set.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::has_throw_button
+//       Access: Published
+//  Description: Returns true if the indicated button is on the set of
+//               buttons that will be processed by the qpButtonThrower,
+//               false otherwise.  See add_throw_button().
+////////////////////////////////////////////////////////////////////
+bool qpButtonThrower::
+has_throw_button(const ModifierButtons &mods, const ButtonHandle &button) const {
+  ThrowButtons::const_iterator ti = _throw_buttons.find(button);
+  if (ti == _throw_buttons.end()) {
+    // No buttons of this kind are in the set.
+    return false;
+  }
+
+  const ThrowButtonDef &def = (*ti).second;
+
+  // This is a vector of ModifierButtons for which the indicated
+  // button is handled.
+  ThrowButtonDef::const_iterator di;
+  for (di = def.begin(); di != def.end(); ++di) {
+    if (mods.matches(*di)) {
+      return true;
+    }
+  }
+
+  // The indicated ModifierButtons are not applied to this button in
+  // the set.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::has_throw_button
+//       Access: Published
+//  Description: Returns true if the indicated button, in conjunction
+//               with any nonspecified modifier buttons, is on the set
+//               of buttons that will be processed by the
+//               qpButtonThrower.  That is to say, returns true if this
+//               button was ever passed as the second parameter
+//               add_throw_button(), regardless of what the first
+//               parameter was.
+////////////////////////////////////////////////////////////////////
+bool qpButtonThrower::
+has_throw_button(const ButtonHandle &button) const {
+  ThrowButtons::const_iterator ti = _throw_buttons.find(button);
+  if (ti == _throw_buttons.end()) {
+    // No buttons of this kind are in the set.
+    return false;
+  }
+
+  const ThrowButtonDef &def = (*ti).second;
+  return !def.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::clear_throw_buttons
+//       Access: Published
+//  Description: Empties the set of buttons that were added via
+//               add_throw_button().  See add_throw_button().
+////////////////////////////////////////////////////////////////////
+void qpButtonThrower::
+clear_throw_buttons() {
+  _throw_buttons.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::write
+//       Access: Public, Virtual
+//  Description: Throw all events for button events found in the data
+//               element.
+////////////////////////////////////////////////////////////////////
+void qpButtonThrower::
+write(ostream &out, int indent_level) const {
+  qpDataNode::write(out, indent_level);
+  if (_throw_buttons_active) {
+    indent(out, indent_level)
+      << "Processing keys:\n";
+    // Write the list of buttons that we're processing too.
+    ThrowButtons::const_iterator ti;
+    for (ti = _throw_buttons.begin(); ti != _throw_buttons.end(); ++ti) {
+      ButtonHandle button = (*ti).first;
+      const ThrowButtonDef &def = (*ti).second;
+      ThrowButtonDef::const_iterator di;
+      for (di = def.begin(); di != def.end(); ++di) {
+        indent(out, indent_level + 2)
+          << (*di).get_prefix() << button.get_name() << "\n";
+      }
+    }
+  }    
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonThrower::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpButtonThrower::
+do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+  // Clear our outgoing button events.  We'll fill it up again with
+  // just those events that want to carry on.
+  _button_events->clear();
+
+  if (input.has_data(_button_events_input)) {
+    const ButtonEventList *button_events;
+    DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
+
+    int num_events = button_events->get_num_events();
+    for (int i = 0; i < num_events; i++) {
+      const ButtonEvent &be = button_events->get_event(i);
+      string event_name = be._button.get_name();
+
+      if (be._type == ButtonEvent::T_down) {
+        // Button down.
+        if (!_mods.button_down(be._button)) {
+          // We only prepend modifier names on the button-down events,
+          // and only for buttons which are not themselves modifiers.
+          event_name = _mods.get_prefix() + event_name;
+        }
+
+        if (!_throw_buttons_active || has_throw_button(_mods, be._button)) {
+          // Process this button.
+          throw_event(_prefix + event_name);
+          
+        } else {
+          // Don't process this button; instead, pass it down to future
+          // generations.
+          _button_events->add_event(be);
+        }
+          
+      } else if (be._type == ButtonEvent::T_up) {
+        // Button up.
+        _mods.button_up(be._button);
+
+        // We always throw button "up" events if we have any
+        // definition for the button at all, regardless of the state
+        // of the modifier keys.
+        if (!_throw_buttons_active || has_throw_button(be._button)) {
+          throw_event(_prefix + event_name + "-up");
+        }
+        if (_throw_buttons_active) {
+          // Now pass the event on to future generations.  We always
+          // pass "up" events, even if we are intercepting this
+          // particular button; unless we're processing all buttons in
+          // which case it doesn't matter.
+          _button_events->add_event(be);
+        }
+
+      } else {
+        // Some other kind of button event (e.g. keypress).  Don't
+        // throw an event for this, but do pass it down.
+        _button_events->add_event(be);
+      }
+    }
+  }
+
+  output.set_data(_button_events_output, EventParameter(_button_events));
+}

+ 106 - 0
panda/src/tform/qpbuttonThrower.h

@@ -0,0 +1,106 @@
+// Filename: qpbuttonThrower.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpBUTTONTHROWER_H
+#define qpBUTTONTHROWER_H
+
+#include "pandabase.h"
+
+#include "qpdataNode.h"
+#include "modifierButtons.h"
+#include "buttonEventList.h"
+#include "pvector.h"
+#include "pmap.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpButtonThrower
+// Description : Throws Panda Events for button down/up events
+//               generated within the data graph.
+//
+//               This is a DataNode which is intended to be parented
+//               to the data graph below a device which is generating
+//               a sequence of button events, like a MouseAndKeyboard
+//               device.  It simply takes each button it finds and
+//               throws a corresponding event based on the button name
+//               via the throw_event() call.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpButtonThrower : public qpDataNode {
+PUBLISHED:
+  qpButtonThrower(const string &name);
+  ~qpButtonThrower();
+
+  void set_prefix(const string &prefix);
+  bool has_prefix() const;
+  string get_prefix() const;
+
+  const ModifierButtons &get_modifier_buttons() const;
+  void set_modifier_buttons(const ModifierButtons &mods);
+
+  void set_throw_buttons_active(bool flag);
+  bool get_throw_buttons_active() const;
+
+  bool add_throw_button(const ModifierButtons &mods, const ButtonHandle &button);
+  bool remove_throw_button(const ModifierButtons &mods, const ButtonHandle &button);
+  bool has_throw_button(const ModifierButtons &mods, const ButtonHandle &button) const;
+  bool has_throw_button(const ButtonHandle &button) const;
+  void clear_throw_buttons();
+
+public:
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+private:
+  string _prefix;
+  ModifierButtons _mods;
+
+  typedef pvector<ModifierButtons> ThrowButtonDef;
+  typedef pmap<ButtonHandle, ThrowButtonDef> ThrowButtons;
+  ThrowButtons _throw_buttons;
+  bool _throw_buttons_active;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // inputs
+  int _button_events_input;
+
+  // outputs
+  int _button_events_output;
+  PT(ButtonEventList) _button_events;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpButtonThrower",
+                  qpDataNode::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;
+};
+
+#endif

+ 530 - 0
panda/src/tform/qptrackball.cxx

@@ -0,0 +1,530 @@
+// Filename: qptrackball.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qptrackball.h"
+#include "buttonEvent.h"
+#include "buttonEventList.h"
+#include "dataNodeTransmit.h"
+#include "compose_matrix.h"
+#include "mouseData.h"
+#include "modifierButtons.h"
+
+TypeHandle qpTrackball::_type_handle;
+
+// These are used internally.
+#define B1_MASK 0x1
+#define B2_MASK 0x2
+#define B3_MASK 0x4
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpTrackball::
+qpTrackball(const string &name) :
+  qpDataNode(name)
+{
+  _pixel_xy_input = define_input("PixelXY", EventStoreVec2::get_class_type());
+  _button_events_input = define_input("ButtonEvents", ButtonEventList::get_class_type());
+
+  _transform_output = define_output("Transform", EventStoreMat4::get_class_type());
+
+  _transform = new EventStoreMat4(LMatrix4f::ident_mat());
+
+  _rotscale = 0.3f;
+  _fwdscale = 0.3f;
+
+  _lastx = _lasty = 0.5f;
+
+  _rotation = LMatrix4f::ident_mat();
+  _translation.set(0.0f, 0.0f, 0.0f);
+  _mat = LMatrix4f::ident_mat();
+  _orig = LMatrix4f::ident_mat();
+  _invert = true;
+  _cs = default_coordinate_system;
+
+  _mods.add_button(MouseButton::one());
+  _mods.add_button(MouseButton::two());
+  _mods.add_button(MouseButton::three());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::Destructor
+//       Access: Public, Scheme
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpTrackball::
+~qpTrackball() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::reset
+//       Access: Public, Scheme
+//  Description: Reinitializes all transforms to identity.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+reset() {
+  _rotation = LMatrix4f::ident_mat();
+  _translation.set(0.0f, 0.0f, 0.0f);
+  _orig = LMatrix4f::ident_mat();
+  _mat = LMatrix4f::ident_mat();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_pos
+//       Access: Public, Scheme
+//  Description: Return the offset from the center of rotation.
+////////////////////////////////////////////////////////////////////
+const LPoint3f &qpTrackball::
+get_pos() const {
+  return _translation;
+}
+
+float qpTrackball::
+get_x() const {
+  return _translation[0];
+}
+
+float qpTrackball::
+get_y() const {
+  return _translation[1];
+}
+
+float qpTrackball::
+get_z() const {
+  return _translation[2];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::set_pos
+//       Access: Public, Scheme
+//  Description: Directly set the offset from the rotational origin.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+set_pos(const LVecBase3f &vec) {
+  _translation = vec;
+  recompute();
+}
+
+void qpTrackball::
+set_pos(float x, float y, float z) {
+  _translation.set(x, y, z);
+  recompute();
+}
+
+void qpTrackball::
+set_x(float x) {
+  _translation[0] = x;
+  recompute();
+}
+
+void qpTrackball::
+set_y(float y) {
+  _translation[1] = y;
+  recompute();
+}
+
+void qpTrackball::
+set_z(float z) {
+  _translation[2] = z;
+  recompute();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_hpr
+//       Access: Public, Scheme
+//  Description: Return the trackball's orientation.
+////////////////////////////////////////////////////////////////////
+LVecBase3f qpTrackball::
+get_hpr() const {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  return hpr;
+}
+
+float qpTrackball::
+get_h() const {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  return hpr[0];
+}
+
+float qpTrackball::
+get_p() const {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  return hpr[1];
+}
+
+float qpTrackball::
+get_r() const {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  return hpr[2];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::set_hpr
+//       Access: Public, Scheme
+//  Description: Directly set the mover's orientation.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+set_hpr(const LVecBase3f &hpr) {
+  LVecBase3f scale, old_hpr, translate;
+  decompose_matrix(_rotation, scale, old_hpr, translate);
+  compose_matrix(_rotation, scale, hpr, translate);
+  recompute();
+}
+
+void qpTrackball::
+set_hpr(float h, float p, float r) {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  hpr.set(h, p, r);
+  compose_matrix(_rotation, scale, hpr, translate);
+  recompute();
+}
+
+void qpTrackball::
+set_h(float h) {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  hpr[0] = h;
+  compose_matrix(_rotation, scale, hpr, translate);
+  recompute();
+}
+
+void qpTrackball::
+set_p(float p) {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  hpr[1] = p;
+  compose_matrix(_rotation, scale, hpr, translate);
+  recompute();
+}
+
+void qpTrackball::
+set_r(float r) {
+  LVecBase3f scale, hpr, translate;
+  decompose_matrix(_rotation, scale, hpr, translate);
+  hpr[2] = r;
+  compose_matrix(_rotation, scale, hpr, translate);
+  recompute();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::reset_origin_here
+//       Access: Public, Scheme
+//  Description: Reposition the center of rotation to coincide with
+//               the current translation offset.  Future rotations
+//               will be about the current origin.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+reset_origin_here() {
+  recompute();
+  _rotation = _orig;
+  _translation.set(0.0f, 0.0f, 0.0f);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::move_origin
+//       Access: Public, Scheme
+//  Description: Moves the center of rotation by the given amount.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+move_origin(float x, float y, float z) {
+  _rotation = LMatrix4f::translate_mat(LVecBase3f(x, y, z)) *  _rotation;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::set_invert
+//       Access: Public, Scheme
+//  Description: Sets the invert flag.  When this is set, the inverse
+//               matrix is generated, suitable for joining to a
+//               camera, instead of parenting the scene under it.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+set_invert(bool flag) {
+  _invert = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_invert
+//       Access: Public, Scheme
+//  Description: Returns the invert flag.  When this is set, the
+//               inverse matrix is generated, suitable for joining to
+//               a camera, instead of parenting the scene under it.
+////////////////////////////////////////////////////////////////////
+bool qpTrackball::
+get_invert() const {
+  return _invert;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::set_rel_to
+//       Access: Public, Scheme
+//  Description: Sets the NodePath that all trackball manipulations
+//               are to be assumed to be relative to.  For instance,
+//               set your camera node here to make the trackball
+//               motion camera relative.  The default is the empty
+//               path, which means trackball motion is in global
+//               space.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+set_rel_to(const qpNodePath &rel_to) {
+  _rel_to = rel_to;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_rel_to
+//       Access: Public, Scheme
+//  Description: Returns the NodePath that all trackball manipulations
+//               are relative to, or the empty path.
+////////////////////////////////////////////////////////////////////
+const qpNodePath &qpTrackball::
+get_rel_to() const {
+  return _rel_to;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::set_coordinate_system
+//       Access: Public, Scheme
+//  Description: Sets the coordinate system of the Trackball.
+//               Normally, this is the default coordinate system.
+//               This changes the axes the Trackball manipulates so
+//               that the user interface remains consistent across
+//               different coordinate systems.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+set_coordinate_system(CoordinateSystem cs) {
+  _cs = cs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_coordinate_system
+//       Access: Public, Scheme
+//  Description: Returns the coordinate system of the Trackball.
+//               See set_coordinate_system().
+////////////////////////////////////////////////////////////////////
+CoordinateSystem qpTrackball::
+get_coordinate_system() const {
+  return _cs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::set_mat
+//       Access: Public, Scheme
+//  Description: Stores the indicated transform in the trackball.
+//               This is a transform in global space, regardless of
+//               the rel_to node.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+set_mat(const LMatrix4f &mat) {
+  _orig = mat;
+  if (_invert) {
+    _mat = invert(_orig);
+  } else {
+    _mat = _orig;
+  }
+
+  reextract();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_mat
+//       Access: Public, Scheme
+//  Description: Returns the matrix represented by the trackball
+//               rotation.
+////////////////////////////////////////////////////////////////////
+const LMatrix4f &qpTrackball::
+get_mat() const {
+  return _orig;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::get_trans_mat
+//       Access: Public, Scheme
+//  Description: Returns the actual transform that will be applied to
+//               the scene graph.  This is the same as get_mat(),
+//               unless invert is in effect.
+////////////////////////////////////////////////////////////////////
+const LMatrix4f &qpTrackball::
+get_trans_mat() const {
+  return _mat;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::apply
+//       Access: Private
+//  Description: Applies the operation indicated by the user's mouse
+//               motion to the current state.  Returns the matrix
+//               indicating the new state.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+apply(double x, double y, int button) {
+  if (button && !_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();
+  }
+  if (button == B1_MASK) {
+    // Button 1: translate in plane parallel to screen.
+
+    _translation +=
+      x * _fwdscale * LVector3f::right(_cs) +
+      y * _fwdscale * LVector3f::down(_cs);
+
+  } else if (button == (B2_MASK | B3_MASK)) {
+    // Buttons 2 + 3: rotate about the vector perpendicular to the
+    // screen.
+
+    _rotation *=
+      LMatrix4f::rotate_mat_normaxis((x - y) * _rotscale,
+                            LVector3f::forward(_cs), _cs);
+
+  } else if ((button == B2_MASK) || (button == (B1_MASK | B3_MASK))) {
+    // Button 2, or buttons 1 + 3: rotate about the right and up
+    // vectors.  (We alternately define this as buttons 1 + 3, to
+    // support two-button mice.)
+
+    _rotation *=
+      LMatrix4f::rotate_mat_normaxis(x * _rotscale, LVector3f::up(_cs), _cs) *
+      LMatrix4f::rotate_mat_normaxis(y * _rotscale, LVector3f::right(_cs), _cs);
+
+  } else if ((button == B3_MASK) || (button == (B1_MASK | B2_MASK))) {
+    // Button 3, or buttons 1 + 2: dolly in and out along the forward
+    // vector.  (We alternately define this as buttons 1 + 2, to
+    // support two-button mice.)
+    _translation -= y * _fwdscale * LVector3f::forward(_cs);
+  }
+
+  if (button) {
+    recompute();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::reextract
+//       Access: Private
+//  Description: Given a correctly computed _orig matrix, rederive the
+//               translation and rotation elements.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+reextract() {
+  LMatrix4f m = _orig;
+  if (!_rel_to.is_empty()) {
+    qpNodePath root;
+    const LMatrix4f &rel_mat = root.get_mat(_rel_to);
+    m = _orig * rel_mat;
+  }
+
+  m.get_row3(_translation,3);
+  _rotation = m;
+  _rotation.set_row(3, LVecBase3f(0.0f, 0.0f, 0.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::recompute
+//       Access: Private
+//  Description: Rebuilds the matrix according to the stored rotation
+//               and translation components.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+recompute() {
+  _orig = _rotation * LMatrix4f::translate_mat(_translation);
+
+  if (!_rel_to.is_empty()) {
+    qpNodePath root;
+    const LMatrix4f &rel_mat = _rel_to.get_mat(root);
+    _orig = _orig * rel_mat;
+  }
+
+  if (_invert) {
+    _mat = invert(_orig);
+  } else {
+    _mat = _orig;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackball::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpTrackball::
+do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+  // First, update our modifier buttons.
+  if (input.has_data(_button_events_input)) {
+    const ButtonEventList *button_events;
+    DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
+    button_events->update_mods(_mods);
+  }
+
+  // Now, check for mouse motion.
+  if (input.has_data(_pixel_xy_input)) {
+    const EventStoreVec2 *pixel_xy;
+    DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
+    const LVecBase2f &p = pixel_xy->get_value();
+    float this_x = p[0];
+    float this_y = p[1];
+    int this_button = 0;
+
+    if (_mods.is_down(MouseButton::one())) {
+      this_button |= B1_MASK;
+    }
+    if (_mods.is_down(MouseButton::two())) {
+      this_button |= B2_MASK;
+    }
+    if (_mods.is_down(MouseButton::three())) {
+      this_button |= B3_MASK;
+    }
+
+    float x = this_x - _lastx;
+    float y = this_y - _lasty;
+
+    apply(x, y, this_button);
+
+    _lastx = this_x;
+    _lasty = this_y;
+  }
+
+  // Now send our matrix down the pipe.
+  _transform->set_value(_mat);
+  output.set_data(_transform_output, EventParameter(_transform));
+}

+ 152 - 0
panda/src/tform/qptrackball.h

@@ -0,0 +1,152 @@
+// Filename: qptrackball.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpTRACKBALL_H
+#define qpTRACKBALL_H
+
+#include "pandabase.h"
+
+#include "qpdataNode.h"
+#include "qpnodePath.h"
+#include "modifierButtons.h"
+#include "luse.h"
+#include "linmath_events.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpTrackball
+// Description : Trackball acts like Performer in trackball mode.  It
+//               can either spin around a piece of geometry directly,
+//               or it can spin around a camera with the inverse
+//               transform to make it appear that the whole world is
+//               spinning.
+//
+//               The Trackball object actually just places a transform
+//               in the data graph; parent a Transform2SG node under
+//               it to actually transform objects (or cameras) in the
+//               world.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpTrackball : public qpDataNode {
+PUBLISHED:
+  qpTrackball(const string &name);
+  ~qpTrackball();
+
+  void reset();
+
+  /// **** Translation ****
+
+  const LPoint3f &get_pos() const;
+  float get_x() const;
+  float get_y() const;
+  float get_z() const;
+  void set_pos(const LVecBase3f &vec);
+  void set_pos(float x, float y, float z);
+  void set_x(float x);
+  void set_y(float y);
+  void set_z(float z);
+
+  /// **** Rotation ****
+
+  LVecBase3f get_hpr() const;
+  float get_h() const;
+  float get_p() const;
+  float get_r() const;
+  void set_hpr(const LVecBase3f &hpr);
+  void set_hpr(float h, float p, float r);
+  void set_h(float h);
+  void set_p(float p);
+  void set_r(float r);
+
+  /// **** Origin of Rotation ****
+
+  void reset_origin_here();
+  void move_origin(float x, float y, float z);
+
+  /// **** Misc ****
+
+  void set_invert(bool flag);
+  bool get_invert() const;
+
+  void set_rel_to(const qpNodePath &_rel_to);
+  const qpNodePath &get_rel_to() const;
+
+  void set_coordinate_system(CoordinateSystem cs);
+  CoordinateSystem get_coordinate_system() const;
+
+  void set_mat(const LMatrix4f &mat);
+  const LMatrix4f &get_mat() const;
+  const LMatrix4f &get_trans_mat() const;
+
+
+private:
+  void apply(double x, double y, int button);
+
+  void reextract();
+  void recompute();
+
+
+  float _lastx, _lasty;
+
+  float _rotscale;
+  float _fwdscale;
+
+  LMatrix4f _rotation;
+  LPoint3f _translation;
+  LMatrix4f _mat, _orig;
+  bool _invert;
+  qpNodePath _rel_to;
+  CoordinateSystem _cs;
+
+  // Remember which mouse buttons are being held down.
+  ModifierButtons _mods;
+
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // inputs
+  int _pixel_xy_input;
+  int _button_events_input;
+
+  // outputs
+  int _transform_output;
+
+  PT(EventStoreMat4) _transform;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpTrackball",
+                  qpDataNode::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;
+};
+
+#endif

+ 84 - 0
panda/src/tform/qptransform2sg.cxx

@@ -0,0 +1,84 @@
+// Filename: qptransform2sg.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qptransform2sg.h"
+#include "transformState.h"
+
+
+TypeHandle qpTransform2SG::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTransform2SG::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpTransform2SG::
+qpTransform2SG(const string &name) :
+  qpDataNode(name)
+{
+  _transform_input = define_input("Transform", EventStoreMat4::get_class_type());
+
+  _node = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTransform2SG::set_node
+//       Access: Public
+//  Description: Sets the node that this node will adjust.
+////////////////////////////////////////////////////////////////////
+void qpTransform2SG::
+set_node(PandaNode *node) {
+  _node = node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTransform2SG::get_node
+//       Access: Public
+//  Description: Returns the node that this node will adjust, or NULL
+//               if the node has not yet been set.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpTransform2SG::
+get_node() const {
+  return _node;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTransform2SG::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpTransform2SG::
+do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &) {
+  if (input.has_data(_transform_input)) {
+    const EventStoreMat4 *transform;
+    DCAST_INTO_V(transform, input.get_data(_transform_input).get_ptr());
+    const LMatrix4f &mat = transform->get_value();
+    if (_node != (PandaNode *)NULL) {
+      _node->set_transform(TransformState::make_mat(mat));
+    }
+  }
+}

+ 72 - 0
panda/src/tform/qptransform2sg.h

@@ -0,0 +1,72 @@
+// Filename: qptransform2sg.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpTRANSFORM2SG_H
+#define qpTRANSFORM2SG_H
+
+#include "pandabase.h"
+
+#include "qpdataNode.h"
+#include "pandaNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpTransform2SG
+// Description : input: Transform (matrix)
+//
+//               output: none, but applies the matrix as the transform
+//               transition for a given arc of the scene graph.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpTransform2SG : public qpDataNode {
+PUBLISHED:
+  qpTransform2SG(const string &name);
+
+  void set_node(PandaNode *node);
+  PandaNode *get_node() const;
+
+private:
+  PandaNode *_node;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // inputs
+  int _transform_input;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpTransform2SG",
+                  qpDataNode::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;
+};
+
+#endif
+

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

@@ -1,5 +1,6 @@
 
 #include "buttonThrower.cxx"
+#include "qpbuttonThrower.cxx"
 #include "config_tform.cxx"
 #include "dataValve.cxx"
 #include "driveInterface.cxx"

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

@@ -4,5 +4,7 @@
 #include "mouseWatcherParameter.cxx"
 #include "mouseWatcherRegion.cxx"
 #include "trackball.cxx"
+#include "qptrackball.cxx"
 #include "transform2sg.cxx"
+#include "qptransform2sg.cxx"