vrecko
virtual reality framework
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DynamicMenu.h
Go to the documentation of this file.
1 #ifndef DYNAMICMENU_H
2 #define DYNAMICMENU_H
3 
4 #include <vrecko/Ability.h>
5 
6 #include <osg/Group>
7 #include <osg/ShapeDrawable>
8 #include <osgText/Text>
9 
10 #include <helpers/DynamicArray.h>
11 #include <helpers/DynamicTree.h>
12 //#include <xercesc/dom/DOMErrorHandler.hpp>
13 
15 
18 
19 
20 using namespace vrecko;
21 
22 
23 namespace APMenu
24 {
25  class DynamicMenu;
26 
27  // Constants for MenuItem::dwFlags (retreived and set by MenuItem::GetFlags() and MenuItem::SetFlags())
28  #define MI_F_SELECTED 0x00000001
29  // The item is selected
30  #define MI_F_EXPANDED 0x00000002
31  // The item is expanded
32 
33 
34  /* Internal structure of a single MenuItem
35 
36 
37  mainTransform
38  |
39  \- osgTransform
40  | |
41  | \- osgGeode
42  | | |
43  | | \- shapeBox OR shapeSection
44  | | | | |
45  | | | \- shapeDraw \- qshapeDraw
46  | | |
47  | | \- osgText ( <- only for MenuItemText and descendats)
48  | |
49  | \- modelTransform ( <- only for MenuItemText and descendats)
50  | |
51  | \- model ( <- only for MenuItemText and descendats)
52  |
53  \- childMenuObject
54 
55 
56 
57  */
58 
59 
60  class MenuItem : public DynTreeNode<MenuItem>, public BaseClass {
61  public:
62  MenuItem(DynamicMenu *_owner);
63  virtual MenuItem* createNewItem(DynamicMenu* _owner) = 0;
64  virtual ~MenuItem();
65  // Note that every child of this class should call Destroy() in its destructor.
66 
67  virtual void destroy();
68  // Here should be all the code required to destroy the graphics & input/outputs etc.
69  // (including call to parent's Destroy())
70 
71  virtual bool loadParametersFromNode(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node);
72 
73  // The name can be empty. In such case the item will not send events upon selection.
74  inline void setName(const char* newName);
75  inline const std::string& getName() { return name; }
76 
77  bool setFlags(unsigned long dwNewFlags, bool bForceUpdate = false);
78  // Combination of MI_F_*
79  inline unsigned long getFlags() { return dwFlags; }
80 
81  virtual bool invalidate(bool bIncludeSubItems = false);
82  // This method is called if anything changing the graphical representation has been changed.
83  // This method will discard (= DESTROY COMPLETELY) old grapical representation.
84  // If the item was visible, create new graphics and show it.
85 
86  bool isPointed(osg::Vec3 *projPointerPos);
87  // Is intersected by the pointer, so it should be selected?
88  // [projPointerPos] is a pointer position projected onto thwe menu plane.
89 
90  virtual bool canBeClicked() { return true; }
91  inline bool isVisible() { return bIsVisible; };
92  inline bool isGraphicsCreated() { return bGraphicsCreated; };
93  virtual bool show(unsigned long _dwItemIndex, unsigned long _dwNumItems,
94  osg::Vec3 *_refPoint1, osg::Vec3 *_refPoint2, osg::Vec3 *_refPoint3);
95  // This method should not need to be overriden.
96  virtual void hide(bool bIncludeSubItems = false);
97  // This method should not need to be overriden.
98 
99  virtual bool pointerButtonDown() { return true; };
100  // Called when the user clicks presses the pointer button on the item (and the item has no children)
101  virtual bool pointerButtonUp() { return true; };
102  // Called when the user clicks releases the pointer (and the item has no children).
103  // Is only called if the pointerButtonDown() was called previously.
104  virtual bool pointerMove(osg::Vec3 *projPointerPos) { return true; };
105  // Called when the user moves the pointer over the item with an EXCEPTION:
106  // If user puses the pointer button down on AN ITEM, then before the pointer button gets released
107  // this particular ITEM will get all the pointerMove()'s even if the user is pointin outside the item boundaries.
108  // (Similiar behaviour as for components in Borland Delphi and possibly in other Windows development GUIs)
109  virtual bool clicked() { return true; };
110  // Called when the user clicks [pointer button down + up + confirmation animation] on the item (and the item has no children).
111 
112  virtual void getClickedString(char *outClickedEventString, size_t bufferSize) { sprintf_s(outClickedEventString, bufferSize, "%s", name.c_str()); }
113  // The method should return a string, that is later sent by DynamicMenu to its "clicked" output.
114  // This is usually a name of the item, but could be something more complex, such as "name;value" in case
115  // of a slider. The string can be "" in whick case the Menu will NOT send any event.
116 
117  virtual bool setAndPlayConfirmationEffect();
118  // This method has two alternative behaviours.
119  // It may return "false"
120  // and do nothing else. The DynamicMenu will take action and executes a command
121  // belonging to this item. (executes clicked() method etc.)
122  // It may return "true"
123  // in which case the menu item should set up and play confirmation effect
124  // (an animation if it would like) and after playing IT HAVE TO REPORT EVENT
125  // "ConfirmAnimationEnded" with BOOL value "true" to the DynamicMenu ability.
126  // (The Animator ability is designed to report the "AnimationEnded" event
127  // which exactly suits :) )
128  virtual bool resetConfirmationEffect();
129  // Called after the effect was played (or maybe even in the middle of it!)
130  // This method should reset the menu item to the original position.
131 
132  inline bool isExpanded() { return (lpChild && lpChild->isVisible()); }
133  inline bool canBeExpanded() { return (NULL != lpChild); }
134 
135  vrecko::EnvironmentObject* getChildMenuObject() { return childMenuObject.get(); }
136 
137  protected:
139  int pos; // position in the current menu level
140  bool bIsVisible, bGraphicsCreated;
141 
142  unsigned long dwFlags;
143  // combination of MI_F_*
144 
145  // following variables are initialized every time the MenuItem::Show() is called:
146  // MenuItem::show() parameters:
147  unsigned long dwItemIndex, dwNumItems;
148  osg::Vec3 refPoint1, refPoint2, refPoint3;
149  // cached variables, retreived from [owner]
150  float itemSize, itemSize2;
151 
152  osg::Vec4 color;
153 
154  std::string name; // item name
155  // (can be empty, in which case the item will not send events upon selection)
156 
157  osg::ref_ptr<osg::MatrixTransform> mainTransform;
158 // osg::ref_ptr<vrecko::EnvironmentObject> mainTransform;
159  osg::ref_ptr<vrecko::EnvironmentObject> childMenuObject;
160 // osg::ref_ptr<osg::MatrixTransform> osgTransform;
161  osg::ref_ptr<vrecko::EnvironmentObject> osgTransform;
162  osg::ref_ptr<osg::Geode> osgGeode;
164 
166  // Was the "ConfirmationEnded" interconnection already created?
167 
168  osg::ref_ptr<osg::Box> shapeBox;
169  osg::ref_ptr<osg::ShapeDrawable> shapeDraw;
170 
171  osg::ref_ptr<osgQ::QSection> shapeSection;
172  osg::ref_ptr<osgQ::QShapeDrawable> qshapeDraw;
173 
174  bool createGraphics();
175  bool destroyGraphics();
176 
177  float menuRadial_CalcItemAngle(int _iItemIndex);
178  // the [_iItemIndex] can be even negative or too large
179 
180  unsigned long menuBoxes_CalcItemDisplayIndex();
181 
182  virtual bool createGraphicsCore();
183  // Creates new graphics according to the owner->GetType().
184  // The graphics need not to be set according to the flags.
185  // The SetFlags() (thus SetFlagsCore()) will be called afterwards.
186  virtual bool destroyGraphicsCore();
187  virtual bool showGraphicsAtPoint();
188  virtual bool hideGraphics();
189  virtual bool setFlagsCore(unsigned long dwOldFlags, unsigned long dwNewFlags);
190  // Updates the graphics to include the flags (MI_F_SELECTED especially)
191  virtual const osg::BoundingBox& getInnerBoundingBox() = 0;
192  // Get the bounding box of the inner contents
193 
194  virtual bool addInputOutputs();
195  // This function is called after setting the name of the item.
196  // It should call addOutput() to create an output to signalize
197  // clicking on the item (or its changes).
198  // Outputs and inputs mut have exactly the same name as is stored in [name]
199  virtual bool removeInputOutputs();
200  // This function should call removeOutput() / removeInput()
201  // to destroy previously created inputs and outputs.
202 
203  virtual int processNotification(BaseClass *sender, unsigned long notificationType,
204  void *notificationData);
205  };
206 
207 
208  class MenuItemNull : public MenuItem {
209  public:
210  MenuItemNull(DynamicMenu* _owner) : MenuItem(_owner) { name = "null"; }
211  virtual MenuItem* createNewItem(DynamicMenu* _owner) { return new MenuItemNull(_owner); }
212 
213  virtual bool canBeClicked() { return false; }
214 
215  protected:
216  osg::BoundingBox bbox;
217 
218  virtual bool createGraphicsCore() { return true; }
219  virtual bool destroyGraphicsCore() { return true; }
220  virtual bool showGraphicsAtPoint() { return true; }
221  virtual bool hideGraphics() { return true; }
222  virtual const osg::BoundingBox& getInnerBoundingBox() { return bbox; }
223  };
224 
225 
226  class MenuItemText : public MenuItem {
227  public:
228  MenuItemText(DynamicMenu *_owner);
229  virtual ~MenuItemText();
230  virtual MenuItem* createNewItem(DynamicMenu* _owner) { return new MenuItemText(_owner); }
231 
232  virtual void destroy();
233 
234  virtual bool loadParametersFromNode(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node);
235 
236  virtual bool clicked();
237 
238  virtual const std::string& getCaption() { return caption; }
239  virtual bool setCaption(const char* newCaption);
240 
241  virtual const std::string& getModelFile() { return modelFile; }
242  virtual bool setModelFile(const char* newModelFile);
243 
244  void setSignName(const char* name);
245 
246  protected:
247  std::string caption;
248 
249  std::string modelFile;
250  osg::ref_ptr<osg::Node> model;
251  osg::ref_ptr<osg::MatrixTransform> modelTransform;
252 
253  osg::ref_ptr<osgText::Text> osgText;
254 
255  osg::Vec2 textShift;
256  // this variable must be set before initialization of graphics
257 
258  std::string signName; // name of the sign from the menu's repository
259  osg::ref_ptr<osg::Node> signObject; // sign that is currently loaded
260  osg::ref_ptr<osg::PositionAttitudeTransform> signTransform;
261 
262  virtual bool createGraphicsCore();
263  virtual bool destroyGraphicsCore();
264  virtual bool showGraphicsAtPoint();
265  virtual const osg::BoundingBox& getInnerBoundingBox();
266 
267  bool getTextBoundingBox(osg::BoundingBox *outBox);
268 
269  osg::BoundingBox boundingBox;
270 
271  virtual bool addInputOutputs();
272  virtual bool removeInputOutputs();
273 
274  virtual bool createModel();
275  virtual void destroyModel();
276  virtual void repositionModel();
277  // Scales and translates the model according the space of the item left
278  // around the text (if any)
279 
280  virtual void updateText();
281 
282  DECLARE_OUTPUT(Clicked, MessageBool);
283  };
284 
285 
286  class MenuItemSlider : public MenuItemText {
287  public:
288  MenuItemSlider(DynamicMenu *_owner);
289  virtual ~MenuItemSlider();
290 
291  virtual MenuItem* createNewItem(DynamicMenu* _owner) { return new MenuItemSlider(_owner); }
292 
293  virtual void destroy();
294 
295  virtual bool loadParametersFromNode(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node);
296 
297  virtual bool canBeClicked() { return false; }
298  virtual bool clicked() { return true; }
299 
300  virtual void getClickedString(char *outClickedEventString, size_t bufferSize);
301 
302  virtual bool pointerButtonDown();
303  virtual bool pointerButtonUp();
304  virtual bool pointerMove(osg::Vec3 *projPointerPos);
305 
306  virtual float getSliderPos01() { return fSliderPos01; }
307  virtual bool setSliderPos01(float fNewSliderPos01, bool bSendEvent = true);
308 
309  virtual float getSliderPosXY();
310  virtual bool setSliderPosXY(float fNewSliderPos, bool bSendEvent = true) { return setSliderPos01((fNewSliderPos - fSliderFromX) / (fSliderToY - fSliderFromX), bSendEvent); }
311 
312  protected:
313  float fSliderPos01; // range: 0.0 to 1.0
314  bool bSliding; // is pointer dragged by the mouse movement?
315 
316  float fSliderFromX, fSliderToY;
317  bool bIntegerOnlySlider; // return integers or floats?
318 
319  osg::ref_ptr<osg::Cylinder> sliderNonmovable;
320  osg::ref_ptr<osg::ShapeDrawable> sliderDrawNM;
321  osg::ref_ptr<osg::Box> sliderMovable;
322  osg::ref_ptr<osg::ShapeDrawable> sliderDrawM;
323 
324  virtual bool createGraphicsCore();
325  virtual bool destroyGraphicsCore();
326  virtual bool showGraphicsAtPoint();
327  virtual const osg::BoundingBox& getInnerBoundingBox();
328 
329  virtual bool addInputOutputs();
330  virtual bool removeInputOutputs();
331 
332  bool updateSliderGraphicPos();
333 
334  float getPointerSliderPos01(bool *bIsOutside);
335  // Calculates the position of the pointer (TransPointerPos) on the slider.
336  // This method will return a number from 0.0 to 1.0 - even when the mouse pointer is far outside,
337  // the position will be clipped to this interval, however the [bIsOutside] will be set to "true".
338  // [bIsOutside] can be NULL.
339 
340  virtual void updateText();
341 
342  DECLARE_OUTPUT(Slide, MessageFloat);
343  };
344 
345 
347 
348 
350  public:
351  std::string value;
352  osg::ref_ptr<osg::Node> geometry;
353  };
354 
355 
356  class DynamicMenu: public Ability
357  {
358  friend class MenuItem;
359  public:
360  //
361  // constructors / destructors
362  //
363  DynamicMenu();
364  virtual ~DynamicMenu();
365 
366 
368 
369  EnvironmentObject* getOwner() { return owner; };
371 
372 // EnvironmentObject* getInputOuputEO();
373  // Get the EO used
374 
375 
376  //
377  // methods inherited from VRECKO
378  //
380  virtual void preInitialize();
382  virtual void postInitialize();
384  virtual bool loadXMLParameters(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *pParametersNode);
386  virtual void update();
387 
388 // virtual void processEvent(const std::string &strInputName, void *pValue);
389 // virtual void *processRequest(const std::string &strInputName, void *pValue);
390 
391 
392  // Sets the new menu items
393  bool setMenuString(const char *newMenuString);
394 
395  bool addMenuItem(MenuItem *parentItem, MenuItem *itemToAdd);
396  bool insertMenuItem(MenuItem *parentItem, MenuItem *prevSibling, MenuItem *itemToAdd);
397 
398  // Delete MenuItem including its children ("delete [...];" is called too)
399  bool deleteMenuItem(MenuItem *item);
400 
401  bool registerNewItemType(MenuItem *itemTemplate);
402 
403  MenuType getMenuType() { return menuType; }
404 
405  inline void setAttribute(const std::string &name, float value) { attributes[name] = value; }
406  inline float getAttribute(const std::string &name) { return attributes[name]; }
407 
408  void setSign(const std::string &name, const std::string &value);
409  osg::Node* getSignObject(const std::string &name);
410 
411  inline bool isMainButtonPressed() { return bMainButtonPressed; }
412  inline bool isSelectionLocked() { return isMainButtonPressed() || selectedItem; }
413 
414  bool showMenu(osg::Vec3 *_menuPos);
415  void hideMenu();
416 
417  inline osg::Vec3& getProjPointerPos() { return projPointerPos; }
418  inline osg::Vec3& getLastPointerPos() { return lastPointerPos; }
419 
420  void sendToClickedOutput(char *eventValue);
421  // This is for use by MenuItem-s.
422  // [eventValue] can be NULL
423  // The [eventValue] will be copied (new memory will be created) and sent away
424 
425  protected:
427 
428  std::string baseDirectory;
429  // Base menu directory (from where are signs searched for instance).
430  // Is determined automatically from the .xml menu file path, but can be
431  // overridden in this file.
432 
434 
435  osg::Vec3 origMenuPos, origViewPos, origViewUp, origViewLookAt;
436  // The position of the menu.
437 // osg::Vec3 lastViewPos, lastViewDir;
438  // View position and direction from the last ViewMoved() call to detect whether a change occured.
439  osg::Vec3 autoMoveShift;
440  // If "AutoMoveWithView" attribute is not 0, then this vector is
441  // the difference between the current view position and the menu position.
442  // (It have to be rotated)
443  osg::ref_ptr<EnvironmentObject> mainObject;
444  osg::ref_ptr<EnvironmentObject> shadowEO, dispEO;
445  // [mainObject] is the main object containing all menu items
446  // [shadowEO] is inserted into the scene with its child being [mainObject].
447  // It's rendered after all other objects (excluding [shadowEO]) to prepare the Z-buffer
448  // [dispEO] is inserted into the scene with its child also being [mainObject].
449  // It's rendred after [shadowEO] to fially render the geometry.
451  // Matrix inverse to the transform matrix of the mainObject.
452  // It's calculated in UpdateMainObjectTransformation().
453 
454  osg::Vec3 lastPointerPos;
455  // last pointer position
456  osg::Vec3 lastViewPos;
457  // last view position
458  osg::Vec3 transPointerPos;
459  // pointer position transformed into object coordinates
460  osg::Vec3 projPointerPos;
461  // pointer position projected to the menu plane (X-Y)
462  osg::Vec3 transViewPos;
463  // view position transformed into object coordinates
464 
466  // the current state of the main button
468  // If true, the user will select an item with the button release even if the
469  // selection is not locked. (Useful after the start of the menu caused by
470  // a button press).
471 
472 // std::string strPointerPosReq, strClickedOutput;
473 
475 
476 // osg::Node * loaded_model;
477 
478  std::string menuString;
479  // may be either XML string describing the menu ('... <menu> ... </menu> ...') or
480  // a .xml file name preceded by an asterisk ('*C:\mymenu.xml')
481 
482  std::map<std::string, float> attributes;
483  // Some common attributes are:
484  // ItemSize (> 0.0) ... the size of an item (also dependent on the MenuType)
485  // ItemSize2 (> 0.0) ... "second size" of an item (on Y coordinate usually)
486  // AutoExpand (0 / 1) ... expand the menu items after selecting by pointer?
487  // AutoRotateToView (0 / 1) ... rotate automatically (like a billboard)
488  // AutoMoveWithView (0 / 1) ... move automatically so it remains on the same screen position
489  // AutoSameDistance (0 / 1) ... the menu will maintain the same distance from the viewer
490  // (useful if AutoRotate is specified, but AutoMove is not)
491  // AlwaysOnTop (0 / 1) ... the menu will be on top of any other graphics
492  // ForceMenuDistance
493  // 0.0 or less - no forced distance, the menu will be displayed where the user will request
494  // more than 0.0 - the menu will be displayed in the direction of user click, but with the specified distance
495  // MenuScale ... scales the whole menu component
496 
497 
498  std::map<std::string, DynamicMenuSign*> signs;
499  // Graphical signs
500 
502 
504  // an item that is selected by the pointer
506  // root of the visible part of the menu (the root item itself is not visible)
507 
509  // the KEY variable. It determines whether the menu is in "normal" state (selectedItem = NULL) or
510  // the menu item has been already selected and the final confirmation effect is being played
511  // (selectedItem != NULL). In the latter case, the menu is irresponsive to the user acions.
512 
513  // Variables for the temporary cylinder directing from the menu center to the pointer
514  osg::ref_ptr<osg::MatrixTransform> osgPointerTrailTransform;
515  osg::ref_ptr<osg::Geode> osgPointerTrailGeode;
516  osg::ref_ptr<osg::Cylinder> osgPointerTrail;
517  osg::ref_ptr<osg::ShapeDrawable> osgPointerTrailDrawable;
518 
519  bool mainButtonPressed();
520  bool mainButtonReleased();
521 
522  bool showChildMenu(MenuItem* parentItem, bool bSetNewRoot);
523  void hideChildMenu(MenuItem* parentItem = NULL, bool bIncludeSubMenus = true);
524  bool isVisible();
525 
526  bool showMainMenuObject();
527  void hideMainMenuObject();
528 
529  void updatePointerPosition();
530 
531  void updateMainObjectTransformation();
532 
533  void updateInverseCalc();
534 
535  bool pointerMoved(osg::Vec3 *pointerPos);
536  bool viewMoved();
537  bool pointerViewMovedHelper();
538 
539  bool createMenuFromString();
540  bool createMenuFromNode(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* paramNode);
541 
542  bool loadMenuStructureFromNode (XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* paramNode);
543  bool loadSignsFromXML(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* attribsNode);
544  bool loadAttributes(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* attribsNode);
545  bool loadMenuItems(MenuItem *parentItem, XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* parentNode);
546  void destroyMenuStructure();
547 
548  bool createMainObjects();
549  void destroyMainObjects();
550 
551  bool createPointerTrail();
552  void destroyPointerTrail();
553  void showPointerTrail();
554  void hidePointerTrail();
555 
556  bool pointerUnselectItem(bool bLeaveSelectedIfLocked = true);
557  // [bLeaveSelectedIfLocked] will cause the item to remain selected
558  // if the menu button is down (so the menu item is "locked")
559  bool pointerSelectItem(MenuItem *item);
560  bool solvePointerSelection();
561  // Search for the menu item that is selected
562  MenuItem* getPointedItemRecursive(MenuItem *parent, int *depth);
563  // Retreive the pointed MenuItem with the maximum depth.
564  // [depth] is the depth of the parent item at the call of the function
565  // and depth of the pointed item at the finish of the function.
566  // (The [parent] won't be tested)
567 
568  bool expandItem(MenuItem *item);
569  bool collapseItem(MenuItem *item);
570  bool collapseSiblings(MenuItem *item);
571 
572  void itemClicked(MenuItem *item);
573 
574  void confirmAnimationEnded();
575 
576  void resetBaseValues();
577  void initStandardSignSet();
578  void changeToStructureOccured();
579 
580 
581  bool unselectItem();
582  // Unselects a selected item (if there is any). Also stops its effect if it is being played
583  // and resets is position back to normal.
584 
585 
586  int menuGrid_columnsCached, menuGrid_rowsCached;
587  // If either of the values is equal to -1, then the values will
588  // be recalculated in the menuGrid_getSize() method. Otherwise the
589  // method just returns the cached values.
590  void menuGrid_getSize(int *columns, int *rows);
591  // This method will return both values greater than 0 in every case.
592 
593  DECLARE_INPUT(Create, MessageString);
594  DECLARE_INPUT(ButtonPress, MessageBool);
595  DECLARE_INPUT(ConfirmAnimationEnded, MessageBool);
596 
597  DECLARE_OUTPUT(Clicked, MessageString);
598  DECLARE_OUTPUT(HidingMenu, MessageBool);
599 
600  DECLARE_REQUEST_INPUT(ActivePointerID, MessageInt, MessageInt);
601 
602  DECLARE_REQUEST_OUTPUT(PointerPosition, MessageInt, MessageVec3);
603  };
604 
605 }
606 
607 
608 
609 #endif