Browse Source

anim controls in pview

David Rose 20 years ago
parent
commit
5bf4f3557d

+ 14 - 0
panda/src/chan/animControlCollection.I

@@ -186,6 +186,20 @@ get_num_frames(const string &anim_name) const {
   return control->get_num_frames();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AnimControlCollection::get_num_frames
+//       Access: Public
+//  Description: Returns the total number of frames in the
+//               last-started animation.
+////////////////////////////////////////////////////////////////////
+INLINE int AnimControlCollection::
+get_num_frames() const {
+  if (_last_started_control == (AnimControl *)NULL) {
+    return 0;
+  }
+  return _last_started_control->get_num_frames();
+}
+
 INLINE ostream &
 operator << (ostream &out, const AnimControlCollection &collection) {
   collection.output(out);

+ 5 - 0
panda/src/chan/animControlCollection.cxx

@@ -185,6 +185,7 @@ play_all() {
   Controls::const_iterator ci;
   for (ci = _controls.begin(); ci != _controls.end(); ++ci) {
     (*ci)._control->play();
+    _last_started_control = (*ci)._control;
   }
 }
 
@@ -198,6 +199,7 @@ play_all(int from, int to) {
   Controls::const_iterator ci;
   for (ci = _controls.begin(); ci != _controls.end(); ++ci) {
     (*ci)._control->play(from, to);
+    _last_started_control = (*ci)._control;
   }
 }
 
@@ -211,6 +213,7 @@ loop_all(bool restart) {
   Controls::const_iterator ci;
   for (ci = _controls.begin(); ci != _controls.end(); ++ci) {
     (*ci)._control->loop(restart);
+    _last_started_control = (*ci)._control;
   }
 }
 
@@ -224,6 +227,7 @@ loop_all(bool restart, int from, int to) {
   Controls::const_iterator ci;
   for (ci = _controls.begin(); ci != _controls.end(); ++ci) {
     (*ci)._control->loop(restart, from, to);
+    _last_started_control = (*ci)._control;
   }
 }
 
@@ -258,6 +262,7 @@ pose_all(int frame) {
   Controls::const_iterator ci;
   for (ci = _controls.begin(); ci != _controls.end(); ++ci) {
     (*ci)._control->pose(frame);
+    _last_started_control = (*ci)._control;
   }
 }
 

+ 1 - 0
panda/src/chan/animControlCollection.h

@@ -74,6 +74,7 @@ PUBLISHED:
   INLINE int get_frame() const;
 
   INLINE int get_num_frames(const string &anim_name) const;
+  INLINE int get_num_frames() const;
 
   INLINE bool is_playing(const string &anim_name) const;
   INLINE bool is_playing() const;

+ 21 - 0
panda/src/framework/pandaFramework.cxx

@@ -28,6 +28,7 @@
 #include "textNode.h"
 #include "mouseAndKeyboard.h"
 #include "mouseRecorder.h"
+#include "throw_event.h"
 
 LoaderOptions PandaFramework::_loader_options;
 
@@ -747,6 +748,8 @@ do_frame() {
   nassertr(_is_open, false);
   DataGraphTraverser dg_trav;
   dg_trav.traverse(_data_root.node());
+
+  throw_event("NewFrame");
   _event_handler.process_events();
 
   if (!_screenshot_text.is_empty()) {
@@ -834,6 +837,7 @@ do_enable_default_keys() {
   define_key("i", "invert (reverse) single-sided faces", event_i, this);
   define_key("l", "toggle lighting", event_l, this);
   define_key("c", "recenter view on object", event_c, this);
+  define_key("a", "toggle animation controls", event_a, this);
   define_key("shift-c", "toggle collision surfaces", event_C, this);
   define_key("shift-b", "report bounding volume", event_B, this);
   define_key("shift-l", "list hierarchy", event_L, this);
@@ -1025,6 +1029,23 @@ event_c(CPT_Event event, void *data) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaFramework::event_a
+//       Access: Public, Static
+//  Description: Default handler for a key: toggle the animation
+//               controls.
+////////////////////////////////////////////////////////////////////
+void PandaFramework::
+event_a(CPT_Event event, void *data) {
+  if (event->get_num_parameters() == 1) {
+    EventParameter param = event->get_parameter(0);
+    WindowFramework *wf;
+    DCAST_INTO_V(wf, param.get_ptr());
+
+    wf->next_anim_control();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_C
 //       Access: Public, Static

+ 1 - 0
panda/src/framework/pandaFramework.h

@@ -132,6 +132,7 @@ public:
   static void event_i(CPT_Event, void *data);
   static void event_l(CPT_Event, void *data);
   static void event_c(CPT_Event, void *data);
+  static void event_a(CPT_Event, void *data);
   static void event_C(CPT_Event, void *data);
   static void event_B(CPT_Event, void *data);
   static void event_L(CPT_Event, void *data);

+ 10 - 0
panda/src/framework/windowFramework.I

@@ -63,6 +63,16 @@ get_camera(int n) const {
   return _cameras[n];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::get_anim_controls
+//       Access: Public
+//  Description: Returns the current state of the anim_controls flag.
+////////////////////////////////////////////////////////////////////
+INLINE bool WindowFramework::
+get_anim_controls() const {
+  return _anim_controls_enabled;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WindowFramework::get_wireframe
 //       Access: Public

+ 169 - 2
panda/src/framework/windowFramework.cxx

@@ -54,6 +54,7 @@
 #include "loaderFileTypeRegistry.h"
 #include "pnmImage.h"
 #include "virtualFileSystem.h"
+#include "string_utils.h"
 
 // This is generated data for the standard texture we apply to the
 // blue triangle.
@@ -79,6 +80,8 @@ WindowFramework(PandaFramework *panda_framework) :
   _got_keyboard = false;
   _got_trackball = false;
   _got_lights = false;
+  _anim_controls_enabled = false;
+  _anim_index = 0;
   _wireframe_enabled = false;
   _texture_enabled = true;
   _two_sided_enabled = false;
@@ -103,6 +106,8 @@ WindowFramework(const WindowFramework &copy, DisplayRegion *display_region) :
   _got_keyboard = false;
   _got_trackball = false;
   _got_lights = false;
+  _anim_controls_enabled = false;
+  _anim_index = 0;
   _wireframe_enabled = false;
   _texture_enabled = true;
   _two_sided_enabled = false;
@@ -345,8 +350,8 @@ get_mouse() {
     // from get_mouse() is actually a MouseWatcher, but since it
     // presents the same interface as a Mouse, no one should mind.
 
-    // Another advantage to using a MouseWatcher is that it the PGTop
-    // of aspect2d likes it better.
+    // Another advantage to using a MouseWatcher is that the PGTop of
+    // aspect2d likes it better.
     PT(MouseWatcher) mw = new MouseWatcher("watcher");
     mw->set_display_region(_display_region_3d);
     _mouse = mouse.attach_new_node(mw);
@@ -674,6 +679,50 @@ loop_animations(int hierarchy_match_flags) {
   _anim_controls.loop_all(true);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::next_anim_control
+//       Access: Public
+//  Description: Rotates the animation controls through all of the
+//               available animations.  If the animation controls are
+//               not already enabled, enables them at sets to the
+//               first animation; if they are already enabled, steps
+//               to the next animation; if that is the last animation,
+//               disables the animation controls.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+next_anim_control() {
+  if (_anim_controls_enabled) {
+    destroy_anim_controls();
+
+    ++_anim_index;
+    if (_anim_index >= _anim_controls.get_num_anims()) {
+      set_anim_controls(false);
+    } else {
+      create_anim_controls();
+    }
+  } else {
+    _anim_index = 0;
+    set_anim_controls(true);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::set_anim_controls
+//       Access: Public
+//  Description: Creates an onscreen animation slider for
+//               frame-stepping through the animations.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+set_anim_controls(bool enable) {
+  _anim_controls_enabled = enable;
+  if (_anim_controls_enabled) {
+    create_anim_controls();
+
+  } else {
+    destroy_anim_controls();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WindowFramework::split_window
 //       Access: Public
@@ -1060,3 +1109,121 @@ load_image_as_model(const Filename &filename) {
 
   return card_node.p();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::create_anim_controls
+//       Access: Private
+//  Description: Creates an onscreen animation slider for
+//               frame-stepping through the animations.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+create_anim_controls() {
+  destroy_anim_controls();
+
+  AnimControl *control = _anim_controls.get_anim(_anim_index);
+  nassertv(control != (AnimControl *)NULL);
+
+  PT(PGItem) group = new PGItem("anim_controls_group");
+  PGFrameStyle style;
+  style.set_type(PGFrameStyle::T_flat);
+  style.set_color(0.0f, 0.0f, 0.0f, 0.3f);
+  group->set_frame(-1.0f, 1.0f, 0.0f, 0.2f);
+  group->set_frame_style(0, style);
+  group->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
+  group->set_active(true);
+
+  _anim_controls_group = get_aspect_2d().attach_new_node(group);
+  _anim_controls_group.set_pos(0.0f, 0.0f, -0.9f);
+
+  if (_anim_index >= _anim_controls.get_num_anims()) {
+    PT(TextNode) label = new TextNode("label");
+    label->set_align(TextNode::A_center);
+    label->set_text("No animation.");
+    NodePath tnp = _anim_controls_group.attach_new_node(label);
+    tnp.set_pos(0.0f, 0.0f, 0.07f);
+    tnp.set_scale(0.1f);
+    
+    return;
+  }
+
+  PT(TextNode) label = new TextNode("anim_name");
+  label->set_align(TextNode::A_left);
+  label->set_text(_anim_controls.get_anim_name(_anim_index));
+  NodePath tnp = _anim_controls_group.attach_new_node(label);
+  tnp.set_pos(-1.0f, 0.0f, 0.15f);
+  tnp.set_scale(0.05f);
+
+  _anim_slider = new PGSliderBar("anim_slider");
+  _anim_slider->setup_slider(false, 1.8f, 0.1f, 0.01f);
+  _anim_slider->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
+  _anim_slider->get_thumb_button()->set_suppress_flags(MouseWatcherRegion::SF_mouse_button);
+
+  _anim_slider->set_range(0.0f, (float)(control->get_num_frames() - 1));
+  _anim_slider->set_scroll_size(0.0f);
+  _anim_slider->set_page_size(1.0f);
+  NodePath snp = _anim_controls_group.attach_new_node(_anim_slider);
+  snp.set_pos(0.0f, 0.0f, 0.06f);
+
+  _frame_number = new TextNode("frame_number");
+  _frame_number->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
+  _frame_number->set_align(TextNode::A_center);
+  _frame_number->set_text(format_string(control->get_frame()));
+  NodePath fnp = NodePath(_anim_slider->get_thumb_button()).attach_new_node(_frame_number);
+  fnp.set_scale(0.05f);
+  fnp.set_pos(0.0f, 0.0f, -0.01f);
+
+  _panda_framework->get_event_handler().add_hook("NewFrame", st_update_anim_controls, (void *)this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::destroy_anim_controls
+//       Access: Private
+//  Description: Removes the previously-created anim controls, if any.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+destroy_anim_controls() {
+  if (!_anim_controls_group.is_empty()) {
+    _anim_controls_group.remove_node();
+
+    _panda_framework->get_event_handler().remove_hook("NewFrame", st_update_anim_controls, (void *)this);
+
+    if (_anim_index < _anim_controls.get_num_anims()) {
+      AnimControl *control = _anim_controls.get_anim(_anim_index);
+      nassertv(control != (AnimControl *)NULL);
+      control->loop(false);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::update_anim_controls
+//       Access: Private
+//  Description: A per-frame callback to update the anim slider for
+//               the current frame.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+update_anim_controls() {
+  AnimControl *control = _anim_controls.get_anim(_anim_index);
+  nassertv(control != (AnimControl *)NULL);
+
+  if (_anim_slider->is_button_down()) {
+    control->pose((int)(_anim_slider->get_value() + 0.5));
+  } else {
+    _anim_slider->set_value((float)control->get_frame());
+  }
+
+  _frame_number->set_text(format_string(control->get_frame()));
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: WindowFramework::st_update_anim_controls
+//       Access: Private, Static
+//  Description: The static event handler function.
+////////////////////////////////////////////////////////////////////
+void WindowFramework::
+st_update_anim_controls(CPT_Event, void *data) {
+  WindowFramework *self = (WindowFramework *)data;
+  self->update_anim_controls();
+}
+

+ 15 - 0
panda/src/framework/windowFramework.h

@@ -33,6 +33,8 @@
 #include "typedWritableReferenceCount.h"
 #include "graphicsWindow.h"
 #include "loaderOptions.h"
+#include "pgSliderBar.h"
+#include "textNode.h"
 
 class PandaFramework;
 class AmbientLight;
@@ -92,6 +94,9 @@ public:
   void loop_animations(int hierarchy_match_flags = 
                        PartGroup::HMF_ok_part_extra |
                        PartGroup::HMF_ok_anim_extra);
+  void next_anim_control();
+  void set_anim_controls(bool enable);
+  INLINE bool get_anim_controls() const;
 
   enum BackgroundType {
     BT_other = 0,
@@ -129,6 +134,10 @@ protected:
 
 private:
   PT(PandaNode) load_image_as_model(const Filename &filename);
+  void create_anim_controls();
+  void destroy_anim_controls();
+  void update_anim_controls();
+  static void st_update_anim_controls(CPT_Event, void *data);
 
 private:
   PandaFramework *_panda_framework;
@@ -143,7 +152,13 @@ private:
   NodePath _render;
   NodePath _render_2d;
   NodePath _aspect_2d;
+
   AnimControlCollection _anim_controls;
+  bool _anim_controls_enabled;
+  int _anim_index;
+  NodePath _anim_controls_group;
+  PT(PGSliderBar) _anim_slider;
+  PT(TextNode) _frame_number;
 
   NodePath _mouse;
   PT(Trackball) _trackball;

+ 2 - 1
panda/src/pgui/pgButton.I

@@ -103,7 +103,8 @@ get_click_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: PGButton::is_button_down
 //       Access: Published
-//  Description: Returns if the value of button_down
+//  Description: Returns true if the user is currently holding the
+//               mouse button down on the button, false otherwise.
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGButton::
 is_button_down() {

+ 31 - 3
panda/src/pgui/pgSliderBar.I

@@ -196,9 +196,9 @@ get_value() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 set_ratio(float ratio) {
-  _ratio = max(min(ratio, 1.0f), 0.0f);
-  _needs_reposition = true;
-  adjust();
+  if (!is_button_down()) {
+    internal_set_ratio(ratio);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -212,6 +212,20 @@ get_ratio() const {
   return _ratio;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PGSliderBar::is_button_down
+//       Access: Published
+//  Description: Returns true if the user is currently holding down
+//               the mouse button to manipulate the slider.  When
+//               true, calls to set_ratio() or set_value() will have
+//               no effect.
+////////////////////////////////////////////////////////////////////
+INLINE bool PGSliderBar:: 
+is_button_down() const {
+  return _dragging || _mouse_button_page || 
+    (_scroll_button_held != (PGItem *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PGSliderBar::set_resize_thumb
 //       Access: Published
@@ -433,3 +447,17 @@ INLINE string PGSliderBar::
 get_adjust_event() const {
   return get_adjust_prefix() + get_id();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PGSliderBar::internal_set_ratio
+//       Access: Private
+//  Description: Sets the current value of the slider, expressed in
+//               the range 0 .. 1, without checking whether the user
+//               is currently manipulating the slider.
+////////////////////////////////////////////////////////////////////
+INLINE void PGSliderBar:: 
+internal_set_ratio(float ratio) {
+  _ratio = max(min(ratio, 1.0f), 0.0f);
+  _needs_reposition = true;
+  adjust();
+}

+ 4 - 4
panda/src/pgui/pgSliderBar.cxx

@@ -713,10 +713,10 @@ reposition() {
 void PGSliderBar::
 advance_scroll() {
   if (_scroll_button_held == _left_button) {
-    set_ratio(max(_ratio - _scroll_ratio, 0.0f));
+    internal_set_ratio(max(_ratio - _scroll_ratio, 0.0f));
 
   } else if (_scroll_button_held == _right_button) {
-    set_ratio(min(_ratio + _scroll_ratio, 1.0f));
+    internal_set_ratio(min(_ratio + _scroll_ratio, 1.0f));
   }
 
   _next_advance_time = 
@@ -744,7 +744,7 @@ advance_page() {
   } else {
     t = min(_ratio + _page_ratio - _scroll_ratio, target_ratio);
   }
-  set_ratio(t);
+  internal_set_ratio(t);
   if (t == target_ratio) {
     // We made it; begin dragging from now on until the user releases
     // the mouse.
@@ -786,7 +786,7 @@ continue_drag() {
   }
   if (_range_x != 0.0f) {
     float current_x = mouse_to_local(_mouse_pos).dot(_axis);
-    set_ratio((current_x - _drag_start_x) / _range_x);
+    internal_set_ratio((current_x - _drag_start_x) / _range_x);
   }
 }
 

+ 4 - 0
panda/src/pgui/pgSliderBar.h

@@ -81,6 +81,8 @@ PUBLISHED:
   INLINE void set_ratio(float ratio);
   INLINE float get_ratio() const;
 
+  INLINE bool is_button_down() const;
+
   INLINE void set_resize_thumb(bool resize_thumb);
   INLINE bool get_resize_thumb() const;
 
@@ -118,6 +120,8 @@ protected:
   virtual void item_move(PGItem *item, const MouseWatcherParameter &param);
 
 private:
+  INLINE void internal_set_ratio(float ratio);
+
   void reposition();
   void advance_scroll();
   void advance_page();

+ 8 - 1
panda/src/testbed/pview.cxx

@@ -213,7 +213,8 @@ help() {
 
     "  -a\n"
     "      Convert and play animations, if loading an external file type\n"
-    "      (like .mb) directly and if the converter supports animations.\n\n"
+    "      (like .mb) directly and if the converter supports animations.\n"
+    "      Also implicitly enables the animation controls.\n\n"
     
     "  -c\n"
     "      Automatically center models within the viewing window on startup.\n"
@@ -252,6 +253,7 @@ main(int argc, char *argv[]) {
   framework.open_framework(argc, argv);
   framework.set_window_title("Panda Viewer");
 
+  bool anim_controls = false;
   bool auto_center = false;
   bool show_loading = false;
   bool auto_screenshot = false;
@@ -267,6 +269,7 @@ main(int argc, char *argv[]) {
   while (flag != EOF) {
     switch (flag) {
     case 'a':
+      anim_controls = true;
       PandaFramework::_loader_options.set_flags(PandaFramework::_loader_options.get_flags() | LoaderOptions::LF_convert_anim);
       break;
 
@@ -353,6 +356,10 @@ main(int argc, char *argv[]) {
       return(output_screenshot(screenshotfn) ? 0:1);
     }
 
+    if (anim_controls) {
+      window->set_anim_controls(true);
+    }
+
     framework.enable_default_keys();
     framework.define_key("shift-w", "open a new window", event_W, NULL);
     framework.define_key("shift-f", "flatten hierarchy", event_F, NULL);