From 1e81e656ab3935547de4c3a4c71b9c6f67e4e3da Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 27 Aug 2005 00:15:54 +0000 Subject: [PATCH] PGItemNotify interface, PGScrollFrame, and more general PGSliderBar --- direct/src/gui/DirectButton.py | 1 + direct/src/gui/DirectGui.py | 4 +- direct/src/gui/DirectGuiBase.py | 21 +- direct/src/gui/DirectGuiGlobals.py | 6 + direct/src/gui/DirectScrollBar.py | 123 +++ direct/src/gui/DirectScrolledFrame.py | 95 ++ direct/src/gui/DirectSlider.py | 113 +++ direct/src/gui/DirectSliderBar.py | 147 ---- panda/src/collide/collisionEntry.cxx | 6 +- panda/src/collide/collisionPlane.cxx | 5 +- panda/src/collide/collisionPolygon.cxx | 24 +- panda/src/collide/collisionVisualizer.cxx | 4 +- panda/src/mathutil/Sources.pp | 12 +- .../boundingPlane.I} | 39 +- panda/src/mathutil/boundingPlane.cxx | 153 ++++ panda/src/mathutil/boundingPlane.h | 86 ++ panda/src/mathutil/boundingVolume.cxx | 47 + panda/src/mathutil/boundingVolume.h | 6 + panda/src/mathutil/config_mathutil.cxx | 2 + panda/src/mathutil/mathutil_composite1.cxx | 1 + panda/src/mathutil/plane_src.I | 22 +- panda/src/mathutil/plane_src.cxx | 2 +- panda/src/mathutil/plane_src.h | 14 +- panda/src/pgraph/Sources.pp | 3 + panda/src/pgraph/clipPlaneAttrib.cxx | 77 ++ panda/src/pgraph/clipPlaneAttrib.h | 1 + panda/src/pgraph/cullPlanes.I | 68 ++ panda/src/pgraph/cullPlanes.cxx | 218 +++++ panda/src/pgraph/cullPlanes.h | 77 ++ panda/src/pgraph/cullTraverser.cxx | 3 +- panda/src/pgraph/cullTraverserData.I | 42 +- panda/src/pgraph/cullTraverserData.cxx | 165 +++- panda/src/pgraph/cullTraverserData.h | 11 +- panda/src/pgraph/modelNode.I | 31 +- panda/src/pgraph/modelNode.cxx | 21 + panda/src/pgraph/modelNode.h | 5 + panda/src/pgraph/pandaNode.I | 74 +- panda/src/pgraph/pandaNode.cxx | 31 + panda/src/pgraph/pandaNode.h | 10 + panda/src/pgraph/pgraph_composite2.cxx | 1 + panda/src/pgraph/planeNode.cxx | 3 +- panda/src/pgraph/planeNode.h | 2 +- panda/src/pgraph/renderState.I | 19 + panda/src/pgraph/renderState.cxx | 16 + panda/src/pgraph/renderState.h | 5 + panda/src/pgui/Sources.pp | 30 +- panda/src/pgui/config_pgui.cxx | 26 +- panda/src/pgui/config_pgui.h | 3 + panda/src/pgui/pgButton.I | 26 + panda/src/pgui/pgButton.cxx | 18 +- panda/src/pgui/pgButton.h | 11 +- panda/src/pgui/pgButtonNotify.I | 27 + panda/src/pgui/pgButtonNotify.cxx | 29 + panda/src/pgui/pgButtonNotify.h | 45 + panda/src/pgui/pgFrameStyle.I | 47 +- panda/src/pgui/pgFrameStyle.cxx | 54 +- panda/src/pgui/pgFrameStyle.h | 7 + panda/src/pgui/pgItem.I | 86 +- panda/src/pgui/pgItem.cxx | 421 ++++++++- panda/src/pgui/pgItem.h | 33 +- panda/src/pgui/pgItemNotify.I | 27 + panda/src/pgui/pgItemNotify.cxx | 202 +++++ panda/src/pgui/pgItemNotify.h | 67 ++ panda/src/pgui/pgScrollFrame.I | 245 ++++++ panda/src/pgui/pgScrollFrame.cxx | 445 ++++++++++ panda/src/pgui/pgScrollFrame.h | 137 +++ panda/src/pgui/pgSliderBar.I | 494 +++++++---- panda/src/pgui/pgSliderBar.cxx | 809 ++++++++++++++---- panda/src/pgui/pgSliderBar.h | 140 +-- panda/src/pgui/pgSliderBarNotify.I | 27 + panda/src/pgui/pgSliderBarNotify.cxx | 40 + panda/src/pgui/pgSliderBarNotify.h | 46 + panda/src/pgui/pgSliderButton.cxx | 94 -- panda/src/pgui/pgSliderButton.h | 77 -- panda/src/pgui/pgTop.I | 23 +- panda/src/pgui/pgTop.h | 3 +- panda/src/pgui/pgVirtualFrame.I | 104 +++ panda/src/pgui/pgVirtualFrame.cxx | 241 ++++++ panda/src/pgui/pgVirtualFrame.h | 112 +++ panda/src/pgui/pgWaitBar.cxx | 6 +- panda/src/pgui/pgui_composite1.cxx | 7 +- panda/src/pgui/pgui_composite2.cxx | 10 +- panda/src/putil/bam.h | 3 +- panda/src/tform/Sources.pp | 2 +- panda/src/tform/mouseWatcher.cxx | 132 +-- panda/src/tform/mouseWatcher.h | 10 +- panda/src/tform/mouseWatcherGroup.cxx | 210 ++++- panda/src/tform/mouseWatcherGroup.h | 29 +- 88 files changed, 5429 insertions(+), 992 deletions(-) create mode 100644 direct/src/gui/DirectScrollBar.py create mode 100644 direct/src/gui/DirectScrolledFrame.py create mode 100644 direct/src/gui/DirectSlider.py delete mode 100755 direct/src/gui/DirectSliderBar.py rename panda/src/{pgui/pgSliderButton.I => mathutil/boundingPlane.I} (64%) mode change 100755 => 100644 create mode 100644 panda/src/mathutil/boundingPlane.cxx create mode 100644 panda/src/mathutil/boundingPlane.h create mode 100644 panda/src/pgraph/cullPlanes.I create mode 100644 panda/src/pgraph/cullPlanes.cxx create mode 100644 panda/src/pgraph/cullPlanes.h create mode 100644 panda/src/pgui/pgButtonNotify.I create mode 100644 panda/src/pgui/pgButtonNotify.cxx create mode 100644 panda/src/pgui/pgButtonNotify.h create mode 100644 panda/src/pgui/pgItemNotify.I create mode 100644 panda/src/pgui/pgItemNotify.cxx create mode 100644 panda/src/pgui/pgItemNotify.h create mode 100644 panda/src/pgui/pgScrollFrame.I create mode 100644 panda/src/pgui/pgScrollFrame.cxx create mode 100644 panda/src/pgui/pgScrollFrame.h create mode 100644 panda/src/pgui/pgSliderBarNotify.I create mode 100644 panda/src/pgui/pgSliderBarNotify.cxx create mode 100644 panda/src/pgui/pgSliderBarNotify.h delete mode 100755 panda/src/pgui/pgSliderButton.cxx delete mode 100755 panda/src/pgui/pgSliderButton.h create mode 100644 panda/src/pgui/pgVirtualFrame.I create mode 100644 panda/src/pgui/pgVirtualFrame.cxx create mode 100644 panda/src/pgui/pgVirtualFrame.h diff --git a/direct/src/gui/DirectButton.py b/direct/src/gui/DirectButton.py index 0c7da54bee..23463e733e 100644 --- a/direct/src/gui/DirectButton.py +++ b/direct/src/gui/DirectButton.py @@ -28,6 +28,7 @@ class DirectButton(DirectFrame): ('pgFunc', PGButton, None), ('numStates', 4, None), ('state', NORMAL, None), + ('relief', RAISED, None), ('invertedFrames', (1,), None), # Command to be called on button click ('command', None, None), diff --git a/direct/src/gui/DirectGui.py b/direct/src/gui/DirectGui.py index 5bf2c48a07..093ab7c664 100644 --- a/direct/src/gui/DirectGui.py +++ b/direct/src/gui/DirectGui.py @@ -17,6 +17,8 @@ from DirectLabel import * from DirectScrolledList import * from DirectDialog import * from DirectWaitBar import * -from DirectSliderBar import * +from DirectSlider import * +from DirectScrollBar import * +from DirectScrolledFrame import * from DirectCheckButton import * from DirectOptionMenu import * diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index e7748f52a5..2afef7ebcc 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -685,6 +685,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath): ('frameSize', None, self.setFrameSize), ('frameColor', (.8,.8,.8,1), self.setFrameColor), ('frameTexture', None, self.setFrameTexture), + ('frameVisibleScale', (1,1), self.setFrameVisibleScale), ('pad', (0,0), self.resetFrameSize), # Override button id (beware! your name may not be unique!) ('guiId', None, INITOPT), @@ -866,6 +867,8 @@ class DirectGuiWidget(DirectGuiBase, NodePath): # Use user specified bounds self.bounds = self['frameSize'] #print "%s bounds = %s" % (self.getName(),self.bounds) + bw = (0,0) + else: if fClearFrame and (frameType != PGFrameStyle.TNone): self.frameStyle[0].setType(PGFrameStyle.TNone) @@ -878,11 +881,13 @@ class DirectGuiWidget(DirectGuiBase, NodePath): if (frameType != PGFrameStyle.TNone): self.frameStyle[0].setType(frameType) self.guiItem.setFrameStyle(0, self.frameStyle[0]) - if ((frameType != PGFrameStyle.TNone) and - (frameType != PGFrameStyle.TFlat)): - bw = self['borderWidth'] - else: - bw = (0,0) + + if ((frameType != PGFrameStyle.TNone) and + (frameType != PGFrameStyle.TFlat)): + bw = self['borderWidth'] + else: + bw = (0,0) + # Set frame to new dimensions self.guiItem.setFrame( self.bounds[0] - bw[0], @@ -974,6 +979,12 @@ class DirectGuiWidget(DirectGuiBase, NodePath): self.frameStyle[i].setTexture(texture) self.updateFrameStyle() + def setFrameVisibleScale(self): + scale = self['frameVisibleScale'] + for i in range(self['numStates']): + self.frameStyle[i].setVisibleScale(scale[0], scale[1]) + self.updateFrameStyle() + def setBorderWidth(self): width = self['borderWidth'] for i in range(self['numStates']): diff --git a/direct/src/gui/DirectGuiGlobals.py b/direct/src/gui/DirectGuiGlobals.py index 9576a31632..cda43c0a1a 100644 --- a/direct/src/gui/DirectGuiGlobals.py +++ b/direct/src/gui/DirectGuiGlobals.py @@ -29,6 +29,10 @@ FrameStyleDict = {'flat' : FLAT, 'raised' : RAISED, 'sunken': SUNKEN, 'groove' : GROOVE, 'ridge' : RIDGE } +# Orientation of DirectSlider and DirectScrollBar +HORIZONTAL = 'horizontal' +VERTICAL = 'vertical' + # Dialog button values DIALOG_NO = 0 DIALOG_OK = DIALOG_YES = DIALOG_RETRY = 1 @@ -55,6 +59,8 @@ OVERFLOW = PGEntry.getOverflowPrefix() ACCEPT = PGEntry.getAcceptPrefix() + KeyboardButton.enter().getName() + '-' TYPE = PGEntry.getTypePrefix() ERASE = PGEntry.getErasePrefix() +# For DirectSlider and DirectScrollBar widgets +ADJUST = PGSliderBar.getAdjustPrefix() # For setting the sorting order of a widget's visible components diff --git a/direct/src/gui/DirectScrollBar.py b/direct/src/gui/DirectScrollBar.py new file mode 100644 index 0000000000..a0c29bb98c --- /dev/null +++ b/direct/src/gui/DirectScrollBar.py @@ -0,0 +1,123 @@ +from DirectFrame import * +from DirectButton import * +from DirectGuiBase import _OPT_VALUE + +""" +import DirectScrollBar +d = DirectScrollBar(borderWidth=(0,0)) + +""" + +class DirectScrollBar(DirectFrame): + """ + DirectScrollBar -- a widget which represents a scroll bar the user can + use for paging through a large document or panel. + """ + def __init__(self, parent = None, **kw): + optiondefs = ( + # Define type of DirectGuiWidget + ('pgFunc', PGSliderBar, None), + ('state', NORMAL, None), + ('frameColor', (0.6,0.6,0.6,1), None), + + ('range', (0, 1), self.setRange), + ('value', 0, self.__setValue), + ('pageSize', 0.1, self.setPageSize), + ('orientation', HORIZONTAL, self.setOrientation), + ('manageButtons', 1, self.setManageButtons), + ('resizeThumb', 1, self.setResizeThumb), + + # Function to be called repeatedly as the bar is scrolled + ('command', None, None), + ('extraArgs', [], None), + ) + + if kw.get('orientation') == VERTICAL: + # These are the default options for a vertical layout. + optiondefs += ( + ('frameSize', (-0.04,0.04,-0.5,0.5), None), + ) + else: + # These are the default options for a horizontal layout. + optiondefs += ( + ('frameSize', (-0.5,0.5,-0.04,0.04), None), + ) + + # Merge keyword options with default options + self.defineoptions(kw, optiondefs) + + # Initialize superclasses + DirectFrame.__init__(self, parent) + + self.thumb = self.createcomponent( + "thumb", (), None, + DirectButton, (self,), + borderWidth = self['borderWidth']) + self.incButton = self.createcomponent( + "incButton", (), None, + DirectButton, (self,), + borderWidth = self['borderWidth']) + self.decButton = self.createcomponent( + "decButton", (), None, + DirectButton, (self,), + borderWidth = self['borderWidth']) + + self.guiItem.setThumbButton(self.thumb.guiItem) + self.guiItem.setLeftButton(self.decButton.guiItem) + self.guiItem.setRightButton(self.incButton.guiItem) + + # Bind command function + self.bind(ADJUST, self.commandFunc) + + # Call option initialization functions + self.initialiseoptions(DirectScrollBar) + + def setRange(self): + # Try to preserve the value across a setRange call. + v = self['value'] + r = self['range'] + self.guiItem.setRange(r[0], r[1]) + self['value'] = v + + def __setValue(self): + # This is the internal function that is called when + # self['value'] is directly assigned. + self.guiItem.setValue(self['value']) + + def setValue(self, value): + # This is the public function that is meant to be called by a + # user that doesn't like to use (or doesn't understand) the + # preferred interface of self['value']. + self['value'] = value + + def getValue(self): + return self.guiItem.getValue() + + def getRatio(self): + return self.guiItem.getRatio() + + def setPageSize(self): + self.guiItem.setPageSize(self['pageSize']) + + def setOrientation(self): + if self['orientation'] == HORIZONTAL: + self.guiItem.setAxis(Vec3(1, 0, 0)) + else: + self.guiItem.setAxis(Vec3(0, 0, -1)) + + def setManageButtons(self): + self.guiItem.setManagePieces(self['manageButtons']) + + def setResizeThumb(self): + self.guiItem.setResizeThumb(self['resizeThumb']) + + def destroy(self): + DirectFrame.destroy(self) + + def commandFunc(self): + # Store the updated value in self['value'] + self._optionInfo['value'][_OPT_VALUE] = self.guiItem.getValue() + + if self['command']: + apply(self['command'], self['extraArgs']) + diff --git a/direct/src/gui/DirectScrolledFrame.py b/direct/src/gui/DirectScrolledFrame.py new file mode 100644 index 0000000000..56208f6e35 --- /dev/null +++ b/direct/src/gui/DirectScrolledFrame.py @@ -0,0 +1,95 @@ +from DirectFrame import * +from DirectScrollBar import * + +""" +import DirectScrolledFrame +d = DirectScrolledFrame(borderWidth=(0,0)) + +""" + +class DirectScrolledFrame(DirectFrame): + """ + DirectScrolledFrame -- a special frame that uses DirectScrollBar to + implement a small window (the frameSize) into a potentially much + larger virtual canvas (the canvasSize, scrolledFrame.getCanvas()). + + Unless specified otherwise, scroll bars are automatically created + and managed as needed, based on the relative sizes od the + frameSize and the canvasSize. You can also set manageScrollBars = + 0 and explicitly position and hide or show the scroll bars + yourself. + """ + def __init__(self, parent = None, **kw): + optiondefs = ( + # Define type of DirectGuiWidget + ('pgFunc', PGScrollFrame, None), + ('frameSize', (-0.5,0.5,-0.5,0.5), None), + + ('canvasSize', (-1,1,-1,1), self.setCanvasSize), + ('manageScrollBars', 1, self.setManageScrollBars), + ('autoHideScrollBars', 1, self.setAutoHideScrollBars), + ('scrollBarWidth', 0.08, None), + ) + + # Merge keyword options with default options + self.defineoptions(kw, optiondefs) + + # Initialize superclasses + DirectFrame.__init__(self, parent) + + # The scrollBarWidth parameter is just used at scroll bar + # construction time, and supplies a default frame. It does + # not override an explicit frame specified on the scroll bar. + # If you want to change the frame width after construction, + # you must specify their frameSize tuples explicitly. + w = self['scrollBarWidth'] + + self.verticalScroll = self.createcomponent( + "verticalScroll", (), None, + DirectScrollBar, (self,), + borderWidth = self['borderWidth'], + frameSize = (-w / 2.0, w / 2.0, -1, 1), + orientation = VERTICAL) + + self.horizontalScroll = self.createcomponent( + "horizontalScroll", (), None, + DirectScrollBar, (self,), + borderWidth = self['borderWidth'], + frameSize = (-1, 1, -w / 2.0, w / 2.0), + orientation = HORIZONTAL) + + self.guiItem.setVerticalSlider(self.verticalScroll.guiItem) + self.guiItem.setHorizontalSlider(self.horizontalScroll.guiItem) + + self.canvas = NodePath(self.guiItem.getCanvasNode()) + + # Call option initialization functions + self.initialiseoptions(DirectScrolledFrame) + + def setCanvasSize(self): + f = self['canvasSize'] + self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3]) + + def getCanvas(self): + return self.canvas + + def setManageScrollBars(self): + self.guiItem.setManagePieces(self['manageScrollBars']) + + def setAutoHideScrollBars(self): + self.guiItem.setAutoHide(self['autoHideScrollBars']) + + def destroy(self): + DirectFrame.destroy(self) + + def commandFunc(self): + if self['command']: + apply(self['command'], self['extraArgs']) + + def destroy(self): + # Destroy children of the canvas + for child in self.canvas.getChildrenAsList(): + childGui = self.guiDict.get(child.getName()) + if childGui: childGui.destroy() + DirectFrame.DirectFrame.destroy(self) + diff --git a/direct/src/gui/DirectSlider.py b/direct/src/gui/DirectSlider.py new file mode 100644 index 0000000000..8891242dfe --- /dev/null +++ b/direct/src/gui/DirectSlider.py @@ -0,0 +1,113 @@ +from DirectFrame import * +from DirectButton import * +from DirectGuiBase import _OPT_VALUE + +""" +import DirectSlider +d = DirectSlider(borderWidth=(0,0)) + +""" + +class DirectSlider(DirectFrame): + """ + DirectSlider -- a widget which represents a slider that the + user can pull left and right to represent a continuous value. + """ + def __init__(self, parent = None, **kw): + optiondefs = ( + # Define type of DirectGuiWidget + ('pgFunc', PGSliderBar, None), + ('state', NORMAL, None), + ('frameColor', (0.6,0.6,0.6,1), None), + + ('range', (0, 1), self.setRange), + ('value', 0, self.__setValue), + ('pageSize', 0.1, self.setPageSize), + ('orientation', HORIZONTAL, self.setOrientation), + + # Function to be called repeatedly as slider is moved + ('command', None, None), + ('extraArgs', [], None), + ) + + if kw.get('orientation') == VERTICAL: + # These are the default options for a vertical layout. + optiondefs += ( + ('frameSize', (-0.08,0.08,-1,1), None), + ('frameVisibleScale', (0.25,1), None), + ) + else: + # These are the default options for a horizontal layout. + optiondefs += ( + ('frameSize', (-1,1,-0.08,0.08), None), + ('frameVisibleScale', (1,0.25), None), + ) + + # Merge keyword options with default options + self.defineoptions(kw, optiondefs) + + # Initialize superclasses + DirectFrame.__init__(self, parent) + + self.thumb = self.createcomponent("thumb", (), None, + DirectButton, (self,), + borderWidth = self['borderWidth']) + if self.thumb['frameSize'] == None and \ + self.thumb.bounds == [0.0, 0.0, 0.0, 0.0]: + # Compute a default frameSize for the thumb. + f = self['frameSize'] + if self['orientation'] == HORIZONTAL: + self.thumb['frameSize'] = (f[0]*0.05,f[1]*0.05,f[2],f[3]) + else: + self.thumb['frameSize'] = (f[0],f[1],f[2]*0.05,f[3]*0.05) + + self.guiItem.setThumbButton(self.thumb.guiItem) + + # Bind command function + self.bind(ADJUST, self.commandFunc) + + # Call option initialization functions + self.initialiseoptions(DirectSlider) + + def setRange(self): + # Try to preserve the value across a setRange call. + v = self['value'] + r = self['range'] + self.guiItem.setRange(r[0], r[1]) + self['value'] = v + + def __setValue(self): + # This is the internal function that is called when + # self['value'] is directly assigned. + self.guiItem.setValue(self['value']) + + def setValue(self, value): + # This is the public function that is meant to be called by a + # user that doesn't like to use (or doesn't understand) the + # preferred interface of self['value']. + self['value'] = value + + def getValue(self): + return self.guiItem.getValue() + + def getRatio(self): + return self.guiItem.getRatio() + + def setPageSize(self): + self.guiItem.setPageSize(self['pageSize']) + + def setOrientation(self): + if self['orientation'] == HORIZONTAL: + self.guiItem.setAxis(Vec3(1, 0, 0)) + else: + self.guiItem.setAxis(Vec3(0, 0, 1)) + + def destroy(self): + DirectFrame.destroy(self) + + def commandFunc(self): + # Store the updated value in self['value'] + self._optionInfo['value'][_OPT_VALUE] = self.guiItem.getValue() + + if self['command']: + apply(self['command'], self['extraArgs']) diff --git a/direct/src/gui/DirectSliderBar.py b/direct/src/gui/DirectSliderBar.py deleted file mode 100755 index 6eef08a3d9..0000000000 --- a/direct/src/gui/DirectSliderBar.py +++ /dev/null @@ -1,147 +0,0 @@ -from DirectButton import * -from DirectFrame import * - -""" -import DirectSliderBar -d = DirectSliderBar(borderWidth=(0,0)) - -""" - -class DirectSliderButton(DirectButton): - def __init__(self, parent = None, **kw): - optiondefs = ( - # Define type of DirectGuiWidget - ('pgFunc', PGSliderButton, None), - ) - # Merge keyword options with default options - self.defineoptions(kw, optiondefs) - # Initialize superclasses - DirectButton.__init__(self,parent) - # Call option initialization functions - self.initialiseoptions(DirectSliderButton) - -class DirectSliderBar(DirectFrame): - """ - DirectEntry(parent) - Create a DirectGuiWidget which responds - to keyboard buttons - """ - def __init__(self, parent = None, **kw): - # Inherits from DirectFrame - # A Direct Frame can have: - # - A background texture (pass in path to image, or Texture Card) - # - A midground geometry item (pass in geometry) - # - A foreground text Node (pass in text string or Onscreen Text) - optiondefs = ( - # Define type of DirectGuiWidget - ('pgFunc', PGSliderBar, None), - ('width', 10, None), - ('height', 1, None), - ('button', None, None), - ('sliderOnly', 0, self.setSliderOnly), - ('negativeMapping',0, self.setNegativeMapping), - ('range', 100, self.setRange), - ('value', 0, self.setValue), - ('barBorderWidth', (0,0), self.setBarBorderWidth), - ('barColor', (1,0,0,1), self.setBarColor), - ('barRelief', FLAT, self.setBarRelief), - ('active', 0, self.setActive), - ('sortOrder', NO_FADE_SORT_INDEX, None), - # Command to be called on button movement - ('command', None, None), - ('extraArgs', [], None), - ) - if kw.has_key('text'): - textoptiondefs = ( - ('text_pos', (0,-0.025), None), - ('text_scale', 0.1, None), - ('text_fg', (1,1,1,1), None), - ('text_shadow', (0,0,0,1), None), - ('text_align', TextNode.ALeft, None) - ) - else: - textoptiondefs = () - # Merge keyword options with default options - self.defineoptions(kw, optiondefs + textoptiondefs) - # Initialize superclasses - DirectFrame.__init__(self, parent) - self.barStyle = PGFrameStyle() - # Call option initialization functions - self.initialiseoptions(DirectSliderBar) - - if (self['button'] != None): - self.guiItem.setSliderButton(self['button'], self['button'].guiItem) - - if (self['image'] != None): - self.guiItem.setScale(self['image_scale'][0]) - self.guiItem.setup(self.getWidth(), self.getHeight(), self['range']) - else: - #figure out what is happening????? - #self.guiItem.setState(0) - #self.guiItem.clearStateDef(0) - self.guiItem.setFrame(-3.0, 3.0, -0.125, 0.125) - self.barStyle.setWidth(0.05, 0.05) - self.barStyle.setColor(0.6,0.6,0.6,1) - self.barStyle.setType(PGFrameStyle.TBevelIn) - self.guiItem.setFrameStyle(0, self.barStyle) - self.guiItem.setScale(2) - self.guiItem.setup(6, 0.5, self['range']) - self.guiItem.setValue(self['value']) - if (self['scale'] != None): - self.setScale(self['scale']) - else: - self.setScale(0.1) - - self.guiItem.setActive(1) - - #self.barStyle.setColor(0.8,0.8,0.8,1) - #self.barStyle.setType(PGFrameStyle.TBevelOut) - #self.updateBarStyle() - - if (self['command'] != None): - # Attach command function to slider button movement - self.bind('updated-slider-', self.commandFunc) - - def destroy(self): - del self.barStyle - DirectFrame.destroy(self) - - def setSliderOnly(self): - self.guiItem.setSliderOnly(self['sliderOnly']) - - def setNegativeMapping(self): - self.guiItem.setNegativeMapping(self['negativeMapping']) - - def setRange(self): - self.guiItem.setRange(self['range']) - - def setValue(self): - self.guiItem.setValue(self['value']) - - def getPercent(self): - return self.guiItem.getPercent() - - def updateBarStyle(self): - if not self.fInit: - self.guiItem.setBarStyle(self.barStyle) - - def setBarRelief(self): - self.barStyle.setType(self['barRelief']) - self.updateBarStyle() - - def setBarBorderWidth(self): - self.barStyle.setWidth(*self['barBorderWidth']) - self.updateBarStyle() - - def setBarColor(self): - color = self['barColor'] - self.barStyle.setColor(color[0], color[1], color[2], color[3]) - self.updateBarStyle() - - def setActive(self): - self.guiItem.setActive(self['active']) - - def commandFunc(self): - if self['command']: - # Pass any extra args to command - apply(self['command'], self['extraArgs']) - diff --git a/panda/src/collide/collisionEntry.cxx b/panda/src/collide/collisionEntry.cxx index 7208103fff..5bed0ed06b 100644 --- a/panda/src/collide/collisionEntry.cxx +++ b/panda/src/collide/collisionEntry.cxx @@ -230,10 +230,6 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// void CollisionEntry:: check_clip_planes() { - const RenderAttrib *cpa_attrib = - _into_node_path.get_net_state()->get_attrib(ClipPlaneAttrib::get_class_type()); - if (cpa_attrib != (const RenderAttrib *)NULL) { - _into_clip_planes = DCAST(ClipPlaneAttrib, cpa_attrib); - } + _into_clip_planes = _into_node_path.get_net_state()->get_clip_plane(); _flags |= F_checked_clip_planes; } diff --git a/panda/src/collide/collisionPlane.cxx b/panda/src/collide/collisionPlane.cxx index ed21dc0a30..4c192071de 100644 --- a/panda/src/collide/collisionPlane.cxx +++ b/panda/src/collide/collisionPlane.cxx @@ -32,7 +32,7 @@ #include "datagramIterator.h" #include "bamReader.h" #include "bamWriter.h" -#include "omniBoundingVolume.h" +#include "boundingPlane.h" #include "geom.h" #include "geomTrifans.h" #include "geomLinestrips.h" @@ -94,11 +94,10 @@ output(ostream &out) const { //////////////////////////////////////////////////////////////////// BoundingVolume *CollisionPlane:: recompute_bound() { - // Planes always have an infinite bounding volume. BoundedObject::recompute_bound(); // Less than ideal: we throw away whatever we just allocated in // BoundedObject. - return set_bound_ptr(new OmniBoundingVolume); + return set_bound_ptr(new BoundingPlane(_plane)); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/collide/collisionPolygon.cxx b/panda/src/collide/collisionPolygon.cxx index 59d49274e3..91123028f7 100644 --- a/panda/src/collide/collisionPolygon.cxx +++ b/panda/src/collide/collisionPolygon.cxx @@ -279,9 +279,8 @@ get_collision_origin() const { PT(PandaNode) CollisionPolygon:: get_viz(const CullTraverser *trav, const CullTraverserData &data, bool bounds_only) const { - const RenderAttrib *cpa_attrib = - data._state->get_attrib(ClipPlaneAttrib::get_class_type()); - if (cpa_attrib == (const RenderAttrib *)NULL) { + const ClipPlaneAttrib *cpa = data._state->get_clip_plane(); + if (cpa == (const ClipPlaneAttrib *)NULL) { // Fortunately, the polygon is not clipped. This is the normal, // easy case. return CollisionSolid::get_viz(trav, data, bounds_only); @@ -289,7 +288,7 @@ get_viz(const CullTraverser *trav, const CullTraverserData &data, if (collide_cat.is_debug()) { collide_cat.debug() - << "drawing polygon with clip plane " << *cpa_attrib << "\n"; + << "drawing polygon with clip plane " << *cpa << "\n"; } // The polygon is clipped. We need to render it clipped. We could @@ -298,7 +297,6 @@ get_viz(const CullTraverser *trav, const CullTraverserData &data, // and clip it by hand instead, just to prove that our clipping // algorithm works properly. This does require some more dynamic // work. - const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, cpa_attrib); Points new_points; if (apply_clip_plane(new_points, cpa, data.get_net_transform(trav))) { // All points are behind the clip plane; just draw the original @@ -1093,15 +1091,21 @@ clip_polygon(CollisionPolygon::Points &new_points, const LPoint2f &this_point = (*pi)._p; bool this_is_in = !is_right(this_point - from2d, delta2d); - if (this_is_in != last_is_in) { + // There appears to be a compiler bug in gcc 4.0: we need to + // extract this comparison outside of the if statement. + bool crossed_over = (this_is_in != last_is_in); + if (crossed_over) { // We have just crossed over the clipping line. Find the point // of intersection. LVector2f d = this_point - last_point; - float t = -(a * last_point[0] + b * last_point[1] + c) / (a * d[0] + b * d[1]); - LPoint2f p = last_point + t * d; + float denom = (a * d[0] + b * d[1]); + if (denom != 0.0) { + float t = -(a * last_point[0] + b * last_point[1] + c) / denom; + LPoint2f p = last_point + t * d; - new_points.push_back(PointDef(p[0], p[1])); - last_is_in = this_is_in; + new_points.push_back(PointDef(p[0], p[1])); + last_is_in = this_is_in; + } } if (this_is_in) { diff --git a/panda/src/collide/collisionVisualizer.cxx b/panda/src/collide/collisionVisualizer.cxx index 8816448820..05cba65cf2 100644 --- a/panda/src/collide/collisionVisualizer.cxx +++ b/panda/src/collide/collisionVisualizer.cxx @@ -34,6 +34,7 @@ #include "depthOffsetAttrib.h" #include "colorScaleAttrib.h" #include "transparencyAttrib.h" +#include "clipPlaneAttrib.h" #ifdef DO_COLLISION_RECORDING @@ -137,7 +138,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { xform_data._modelview_transform = trav->get_world_transform(); xform_data.apply_transform_and_state(trav, net_transform, RenderState::make_empty(), - RenderEffects::make_empty()); + RenderEffects::make_empty(), + ClipPlaneAttrib::make()); // Draw all the collision solids. Solids::const_iterator si; diff --git a/panda/src/mathutil/Sources.pp b/panda/src/mathutil/Sources.pp index 6c700ca4b3..c86bbe1e84 100644 --- a/panda/src/mathutil/Sources.pp +++ b/panda/src/mathutil/Sources.pp @@ -12,7 +12,9 @@ #define SOURCES \ boundingHexahedron.I boundingHexahedron.h boundingLine.I \ - boundingLine.h boundingSphere.I boundingSphere.h \ + boundingLine.h \ + boundingPlane.I boundingPlane.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 \ @@ -24,7 +26,9 @@ plane_src.h rotate_to.h rotate_to_src.cxx #define INCLUDED_SOURCES \ - boundingHexahedron.cxx boundingLine.cxx boundingSphere.cxx \ + boundingHexahedron.cxx boundingLine.cxx \ + boundingPlane.cxx \ + boundingSphere.cxx \ boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx \ finiteBoundingVolume.cxx geometricBoundingVolume.cxx \ look_at.cxx \ @@ -33,7 +37,9 @@ #define INSTALL_HEADERS \ boundingHexahedron.I boundingHexahedron.h boundingLine.I \ - boundingLine.h boundingSphere.I boundingSphere.h boundingVolume.I \ + boundingLine.h \ + boundingPlane.I boundingPlane.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 \ diff --git a/panda/src/pgui/pgSliderButton.I b/panda/src/mathutil/boundingPlane.I old mode 100755 new mode 100644 similarity index 64% rename from panda/src/pgui/pgSliderButton.I rename to panda/src/mathutil/boundingPlane.I index e329c6bd2a..951e004f9c --- a/panda/src/pgui/pgSliderButton.I +++ b/panda/src/mathutil/boundingPlane.I @@ -1,5 +1,5 @@ -// Filename: pgButton.I -// Created by: masad (21Oct04) +// Filename: boundingPlane.I +// Created by: drose (19Aug05) // //////////////////////////////////////////////////////////////////// // @@ -16,32 +16,37 @@ // //////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::is_drag_n_drop +// Function: BoundingPlane::Default Constructor // Access: Published -// Description: Returns if this button is drag_n_drop kind +// Description: Constructs an empty "plane" that has no +// intersections. //////////////////////////////////////////////////////////////////// -INLINE bool PGSliderButton:: -is_drag_n_drop() { - return _drag_n_drop; +INLINE_MATHUTIL BoundingPlane:: +BoundingPlane() { } //////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::set_drag_n_drop +// Function: BoundingPlane::Constructor // Access: Published -// Description: Makes this button a drag_n_drop kind +// Description: //////////////////////////////////////////////////////////////////// -INLINE void PGSliderButton:: -set_drag_n_drop(bool value) { - _drag_n_drop = value; +INLINE_MATHUTIL BoundingPlane:: +BoundingPlane(const Planef &plane) : + _plane(plane) +{ + _flags = 0; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::set_slider_bar +// Function: BoundingPlane::get_plane // Access: Published -// Description: Store a copy of the controlling slider bar item +// Description: //////////////////////////////////////////////////////////////////// -INLINE void PGSliderButton:: -set_slider_bar(PGItem *item) { - _slider_bar = item; +INLINE_MATHUTIL const Planef &BoundingPlane:: +get_plane() const { + nassertr(!is_empty(), _plane); + nassertr(!is_infinite(), _plane); + return _plane; } diff --git a/panda/src/mathutil/boundingPlane.cxx b/panda/src/mathutil/boundingPlane.cxx new file mode 100644 index 0000000000..7a0f1bf99e --- /dev/null +++ b/panda/src/mathutil/boundingPlane.cxx @@ -0,0 +1,153 @@ +// Filename: boundingPlane.cxx +// Created by: drose (19Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "boundingPlane.h" +#include "boundingSphere.h" +#include "config_mathutil.h" + +TypeHandle BoundingPlane::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +BoundingVolume *BoundingPlane:: +make_copy() const { + return new BoundingPlane(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::get_approx_center +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +LPoint3f BoundingPlane:: +get_approx_center() const { + nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); + nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f)); + return _plane.get_point(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::xform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void BoundingPlane:: +xform(const LMatrix4f &mat) { + nassertv(!mat.is_nan()); + + if (!is_empty() && !is_infinite()) { + _plane.xform(mat); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void BoundingPlane:: +output(ostream &out) const { + if (is_empty()) { + out << "bplane, empty"; + } else if (is_infinite()) { + out << "bplane, infinite"; + } else { + out << "bplane: " << _plane; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::extend_other +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool BoundingPlane:: +extend_other(BoundingVolume *other) const { + return other->extend_by_plane(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::around_other +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool BoundingPlane:: +around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const { + return other->around_planes(first, last); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::contains_other +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int BoundingPlane:: +contains_other(const BoundingVolume *other) const { + return other->contains_plane(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::extend_by_plane +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool BoundingPlane:: +extend_by_plane(const BoundingPlane *plane) { + nassertr(!plane->is_empty() && !plane->is_infinite(), false); + nassertr(!is_infinite(), false); + + if (is_empty()) { + _plane = plane->get_plane(); + _flags = 0; + } else { + _flags = F_infinite; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingPlane::contains_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int BoundingPlane:: +contains_sphere(const BoundingSphere *sphere) const { + nassertr(!is_empty() && !is_infinite(), 0); + nassertr(!sphere->is_empty() && !sphere->is_infinite(), 0); + + float r = sphere->get_radius(); + float d = _plane.dist_to_plane(sphere->get_center()); + + if (d <= -r) { + // The sphere is completely behind the plane. + return IF_all | IF_possible | IF_some; + + } else if (d <= r) { + // The sphere is intersecting with the plane itself. + return IF_possible | IF_some; + + } else { + // The sphere is completely in front of the plane. + return IF_no_intersection; + } +} diff --git a/panda/src/mathutil/boundingPlane.h b/panda/src/mathutil/boundingPlane.h new file mode 100644 index 0000000000..f9590e4fca --- /dev/null +++ b/panda/src/mathutil/boundingPlane.h @@ -0,0 +1,86 @@ +// Filename: boundingPlane.h +// Created by: drose (19Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOUNDINGPLANE_H +#define BOUNDINGPLANE_H + +#include "pandabase.h" + +#include "geometricBoundingVolume.h" +#include "plane.h" + +//////////////////////////////////////////////////////////////////// +// Class : BoundingPlane +// Description : This funny bounding volume is an infinite plane that +// divides space into two regions: the part behind the +// normal, which is "inside" the bounding volume, and +// the part in front of the normal, which is "outside" +// the bounding volume. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BoundingPlane : public GeometricBoundingVolume { +PUBLISHED: + INLINE_MATHUTIL BoundingPlane(); + INLINE_MATHUTIL BoundingPlane(const Planef &plane); + +public: + virtual BoundingVolume *make_copy() const; + + virtual LPoint3f get_approx_center() const; + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + +PUBLISHED: + INLINE_MATHUTIL const Planef &get_plane() const; + +protected: + virtual bool extend_other(BoundingVolume *other) const; + virtual bool around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const; + virtual int contains_other(const BoundingVolume *other) const; + + virtual bool extend_by_plane(const BoundingPlane *plane); + + virtual int contains_sphere(const BoundingSphere *sphere) const; + +private: + Planef _plane; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeometricBoundingVolume::init_type(); + register_type(_type_handle, "BoundingPlane", + GeometricBoundingVolume::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 "boundingPlane.I" + +#endif diff --git a/panda/src/mathutil/boundingVolume.cxx b/panda/src/mathutil/boundingVolume.cxx index bde970e250..c0e1a88744 100644 --- a/panda/src/mathutil/boundingVolume.cxx +++ b/panda/src/mathutil/boundingVolume.cxx @@ -119,6 +119,19 @@ extend_by_line(const BoundingLine *) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::extend_by_plane +// Access: Protected, Virtual +// Description: Double-dispatch support: called by extend_other() +// when the type we're extending by is known to be a +// plane. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +extend_by_plane(const BoundingPlane *) { + _flags = F_infinite; + return false; +} + //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::around_spheres // Access: Protected, Virtual @@ -167,6 +180,28 @@ around_lines(const BoundingVolume **, const BoundingVolume **) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::around_planes +// Access: Protected, Virtual +// Description: Double-dispatch support: called by around_other() +// when the type of the first element in the list is +// known to be a nonempty plane. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +around_planes(const BoundingVolume **, const BoundingVolume **) { + _flags = F_infinite; + if (is_of_type(FiniteBoundingVolume::get_class_type())) { + // If it's a FiniteBoundingVolume, we can't do any better than + // making it infinite. So we return true. + return true; + } + + // Otherwise, we might do better, and we require each class to + // define a function. If we get here, the function isn't defined, + // so we return false to indicate this. + return false; +} + //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::contains_sphere // Access: Protected, Virtual @@ -202,3 +237,15 @@ int BoundingVolume:: contains_line(const BoundingLine *) const { return IF_dont_understand; } + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::contains_plane +// Access: Protected, Virtual +// Description: Double-dispatch support: called by contains_other() +// when the type we're testing for intersection is known +// to be a plane. +//////////////////////////////////////////////////////////////////// +int BoundingVolume:: +contains_plane(const BoundingPlane *) const { + return IF_dont_understand; +} diff --git a/panda/src/mathutil/boundingVolume.h b/panda/src/mathutil/boundingVolume.h index 8dc018f3d3..6fa6bc6c31 100644 --- a/panda/src/mathutil/boundingVolume.h +++ b/panda/src/mathutil/boundingVolume.h @@ -27,6 +27,7 @@ class BoundingSphere; class BoundingHexahedron; class BoundingLine; +class BoundingPlane; //////////////////////////////////////////////////////////////////// @@ -118,6 +119,7 @@ protected: virtual bool extend_by_sphere(const BoundingSphere *sphere); virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron); virtual bool extend_by_line(const BoundingLine *line); + virtual bool extend_by_plane(const BoundingPlane *plane); virtual bool around_spheres(const BoundingVolume **first, const BoundingVolume **last); @@ -125,10 +127,13 @@ protected: const BoundingVolume **last); virtual bool around_lines(const BoundingVolume **first, const BoundingVolume **last); + virtual bool around_planes(const BoundingVolume **first, + const BoundingVolume **last); virtual int contains_sphere(const BoundingSphere *sphere) const; virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; virtual int contains_line(const BoundingLine *line) const; + virtual int contains_plane(const BoundingPlane *plane) const; public: @@ -151,6 +156,7 @@ private: friend class BoundingSphere; friend class BoundingHexahedron; friend class BoundingLine; + friend class BoundingPlane; }; INLINE_MATHUTIL ostream &operator << (ostream &out, const BoundingVolume &bound); diff --git a/panda/src/mathutil/config_mathutil.cxx b/panda/src/mathutil/config_mathutil.cxx index b9d301b4d0..8f6012bab5 100644 --- a/panda/src/mathutil/config_mathutil.cxx +++ b/panda/src/mathutil/config_mathutil.cxx @@ -24,6 +24,7 @@ #include "boundingSphere.h" #include "boundingHexahedron.h" #include "boundingLine.h" +#include "boundingPlane.h" #include "linmath_events.h" #include "dconfig.h" #include "pandaSystem.h" @@ -51,6 +52,7 @@ ConfigureFn(config_mathutil) { GeometricBoundingVolume::init_type(); OmniBoundingVolume::init_type(); BoundingLine::init_type(); + BoundingPlane::init_type(); EventStoreVec2::init_type("EventStoreVec2"); EventStoreVec3::init_type("EventStoreVec3"); EventStoreMat4::init_type("EventStoreMat4"); diff --git a/panda/src/mathutil/mathutil_composite1.cxx b/panda/src/mathutil/mathutil_composite1.cxx index 7092a5658e..69f70f10bb 100644 --- a/panda/src/mathutil/mathutil_composite1.cxx +++ b/panda/src/mathutil/mathutil_composite1.cxx @@ -2,6 +2,7 @@ #include "boundingVolume.cxx" #include "boundingHexahedron.cxx" #include "boundingLine.cxx" +#include "boundingPlane.cxx" #include "boundingSphere.cxx" #include "finiteBoundingVolume.cxx" #include "geometricBoundingVolume.cxx" diff --git a/panda/src/mathutil/plane_src.I b/panda/src/mathutil/plane_src.I index 1e487e2e56..d02feba1bc 100644 --- a/panda/src/mathutil/plane_src.I +++ b/panda/src/mathutil/plane_src.I @@ -24,7 +24,7 @@ // It's not clear how useful a default plane is. //////////////////////////////////////////////////////////////////// INLINE_MATHUTIL FLOATNAME(Plane):: -FLOATNAME(Plane)(void) { +FLOATNAME(Plane)() { _v.v._0 = 0.0f; _v.v._1 = 0.0f; _v.v._2 = 1.0f; @@ -116,6 +116,26 @@ operator * (const FLOATNAME(LMatrix4) &mat) const { return FLOATNAME(Plane)(new_normal, new_point); } +//////////////////////////////////////////////////////////////////// +// Function: Plane::Operator *= LMatrix4 +// Access: Published +// Description: Transforms the plane by the indicated matrix. +//////////////////////////////////////////////////////////////////// +INLINE_MATHUTIL void FLOATNAME(Plane):: +operator *= (const FLOATNAME(LMatrix4) &mat) { + (*this) = (*this) * mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::xform +// Access: Published +// Description: Transforms the plane by the indicated matrix. +//////////////////////////////////////////////////////////////////// +void FLOATNAME(Plane):: +xform(const FLOATNAME(LMatrix4) &mat) { + (*this) = (*this) * mat; +} + //////////////////////////////////////////////////////////////////// // Function: Plane::Unary - // Access: Published diff --git a/panda/src/mathutil/plane_src.cxx b/panda/src/mathutil/plane_src.cxx index 099dc7431f..0fa8fa6ad2 100644 --- a/panda/src/mathutil/plane_src.cxx +++ b/panda/src/mathutil/plane_src.cxx @@ -25,7 +25,7 @@ // mirror. //////////////////////////////////////////////////////////////////// FLOATNAME(LMatrix4) FLOATNAME(Plane):: -get_reflection_mat(void) const { +get_reflection_mat() const { FLOATTYPE aa = _v.v._0 * _v.v._0; FLOATTYPE ab = _v.v._0 * _v.v._1; FLOATTYPE ac = _v.v._0 * _v.v._2; diff --git a/panda/src/mathutil/plane_src.h b/panda/src/mathutil/plane_src.h index 31e0660025..1f0237f5e4 100644 --- a/panda/src/mathutil/plane_src.h +++ b/panda/src/mathutil/plane_src.h @@ -23,7 +23,7 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA FLOATNAME(Plane) : public FLOATNAME(LVecBase4) { PUBLISHED: - INLINE_MATHUTIL FLOATNAME(Plane)(void); + INLINE_MATHUTIL FLOATNAME(Plane)(); INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LVecBase4) ©); INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LPoint3) &a, const FLOATNAME(LPoint3) &b, @@ -35,20 +35,22 @@ PUBLISHED: INLINE_MATHUTIL FLOATNAME(Plane) operator * (const FLOATNAME(LMatrix3) &mat) const; INLINE_MATHUTIL FLOATNAME(Plane) operator * (const FLOATNAME(LMatrix4) &mat) const; + INLINE_MATHUTIL void operator *= (const FLOATNAME(LMatrix4) &mat); + INLINE_MATHUTIL void xform(const FLOATNAME(LMatrix4) &mat); INLINE_MATHUTIL FLOATNAME(Plane) operator - () const; - FLOATNAME(LMatrix4) get_reflection_mat(void) const; + FLOATNAME(LMatrix4) get_reflection_mat() const; INLINE_MATHUTIL FLOATNAME(LVector3) get_normal() const; FLOATNAME(LPoint3) get_point() const; INLINE_MATHUTIL FLOATTYPE dist_to_plane(const FLOATNAME(LPoint3) &point) const; INLINE_MATHUTIL bool intersects_line(FLOATNAME(LPoint3) &intersection_point, - const FLOATNAME(LPoint3) &p1, - const FLOATNAME(LPoint3) &p2) const; + const FLOATNAME(LPoint3) &p1, + const FLOATNAME(LPoint3) &p2) const; INLINE_MATHUTIL bool intersects_line(FLOATTYPE &t, - const FLOATNAME(LPoint3) &from, - const FLOATNAME(LVector3) &delta) const; + const FLOATNAME(LPoint3) &from, + const FLOATNAME(LVector3) &delta) const; bool intersects_plane(FLOATNAME(LPoint3) &from, FLOATNAME(LVector3) &delta, diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index a330fc1a4f..8afdfad195 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -35,6 +35,7 @@ cullBinUnsorted.I cullBinUnsorted.h \ cullFaceAttrib.I cullFaceAttrib.h \ cullHandler.I cullHandler.h \ + cullPlanes.I cullPlanes.h \ cullResult.I cullResult.h \ cullTraverser.I cullTraverser.h \ cullTraverserData.I cullTraverserData.h \ @@ -134,6 +135,7 @@ cullBinUnsorted.cxx \ cullFaceAttrib.cxx \ cullHandler.cxx \ + cullPlanes.cxx \ cullResult.cxx \ cullTraverser.cxx \ cullTraverserData.cxx \ @@ -229,6 +231,7 @@ cullBinUnsorted.I cullBinUnsorted.h \ cullFaceAttrib.I cullFaceAttrib.h \ cullHandler.I cullHandler.h \ + cullPlanes.I cullPlanes.h \ cullResult.I cullResult.h \ cullTraverser.I cullTraverser.h \ cullTraverserData.I cullTraverserData.h \ diff --git a/panda/src/pgraph/clipPlaneAttrib.cxx b/panda/src/pgraph/clipPlaneAttrib.cxx index 01de89b5ec..809fc1352b 100644 --- a/panda/src/pgraph/clipPlaneAttrib.cxx +++ b/panda/src/pgraph/clipPlaneAttrib.cxx @@ -516,6 +516,83 @@ filter_to_max(int max_clip_planes) const { return planeNode_attrib; } +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttrib::compose_off +// Access: Public +// Description: This is a special method which composes two +// ClipPlaneAttribs with regard only to their set of +// "off" clip planes, for the purposes of deriving +// PandaNode::get_off_clip_planes(). +// +// The result will be a ClipPlaneAttrib that represents +// the union of all of the clip planes turned off in +// either attrib. The set of on planes in the result is +// undefined and should be ignored. +//////////////////////////////////////////////////////////////////// +CPT(RenderAttrib) ClipPlaneAttrib:: +compose_off(const RenderAttrib *other) const { + const ClipPlaneAttrib *ta; + DCAST_INTO_R(ta, other, 0); + + if (_off_all_planes || (!ta->_off_all_planes && ta->_off_planes.empty())) { + // If we turn off all planes, or the other turns none off, the + // result is the same as this one. + return this; + } + + if (ta->_off_all_planes || _off_planes.empty()) { + // And contrariwise. + return ta; + } + + Planes::const_iterator ai = _off_planes.begin(); + Planes::const_iterator bi = ta->_off_planes.begin(); + + // Create a new ClipPlaneAttrib that will hold the result. + ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib; + back_insert_iterator result = + back_inserter(new_attrib->_on_planes); + + while (ai != _off_planes.end() && + bi != ta->_off_planes.end()) { + if ((*ai) < (*bi)) { + // Here is a plane that we have in the original, which is not + // present in the secondary. + *result = *ai; + ++ai; + ++result; + + } else if ((*bi) < (*ai)) { + // Here is a new plane we have in the secondary, that was not + // present in the original. + *result = *bi; + ++bi; + ++result; + + } else { // (*bi) == (*ai) + // Here is a plane we have in both. + *result = *bi; + ++ai; + ++bi; + ++result; + } + } + + while (ai != _off_planes.end()) { + *result = *ai; + ++ai; + ++result; + } + + while (bi != ta->_off_planes.end()) { + *result = *bi; + ++bi; + ++result; + } + + return return_new(new_attrib); +} + //////////////////////////////////////////////////////////////////// // Function: ClipPlaneAttrib::issue // Access: Public, Virtual diff --git a/panda/src/pgraph/clipPlaneAttrib.h b/panda/src/pgraph/clipPlaneAttrib.h index 95d92b124c..c48d3d92ee 100644 --- a/panda/src/pgraph/clipPlaneAttrib.h +++ b/panda/src/pgraph/clipPlaneAttrib.h @@ -96,6 +96,7 @@ PUBLISHED: CPT(ClipPlaneAttrib) filter_to_max(int max_clip_planes) const; public: + CPT(RenderAttrib) compose_off(const RenderAttrib *other) const; virtual void issue(GraphicsStateGuardianBase *gsg) const; virtual void output(ostream &out) const; diff --git a/panda/src/pgraph/cullPlanes.I b/panda/src/pgraph/cullPlanes.I new file mode 100644 index 0000000000..633c8a7926 --- /dev/null +++ b/panda/src/pgraph/cullPlanes.I @@ -0,0 +1,68 @@ +// Filename: cullPlanes.I +// Created by: drose (23Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullPlanes:: +CullPlanes() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullPlanes:: +CullPlanes(const CullPlanes ©) : + _planes(copy._planes) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::Copy Assignment Operator +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CullPlanes:: +operator = (const CullPlanes ©) { + _planes = copy._planes; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullPlanes:: +~CullPlanes() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::is_empty +// Access: Public +// Description: Returns true if there are no planes listed in the +// CullPlanes object. +//////////////////////////////////////////////////////////////////// +INLINE bool CullPlanes:: +is_empty() const { + return _planes.empty(); +} diff --git a/panda/src/pgraph/cullPlanes.cxx b/panda/src/pgraph/cullPlanes.cxx new file mode 100644 index 0000000000..b34a3641bd --- /dev/null +++ b/panda/src/pgraph/cullPlanes.cxx @@ -0,0 +1,218 @@ +// Filename: cullPlanes.cxx +// Created by: drose (23Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "cullPlanes.h" + + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::make_empty +// Access: Public, Static +// Description: Returns a pointer to an empty CullPlanes object. +//////////////////////////////////////////////////////////////////// +CPT(CullPlanes) CullPlanes:: +make_empty() { + static CPT(CullPlanes) empty; + if (empty == NULL) { + empty = new CullPlanes; + // Artificially tick the reference count, just to ensure we won't + // accidentally modify this object in any of the copy-on-write + // operations below. + empty->ref(); + } + return empty; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::xform +// Access: Public +// Description: Returns a pointer to a new CullPlanes object that is +// the same as this one, but with the clip planes +// modified by the indicated transform. +//////////////////////////////////////////////////////////////////// +CPT(CullPlanes) CullPlanes:: +xform(const LMatrix4f &mat) const { + PT(CullPlanes) new_planes; + if (get_ref_count() == 1) { + new_planes = (CullPlanes *)this; + } else { + new_planes = new CullPlanes(*this); + } + + for (Planes::iterator pi = new_planes->_planes.begin(); + pi != new_planes->_planes.end(); + ++pi) { + if ((*pi).second->get_ref_count() != 1) { + (*pi).second = DCAST(BoundingPlane, (*pi).second->make_copy()); + } + (*pi).second->xform(mat); + } + + return new_planes; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::apply_state +// Access: Public +// Description: Returns a pointer to a new CullPlanes object that is +// the same as this one, but with the indicated +// attributes applied to the state. +// +// In particular, any new ClipPlanes given in +// net_attrib, if it is not NULL, will be added to the +// state, unless those ClipPlanes are also listed in +// off_attrib. +//////////////////////////////////////////////////////////////////// +CPT(CullPlanes) CullPlanes:: +apply_state(const CullTraverser *trav, const CullTraverserData *data, + const ClipPlaneAttrib *net_attrib, + const ClipPlaneAttrib *off_attrib) const { + if (net_attrib == (ClipPlaneAttrib *)NULL) { + return this; + } + + PT(CullPlanes) new_planes; + if (get_ref_count() == 1) { + new_planes = (CullPlanes *)this; + } else { + new_planes = new CullPlanes(*this); + } + + CPT(TransformState) net_transform = NULL; + + int num_on_planes = net_attrib->get_num_on_planes(); + for (int i = 0; i < num_on_planes; ++i) { + NodePath clip_plane = net_attrib->get_on_plane(i); + Planes::const_iterator pi = new_planes->_planes.find(clip_plane); + if (pi == new_planes->_planes.end()) { + if (!off_attrib->has_off_plane(clip_plane)) { + // Here's a new clip plane; add it to the list. For this we + // need the net transform to this node. + if (net_transform == (TransformState *)NULL) { + net_transform = data->get_net_transform(trav); + } + + PlaneNode *plane_node = DCAST(PlaneNode, clip_plane.node()); + CPT(TransformState) new_transform = + net_transform->invert_compose(clip_plane.get_net_transform()); + + Planef plane = plane_node->get_plane() * new_transform->get_mat(); + new_planes->_planes[clip_plane] = new BoundingPlane(-plane); + } + } + } + + return new_planes; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::do_cull +// Access: Public +// Description: Tests the indicated bounding volume against all of +// the clip planes in this object. Sets result to an +// appropriate union of +// BoundingVolume::IntersectionFlags, similar to the +// result of BoundingVolume::contains(). +// +// Also, if the bounding volume is completely in front +// of any of the clip planes, removes those planes both +// from this object and from the indicated state, +// returning a new CullPlanes object in that case. +//////////////////////////////////////////////////////////////////// +CPT(CullPlanes) CullPlanes:: +do_cull(int &result, CPT(RenderState) &state, + const GeometricBoundingVolume *node_gbv) const { + // We should have a ClipPlaneAttrib in the state if we've called + // this method. + CPT(ClipPlaneAttrib) orig_cpa = state->get_clip_plane(); + nassertr(orig_cpa != (ClipPlaneAttrib *)NULL, this); + + result = + BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some; + + CPT(CullPlanes) new_planes = this; + CPT(ClipPlaneAttrib) new_cpa = orig_cpa; + + Planes::const_iterator pi; + for (pi = _planes.begin(); pi != _planes.end(); ++pi) { + int plane_result = (*pi).second->contains(node_gbv); + if (plane_result == BoundingVolume::IF_no_intersection) { + // The node is completely behind this clip plane. Short-circuit + // the rest of the logic; none of the other planes matter. + result = plane_result; + return new_planes; + } + + if ((plane_result & BoundingVolume::IF_all) != 0) { + // The node is completely in front of this clip plane. We don't + // need to consider this plane ever again for any descendents of + // this node. + new_planes = new_planes->remove_plane((*pi).first); + nassertr(new_planes != this, new_planes); + new_cpa = DCAST(ClipPlaneAttrib, new_cpa->remove_on_plane((*pi).first)); + } + + result &= plane_result; + } + + if (new_cpa != orig_cpa) { + if (new_cpa->is_identity()) { + state = state->remove_attrib(ClipPlaneAttrib::get_class_type()); + } else { + state = state->add_attrib(new_cpa); + } + } + + return new_planes; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::remove_plane +// Access: Public +// Description: Returns a pointer to a new CullPlanes object that is +// the same as this one, but with the indicated +// clip plane removed. +//////////////////////////////////////////////////////////////////// +CPT(CullPlanes) CullPlanes:: +remove_plane(const NodePath &clip_plane) const { + PT(CullPlanes) new_planes; + if (get_ref_count() == 1) { + new_planes = (CullPlanes *)this; + } else { + new_planes = new CullPlanes(*this); + } + + Planes::iterator pi = new_planes->_planes.find(clip_plane); + nassertr(pi != new_planes->_planes.end(), new_planes); + new_planes->_planes.erase(pi); + + return new_planes; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullPlanes::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CullPlanes:: +write(ostream &out) const { + out << "CullPlanes (" << _planes.size() << " planes):\n"; + Planes::const_iterator pi; + for (pi = _planes.begin(); pi != _planes.end(); ++pi) { + out << " " << (*pi).first << " : " << *(*pi).second << "\n"; + } +} diff --git a/panda/src/pgraph/cullPlanes.h b/panda/src/pgraph/cullPlanes.h new file mode 100644 index 0000000000..22696d7b89 --- /dev/null +++ b/panda/src/pgraph/cullPlanes.h @@ -0,0 +1,77 @@ +// Filename: cullPlanes.h +// Created by: drose (23Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLPLANES_H +#define CULLPLANES_H + +#include "pandabase.h" +#include "referenceCount.h" +#include "nodePath.h" +#include "clipPlaneAttrib.h" +#include "boundingPlane.h" +#include "pointerTo.h" +#include "luse.h" + +//////////////////////////////////////////////////////////////////// +// Class : CullPlanes +// Description : This represents the set of clip planes that are +// definitely in effect for the current node of the +// CullTraverserData, as well as on all child nodes. +// Any clip planes in this list may be safely culled +// against. +// +// This does not include the clip planes that are in +// effect now, but might later be turned off by a child +// node, since we can't safely cull against such clip +// planes. +// +// The bounding volumes in this object are transformed +// for each level of the scene graph. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullPlanes : public ReferenceCount { +protected: + INLINE CullPlanes(); + INLINE CullPlanes(const CullPlanes ©); + INLINE void operator = (const CullPlanes ©); + +public: + INLINE ~CullPlanes(); + + INLINE bool is_empty() const; + + static CPT(CullPlanes) make_empty(); + CPT(CullPlanes) xform(const LMatrix4f &mat) const; + CPT(CullPlanes) apply_state(const CullTraverser *trav, + const CullTraverserData *data, + const ClipPlaneAttrib *net_attrib, + const ClipPlaneAttrib *off_attrib) const; + CPT(CullPlanes) do_cull(int &result, CPT(RenderState) &state, + const GeometricBoundingVolume *node_gbv) const; + + CPT(CullPlanes) remove_plane(const NodePath &clip_plane) const; + + void write(ostream &out) const; + +private: + typedef pmap Planes; + Planes _planes; +}; + +#include "cullPlanes.I" + +#endif diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index 1119b2f0d5..93c82a9a93 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -166,8 +166,7 @@ traverse(CullTraverserData &data) { data.apply_transform_and_state(this); - const RenderState *node_state = node->get_state(); - const FogAttrib *fog = node_state->get_fog(); + const FogAttrib *fog = node->get_state()->get_fog(); if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) { // If we just introduced a FogAttrib here, call adjust_to_camera() // now. This maybe isn't the perfect time to call it, but it's diff --git a/panda/src/pgraph/cullTraverserData.I b/panda/src/pgraph/cullTraverserData.I index e7067b1bd9..2111d455f7 100644 --- a/panda/src/pgraph/cullTraverserData.I +++ b/panda/src/pgraph/cullTraverserData.I @@ -31,7 +31,8 @@ CullTraverserData(const NodePath &start, _modelview_transform(modelview_transform), _state(state), _view_frustum(view_frustum), - _guard_band(guard_band) + _guard_band(guard_band), + _cull_planes(CullPlanes::make_empty()) { } @@ -46,7 +47,8 @@ CullTraverserData(const CullTraverserData ©) : _modelview_transform(copy._modelview_transform), _state(copy._state), _view_frustum(copy._view_frustum), - _guard_band(copy._guard_band) + _guard_band(copy._guard_band), + _cull_planes(copy._cull_planes) { } @@ -62,6 +64,7 @@ operator = (const CullTraverserData ©) { _state = copy._state; _view_frustum = copy._view_frustum; _guard_band = copy._guard_band; + _cull_planes = copy._cull_planes; } //////////////////////////////////////////////////////////////////// @@ -76,7 +79,8 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) : _modelview_transform(parent._modelview_transform), _state(parent._state), _view_frustum(parent._view_frustum), - _guard_band(parent._guard_band) + _guard_band(parent._guard_band), + _cull_planes(parent._cull_planes) { } @@ -132,9 +136,10 @@ is_in_view(const DrawMask &camera_mask) { return false; } - if (_view_frustum == (GeometricBoundingVolume *)NULL) { - // If the transform is valid, but we don't have a frustum, it's - // always in. + if (_view_frustum == (GeometricBoundingVolume *)NULL && + _cull_planes->is_empty()) { + // If the transform is valid, but we don't have a frustum or any + // clip planes, it's always in. return true; } @@ -142,3 +147,28 @@ is_in_view(const DrawMask &camera_mask) { return is_in_view_impl(); } +//////////////////////////////////////////////////////////////////// +// Function: CullTraverserData::test_within_clip_planes +// Access: Public +// Description: Returns the BoundingVolume intersection bits +// appropriate for the intersection of the current clip +// planes with the current node. +// +// That is, if the current node is within all clip +// planes (and will not be clipped at all), returns +// BoundingVolume::IF_all. If it is completely behind +// at least one clip plane, returns +// BoundingVolume::IF_intersection. If it is partially +// behind one or more clip planes, returns +// BoundingVolume::IF_some. +//////////////////////////////////////////////////////////////////// +INLINE int CullTraverserData:: +test_within_clip_planes(const CullTraverser *trav) const { + const ClipPlaneAttrib *cpa = _state->get_clip_plane(); + if (cpa != (ClipPlaneAttrib *)NULL) { + return test_within_clip_planes_impl(trav, cpa); + } + // No clip plane attrib; therefore, the node is not clipped at + // all. + return BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some; +} diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx index 4a238c0c96..1e5a17fb4e 100644 --- a/panda/src/pgraph/cullTraverserData.cxx +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -23,6 +23,8 @@ #include "colorAttrib.h" #include "textureAttrib.h" #include "renderModeAttrib.h" +#include "clipPlaneAttrib.h" +#include "boundingPlane.h" #include "billboardEffect.h" #include "compassEffect.h" #include "polylightEffect.h" @@ -62,7 +64,8 @@ apply_transform_and_state(CullTraverser *trav) { } apply_transform_and_state(trav, node->get_transform(), - node_state, node->get_effects()); + node_state, node->get_effects(), + node->get_off_clip_planes()); } //////////////////////////////////////////////////////////////////// @@ -76,7 +79,8 @@ void CullTraverserData:: apply_transform_and_state(CullTraverser *trav, CPT(TransformState) node_transform, CPT(RenderState) node_state, - CPT(RenderEffects) node_effects) { + CPT(RenderEffects) node_effects, + const RenderAttrib *off_clip_planes) { if (node_effects->has_cull_callback()) { node_effects->cull_callback(trav, *this, node_transform, node_state); } @@ -85,7 +89,8 @@ apply_transform_and_state(CullTraverser *trav, _modelview_transform = _modelview_transform->compose(node_transform); if ((_view_frustum != (GeometricBoundingVolume *)NULL) || - (_guard_band != (GeometricBoundingVolume *)NULL)) { + (_guard_band != (GeometricBoundingVolume *)NULL) || + (!_cull_planes->is_empty())) { // We need to move the viewing frustums into the node's // coordinate space by applying the node's inverse transform. if (node_transform->is_singular()) { @@ -94,6 +99,7 @@ apply_transform_and_state(CullTraverser *trav, // point down. _view_frustum = (GeometricBoundingVolume *)NULL; _guard_band = (GeometricBoundingVolume *)NULL; + _cull_planes = CullPlanes::make_empty(); } else { CPT(TransformState) inv_transform = @@ -110,11 +116,16 @@ apply_transform_and_state(CullTraverser *trav, _guard_band = DCAST(GeometricBoundingVolume, _guard_band->make_copy()); _guard_band->xform(inv_transform->get_mat()); } + + _cull_planes = _cull_planes->xform(inv_transform->get_mat()); } } } _state = _state->compose(node_state); + _cull_planes = _cull_planes->apply_state(trav, this, + _state->get_clip_plane(), + DCAST(ClipPlaneAttrib, off_clip_planes)); } //////////////////////////////////////////////////////////////////// @@ -124,55 +135,131 @@ apply_transform_and_state(CullTraverser *trav, //////////////////////////////////////////////////////////////////// bool CullTraverserData:: is_in_view_impl() { - // By the time we get here, we know we have a viewing frustum. - nassertr(_view_frustum != (GeometricBoundingVolume *)NULL, true); - const BoundingVolume &node_volume = node()->get_bound(); nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), false); const GeometricBoundingVolume *node_gbv = DCAST(GeometricBoundingVolume, &node_volume); - int result = _view_frustum->contains(node_gbv); - - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << _node_path << " cull result = " << hex << result << dec << "\n"; - } - - if (result == BoundingVolume::IF_no_intersection) { - // No intersection at all. Cull. - if (!fake_view_frustum_cull) { - return false; - } - - // If we have fake view-frustum culling enabled, instead of - // actually culling an object we simply force it to be drawn in - // red wireframe. - _view_frustum = (GeometricBoundingVolume *)NULL; - CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); - _state = _state->compose(fake_state); + if (_view_frustum != (GeometricBoundingVolume *)NULL) { + int result = _view_frustum->contains(node_gbv); - } else if ((result & BoundingVolume::IF_all) != 0) { - // The node and its descendants are completely enclosed within - // the frustum. No need to cull further. - _view_frustum = (GeometricBoundingVolume *)NULL; - _guard_band = (GeometricBoundingVolume *)NULL; - - } else { - if (node()->is_final()) { - // The bounding volume is partially, but not completely, - // within the viewing frustum. Normally we'd keep testing - // child bounding volumes as we continue down. But this node - // has the "final" flag, so the user is claiming that there is - // some important reason we should consider everything visible - // at this point. So be it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << _node_path << " cull result = " << hex << result << dec << "\n"; + } + + if (result == BoundingVolume::IF_no_intersection) { + // No intersection at all. Cull. + if (!fake_view_frustum_cull) { + return false; + } + + // If we have fake view-frustum culling enabled, instead of + // actually culling an object we simply force it to be drawn in + // red wireframe. _view_frustum = (GeometricBoundingVolume *)NULL; + CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); + _state = _state->compose(fake_state); + + } else if ((result & BoundingVolume::IF_all) != 0) { + // The node and its descendents are completely enclosed within + // the frustum. No need to cull further. + _view_frustum = (GeometricBoundingVolume *)NULL; + _guard_band = (GeometricBoundingVolume *)NULL; + + } else { + // The node is partially, but not completely, within the viewing + // frustum. + if (node()->is_final()) { + // Normally we'd keep testing child bounding volumes as we + // continue down. But this node has the "final" flag, so the + // user is claiming that there is some important reason we + // should consider everything visible at this point. So be it. + _view_frustum = (GeometricBoundingVolume *)NULL; + } } } + if (!_cull_planes->is_empty()) { + // Also cull against the current clip planes. + int result; + _cull_planes = _cull_planes->do_cull(result, _state, node_gbv); + + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << _node_path << " cull planes cull result = " << hex + << result << dec << "\n"; + _cull_planes->write(pgraph_cat.spam(false)); + } + + if (result == BoundingVolume::IF_no_intersection) { + // No intersection at all. Cull. + return false; + + } else if ((result & BoundingVolume::IF_all) != 0) { + // The node and its descendents are completely in front of all + // of the clip planes. The do_cull() call should therefore have + // removed all of the clip planes. + nassertr(_cull_planes->is_empty(), true); + + } else { + // The node is partially within one or more clip planes. + if (node()->is_final()) { + // Even though the node is only partially within the clip + // planes, stop culling against them. + _cull_planes = CullPlanes::make_empty(); + } + } + } + + return true; } +//////////////////////////////////////////////////////////////////// +// Function: CullTraverserData::test_within_clip_planes_impl +// Access: Private +// Description: The private implementation of test_within_clip_planes(). +//////////////////////////////////////////////////////////////////// +int CullTraverserData:: +test_within_clip_planes_impl(const CullTraverser *trav, + const ClipPlaneAttrib *cpa) const { + int result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some; + + + const BoundingVolume &node_volume = node()->get_bound(); + nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), result); + const GeometricBoundingVolume *node_gbv = + DCAST(GeometricBoundingVolume, &node_volume); + + CPT(TransformState) net_transform = get_net_transform(trav); + + cerr << "considering " << _node_path << ", bv = " << *node_gbv << "\n"; + cerr << " net_transform = " << *net_transform << "\n"; + + int num_planes = cpa->get_num_on_planes(); + for (int i = 0; + i < num_planes && result != BoundingVolume::IF_no_intersection; + ++i) { + NodePath plane_path = cpa->get_on_plane(i); + PlaneNode *plane_node = DCAST(PlaneNode, plane_path.node()); + CPT(TransformState) new_transform = + net_transform->invert_compose(plane_path.get_net_transform()); + + Planef plane = plane_node->get_plane() * new_transform->get_mat(); + BoundingPlane bplane(-plane); + cerr << " " << bplane << " -> " << bplane.contains(node_gbv) << "\n"; + result &= bplane.contains(node_gbv); + } + + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << _node_path << " test_within_clip_planes result = " + << hex << result << dec << "\n"; + } + + return result; +} //////////////////////////////////////////////////////////////////// // Function: CullTraverserData::get_fake_view_frustum_cull_state diff --git a/panda/src/pgraph/cullTraverserData.h b/panda/src/pgraph/cullTraverserData.h index a19ffc860b..85fc92c97c 100644 --- a/panda/src/pgraph/cullTraverserData.h +++ b/panda/src/pgraph/cullTraverserData.h @@ -20,7 +20,7 @@ #define CULLTRAVERSERDATA_H #include "pandabase.h" - +#include "cullPlanes.h" #include "workingNodePath.h" #include "renderState.h" #include "transformState.h" @@ -64,20 +64,27 @@ public: CPT(TransformState) get_net_transform(const CullTraverser *trav) const; INLINE bool is_in_view(const DrawMask &camera_mask); + INLINE int test_within_clip_planes(const CullTraverser *trav) const; + void apply_transform_and_state(CullTraverser *trav); void apply_transform_and_state(CullTraverser *trav, CPT(TransformState) node_transform, CPT(RenderState) node_state, - CPT(RenderEffects) node_effects); + CPT(RenderEffects) node_effects, + const RenderAttrib *off_clip_planes); WorkingNodePath _node_path; CPT(TransformState) _modelview_transform; CPT(RenderState) _state; PT(GeometricBoundingVolume) _view_frustum; PT(GeometricBoundingVolume) _guard_band; + CPT(CullPlanes) _cull_planes; private: bool is_in_view_impl(); + int test_within_clip_planes_impl(const CullTraverser *trav, + const ClipPlaneAttrib *cpa) const; + static CPT(RenderState) get_fake_view_frustum_cull_state(); }; diff --git a/panda/src/pgraph/modelNode.I b/panda/src/pgraph/modelNode.I index 5e2dd56a79..2ed9af35bc 100644 --- a/panda/src/pgraph/modelNode.I +++ b/panda/src/pgraph/modelNode.I @@ -27,6 +27,7 @@ ModelNode(const string &name) : PandaNode(name) { _preserve_transform = PT_none; + _preserve_attributes = 0; } //////////////////////////////////////////////////////////////////// @@ -66,6 +67,33 @@ get_preserve_transform() const { return _preserve_transform; } +//////////////////////////////////////////////////////////////////// +// Function: ModelNode::set_preserve_attributes +// Access: Public +// Description: Sets the preserve_attributes flag. This restricts the +// ability of a flatten operation to affect the +// render attributes stored on this node. +// +// The value should be the union of bits from +// SceneGraphReducer::AttribTypes that represent the +// attributes that should *not* be changed. +//////////////////////////////////////////////////////////////////// +INLINE void ModelNode:: +set_preserve_attributes(int preserve_attributes) { + _preserve_attributes = preserve_attributes; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelNode::get_preserve_attributes +// Access: Public +// Description: Returns the current setting of the preserve_attributes +// flag. See set_preserve_attributes(). +//////////////////////////////////////////////////////////////////// +INLINE int ModelNode:: +get_preserve_attributes() const { + return _preserve_attributes; +} + //////////////////////////////////////////////////////////////////// // Function: ModelNode::Copy Constructor // Access: Protected @@ -74,6 +102,7 @@ get_preserve_transform() const { INLINE ModelNode:: ModelNode(const ModelNode ©) : PandaNode(copy), - _preserve_transform(copy._preserve_transform) + _preserve_transform(copy._preserve_transform), + _preserve_attributes(copy._preserve_attributes) { } diff --git a/panda/src/pgraph/modelNode.cxx b/panda/src/pgraph/modelNode.cxx index d03f456160..e30df805d8 100644 --- a/panda/src/pgraph/modelNode.cxx +++ b/panda/src/pgraph/modelNode.cxx @@ -109,6 +109,22 @@ preserve_name() const { return true; } +//////////////////////////////////////////////////////////////////// +// Function: ModelNode::get_unsafe_to_apply_attribs +// Access: Public, Virtual +// Description: Returns the union of all attributes from +// SceneGraphReducer::AttribTypes that may not safely be +// applied to the vertices of this node. If this is +// nonzero, these attributes must be dropped at this +// node as a state change. +// +// This is a generalization of safe_to_transform(). +//////////////////////////////////////////////////////////////////// +int ModelNode:: +get_unsafe_to_apply_attribs() const { + return _preserve_attributes; +} + //////////////////////////////////////////////////////////////////// // Function: ModelNode::register_with_read_factory // Access: Public, Static @@ -130,6 +146,7 @@ void ModelNode:: write_datagram(BamWriter *manager, Datagram &dg) { PandaNode::write_datagram(manager, dg); dg.add_uint8((int)_preserve_transform); + dg.add_uint16(_preserve_attributes); } //////////////////////////////////////////////////////////////////// @@ -164,4 +181,8 @@ fillin(DatagramIterator &scan, BamReader *manager) { PandaNode::fillin(scan, manager); _preserve_transform = (PreserveTransform)scan.get_uint8(); + _preserve_attributes = 0; + if (manager->get_file_minor_ver() >= 3) { + _preserve_attributes = scan.get_uint16(); + } } diff --git a/panda/src/pgraph/modelNode.h b/panda/src/pgraph/modelNode.h index 4def5ef9b9..e93b771b60 100644 --- a/panda/src/pgraph/modelNode.h +++ b/panda/src/pgraph/modelNode.h @@ -50,6 +50,7 @@ public: virtual bool safe_to_modify_transform() const; virtual bool safe_to_combine() const; virtual bool preserve_name() const; + virtual int get_unsafe_to_apply_attribs() const; PUBLISHED: enum PreserveTransform { @@ -61,8 +62,12 @@ PUBLISHED: INLINE void set_preserve_transform(PreserveTransform preserve_transform); INLINE PreserveTransform get_preserve_transform() const; + INLINE void set_preserve_attributes(int attrib_mask); + INLINE int get_preserve_attributes() const; + private: PreserveTransform _preserve_transform; + int _preserve_attributes; public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 4a58dfb22e..9bd90ee4a3 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -436,7 +436,15 @@ get_stashed_sort(int n) const { INLINE void PandaNode:: set_attrib(const RenderAttrib *attrib, int override) { CDWriter cdata(_cycler); - cdata->_state = cdata->_state->add_attrib(attrib, override); + CPT(RenderState) new_state = cdata->_state->add_attrib(attrib, override); + if (cdata->_state != new_state) { + cdata->_state = new_state; + + // We mark the bound stale when the state changes, in case we have + // changed a ClipPlaneAttrib. + mark_bound_stale(); + state_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -483,7 +491,15 @@ has_attrib(TypeHandle type) const { INLINE void PandaNode:: clear_attrib(TypeHandle type) { CDWriter cdata(_cycler); - cdata->_state = cdata->_state->remove_attrib(type); + CPT(RenderState) new_state = cdata->_state->remove_attrib(type); + if (cdata->_state != new_state) { + cdata->_state = new_state; + + // We mark the bound stale when the state changes, in case we have + // changed a ClipPlaneAttrib. + mark_bound_stale(); + state_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -554,7 +570,14 @@ clear_effect(TypeHandle type) { INLINE void PandaNode:: set_state(const RenderState *state) { CDWriter cdata(_cycler); - cdata->_state = state; + if (cdata->_state != state) { + cdata->_state = state; + + // We mark the bound stale when the state changes, in case we have + // changed a ClipPlaneAttrib. + mark_bound_stale(); + state_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -583,7 +606,14 @@ get_state() const { INLINE void PandaNode:: clear_state() { CDWriter cdata(_cycler); - cdata->_state = RenderState::make_empty(); + if (!cdata->_state->is_empty()) { + cdata->_state = RenderState::make_empty(); + + // We mark the bound stale when the state changes, in case we have + // changed a ClipPlaneAttrib. + mark_bound_stale(); + state_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -633,9 +663,11 @@ clear_effects() { INLINE void PandaNode:: set_transform(const TransformState *transform) { CDWriter cdata(_cycler); - cdata->_transform = transform; - mark_bound_stale(); - transform_changed(); + if (cdata->_transform != transform) { + cdata->_transform = transform; + mark_bound_stale(); + transform_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -661,9 +693,11 @@ get_transform() const { INLINE void PandaNode:: clear_transform() { CDWriter cdata(_cycler); - cdata->_transform = TransformState::make_identity(); - mark_bound_stale(); - transform_changed(); + if (!cdata->_transform->is_identity()) { + cdata->_transform = TransformState::make_identity(); + mark_bound_stale(); + transform_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -819,7 +853,10 @@ ls(ostream &out, int indent_level) const { INLINE void PandaNode:: set_draw_mask(DrawMask mask) { CDWriter cdata(_cycler); - cdata->_draw_mask = mask; + if (cdata->_draw_mask != mask) { + cdata->_draw_mask = mask; + draw_mask_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -887,6 +924,21 @@ get_net_collide_mask() const { return cdata->_net_collide_mask; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_off_clip_planes +// Access: Published +// Description: Returns a ClipPlaneAttrib which represents the union +// of all of the clip planes that have been turned *off* +// at this level and below. +//////////////////////////////////////////////////////////////////// +INLINE const RenderAttrib *PandaNode:: +get_off_clip_planes() const { + // Call get_bound() first to ensure the attrib is recomputed. + BoundedObject::get_bound(); + CDReader cdata(_cycler); + return cdata->_off_clip_planes; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::set_bound // Access: Published diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index e59e12e6b9..4f42746a1a 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -25,6 +25,7 @@ #include "geometricBoundingVolume.h" #include "sceneGraphReducer.h" #include "accumulatedAttribs.h" +#include "clipPlaneAttrib.h" TypeHandle PandaNode::_type_handle; @@ -1581,6 +1582,12 @@ recompute_bound() { CDWriter cdata(_cycler); cdata->_net_collide_mask = cdata->_into_collide_mask; + // And compute the set of "off" clip planes. + cdata->_off_clip_planes = cdata->_state->get_clip_plane(); + if (cdata->_off_clip_planes == (RenderAttrib *)NULL) { + cdata->_off_clip_planes = ClipPlaneAttrib::make(); + } + // Now actually compute the bounding volume by putting it around all // of our child bounding volumes. pvector child_volumes; @@ -1595,6 +1602,8 @@ recompute_bound() { const BoundingVolume &child_bound = child->get_bound(); child_volumes.push_back(&child_bound); cdata->_net_collide_mask |= child->get_net_collide_mask(); + CPT(ClipPlaneAttrib) orig = DCAST(ClipPlaneAttrib, cdata->_off_clip_planes); + cdata->_off_clip_planes = orig->compose_off(child->get_off_clip_planes()); } const BoundingVolume **child_begin = &child_volumes[0]; @@ -1678,6 +1687,28 @@ void PandaNode:: transform_changed() { } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::state_changed +// Access: Protected, Virtual +// Description: Called after the node's RenderState has been changed +// for any reason, this just provides a hook so derived +// classes can do something special in this case. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +state_changed() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::draw_mask_changed +// Access: Protected, Virtual +// Description: Called after the node's DrawMask has been changed +// for any reason, this just provides a hook so derived +// classes can do something special in this case. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +draw_mask_changed() { +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::r_copy_subgraph // Access: Protected, Virtual diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index a30e31be78..f358bb1265 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -187,6 +187,7 @@ PUBLISHED: virtual CollideMask get_legal_collide_mask() const; INLINE CollideMask get_net_collide_mask() const; + INLINE const RenderAttrib *get_off_clip_planes() const; virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; @@ -225,6 +226,8 @@ protected: virtual void parents_changed(); virtual void children_changed(); virtual void transform_changed(); + virtual void state_changed(); + virtual void draw_mask_changed(); INLINE void add_net_collide_mask(CollideMask mask); typedef pmap InstanceMap; @@ -363,6 +366,13 @@ private: // we update them together. CollideMask _net_collide_mask; + // This is a ClipPlaneAttrib that represents the union of all clip + // planes that have been turned *off* at and below this level. As + // above, it's similar to a bounding volume, and is updated at the + // same time. TODO: fix the circular reference counts involved + // here. + CPT(RenderAttrib) _off_clip_planes; + bool _fixed_internal_bound; }; diff --git a/panda/src/pgraph/pgraph_composite2.cxx b/panda/src/pgraph/pgraph_composite2.cxx index 285e37c4b9..711ad83bdb 100644 --- a/panda/src/pgraph/pgraph_composite2.cxx +++ b/panda/src/pgraph/pgraph_composite2.cxx @@ -8,6 +8,7 @@ #include "cullBinUnsorted.cxx" #include "cullFaceAttrib.cxx" #include "cullHandler.cxx" +#include "cullPlanes.cxx" #include "cullResult.cxx" #include "cullTraverser.cxx" #include "cullTraverserData.cxx" diff --git a/panda/src/pgraph/planeNode.cxx b/panda/src/pgraph/planeNode.cxx index b3a623e325..c9bed08e42 100644 --- a/panda/src/pgraph/planeNode.cxx +++ b/panda/src/pgraph/planeNode.cxx @@ -66,10 +66,11 @@ fillin(DatagramIterator &scan, BamReader *) { // Description: //////////////////////////////////////////////////////////////////// PlaneNode:: -PlaneNode(const string &name) : +PlaneNode(const string &name, const Planef &plane) : PandaNode(name), _priority(0) { + set_plane(plane); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/planeNode.h b/panda/src/pgraph/planeNode.h index 37b1ebfe4a..a13f0df91a 100644 --- a/panda/src/pgraph/planeNode.h +++ b/panda/src/pgraph/planeNode.h @@ -34,7 +34,7 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA PlaneNode : public PandaNode { PUBLISHED: - PlaneNode(const string &name); + PlaneNode(const string &name, const Planef &plane = Planef()); protected: PlaneNode(const PlaneNode ©); diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I index ddea42e98f..eeb8cbed38 100644 --- a/panda/src/pgraph/renderState.I +++ b/panda/src/pgraph/renderState.I @@ -336,6 +336,25 @@ get_render_mode() const { return _render_mode; } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::get_clip_plane +// Access: Published +// Description: This function is provided as an optimization, to +// speed up the render-time checking for the existance +// of a ClipPlaneAttrib on this state. It returns a +// pointer to the ClipPlaneAttrib, if there is one, or +// NULL if there is not. +//////////////////////////////////////////////////////////////////// +INLINE const ClipPlaneAttrib *RenderState:: +get_clip_plane() const { + if ((_flags & F_checked_clip_plane) == 0) { + // We pretend this function is const, even though it transparently + // modifies the internal clip_plane cache. + ((RenderState *)this)->determine_clip_plane(); + } + return _clip_plane; +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::set_destructing // Access: Private diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index f6d20db9ff..24a57497bf 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -21,6 +21,7 @@ #include "cullBinAttrib.h" #include "cullBinManager.h" #include "fogAttrib.h" +#include "clipPlaneAttrib.h" #include "transparencyAttrib.h" #include "colorAttrib.h" #include "colorScaleAttrib.h" @@ -1647,6 +1648,21 @@ determine_render_mode() { _flags |= F_checked_render_mode; } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::determine_clip_plane +// Access: Private +// Description: This is the private implementation of get_clip_plane(). +//////////////////////////////////////////////////////////////////// +void RenderState:: +determine_clip_plane() { + const RenderAttrib *attrib = get_attrib(ClipPlaneAttrib::get_class_type()); + _clip_plane = (const ClipPlaneAttrib *)NULL; + if (attrib != (const RenderAttrib *)NULL) { + _clip_plane = DCAST(ClipPlaneAttrib, attrib); + } + _flags |= F_checked_clip_plane; +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::update_pstats // Access: Private diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 80ff96d312..3b3260eac0 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -40,6 +40,7 @@ class ColorAttrib; class ColorScaleAttrib; class TextureAttrib; class TexGenAttrib; +class ClipPlaneAttrib; class FactoryParams; //////////////////////////////////////////////////////////////////// @@ -133,6 +134,7 @@ PUBLISHED: INLINE const TexGenAttrib *get_tex_gen() const; INLINE const TexMatrixAttrib *get_tex_matrix() const; INLINE const RenderModeAttrib *get_render_mode() const; + INLINE const ClipPlaneAttrib *get_clip_plane() const; int get_geom_rendering(int geom_rendering) const; @@ -177,6 +179,7 @@ private: void determine_tex_gen(); void determine_tex_matrix(); void determine_render_mode(); + void determine_clip_plane(); INLINE void set_destructing(); INLINE bool is_destructing() const; @@ -274,6 +277,7 @@ private: const TexGenAttrib *_tex_gen; const TexMatrixAttrib *_tex_matrix; const RenderModeAttrib *_render_mode; + const ClipPlaneAttrib *_clip_plane; enum Flags { F_checked_bin_index = 0x0001, @@ -286,6 +290,7 @@ private: F_checked_tex_gen = 0x0080, F_checked_tex_matrix = 0x0100, F_checked_render_mode = 0x0200, + F_checked_clip_plane = 0x0400, F_is_destructing = 0x8000, }; unsigned short _flags; diff --git a/panda/src/pgui/Sources.pp b/panda/src/pgui/Sources.pp index 192fc07090..dcb09b3ff9 100644 --- a/panda/src/pgui/Sources.pp +++ b/panda/src/pgui/Sources.pp @@ -12,49 +12,61 @@ #define SOURCES \ config_pgui.h \ pgButton.I pgButton.h \ - pgSliderButton.I pgSliderButton.h \ + pgButtonNotify.I pgButtonNotify.h \ pgCullTraverser.I pgCullTraverser.h \ pgEntry.I pgEntry.h \ pgMouseWatcherGroup.I pgMouseWatcherGroup.h \ pgMouseWatcherParameter.I pgMouseWatcherParameter.h \ pgFrameStyle.I pgFrameStyle.h \ pgItem.I pgItem.h \ + pgItemNotify.I pgItemNotify.h \ pgMouseWatcherBackground.h \ pgMouseWatcherRegion.I pgMouseWatcherRegion.h \ + pgScrollFrame.I pgScrollFrame.h \ + pgSliderBar.I pgSliderBar.h \ + pgSliderBarNotify.I pgSliderBarNotify.h \ pgTop.I pgTop.h \ - pgWaitBar.I pgWaitBar.h \ - pgSliderBar.I pgSliderBar.h + pgVirtualFrame.I pgVirtualFrame.h \ + pgWaitBar.I pgWaitBar.h #define INCLUDED_SOURCES \ config_pgui.cxx \ pgButton.cxx \ - pgSliderButton.cxx \ + pgButtonNotify.cxx \ pgCullTraverser.cxx \ pgEntry.cxx \ pgMouseWatcherGroup.cxx \ pgMouseWatcherParameter.cxx \ pgFrameStyle.cxx \ pgItem.cxx \ + pgItemNotify.cxx \ pgMouseWatcherBackground.cxx \ pgMouseWatcherRegion.cxx \ + pgScrollFrame.cxx \ + pgSliderBar.cxx \ + pgSliderBarNotify.cxx \ pgTop.cxx \ - pgWaitBar.cxx \ - pgSliderBar.cxx + pgVirtualFrame.cxx \ + pgWaitBar.cxx #define INSTALL_HEADERS \ pgButton.I pgButton.h \ - pgSliderButton.I pgSliderButton.h \ + pgButtonNotify.I pgButtonNotify.h \ pgCullTraverser.I pgCullTraverser.h \ pgEntry.I pgEntry.h \ pgMouseWatcherGroup.I pgMouseWatcherGroup.h \ pgMouseWatcherParameter.I pgMouseWatcherParameter.h \ pgFrameStyle.I pgFrameStyle.h \ pgItem.I pgItem.h \ + pgItemNotify.I pgItemNotify.h \ pgMouseWatcherBackground.h \ pgMouseWatcherRegion.I pgMouseWatcherRegion.h \ + pgScrollFrame.I pgScrollFrame.h \ + pgSliderBar.I pgSliderBar.h \ + pgSliderBarNotify.I pgSliderBarNotify.h \ pgTop.I pgTop.h \ - pgWaitBar.I pgWaitBar.h \ - pgSliderBar.I pgSliderBar.h + pgVirtualFrame.I pgVirtualFrame.h \ + pgWaitBar.I pgWaitBar.h #define IGATESCAN all diff --git a/panda/src/pgui/config_pgui.cxx b/panda/src/pgui/config_pgui.cxx index 099d7b2a23..f77ecb4c73 100644 --- a/panda/src/pgui/config_pgui.cxx +++ b/panda/src/pgui/config_pgui.cxx @@ -18,7 +18,6 @@ #include "config_pgui.h" #include "pgButton.h" -#include "pgSliderButton.h" #include "pgCullTraverser.h" #include "pgEntry.h" #include "pgMouseWatcherParameter.h" @@ -26,9 +25,11 @@ #include "pgItem.h" #include "pgMouseWatcherBackground.h" #include "pgMouseWatcherRegion.h" -#include "pgTop.h" -#include "pgWaitBar.h" +#include "pgScrollFrame.h" #include "pgSliderBar.h" +#include "pgTop.h" +#include "pgVirtualFrame.h" +#include "pgWaitBar.h" #include "dconfig.h" @@ -39,6 +40,18 @@ ConfigureFn(config_pgui) { init_libpgui(); } +ConfigVariableDouble scroll_initial_delay +("scroll-initial-delay", 0.3, + PRC_DESC("This is the amount of time, in seconds, to delay after the user " + "first clicks and holds on a scrollbar button before the scrolling " + "continues automatically.")); + +ConfigVariableDouble scroll_continued_delay +("scroll-continued-delay", 0.1, + PRC_DESC("This is the amount of time, in seconds, to delay between lines " + "scrolled while the user is continuing to hold down the scrollbar " + "button.")); + //////////////////////////////////////////////////////////////////// // Function: init_libpgui // Description: Initializes the library. This must be called at @@ -56,7 +69,6 @@ init_libpgui() { initialized = true; PGButton::init_type(); - PGSliderButton::init_type(); PGCullTraverser::init_type(); PGEntry::init_type(); PGMouseWatcherParameter::init_type(); @@ -64,7 +76,9 @@ init_libpgui() { PGItem::init_type(); PGMouseWatcherBackground::init_type(); PGMouseWatcherRegion::init_type(); - PGTop::init_type(); - PGWaitBar::init_type(); + PGScrollFrame::init_type(); PGSliderBar::init_type(); + PGTop::init_type(); + PGVirtualFrame::init_type(); + PGWaitBar::init_type(); } diff --git a/panda/src/pgui/config_pgui.h b/panda/src/pgui/config_pgui.h index 58ecb39e61..585a6758aa 100644 --- a/panda/src/pgui/config_pgui.h +++ b/panda/src/pgui/config_pgui.h @@ -21,10 +21,13 @@ #include "pandabase.h" #include "notifyCategoryProxy.h" +#include "configVariableDouble.h" NotifyCategoryDecl(pgui, EXPCL_PANDA, EXPTP_PANDA); // Configure variables for pgui package. +extern ConfigVariableDouble scroll_initial_delay; +extern ConfigVariableDouble scroll_continued_delay; extern EXPCL_PANDA void init_libpgui(); diff --git a/panda/src/pgui/pgButton.I b/panda/src/pgui/pgButton.I index 3ab08255b9..ea389ef8ad 100644 --- a/panda/src/pgui/pgButton.I +++ b/panda/src/pgui/pgButton.I @@ -17,6 +17,32 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: PGButton::set_notify +// Access: Published +// Description: Sets the object which will be notified when the +// PGButton changes. Set this to NULL to disable +// this effect. The PGButton does not retain +// ownership of the pointer; it is your responsibility +// to ensure that the notify object does not destruct. +//////////////////////////////////////////////////////////////////// +INLINE void PGButton:: +set_notify(PGButtonNotify *notify) { + PGItem::set_notify(notify); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGButton::get_notify +// Access: Published +// Description: Returns the object which will be notified when the +// PGButton changes, if any. Returns NULL if there +// is no such object configured. +//////////////////////////////////////////////////////////////////// +INLINE PGButtonNotify *PGButton:: +get_notify() const { + return (PGButtonNotify *)PGItem::get_notify(); +} + //////////////////////////////////////////////////////////////////// // Function: PGButton::setup // Access: Published diff --git a/panda/src/pgui/pgButton.cxx b/panda/src/pgui/pgButton.cxx index 1d4ecd9ef9..96d5cfa499 100644 --- a/panda/src/pgui/pgButton.cxx +++ b/panda/src/pgui/pgButton.cxx @@ -156,18 +156,10 @@ click(const MouseWatcherParameter ¶m) { string event = get_click_event(param.get_button()); play_sound(event); throw_event(event, EventParameter(ep)); -} -//////////////////////////////////////////////////////////////////// -// Function: PGButton::move -// Access: Public, Virtual -// Description: This is a callback hook function, called whenever a -// mouse is moved while the mouse -// is within the region. -//////////////////////////////////////////////////////////////////// -void PGButton:: -move(const MouseWatcherParameter ¶m) { - PGItem::move(param); + if (has_notify()) { + get_notify()->button_click(this, param); + } } //////////////////////////////////////////////////////////////////// @@ -180,7 +172,7 @@ move(const MouseWatcherParameter ¶m) { // according to the size of the text. //////////////////////////////////////////////////////////////////// void PGButton:: -setup(const string &label) { +setup(const string &label, float bevel) { clear_state_def(S_ready); clear_state_def(S_depressed); clear_state_def(S_rollover); @@ -200,7 +192,7 @@ setup(const string &label) { PGFrameStyle style; style.set_color(0.8f, 0.8f, 0.8f, 1.0f); - style.set_width(0.1f, 0.1f); + style.set_width(bevel, bevel); style.set_type(PGFrameStyle::T_bevel_out); set_frame_style(S_ready, style); diff --git a/panda/src/pgui/pgButton.h b/panda/src/pgui/pgButton.h index 4749132562..dc818ea674 100644 --- a/panda/src/pgui/pgButton.h +++ b/panda/src/pgui/pgButton.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "pgItem.h" +#include "pgButtonNotify.h" #include "nodePath.h" #include "pset.h" @@ -37,6 +38,9 @@ PUBLISHED: PGButton(const string &name); virtual ~PGButton(); +protected: + PGButton(const PGButton ©); + public: virtual PandaNode *make_copy() const; @@ -46,10 +50,9 @@ public: virtual void release(const MouseWatcherParameter ¶m, bool background); virtual void click(const MouseWatcherParameter ¶m); - - virtual void move(const MouseWatcherParameter ¶m); - PGButton(const PGButton ©); + INLINE void set_notify(PGButtonNotify *notify); + INLINE PGButtonNotify *get_notify() const; PUBLISHED: enum State { @@ -59,7 +62,7 @@ PUBLISHED: S_inactive }; - void setup(const string &label); + void setup(const string &label, float bevel = 0.1f); INLINE void setup(const NodePath &ready); INLINE void setup(const NodePath &ready, const NodePath &depressed); INLINE void setup(const NodePath &ready, const NodePath &depressed, diff --git a/panda/src/pgui/pgButtonNotify.I b/panda/src/pgui/pgButtonNotify.I new file mode 100644 index 0000000000..06e65f7ce9 --- /dev/null +++ b/panda/src/pgui/pgButtonNotify.I @@ -0,0 +1,27 @@ +// Filename: pgButtonNotify.I +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGButtonNotify::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGButtonNotify:: +PGButtonNotify() { +} diff --git a/panda/src/pgui/pgButtonNotify.cxx b/panda/src/pgui/pgButtonNotify.cxx new file mode 100644 index 0000000000..d38e176512 --- /dev/null +++ b/panda/src/pgui/pgButtonNotify.cxx @@ -0,0 +1,29 @@ +// Filename: pgButtonNotify.cxx +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "pgButtonNotify.h" +#include "pgButton.h" + +//////////////////////////////////////////////////////////////////// +// Function: PGButtonNotify::button_click +// Access: Protected, Virtual +// Description: Called whenever a watched PGButton has been clicked. +//////////////////////////////////////////////////////////////////// +void PGButtonNotify:: +button_click(PGButton *, const MouseWatcherParameter &) { +} diff --git a/panda/src/pgui/pgButtonNotify.h b/panda/src/pgui/pgButtonNotify.h new file mode 100644 index 0000000000..00915e6aae --- /dev/null +++ b/panda/src/pgui/pgButtonNotify.h @@ -0,0 +1,45 @@ +// Filename: pgButtonNotify.h +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGBUTTONNOTIFY_H +#define PGBUTTONNOTIFY_H + +#include "pandabase.h" +#include "pgItemNotify.h" + +class PGButton; + +//////////////////////////////////////////////////////////////////// +// Class : PGButtonNotify +// Description : Objects that inherit from this class can receive +// notify messages when a slider bar moves or otherwise +// is reconfigured. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGButtonNotify : public PGItemNotify { +public: + INLINE PGButtonNotify(); + +protected: + virtual void button_click(PGButton *button, const MouseWatcherParameter ¶m); + + friend class PGButton; +}; + +#include "pgButtonNotify.I" + +#endif diff --git a/panda/src/pgui/pgFrameStyle.I b/panda/src/pgui/pgFrameStyle.I index 7642c1b6d4..fb8c9eda0e 100644 --- a/panda/src/pgui/pgFrameStyle.I +++ b/panda/src/pgui/pgFrameStyle.I @@ -27,6 +27,7 @@ PGFrameStyle() { _type = T_none; _color.set(1.0f, 1.0f, 1.0f, 1.0f); _width.set(0.1f, 0.1f); + _visible_scale.set(1.0f, 1.0f); } //////////////////////////////////////////////////////////////////// @@ -39,7 +40,8 @@ PGFrameStyle(const PGFrameStyle ©) : _type(copy._type), _color(copy._color), _texture(copy._texture), - _width(copy._width) + _width(copy._width), + _visible_scale(copy._visible_scale) { } @@ -54,6 +56,7 @@ operator = (const PGFrameStyle ©) { _color = copy._color; _texture = copy._texture; _width = copy._width; + _visible_scale = copy._visible_scale; } //////////////////////////////////////////////////////////////////// @@ -197,6 +200,48 @@ get_width() const { return _width; } +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_visible_scale +// Access: Published +// Description: Sets a scale factor on the visible representation of +// the frame, in the X and Y directions. If this scale +// factor is other than 1, it will affect the size of +// the visible frame representation within the actual +// frame border. +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +set_visible_scale(float x, float y) { + set_visible_scale(LVecBase2f(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::set_visible_scale +// Access: Published +// Description: Sets a scale factor on the visible representation of +// the frame, in the X and Y directions. If this scale +// factor is other than 1, it will affect the size of +// the visible frame representation within the actual +// frame border. +//////////////////////////////////////////////////////////////////// +INLINE void PGFrameStyle:: +set_visible_scale(const LVecBase2f &visible_scale) { + _visible_scale = visible_scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::get_visible_scale +// Access: Published +// Description: Returns the scale factor on the visible +// representation of the frame, in the X and Y +// directions. If this scale factor is other than 1, it +// will affect the size of the visible frame +// representation within the actual frame border. +//////////////////////////////////////////////////////////////////// +INLINE const LVecBase2f &PGFrameStyle:: +get_visible_scale() const { + return _visible_scale; +} + //////////////////////////////////////////////////////////////////// // Function: PGFrameStyle ostream output // Description: diff --git a/panda/src/pgui/pgFrameStyle.cxx b/panda/src/pgui/pgFrameStyle.cxx index da2eb91366..8684d5e979 100644 --- a/panda/src/pgui/pgFrameStyle.cxx +++ b/panda/src/pgui/pgFrameStyle.cxx @@ -60,6 +60,39 @@ operator << (ostream &out, PGFrameStyle::Type type) { return out << "**unknown(" << (int)type << ")**"; } +//////////////////////////////////////////////////////////////////// +// Function: PGFrameStyle::get_internal_frame +// Access: Published +// Description: Computes the size of the internal frame, given the +// indicated external frame, appropriate for this kind +// of frame style. This simply subtracts the border +// width for those frame styles that include a border. +//////////////////////////////////////////////////////////////////// +LVecBase4f PGFrameStyle:: +get_internal_frame(const LVecBase4f &frame) const { + LPoint2f center((frame[0] + frame[1]) / 2.0f, + (frame[2] + frame[3]) / 2.0f); + LVecBase4f scaled_frame + ((frame[0] - center[0]) * _visible_scale[0] + center[0], + (frame[1] - center[0]) * _visible_scale[0] + center[0], + (frame[2] - center[1]) * _visible_scale[1] + center[1], + (frame[3] - center[1]) * _visible_scale[1] + center[1]); + + switch (_type) { + case T_none: + case T_flat: + return scaled_frame; + + default: + break; + } + + return LVecBase4f(scaled_frame[0] + _width[0], + scaled_frame[1] - _width[0], + scaled_frame[2] + _width[1], + scaled_frame[3] - _width[1]); +} + //////////////////////////////////////////////////////////////////// // Function: PGFrameStyle::output // Access: Published @@ -68,6 +101,9 @@ operator << (ostream &out, PGFrameStyle::Type type) { void PGFrameStyle:: output(ostream &out) const { out << _type << " color = " << _color << " width = " << _width; + if (_visible_scale != LVecBase2f(1.0f, 1.0f)) { + out << "visible_scale = " << get_visible_scale(); + } if (has_texture()) { out << " texture = " << *get_texture(); } @@ -126,28 +162,36 @@ NodePath PGFrameStyle:: generate_into(const NodePath &parent, const LVecBase4f &frame) { PT(PandaNode) new_node; + LPoint2f center((frame[0] + frame[1]) / 2.0f, + (frame[2] + frame[3]) / 2.0f); + LVecBase4f scaled_frame + ((frame[0] - center[0]) * _visible_scale[0] + center[0], + (frame[1] - center[0]) * _visible_scale[0] + center[0], + (frame[2] - center[1]) * _visible_scale[1] + center[1], + (frame[3] - center[1]) * _visible_scale[1] + center[1]); + switch (_type) { case T_none: return NodePath(); case T_flat: - new_node = generate_flat_geom(frame); + new_node = generate_flat_geom(scaled_frame); break; case T_bevel_out: - new_node = generate_bevel_geom(frame, false); + new_node = generate_bevel_geom(scaled_frame, false); break; case T_bevel_in: - new_node = generate_bevel_geom(frame, true); + new_node = generate_bevel_geom(scaled_frame, true); break; case T_groove: - new_node = generate_groove_geom(frame, true); + new_node = generate_groove_geom(scaled_frame, true); break; case T_ridge: - new_node = generate_groove_geom(frame, false); + new_node = generate_groove_geom(scaled_frame, false); break; default: diff --git a/panda/src/pgui/pgFrameStyle.h b/panda/src/pgui/pgFrameStyle.h index 3e5f3ce39b..be9bfe8906 100644 --- a/panda/src/pgui/pgFrameStyle.h +++ b/panda/src/pgui/pgFrameStyle.h @@ -65,6 +65,12 @@ PUBLISHED: INLINE void set_width(const LVecBase2f &width); INLINE const LVecBase2f &get_width() const; + INLINE void set_visible_scale(float x, float y); + INLINE void set_visible_scale(const LVecBase2f &visible_scale); + INLINE const LVecBase2f &get_visible_scale() const; + + LVecBase4f get_internal_frame(const LVecBase4f &frame) const; + void output(ostream &out) const; public: @@ -81,6 +87,7 @@ private: Colorf _color; PT(Texture) _texture; LVecBase2f _width; + LVecBase2f _visible_scale; }; INLINE ostream &operator << (ostream &out, const PGFrameStyle &pfs); diff --git a/panda/src/pgui/pgItem.I b/panda/src/pgui/pgItem.I index ec6666fe0d..ed37b0dda4 100644 --- a/panda/src/pgui/pgItem.I +++ b/panda/src/pgui/pgItem.I @@ -33,6 +33,49 @@ get_region() const { return _region; } +//////////////////////////////////////////////////////////////////// +// Function: PGItem::set_notify +// Access: Published +// Description: Sets the object which will be notified when the +// PGItem changes. Set this to NULL to disable +// this effect. The PGItem does not retain +// ownership of the pointer; it is your responsibility +// to ensure that the notify object does not destruct. +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +set_notify(PGItemNotify *notify) { + if (_notify != (PGItemNotify *)NULL) { + _notify->remove_item(this); + } + _notify = notify; + if (_notify != (PGItemNotify *)NULL) { + _notify->add_item(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::has_notify +// Access: Published +// Description: Returns true if there is an object configured to be +// notified when the PGItem changes, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool PGItem:: +has_notify() const { + return (_notify != (PGItemNotify *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_notify +// Access: Published +// Description: Returns the object which will be notified when the +// PGItem changes, if any. Returns NULL if there +// is no such object configured. +//////////////////////////////////////////////////////////////////// +INLINE PGItemNotify *PGItem:: +get_notify() const { + return _notify; +} + //////////////////////////////////////////////////////////////////// // Function: PGItem::set_frame // Access: Published @@ -58,9 +101,11 @@ set_frame(float left, float right, float bottom, float top) { //////////////////////////////////////////////////////////////////// INLINE void PGItem:: set_frame(const LVecBase4f &frame) { - _has_frame = true; - _frame = frame; - mark_frames_stale(); + if (!_has_frame || _frame != frame) { + _has_frame = true; + _frame = frame; + frame_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -96,8 +141,10 @@ has_frame() const { //////////////////////////////////////////////////////////////////// INLINE void PGItem:: clear_frame() { - _has_frame = false; - mark_frames_stale(); + if (_has_frame) { + _has_frame = false; + frame_changed(); + } } //////////////////////////////////////////////////////////////////// @@ -476,3 +523,32 @@ INLINE LMatrix4f PGItem:: get_frame_inv_xform() const { return _frame_inv_xform; } + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::compute_area +// Access: Private, Static +// Description: Computes the area of the indicated frame. +//////////////////////////////////////////////////////////////////// +INLINE float PGItem:: +compute_area(const LVecBase4f &frame) { + return (frame[1] - frame[0]) * (frame[3] - frame[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::compare_largest +// Access: Private, Static +// Description: Given that largest is the pointer to the largest +// frame so far, and largest_area is its area, compare +// that to the area of the new frame; if the new frame +// is larger, adjust largest and largest_area +// appropriately. +//////////////////////////////////////////////////////////////////// +INLINE void PGItem:: +compare_largest(const LVecBase4f *&largest, float &largest_area, + const LVecBase4f *new_frame) { + float new_area = compute_area(*new_frame); + if (new_area > largest_area) { + largest = new_frame; + largest_area = new_area; + } +} diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index 44b9ff4675..6d36e3dff3 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -20,7 +20,7 @@ #include "pgMouseWatcherParameter.h" #include "pgCullTraverser.h" #include "config_pgui.h" - +#include "boundingVolume.h" #include "pandaNode.h" #include "sceneGraphReducer.h" #include "throw_event.h" @@ -29,6 +29,7 @@ #include "cullTraverser.h" #include "cullTraverserData.h" #include "cullBinManager.h" +#include "clipPlaneAttrib.h" #include "dcast.h" #ifdef HAVE_AUDIO @@ -40,6 +41,16 @@ PT(TextNode) PGItem::_text_node; PGItem *PGItem::_focus_item = (PGItem *)NULL; PGItem::BackgroundFocus PGItem::_background_focus; + +//////////////////////////////////////////////////////////////////// +// Function: is_right +// Description: Returns true if the 2-d v1 is to the right of v2. +//////////////////////////////////////////////////////////////////// +INLINE bool +is_right(const LVector2f &v1, const LVector2f &v2) { + return (-v1[0] * v2[1] + v1[1] * v2[0]) > 0; +} + //////////////////////////////////////////////////////////////////// // Function: PGItem::Constructor // Access: Published @@ -49,6 +60,7 @@ PGItem:: PGItem(const string &name) : PandaNode(name) { + _notify = NULL; _has_frame = false; _frame.set(0, 0, 0, 0); _region = new PGMouseWatcherRegion(this); @@ -63,6 +75,11 @@ PGItem(const string &name) : //////////////////////////////////////////////////////////////////// PGItem:: ~PGItem() { + if (_notify != (PGItemNotify *)NULL) { + _notify->remove_item(this); + _notify = NULL; + } + nassertv(_region->_item == this); _region->_item = (PGItem *)NULL; @@ -85,6 +102,7 @@ PGItem(const PGItem ©) : _state(copy._state), _flags(copy._flags) { + _notify = NULL; _region = new PGMouseWatcherRegion(this); } @@ -102,38 +120,33 @@ make_copy() const { } //////////////////////////////////////////////////////////////////// -// Function: PGItem::xform +// Function: PGItem::transform_changed // Access: Protected, Virtual -// Description: Transforms the contents of this node by the indicated -// matrix, if it means anything to do so. For most -// kinds of nodes, this does nothing. +// Description: Called after the node's transform has been changed +// for any reason, this just provides a hook so derived +// classes can do something special in this case. //////////////////////////////////////////////////////////////////// void PGItem:: -xform(const LMatrix4f &mat) { - // Transform the frame. - LPoint3f ll(_frame[0], 0.0f, _frame[2]); - LPoint3f ur(_frame[1], 0.0f, _frame[3]); - ll = ll * mat; - ur = ur * mat; - _frame.set(ll[0], ur[0], ll[2], ur[2]); - - // Transform the individual states and their frame styles. - StateDefs::iterator di; - for (di = _state_defs.begin(); di != _state_defs.end(); ++di) { - NodePath &root = (*di)._root; - // Apply the matrix to the previous transform. - root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat))); - - // Now flatten the transform into the subgraph. - SceneGraphReducer gr; - gr.apply_attribs(root.node()); - - // Transform the frame style too. - if ((*di)._frame_style.xform(mat)) { - (*di)._frame_stale = true; - } +transform_changed() { + PandaNode::transform_changed(); + if (has_notify()) { + get_notify()->item_transform_changed(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::draw_mask_changed +// Access: Protected, Virtual +// Description: Called after the node's draw_mask has been changed +// for any reason, this just provides a hook so derived +// classes can do something special in this case. +//////////////////////////////////////////////////////////////////// +void PGItem:: +draw_mask_changed() { + PandaNode::draw_mask_changed(); + if (has_notify()) { + get_notify()->item_draw_mask_changed(this); } - mark_bound_stale(); } //////////////////////////////////////////////////////////////////// @@ -221,8 +234,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // which only provides one. sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff); - activate_region(transform, sort); - pg_trav->_top->add_region(get_region()); + if (activate_region(transform, sort, data._state->get_clip_plane())) { + pg_trav->_top->add_region(get_region()); + } } } @@ -273,14 +287,53 @@ recompute_internal_bound() { return bound; } +//////////////////////////////////////////////////////////////////// +// Function: PGItem::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. +//////////////////////////////////////////////////////////////////// +void PGItem:: +xform(const LMatrix4f &mat) { + // Transform the frame. + LPoint3f ll(_frame[0], 0.0f, _frame[2]); + LPoint3f ur(_frame[1], 0.0f, _frame[3]); + ll = ll * mat; + ur = ur * mat; + _frame.set(ll[0], ur[0], ll[2], ur[2]); + + // Transform the individual states and their frame styles. + StateDefs::iterator di; + for (di = _state_defs.begin(); di != _state_defs.end(); ++di) { + NodePath &root = (*di)._root; + // Apply the matrix to the previous transform. + root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat))); + + // Now flatten the transform into the subgraph. + SceneGraphReducer gr; + gr.apply_attribs(root.node()); + + // Transform the frame style too. + if ((*di)._frame_style.xform(mat)) { + (*di)._frame_stale = true; + } + } + mark_bound_stale(); +} + //////////////////////////////////////////////////////////////////// // Function: PGItem::activate_region // Access: Public // Description: Applies the indicated scene graph transform and order // as determined by the traversal from PGTop. +// +// The return value is true if the region is valid, or +// false if it is empty or completely clipped. //////////////////////////////////////////////////////////////////// -void PGItem:: -activate_region(const LMatrix4f &transform, int sort) { +bool PGItem:: +activate_region(const LMatrix4f &transform, int sort, + const ClipPlaneAttrib *cpa) { // Transform all four vertices, and get the new bounding box. This // way the region works (mostly) even if has been rotated. LPoint3f ll(_frame[0], 0.0f, _frame[2]); @@ -291,10 +344,54 @@ activate_region(const LMatrix4f &transform, int sort) { lr = lr * transform; ul = ul * transform; ur = ur * transform; - _region->set_frame(min(min(ll[0], lr[0]), min(ul[0], ur[0])), - max(max(ll[0], lr[0]), max(ul[0], ur[0])), - min(min(ll[2], lr[2]), min(ul[2], ur[2])), - max(max(ll[2], lr[2]), max(ul[2], ur[2]))); + + if (cpa != (ClipPlaneAttrib *)NULL && cpa->get_num_on_planes() != 0) { + // Apply the clip plane(s) now that we are here in world space. + + pvector points; + points.reserve(4); + points.push_back(LPoint2f(ll[0], ll[2])); + points.push_back(LPoint2f(lr[0], lr[2])); + points.push_back(LPoint2f(ur[0], ur[2])); + points.push_back(LPoint2f(ul[0], ul[2])); + + int num_on_planes = cpa->get_num_on_planes(); + for (int i = 0; i < num_on_planes; ++i) { + NodePath plane_path = cpa->get_on_plane(i); + Planef plane = DCAST(PlaneNode, plane_path.node())->get_plane(); + plane.xform(plane_path.get_net_transform()->get_mat()); + + // We ignore the y coordinate, assuming the frame is still in + // the X-Z plane after being transformed. Not sure if we really + // need to support general 3-D transforms on 2-D objects. + clip_frame(points, plane); + } + + if (points.empty()) { + // Turns out it's completely clipped after all. + return false; + } + + pvector::iterator pi; + pi = points.begin(); + LVecBase4f frame((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]); + ++pi; + while (pi != points.end()) { + frame[0] = min(frame[0], (*pi)[0]); + frame[1] = max(frame[1], (*pi)[0]); + frame[2] = min(frame[2], (*pi)[1]); + frame[3] = max(frame[3], (*pi)[1]); + ++pi; + } + _region->set_frame(frame); + + } else { + // Since there are no clip planes involved, just set the frame. + _region->set_frame(min(min(ll[0], lr[0]), min(ul[0], ur[0])), + max(max(ll[0], lr[0]), max(ul[0], ur[0])), + min(min(ll[2], lr[2]), min(ul[2], ur[2])), + max(max(ll[2], lr[2]), max(ul[2], ur[2]))); + } _region->set_sort(sort); _region->set_active(true); @@ -302,6 +399,8 @@ activate_region(const LMatrix4f &transform, int sort) { // calculate the inverse of this transform, which is needed to // go back to the frame space. _frame_inv_xform.invert_from(transform); + + return true; } //////////////////////////////////////////////////////////////////// @@ -319,6 +418,11 @@ enter(const MouseWatcherParameter ¶m) { string event = get_enter_event(); play_sound(event); throw_event(event, EventParameter(ep)); + + if (has_notify()) { + get_notify()->item_enter(this, param); + } + //pgui_cat.info() << get_name() << "::enter()" << endl; } @@ -337,6 +441,11 @@ exit(const MouseWatcherParameter ¶m) { string event = get_exit_event(); play_sound(event); throw_event(event, EventParameter(ep)); + + if (has_notify()) { + get_notify()->item_exit(this, param); + } + //pgui_cat.info() << get_name() << "::exit()" << endl; } @@ -356,6 +465,11 @@ within(const MouseWatcherParameter ¶m) { string event = get_within_event(); play_sound(event); throw_event(event, EventParameter(ep)); + + if (has_notify()) { + get_notify()->item_within(this, param); + } + //pgui_cat.info() << get_name() << "::within()" << endl; } @@ -372,6 +486,11 @@ without(const MouseWatcherParameter ¶m) { string event = get_without_event(); play_sound(event); throw_event(event, EventParameter(ep)); + + if (has_notify()) { + get_notify()->item_without(this, param); + } + //pgui_cat.info() << get_name() << "::without()" << endl; } @@ -386,6 +505,11 @@ focus_in() { string event = get_focus_in_event(); play_sound(event); throw_event(event); + + if (has_notify()) { + get_notify()->item_focus_in(this); + } + //pgui_cat.info() << get_name() << "::focus_in()" << endl; } @@ -400,6 +524,11 @@ focus_out() { string event = get_focus_out_event(); play_sound(event); throw_event(event); + + if (has_notify()) { + get_notify()->item_focus_out(this); + } + //pgui_cat.info() << get_name() << "::focus_out()" << endl; } @@ -418,6 +547,11 @@ press(const MouseWatcherParameter ¶m, bool background) { play_sound(event); throw_event(event, EventParameter(ep)); } + + if (has_notify()) { + get_notify()->item_press(this, param); + } + //pgui_cat.info() << get_name() << "::press()" << endl; } @@ -436,6 +570,11 @@ release(const MouseWatcherParameter ¶m, bool background) { play_sound(event); throw_event(event, EventParameter(ep)); } + + if (has_notify()) { + get_notify()->item_release(this, param); + } + //pgui_cat.info() << get_name() << "::release()" << endl; } @@ -452,6 +591,10 @@ keystroke(const MouseWatcherParameter ¶m, bool background) { string event = get_keystroke_event(); play_sound(event); throw_event(event, EventParameter(ep)); + + if (has_notify()) { + get_notify()->item_keystroke(this, param); + } } } @@ -464,6 +607,11 @@ keystroke(const MouseWatcherParameter ¶m, bool background) { void PGItem:: candidate(const MouseWatcherParameter ¶m, bool background) { // We don't throw sound events for candidate selections for now. + if (!background) { + if (has_notify()) { + get_notify()->item_candidate(this, param); + } + } } //////////////////////////////////////////////////////////////////// @@ -474,11 +622,9 @@ candidate(const MouseWatcherParameter ¶m, bool background) { //////////////////////////////////////////////////////////////////// void PGItem:: move(const MouseWatcherParameter ¶m) { - PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param); - string event = get_press_event(param.get_button()); - play_sound(event); - throw_event(event, EventParameter(ep)); - //pgui_cat.info() << get_name() << "::move()" << endl; + if (has_notify()) { + get_notify()->item_move(this, param); + } } //////////////////////////////////////////////////////////////////// @@ -861,6 +1007,109 @@ play_sound(const string &event) { #endif // HAVE_AUDIO } +//////////////////////////////////////////////////////////////////// +// Function: PGItem::reduce_region +// Access: Protected +// Description: The frame parameter is an in/out parameter. This +// function adjusts frame so that it represents the +// largest part of the rectangular region passed in, +// that does not overlap with the rectangular region of +// the indicated obscurer. If the obscurer is NULL, or +// is a hidden node, it is not considered and the frame +// is left unchanged. +// +// This is used by slider bars and scroll frames, which +// have to automatically figure out how much space they +// have to work with after allowing space for scroll +// bars and buttons. +//////////////////////////////////////////////////////////////////// +void PGItem:: +reduce_region(LVecBase4f &frame, PGItem *obscurer) const { + if (obscurer != (PGItem *)NULL && !obscurer->get_draw_mask().is_zero()) { + LVecBase4f oframe = get_relative_frame(obscurer); + + // Determine the four rectangular regions on the four sides of the + // obscuring region. + LVecBase4f right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]); + LVecBase4f left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]); + LVecBase4f above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]); + LVecBase4f below(frame[0], frame[1], frame[2], min(frame[3], oframe[2])); + + // Now choose the largest of those four. + const LVecBase4f *largest = &right; + float largest_area = compute_area(*largest); + compare_largest(largest, largest_area, &left); + compare_largest(largest, largest_area, &above); + compare_largest(largest, largest_area, &below); + + frame = *largest; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_relative_frame +// Access: Protected +// Description: Returns the LVecBase4f frame of the indicated item, +// converted into this item's coordinate space. +// Presumably, item is a child of this node. +//////////////////////////////////////////////////////////////////// +LVecBase4f PGItem:: +get_relative_frame(PGItem *item) const { + NodePath this_np = NodePath::any_path((PGItem *)this); + NodePath item_np = this_np.find_path_to(item); + if (item_np.is_empty()) { + item_np = NodePath::any_path(item); + } + const LVecBase4f &orig_frame = item->get_frame(); + LMatrix4f transform = item_np.get_mat(this_np); + + // Transform the item's frame into the PGScrollFrame's + // coordinate space. Transform all four vertices, and get the + // new bounding box. This way the region works (mostly) even if + // has been rotated. + LPoint3f ll(orig_frame[0], 0.0f, orig_frame[2]); + LPoint3f lr(orig_frame[1], 0.0f, orig_frame[2]); + LPoint3f ul(orig_frame[0], 0.0f, orig_frame[3]); + LPoint3f ur(orig_frame[1], 0.0f, orig_frame[3]); + ll = ll * transform; + lr = lr * transform; + ul = ul * transform; + ur = ur * transform; + + return LVecBase4f(min(min(ll[0], lr[0]), min(ul[0], ur[0])), + max(max(ll[0], lr[0]), max(ul[0], ur[0])), + min(min(ll[2], lr[2]), min(ul[2], ur[2])), + max(max(ll[2], lr[2]), max(ul[2], ur[2]))); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::mouse_to_local +// Access: Protected +// Description: Converts from the 2-d mouse coordinates into the +// coordinate space of the item. +//////////////////////////////////////////////////////////////////// +LPoint3f PGItem:: +mouse_to_local(const LPoint2f &mouse_point) const { + // This is ambiguous if the PGItem has multiple instances. Why + // would you do that, anyway? + NodePath this_np((PGItem *)this); + CPT(TransformState) inv_transform = NodePath().get_transform(this_np); + return inv_transform->get_mat().xform_point(LVector3f::rfu(mouse_point[0], 0, mouse_point[1])); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::frame_changed +// Access: Protected, Virtual +// Description: Called when the user changes the frame size. +//////////////////////////////////////////////////////////////////// +void PGItem:: +frame_changed() { + mark_frames_stale(); + if (has_notify()) { + get_notify()->item_frame_changed(this); + } +} + //////////////////////////////////////////////////////////////////// // Function: PGItem::slot_state_def // Access: Private @@ -919,3 +1168,91 @@ mark_frames_stale() { } mark_bound_stale(); } + +//////////////////////////////////////////////////////////////////// +// Function: PGItem::clip_frame +// Access: Private +// Description: Clips the four corners of the item's frame by the +// indicated clipping plane, and modifies the points to +// reflect the new set of clipped points. +// +// The return value is true if the set of points is +// unmodified (all points are behind the clip plane), or +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool PGItem:: +clip_frame(pvector &source_points, const Planef &plane) const { + if (source_points.empty()) { + return true; + } + + LPoint3f from3d; + LVector3f delta3d; + if (!plane.intersects_plane(from3d, delta3d, Planef(LVector3f(0, 1, 0), LPoint3f::zero()))) { + // The clipping plane is parallel to the polygon. The polygon is + // either all in or all out. + if (plane.dist_to_plane(LPoint3f::zero()) < 0.0) { + // A point within the polygon is behind the clipping plane: the + // polygon is all in. + return true; + } + return false; + } + + // Project the line of intersection into the X-Z plane. Now we have + // a 2-d clipping line. + LPoint2f from2d(from3d[0], from3d[2]); + LVector2f delta2d(delta3d[0], delta3d[2]); + + float a = -delta2d[1]; + float b = delta2d[0]; + float c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0]; + + // Now walk through the points. Any point on the left of our line + // gets removed, and the line segment clipped at the point of + // intersection. + + // We might increase the number of vertices by as many as 1, if the + // plane clips off exactly one corner. (We might also decrease the + // number of vertices, or keep them the same number.) + pvector new_points; + new_points.reserve(source_points.size() + 1); + + LPoint2f last_point = source_points.back(); + bool last_is_in = !is_right(last_point - from2d, delta2d); + bool all_in = last_is_in; + pvector::const_iterator pi; + for (pi = source_points.begin(); pi != source_points.end(); ++pi) { + const LPoint2f &this_point = (*pi); + bool this_is_in = !is_right(this_point - from2d, delta2d); + + // There appears to be a compiler bug in gcc 4.0: we need to + // extract this comparison outside of the if statement. + bool crossed_over = (this_is_in != last_is_in); + if (crossed_over) { + // We have just crossed over the clipping line. Find the point + // of intersection. + LVector2f d = this_point - last_point; + float denom = (a * d[0] + b * d[1]); + if (denom != 0.0) { + float t = -(a * last_point[0] + b * last_point[1] + c) / denom; + LPoint2f p = last_point + t * d; + + new_points.push_back(p); + last_is_in = this_is_in; + } + } + + if (this_is_in) { + // We are behind the clipping line. Keep the point. + new_points.push_back(this_point); + } else { + all_in = false; + } + + last_point = this_point; + } + + source_points.swap(new_points); + return all_in; +} diff --git a/panda/src/pgui/pgItem.h b/panda/src/pgui/pgItem.h index 8aa7deba07..f0733eb319 100644 --- a/panda/src/pgui/pgItem.h +++ b/panda/src/pgui/pgItem.h @@ -23,6 +23,7 @@ #include "pgMouseWatcherRegion.h" #include "pgFrameStyle.h" +#include "pgItemNotify.h" #include "pandaNode.h" #include "nodePath.h" @@ -30,12 +31,13 @@ #include "pointerTo.h" #include "audioSound.h" #include "textNode.h" - +#include "plane.h" #include "pmap.h" class PGTop; class MouseWatcherParameter; class AudioSound; +class ClipPlaneAttrib; //////////////////////////////////////////////////////////////////// // Class : PGItem @@ -60,14 +62,18 @@ protected: PGItem(const PGItem ©); virtual PandaNode *make_copy() const; - virtual void xform(const LMatrix4f &mat); + virtual void transform_changed(); + virtual void draw_mask_changed(); + virtual bool has_cull_callback() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); virtual BoundingVolume *recompute_internal_bound(); public: - void activate_region(const LMatrix4f &transform, int sort); + virtual void xform(const LMatrix4f &mat); + bool activate_region(const LMatrix4f &transform, int sort, + const ClipPlaneAttrib *cpa); INLINE PGMouseWatcherRegion *get_region() const; virtual void enter(const MouseWatcherParameter ¶m); @@ -87,6 +93,10 @@ public: static void background_keystroke(const MouseWatcherParameter ¶m); static void background_candidate(const MouseWatcherParameter ¶m); + INLINE void set_notify(PGItemNotify *notify); + INLINE bool has_notify() const; + INLINE PGItemNotify *get_notify() const; + PUBLISHED: INLINE void set_frame(float left, float right, float bottom, float top); INLINE void set_frame(const LVecBase4f &frame); @@ -158,11 +168,28 @@ PUBLISHED: protected: void play_sound(const string &event); + void reduce_region(LVecBase4f &clip, PGItem *obscurer) const; + void reduce_region(LVecBase4f &frame, float px, float py) const; + LVecBase4f get_relative_frame(PGItem *item) const; + LPoint3f mouse_to_local(const LPoint2f &mouse_point) const; + + virtual void frame_changed(); + private: void slot_state_def(int state); void update_frame(int state); void mark_frames_stale(); + INLINE static float compute_area(const LVecBase4f &frame); + INLINE static void compare_largest(const LVecBase4f *&largest, + float &largest_area, + const LVecBase4f *new_frame); + + bool clip_frame(pvector &source_points, const Planef &plane) const; + +private: + PGItemNotify *_notify; + bool _has_frame; LVecBase4f _frame; int _state; diff --git a/panda/src/pgui/pgItemNotify.I b/panda/src/pgui/pgItemNotify.I new file mode 100644 index 0000000000..fbb79e8cda --- /dev/null +++ b/panda/src/pgui/pgItemNotify.I @@ -0,0 +1,27 @@ +// Filename: pgItemNotify.I +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGItemNotify:: +PGItemNotify() { +} diff --git a/panda/src/pgui/pgItemNotify.cxx b/panda/src/pgui/pgItemNotify.cxx new file mode 100644 index 0000000000..2dd959622f --- /dev/null +++ b/panda/src/pgui/pgItemNotify.cxx @@ -0,0 +1,202 @@ +// Filename: pgItemNotify.cxx +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "pgItemNotify.h" +#include "pgItem.h" + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGItemNotify:: +~PGItemNotify() { + while (!_items.empty()) { + // Disconnect all of the items that are connected to this + // object. + PGItem *item = (*_items.begin()); + nassertv(item->get_notify() == this); + (*_items.begin())->set_notify(NULL); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_transform_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's local transform +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_transform_changed(PGItem *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_frame_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's frame +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_frame_changed(PGItem *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_draw_mask_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's draw_mask +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_draw_mask_changed(PGItem *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_enter +// Access: Protected, Virtual +// Description: Called whenever the "enter" event is triggered on a +// watched PGItem. See PGItem::enter(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_enter(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_exit +// Access: Protected, Virtual +// Description: Called whenever the "exit" event is triggered on a +// watched PGItem. See PGItem::exit(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_exit(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_within +// Access: Protected, Virtual +// Description: Called whenever the "within" event is triggered on a +// watched PGItem. See PGItem::within(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_within(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_without +// Access: Protected, Virtual +// Description: Called whenever the "without" event is triggered on a +// watched PGItem. See PGItem::without(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_without(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_focus_in +// Access: Protected, Virtual +// Description: Called whenever the "focus_in" event is triggered on a +// watched PGItem. See PGItem::focus_in(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_focus_in(PGItem *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_focus_out +// Access: Protected, Virtual +// Description: Called whenever the "focus_out" event is triggered on a +// watched PGItem. See PGItem::focus_out(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_focus_out(PGItem *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_press +// Access: Protected, Virtual +// Description: Called whenever the "press" event is triggered on a +// watched PGItem. See PGItem::press(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_press(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_release +// Access: Protected, Virtual +// Description: Called whenever the "release" event is triggered on a +// watched PGItem. See PGItem::release(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_release(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_keystroke +// Access: Protected, Virtual +// Description: Called whenever the "keystroke" event is triggered on a +// watched PGItem. See PGItem::keystroke(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_keystroke(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_candidate +// Access: Protected, Virtual +// Description: Called whenever the "candidate" event is triggered on a +// watched PGItem. See PGItem::candidate(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_candidate(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::item_move +// Access: Protected, Virtual +// Description: Called whenever the "move" event is triggered on a +// watched PGItem. See PGItem::move(). +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +item_move(PGItem *, const MouseWatcherParameter &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::add_item +// Access: Protected, Virtual +// Description: Called by PGItem when a new item is set up to +// notify this object. +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +add_item(PGItem *item) { + bool inserted = _items.insert(item).second; + nassertv(inserted); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGItemNotify::remove_item +// Access: Protected, Virtual +// Description: Called by PGItem when an item is no longer set up +// to notify this object. +//////////////////////////////////////////////////////////////////// +void PGItemNotify:: +remove_item(PGItem *item) { + Items::iterator bi; + bi = _items.find(item); + nassertv(bi != _items.end()); + _items.erase(bi); +} diff --git a/panda/src/pgui/pgItemNotify.h b/panda/src/pgui/pgItemNotify.h new file mode 100644 index 0000000000..89883601e9 --- /dev/null +++ b/panda/src/pgui/pgItemNotify.h @@ -0,0 +1,67 @@ +// Filename: pgItemNotify.h +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGITEMNOTIFY_H +#define PGITEMNOTIFY_H + +#include "pandabase.h" + +class PGItem; + +//////////////////////////////////////////////////////////////////// +// Class : PGItemNotify +// Description : Objects that inherit from this class can receive +// specialized messages when PGItems change in certain +// ways. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGItemNotify { +public: + INLINE PGItemNotify(); + virtual ~PGItemNotify(); + +protected: + virtual void item_transform_changed(PGItem *item); + virtual void item_frame_changed(PGItem *item); + virtual void item_draw_mask_changed(PGItem *item); + + virtual void item_enter(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_exit(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_within(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_without(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_focus_in(PGItem *item); + virtual void item_focus_out(PGItem *item); + virtual void item_press(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_release(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_keystroke(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_candidate(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_move(PGItem *item, const MouseWatcherParameter ¶m); + +protected: + void add_item(PGItem *item); + void remove_item(PGItem *item); + +private: + typedef pset Items; + Items _items; + + friend class PGItem; +}; + +#include "pgItemNotify.I" + +#endif diff --git a/panda/src/pgui/pgScrollFrame.I b/panda/src/pgui/pgScrollFrame.I new file mode 100644 index 0000000000..d9a865492c --- /dev/null +++ b/panda/src/pgui/pgScrollFrame.I @@ -0,0 +1,245 @@ +// Filename: pgScrollFrame.I +// Created by: drose (17Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::set_virtual_frame +// Access: Published +// Description: Sets the bounding rectangle of the virtual frame. +// This is the size of the large, virtual canvas which +// we can see only a portion of at any given time. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +set_virtual_frame(float left, float right, float bottom, float top) { + set_virtual_frame(LVecBase4f(left, right, bottom, top)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::set_virtual_frame +// Access: Published +// Description: Sets the bounding rectangle of the virtual frame. +// This is the size of the large, virtual canvas which +// we can see only a portion of at any given time. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +set_virtual_frame(const LVecBase4f &frame) { + _has_virtual_frame = true; + _virtual_frame = frame; + + if (_auto_hide) { + _needs_remanage = true; + } + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::get_virtual_frame +// Access: Published +// Description: Returns the bounding rectangle of the virtual frame. +// See set_virtual_frame(). If has_virtual_frame() is +// false, this returns the item's clip frame. +//////////////////////////////////////////////////////////////////// +INLINE const LVecBase4f &PGScrollFrame:: +get_virtual_frame() const { + return _has_virtual_frame ? _virtual_frame : get_clip_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::has_virtual_frame +// Access: Published +// Description: Returns true if the virtual frame has a bounding +// rectangle; see set_virtual_frame(). Most +// PGScrollFrame objects will have a virtual frame. +//////////////////////////////////////////////////////////////////// +INLINE bool PGScrollFrame:: +has_virtual_frame() const { + return _has_virtual_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::clear_virtual_frame +// Access: Published +// Description: Removes the virtual frame from the item. This +// effectively sets the virtual frame to the same size +// as the clip frame. Scrolling will no longer be +// possible. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +clear_virtual_frame() { + _has_virtual_frame = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::set_manage_pieces +// Access: Published +// Description: Sets the manage_pieces flag. When this is true, the +// sub-pieces of the scroll frame--that is, the two +// scroll bars--are automatically positioned and/or +// resized when the scroll frame's overall frame is +// changed. They are also automatically resized to fill +// in the gap when one or the other is hidden. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +set_manage_pieces(bool manage_pieces) { + _manage_pieces = manage_pieces; + _needs_remanage = true; + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::get_manage_pieces +// Access: Published +// Description: Returns the manage_pieces flag. See +// set_manage_pieces(). +//////////////////////////////////////////////////////////////////// +INLINE bool PGScrollFrame:: +get_manage_pieces() const { + return _manage_pieces; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::set_auto_hide +// Access: Published +// Description: Sets the auto_hide flag. When this is true, the +// two scroll bars are automatically hidden if they are +// not needed (that is, if the virtual frame would fit +// within the clip frame without them), and they are +// automatically shown when they are needed. +// +// Setting this flag true forces the manage_pieces flag +// to also be set true. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +set_auto_hide(bool auto_hide) { + _auto_hide = auto_hide; + if (_auto_hide) { + set_manage_pieces(true); + _needs_remanage = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::get_auto_hide +// Access: Published +// Description: Returns the auto_hide flag. See +// set_auto_hide(). +//////////////////////////////////////////////////////////////////// +INLINE bool PGScrollFrame:: +get_auto_hide() const { + return _auto_hide; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::set_horizontal_slider +// Access: Published +// Description: Sets the PGSliderBar object that will serve as the +// horizontal scroll bar for this frame. It is your +// responsibility to parent this slider bar to the frame +// and move it to the appropriate place. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +set_horizontal_slider(PGSliderBar *horizontal_slider) { + if (_horizontal_slider != (PGSliderBar *)NULL) { + _horizontal_slider->set_notify(NULL); + } + _horizontal_slider = horizontal_slider; + if (_horizontal_slider != (PGSliderBar *)NULL) { + _horizontal_slider->set_notify(this); + } + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::clear_horizontal_slider +// Access: Published +// Description: Removes the horizontal scroll bar from control of the +// frame. It is your responsibility to actually remove +// or hide the object itself. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +clear_horizontal_slider() { + set_horizontal_slider(NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::get_horizontal_slider +// Access: Published +// Description: Returns the PGSliderBar that serves as the horizontal +// scroll bar for this frame, if any, or NULL if it is +// not set. +//////////////////////////////////////////////////////////////////// +INLINE PGSliderBar *PGScrollFrame:: +get_horizontal_slider() const { + return _horizontal_slider; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::set_vertical_slider +// Access: Published +// Description: Sets the PGSliderBar object that will serve as the +// vertical scroll bar for this frame. It is your +// responsibility to parent this slider bar to the frame +// and move it to the appropriate place. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +set_vertical_slider(PGSliderBar *vertical_slider) { + if (_vertical_slider != (PGSliderBar *)NULL) { + _vertical_slider->set_notify(NULL); + } + _vertical_slider = vertical_slider; + if (_vertical_slider != (PGSliderBar *)NULL) { + _vertical_slider->set_notify(this); + } + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::clear_vertical_slider +// Access: Published +// Description: Removes the vertical scroll bar from control of the +// frame. It is your responsibility to actually remove +// or hide the object itself. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +clear_vertical_slider() { + set_vertical_slider(NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::get_vertical_slider +// Access: Published +// Description: Returns the PGSliderBar that serves as the vertical +// scroll bar for this frame, if any, or NULL if it is +// not set. +//////////////////////////////////////////////////////////////////// +INLINE PGSliderBar *PGScrollFrame:: +get_vertical_slider() const { + return _vertical_slider; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::recompute +// Access: Published +// Description: Forces the PGScrollFrame to recompute itself right +// now. Normally this should not be required. +//////////////////////////////////////////////////////////////////// +INLINE void PGScrollFrame:: +recompute() { + recompute_clip(); + recompute_canvas(); +} diff --git a/panda/src/pgui/pgScrollFrame.cxx b/panda/src/pgui/pgScrollFrame.cxx new file mode 100644 index 0000000000..bb35aacae0 --- /dev/null +++ b/panda/src/pgui/pgScrollFrame.cxx @@ -0,0 +1,445 @@ +// Filename: pgScrollFrame.cxx +// Created by: drose (17Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "pgScrollFrame.h" + +TypeHandle PGScrollFrame::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +PGScrollFrame:: +PGScrollFrame(const string &name) : PGVirtualFrame(name) +{ + _needs_remanage = false; + _needs_recompute_canvas = false; + _needs_recompute_clip = false; + _has_virtual_frame = false; + _virtual_frame.set(0.0f, 0.0f, 0.0f, 0.0f); + _manage_pieces = false; + _auto_hide = false; + _horizontal_slider = NULL; + _vertical_slider = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGScrollFrame:: +~PGScrollFrame() { + set_horizontal_slider(NULL); + set_vertical_slider(NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +PGScrollFrame:: +PGScrollFrame(const PGScrollFrame ©) : + PGVirtualFrame(copy), + _has_virtual_frame(copy._has_virtual_frame), + _virtual_frame(copy._virtual_frame), + _manage_pieces(copy._manage_pieces), + _auto_hide(copy._auto_hide) +{ + _needs_remanage = false; + _needs_recompute_canvas = true; + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::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 *PGScrollFrame:: +make_copy() const { + return new PGScrollFrame(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::has_cull_callback +// Access: Protected, Virtual +// Description: Should be overridden by derived classes to return +// true if cull_callback() has been defined. Otherwise, +// returns false to indicate cull_callback() does not +// need to be called for this node during the cull +// traversal. +//////////////////////////////////////////////////////////////////// +bool PGScrollFrame:: +has_cull_callback() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::cull_callback +// Access: Protected, Virtual +// Description: If has_cull_callback() returns true, this function +// will be called during the cull traversal to perform +// any additional operations that should be performed at +// cull time. This may include additional manipulation +// of render state or additional visible/invisible +// decisions, or any other arbitrary operation. +// +// By the time this function is called, the node has +// already passed the bounding-volume test for the +// viewing frustum, and the node's transform and state +// have already been applied to the indicated +// CullTraverserData object. +// +// The return value is true if this node should be +// visible, or false if it should be culled. +//////////////////////////////////////////////////////////////////// +bool PGScrollFrame:: +cull_callback(CullTraverser *trav, CullTraverserData &data) { + if (_manage_pieces && _needs_remanage) { + remanage(); + } + if (_needs_recompute_clip) { + recompute_clip(); + } + if (_needs_recompute_canvas) { + recompute_canvas(); + } + return PGVirtualFrame::cull_callback(trav, data); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +xform(const LMatrix4f &mat) { + PGVirtualFrame::xform(mat); + + _needs_remanage = true; + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::setup +// Access: Published +// Description: Creates a PGScrollFrame with the indicated +// dimensions, and the indicated virtual frame. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +setup(float width, float height, + float left, float right, float bottom, float top, + float slider_width, float bevel) { + set_state(0); + clear_state_def(0); + + set_frame(0, width, 0, height); + + PGFrameStyle style; + style.set_width(bevel, bevel); + + style.set_color(0.8f, 0.8f, 0.8f, 1.0f); + style.set_type(PGFrameStyle::T_ridge); + set_frame_style(0, style); + + set_clip_frame(bevel, width - bevel, + bevel, height - bevel); + + set_virtual_frame(left, right, bottom, top); + + // Remove the slider nodes created by a previous call to setup(), if + // any. + if (_horizontal_slider != (PGSliderBar *)NULL) { + remove_child(_horizontal_slider); + set_horizontal_slider(NULL); + } + if (_vertical_slider != (PGSliderBar *)NULL) { + remove_child(_vertical_slider); + set_vertical_slider(NULL); + } + + // Create new slider bars. + PT(PGSliderBar) horizontal_slider = new PGSliderBar("horizontal"); + horizontal_slider->setup_scroll_bar(false, width - slider_width - bevel * 2, slider_width, bevel); + horizontal_slider->set_transform(TransformState::make_pos(LVector3f::rfu(width / 2.0f - slider_width / 2.0f, 0, slider_width / 2.0f + bevel))); + add_child(horizontal_slider); + set_horizontal_slider(horizontal_slider); + + PT(PGSliderBar) vertical_slider = new PGSliderBar("vertical"); + vertical_slider->setup_scroll_bar(true, width - slider_width - bevel * 2, slider_width, bevel); + add_child(vertical_slider); + vertical_slider->set_transform(TransformState::make_pos(LVector3f::rfu(width - slider_width / 2.0f - bevel, 0, width / 2.0f + slider_width / 2.0f))); + set_vertical_slider(vertical_slider); + + set_manage_pieces(true); + set_auto_hide(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::remanage +// Access: Published +// Description: Manages the position and size of the scroll bars. +// Normally this should not need to be called directly. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +remanage() { + _needs_remanage = false; + + const LVecBase4f &frame = get_frame(); + LVecBase4f clip = get_frame_style(get_state()).get_internal_frame(frame); + + // Determine which scroll bars we have in the frame. + + bool got_horizontal = false; + float horizontal_width = 0.0f; + if (_horizontal_slider != (PGSliderBar *)NULL) { + got_horizontal = true; + const LVecBase4f &slider_frame = _horizontal_slider->get_frame(); + horizontal_width = slider_frame[3] - slider_frame[2]; + } + + bool got_vertical = false; + float vertical_width = 0.0f; + if (_vertical_slider != (PGSliderBar *)NULL) { + got_vertical = true; + const LVecBase4f &slider_frame = _vertical_slider->get_frame(); + vertical_width = slider_frame[1] - slider_frame[0]; + } + + if (_auto_hide) { + // Should we hide or show either of the scroll bars? TODO. + + // Start by figuring out what our maximum clipping area will be. + float clip_width = clip[1] - clip[0]; + float clip_height = clip[3] - clip[2]; + + // And our virtual frame too. + const LVecBase4f &virtual_frame = get_virtual_frame(); + float virtual_width = virtual_frame[1] - virtual_frame[0]; + float virtual_height = virtual_frame[3] - virtual_frame[2]; + + if (virtual_width <= clip_width && + virtual_height <= clip_height) { + // No need for either slider. + got_horizontal = false; + got_vertical = false; + + } else { + if (virtual_width <= clip_width - vertical_width) { + // No need for the horizontal slider. + got_horizontal = false; + } + + if (virtual_height <= clip_height - horizontal_width) { + // No need for the vertical slider. + got_vertical = false; + + // Now reconsider the need for the horizontal slider. + if (virtual_width <= clip_width) { + got_horizontal = false; + } + } + } + + // Now show or hide the sliders appropriately. + if (_horizontal_slider != (PGSliderBar *)NULL) { + if (got_horizontal) { + _horizontal_slider->set_draw_mask(DrawMask::all_on()); + } else { + _horizontal_slider->set_draw_mask(DrawMask::all_off()); + _horizontal_slider->set_ratio(0.0f); + horizontal_width = 0.0f; + } + } + if (_vertical_slider != (PGSliderBar *)NULL) { + if (got_vertical) { + _vertical_slider->set_draw_mask(DrawMask::all_on()); + } else { + _vertical_slider->set_draw_mask(DrawMask::all_off()); + _vertical_slider->set_ratio(0.0f); + vertical_width = 0.0f; + } + } + + // Showing or hiding one of the scroll bars might have set this + // flag again indirectly; we clear it again to avoid a feedback + // loop. + _needs_remanage = false; + } + + // Are either or both of the scroll bars hidden? + if (got_horizontal && _horizontal_slider->get_draw_mask().is_zero()) { + got_horizontal = false; + horizontal_width = 0.0f; + } + if (got_vertical && _vertical_slider->get_draw_mask().is_zero()) { + got_vertical = false; + vertical_width = 0.0f; + } + + if (got_horizontal) { + _horizontal_slider->set_frame(clip[0], clip[1] - vertical_width, + clip[2], clip[2] + horizontal_width); + _horizontal_slider->clear_transform(); + } + if (got_vertical) { + _vertical_slider->set_frame(clip[1] - vertical_width, clip[1], + clip[2] + horizontal_width, clip[3]); + _vertical_slider->clear_transform(); + } + + + recompute(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::frame_changed +// Access: Protected, Virtual +// Description: Called when the user changes the frame size. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +frame_changed() { + PGVirtualFrame::frame_changed(); + _needs_remanage = true; + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::item_transform_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's local transform +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +item_transform_changed(PGItem *) { + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::item_frame_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's frame +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +item_frame_changed(PGItem *) { + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::item_draw_mask_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's draw_mask +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +item_draw_mask_changed(PGItem *) { + _needs_remanage = true; + _needs_recompute_clip = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::slider_bar_adjust +// Access: Protected, Virtual +// Description: Called whenever a watched PGSliderBar's value +// has been changed by the user or programmatically. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +slider_bar_adjust(PGSliderBar *) { + _needs_recompute_canvas = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::recompute_clip +// Access: Private +// Description: Recomputes the clipping window of the PGScrollFrame, +// based on the position of the slider bars. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +recompute_clip() { + _needs_recompute_clip = false; + _needs_recompute_canvas = true; + + // Figure out how to remove the scroll bars from the clip region. + LVecBase4f clip = get_frame_style(get_state()).get_internal_frame(get_frame()); + reduce_region(clip, _horizontal_slider); + reduce_region(clip, _vertical_slider); + + set_clip_frame(clip); + + if (_horizontal_slider != (PGSliderBar *)NULL) { + _horizontal_slider->set_page_size((clip[1] - clip[0]) / (_virtual_frame[1] - _virtual_frame[0])); + } + if (_vertical_slider != (PGSliderBar *)NULL) { + _vertical_slider->set_page_size((clip[3] - clip[2]) / (_virtual_frame[3] - _virtual_frame[2])); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::recompute_canvas +// Access: Private +// Description: Recomputes the portion of the virtual canvas that is +// visible within the PGScrollFrame, based on the values +// of the slider bars. +//////////////////////////////////////////////////////////////////// +void PGScrollFrame:: +recompute_canvas() { + _needs_recompute_canvas = false; + + const LVecBase4f &clip = get_clip_frame(); + + float x = interpolate_canvas(clip[0], clip[1], + _virtual_frame[0], _virtual_frame[1], + _horizontal_slider); + + float y = interpolate_canvas(clip[3], clip[2], + _virtual_frame[3], _virtual_frame[2], + _vertical_slider); + + get_canvas_node()->set_transform(TransformState::make_pos(LVector3f::rfu(x, 0, y))); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGScrollFrame::interpolate_canvas +// Access: Private +// Description: Computes the linear translation that should be +// applied to the virtual canvas node, based on the +// corresponding slider bar's position. +//////////////////////////////////////////////////////////////////// +float PGScrollFrame:: +interpolate_canvas(float clip_min, float clip_max, + float canvas_min, float canvas_max, + PGSliderBar *slider_bar) { + float t = 0.0f; + if (slider_bar != (PGSliderBar *)NULL) { + t = slider_bar->get_ratio(); + } + + float min = clip_min - canvas_min; + float max = clip_max - canvas_max; + + return min + t * (max - min); +} diff --git a/panda/src/pgui/pgScrollFrame.h b/panda/src/pgui/pgScrollFrame.h new file mode 100644 index 0000000000..299f9d9f3f --- /dev/null +++ b/panda/src/pgui/pgScrollFrame.h @@ -0,0 +1,137 @@ +// Filename: pgScrollFrame.h +// Created by: drose (17Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGSCROLLFRAME_H +#define PGSCROLLFRAME_H + +#include "pandabase.h" + +#include "pgVirtualFrame.h" +#include "pgSliderBarNotify.h" +#include "pgSliderBar.h" + +//////////////////////////////////////////////////////////////////// +// Class : PGScrollFrame +// Description : This is a special kind of frame that pretends to be +// much larger than it actually is. You can scroll +// through the frame, as if you're looking through a +// window at the larger frame beneath. All children of +// this frame node are scrolled and clipped as if they +// were children of the larger, virtual frame. +// +// This is implemented as a specialization of +// PGVirtualFrame, which handles the meat of the virtual +// canvas. This class adds automatic support for scroll +// bars, and restricts the virtual transform to +// translate only (no scale or rotate). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGScrollFrame : public PGVirtualFrame, public PGSliderBarNotify { +PUBLISHED: + PGScrollFrame(const string &name = ""); + virtual ~PGScrollFrame(); + +protected: + PGScrollFrame(const PGScrollFrame ©); + +public: + virtual PandaNode *make_copy() const; + virtual bool has_cull_callback() const; + virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); + virtual void xform(const LMatrix4f &mat); + +PUBLISHED: + void setup(float width, float height, + float left, float right, float bottom, float top, + float slider_width, float bevel); + + INLINE void set_virtual_frame(float left, float right, float bottom, float top); + INLINE void set_virtual_frame(const LVecBase4f &virtual_frame); + INLINE const LVecBase4f &get_virtual_frame() const; + INLINE bool has_virtual_frame() const; + INLINE void clear_virtual_frame(); + + INLINE void set_manage_pieces(bool manage_pieces); + INLINE bool get_manage_pieces() const; + + INLINE void set_auto_hide(bool auto_hide); + INLINE bool get_auto_hide() const; + + INLINE void set_horizontal_slider(PGSliderBar *horizontal_slider); + INLINE void clear_horizontal_slider(); + INLINE PGSliderBar *get_horizontal_slider() const; + + INLINE void set_vertical_slider(PGSliderBar *vertical_slider); + INLINE void clear_vertical_slider(); + INLINE PGSliderBar *get_vertical_slider() const; + + void remanage(); + INLINE void recompute(); + +protected: + virtual void frame_changed(); + + virtual void item_transform_changed(PGItem *item); + virtual void item_frame_changed(PGItem *item); + virtual void item_draw_mask_changed(PGItem *item); + virtual void slider_bar_adjust(PGSliderBar *slider_bar); + +private: + void recompute_clip(); + + void recompute_canvas(); + float interpolate_canvas(float clip_min, float clip_max, + float canvas_min, float canvas_max, + PGSliderBar *slider_bar); + +private: + bool _needs_remanage; + bool _needs_recompute_clip; + bool _needs_recompute_canvas; + + LVecBase4f _orig_clip_frame; + + bool _has_virtual_frame; + LVecBase4f _virtual_frame; + + bool _manage_pieces; + bool _auto_hide; + + PT(PGSliderBar) _horizontal_slider; + PT(PGSliderBar) _vertical_slider; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PGVirtualFrame::init_type(); + register_type(_type_handle, "PGScrollFrame", + PGVirtualFrame::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 "pgScrollFrame.I" + +#endif diff --git a/panda/src/pgui/pgSliderBar.I b/panda/src/pgui/pgSliderBar.I index 225fd17b7a..94d38abf43 100755 --- a/panda/src/pgui/pgSliderBar.I +++ b/panda/src/pgui/pgSliderBar.I @@ -17,265 +17,419 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::set_notify +// Access: Published +// Description: Sets the object which will be notified when the +// PGSliderBar changes. Set this to NULL to disable +// this effect. The PGSliderBar does not retain +// ownership of the pointer; it is your responsibility +// to ensure that the notify object does not destruct. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +set_notify(PGSliderBarNotify *notify) { + PGItem::set_notify(notify); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_notify +// Access: Published +// Description: Returns the object which will be notified when the +// PGSliderBar changes, if any. Returns NULL if there +// is no such object configured. +//////////////////////////////////////////////////////////////////// +INLINE PGSliderBarNotify *PGSliderBar:: +get_notify() const { + return (PGSliderBarNotify *)PGItem::get_notify(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::set_axis +// Access: Published +// Description: Specifies the axis of the slider bar's motion. This +// should be only one of four vectors: (1, 0, 0), (0, 0, +// 1), (-1, 0, 0), or (0, 0, -1). +// +// This specifies the vector in which the thumb moves +// when it is moving from the minimum to the maximum +// value. +// +// The axis must be parallel to one of the screen axes, +// and it must be normalized. Hence, it may only be one +// of the above four possibilities; anything else is an +// error and will result in indeterminate behavior. +// +// Normally, you should not try to set the axis +// directly. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +set_axis(const LVector3f &axis) { + _axis = axis; + _needs_remanage = true; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_axis +// Access: Published +// Description: Returns the axis of the slider bar's motion. See +// set_axis(). +//////////////////////////////////////////////////////////////////// +INLINE const LVector3f &PGSliderBar:: +get_axis() const { + return _axis; +} + //////////////////////////////////////////////////////////////////// // Function: PGSliderBar::set_range // Access: Published -// Description: Sets the value at which the SliderBar indicates 100%. +// Description: Sets the minimum and maxmimum value for the slider. //////////////////////////////////////////////////////////////////// INLINE void PGSliderBar:: -set_range(float range) { - _range = range; - _bar_state = -1; +set_range(float min_value, float max_value) { + nassertv(min_value != max_value); + _min_value = min_value; + _max_value = max_value; + _needs_recompute = true; + + if (has_notify()) { + get_notify()->slider_bar_set_range(this); + } } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_range +// Function: PGSliderBar::get_min_value // Access: Published -// Description: Returns the value at which the SliderBar indicates 100%. +// Description: Returns the value when the slider is all the way to +// the left. //////////////////////////////////////////////////////////////////// INLINE float PGSliderBar:: -get_range() const { - return _range; +get_min_value() const { + return _min_value; } -/* //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_magnifier +// Function: PGSliderBar::get_max_value // Access: Published -// Description: Sets a magnifier value for the mouseWatcherRegion in -// the Y direction. -//////////////////////////////////////////////////////////////////// -INLINE void PGSliderBar:: -set_magnifier(float value) { - PGItem::_mouseWatcherMagnifier = value; -} -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_value -// Access: Published -// Description: Returns the current value of the bar. +// Description: Returns the value when the slider is all the way to +// the right. //////////////////////////////////////////////////////////////////// INLINE float PGSliderBar:: -get_magnifier() const { - return PGItem::_mouseWatcherMagnifier; +get_max_value() const { + return _max_value; } -*/ + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::set_scroll_size +// Access: Published +// Description: Specifies the amount the slider will move when the +// user clicks on the left or right buttons. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +set_scroll_size(float value) { + _scroll_value = value; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_scroll_size +// Access: Published +// Description: Returns the value last set by set_scroll_size(). +//////////////////////////////////////////////////////////////////// +INLINE float PGSliderBar:: +get_scroll_size() const { + return _scroll_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::set_page_size +// Access: Published +// Description: Specifies the amount of data contained in a single +// page. This indicates how much the thumb will jump +// when the trough is directly clicked; and if +// resize_thumb is true, it also controls the visible +// size of the thumb button. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +set_page_size(float value) { + _page_value = value; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_page_size +// Access: Published +// Description: Returns the value last set by set_page_size(). +//////////////////////////////////////////////////////////////////// +INLINE float PGSliderBar:: +get_page_size() const { + return _page_value; +} + //////////////////////////////////////////////////////////////////// // Function: PGSliderBar::set_value // Access: Published -// Description: Sets the current value of the bar. This should range -// between 0 and get_range(). +// Description: Sets the current value of the slider +// programmatically. This should range between +// get_min_value() and get_max_value(). //////////////////////////////////////////////////////////////////// INLINE void PGSliderBar:: set_value(float value) { - _value = value; - _mapped_value = _negative_mapping ? (_value*0.5 + 0.5) : _value; - _update_x = _width * (2*_mapped_value - 1); - _bar_state = -1; - _update_slider = true; + set_ratio((value - _min_value) / (_max_value - _min_value)); } //////////////////////////////////////////////////////////////////// // Function: PGSliderBar::get_value // Access: Published -// Description: Returns the current value of the bar. +// Description: Returns the current value of the slider. //////////////////////////////////////////////////////////////////// INLINE float PGSliderBar:: get_value() const { - return _value; + return get_ratio() * (_max_value - _min_value) + _min_value; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar:get_mapped_value +// Function: PGSliderBar::set_ratio // Access: Published -// Description: Returns the _mapped_value which is caluclated from -// _value with negative_mapping bool -//////////////////////////////////////////////////////////////////// -INLINE float PGSliderBar:: -get_mapped_value() const { - return _mapped_value; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_update_x -// Access: Published -// Description: Returns the _update_x which is caluclated from -// _mapped_value with width formula -//////////////////////////////////////////////////////////////////// -INLINE float PGSliderBar:: -get_update_x() const { - return _update_x; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_update_y -// Access: Published -// Description: Returns the _update_y which is caluclated from -// _mapped_value with width formula -//////////////////////////////////////////////////////////////////// -INLINE float PGSliderBar:: -get_update_y() const { - return _update_y; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_speed -// Access: Published -// Description: Sets the current speed at which the slider moves +// Description: Sets the current value of the slider, expressed in +// the range 0 .. 1. //////////////////////////////////////////////////////////////////// INLINE void PGSliderBar:: -set_speed(float speed) { - _speed = speed; +set_ratio(float ratio) { + _ratio = max(min(ratio, 1.0f), 0.0f); + _needs_reposition = true; + adjust(); } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_speed +// Function: PGSliderBar::get_ratio // Access: Published -// Description: Returns the current speed at which the slider moves. +// Description: Returns the current value of the slider, expressed in +// the range 0 .. 1. //////////////////////////////////////////////////////////////////// INLINE float PGSliderBar:: -get_speed() const { - return _speed; +get_ratio() const { + return _ratio; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_scale +// Function: PGSliderBar::set_resize_thumb // Access: Published -// Description: Sets the scale of the traugh image +// Description: Sets the resize_thumb flag. When this is true, the +// thumb button's frame will be adjusted so that its +// width visually represents the page size. When this +// is false, the thumb button will be left alone. //////////////////////////////////////////////////////////////////// INLINE void PGSliderBar:: -set_scale(float scale) { - _scale = scale/10; +set_resize_thumb(bool resize_thumb) { + _resize_thumb = resize_thumb; + _needs_recompute = true; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_scale +// Function: PGSliderBar::get_resize_thumb // Access: Published -// Description: Returns the scale set on the traugh image -//////////////////////////////////////////////////////////////////// -INLINE float PGSliderBar:: -get_scale() const { - return _scale; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_slider_only -// Access: Published -// Description: Sets _slider_only. True:only slider button desired. -// No left or right button to control slider needed. -// Else, the left and right slider buttons are shown. -//////////////////////////////////////////////////////////////////// -INLINE void PGSliderBar:: -set_slider_only(bool value) { - _slider_only = value; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_slider_only -// Access: Published -// Description: Returns what type of slider option set +// Description: Returns the resize_thumb flag. See +// set_resize_thumb(). //////////////////////////////////////////////////////////////////// INLINE bool PGSliderBar:: -get_slider_only() const { - return _slider_only; +get_resize_thumb() const { + return _resize_thumb; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_negative_mapping +// Function: PGSliderBar::set_manage_pieces // Access: Published -// Description: Sets _negative_mapping. True: slider will receive -// _value from -1 to +1 else, _value from 0 to + 1 +// Description: Sets the manage_pieces flag. When this is true, the +// sub-pieces of the slider bar--that is, the thumb, and +// the left and right scroll buttons--are automatically +// positioned and/or resized when the slider bar's +// overall frame is changed. //////////////////////////////////////////////////////////////////// INLINE void PGSliderBar:: -set_negative_mapping(bool value) { - _negative_mapping = value; +set_manage_pieces(bool manage_pieces) { + _manage_pieces = manage_pieces; + _needs_remanage = true; + _needs_recompute = true; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_negative_mapping +// Function: PGSliderBar::get_manage_pieces // Access: Published -// Description: Returns what type of value range option set +// Description: Returns the manage_pieces flag. See +// set_manage_pieces(). //////////////////////////////////////////////////////////////////// INLINE bool PGSliderBar:: -get_negative_mapping() const { - return _negative_mapping; +get_manage_pieces() const { + return _manage_pieces; } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_bar_style +// Function: PGSliderBar::set_thumb_button // Access: Published -// Description: Sets the kind of frame that is drawn on top of the -// SliderBar to represent the amount completed. -//////////////////////////////////////////////////////////////////// -INLINE void PGSliderBar:: -set_bar_style(const PGFrameStyle &style) { - _bar_style = style; - _bar_state = -1; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_bar_style -// Access: Published -// Description: Returns the kind of frame that is drawn on top of the -// SliderBar to represent the amount completed. -//////////////////////////////////////////////////////////////////// -INLINE PGFrameStyle PGSliderBar:: -get_bar_style() const { - return _bar_style; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_click_event -// Access: Published -// Description: Returns the event name that will be thrown when the -// slider button is moved -//////////////////////////////////////////////////////////////////// -INLINE string PGSliderBar:: -get_click_event() const { - return "updated-slider-" + get_id(); -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::get_slider_button -// Access: Published -// Description: Returns the slider button that is drawn on the -// SliderBar to move the slider left or right -//////////////////////////////////////////////////////////////////// -INLINE NodePath PGSliderBar:: -get_slider_button() const { - return _slider_button; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::set_slider_button -// Access: Published -// Description: sets slider button that is drawn on the -// SliderBar to move the slider left or right +// Description: Sets the PGButton object that will serve as the thumb +// for this slider. This button visually represents the +// position of the slider, and can be dragged left and +// right by the user. +// +// It is the responsibility of the caller to ensure that +// the button object is parented to the PGSliderBar +// node. //////////////////////////////////////////////////////////////////// INLINE void PGSliderBar:: -set_slider_button(NodePath &np, PGSliderButton *button) { - _slider_button = np; - _slider = button; +set_thumb_button(PGButton *thumb_button) { + if (_thumb_button != (PGButton *)NULL) { + _thumb_button->set_notify(NULL); + } + _thumb_button = thumb_button; + if (_thumb_button != (PGButton *)NULL) { + _thumb_button->set_notify(this); + } + _needs_remanage = true; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::clear_thumb_button +// Access: Published +// Description: Removes the thumb button object from control of the +// frame. It is your responsibility to actually remove +// or hide the button itself. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +clear_thumb_button() { + set_thumb_button(NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_thumb_button +// Access: Published +// Description: Returns the PGButton that serves as the thumb for +// this slider, or NULL if it is not set. +//////////////////////////////////////////////////////////////////// +INLINE PGButton *PGSliderBar:: +get_thumb_button() const { + return _thumb_button; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::set_left_button +// Access: Published +// Description: Sets the PGButton object that will serve as the left +// scroll button for this slider. This button is +// optional; if present, the user can click on it to +// move scroll_size units at a time to the left. +// +// It is the responsibility of the caller to ensure that +// the button object is parented to the PGSliderBar +// node. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +set_left_button(PGButton *left_button) { + if (_left_button != (PGButton *)NULL) { + _left_button->set_notify(NULL); + } + _left_button = left_button; + if (_left_button != (PGButton *)NULL) { + _left_button->set_notify(this); + } + _needs_remanage = true; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::clear_left_button +// Access: Published +// Description: Removes the left button object from control of the +// frame. It is your responsibility to actually remove +// or hide the button itself. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +clear_left_button() { + set_left_button(NULL); } //////////////////////////////////////////////////////////////////// // Function: PGSliderBar::get_left_button // Access: Published -// Description: Returns the left button that is drawn on left of the -// SliderBar to move the slider left +// Description: Returns the PGButton that serves as the left scroll +// button for this slider, if any, or NULL if it is not +// set. //////////////////////////////////////////////////////////////////// -INLINE NodePath PGSliderBar:: +INLINE PGButton *PGSliderBar:: get_left_button() const { return _left_button; } +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::set_right_button +// Access: Published +// Description: Sets the PGButton object that will serve as the right +// scroll button for this slider. This button is +// optional; if present, the user can click on it to +// move scroll_size units at a time to the right. +// +// It is the responsibility of the caller to ensure that +// the button object is parented to the PGSliderBar +// node. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +set_right_button(PGButton *right_button) { + if (_right_button != (PGButton *)NULL) { + _right_button->set_notify(NULL); + } + _right_button = right_button; + if (_right_button != (PGButton *)NULL) { + _right_button->set_notify(this); + } + _needs_remanage = true; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::clear_right_button +// Access: Published +// Description: Removes the right button object from control of the +// frame. It is your responsibility to actually remove +// or hide the button itself. +//////////////////////////////////////////////////////////////////// +INLINE void PGSliderBar:: +clear_right_button() { + set_right_button(NULL); +} + //////////////////////////////////////////////////////////////////// // Function: PGSliderBar::get_right_button // Access: Published -// Description: Returns the right button that is drawn on right of the -// SliderBar to move the slider right +// Description: Returns the PGButton that serves as the right scroll +// button for this slider, if any, or NULL if it is not +// set. //////////////////////////////////////////////////////////////////// -INLINE NodePath PGSliderBar:: +INLINE PGButton *PGSliderBar:: get_right_button() const { return _right_button; } +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_adjust_prefix +// Access: Published, Static +// Description: Returns the prefix that is used to define the adjust +// event for all PGSliderBars. The adjust event is the +// concatenation of this string followed by get_id(). +//////////////////////////////////////////////////////////////////// +INLINE string PGSliderBar:: +get_adjust_prefix() { + return "adjust-"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::get_adjust_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// slider bar value is adjusted by the user or +// programmatically. +//////////////////////////////////////////////////////////////////// +INLINE string PGSliderBar:: +get_adjust_event() const { + return get_adjust_prefix() + get_id(); +} diff --git a/panda/src/pgui/pgSliderBar.cxx b/panda/src/pgui/pgSliderBar.cxx index 3bf2890d82..9471aeafa2 100755 --- a/panda/src/pgui/pgSliderBar.cxx +++ b/panda/src/pgui/pgSliderBar.cxx @@ -18,8 +18,12 @@ #include "pgSliderBar.h" #include "pgMouseWatcherParameter.h" - +#include "clockObject.h" #include "throw_event.h" +#include "config_pgui.h" +#include "throw_event.h" +#include "transformState.h" +#include "mouseButton.h" TypeHandle PGSliderBar::_type_handle; @@ -29,26 +33,26 @@ TypeHandle PGSliderBar::_type_handle; // Description: //////////////////////////////////////////////////////////////////// PGSliderBar:: -PGSliderBar(const string &name) : - PGItem(name), - // _slider("slider"), - _left("left"), - _right("right") +PGSliderBar(const string &name) + : PGItem(name) { - _slider = NULL; - _range = 100.0; - // _value is a range from (-1 to +1) that represents slider - _value = 0.0; - // _mapped_value is a mapping of (-1<->1) into (0<->1) - _mapped_value = 0.5*_value + 0.5; - // _update_x is mapping of _mapped_value wrt slider width - _update_x = 0.0; //will be set when _width is defined - _speed = 0.05; - _scale = 0.05; - _bar_state = -1; - _update_slider = false; - _slider_only = true; - _negative_mapping = false; + _min_value = 0.0f; + _max_value = 1.0f; + set_scroll_size(0.01f); + set_page_size(0.1f); + _ratio = 0.0f; + _resize_thumb = false; + _manage_pieces = false; + _axis.set(1.0f, 0.0f, 0.0f); + _needs_remanage = false; + _needs_recompute = true; + _needs_reposition = false; + _scroll_button_held = NULL; + _mouse_button_page = false; + _dragging = false; + _thumb_width = 0.1f; + + set_active(true); } //////////////////////////////////////////////////////////////////// @@ -68,14 +72,22 @@ PGSliderBar:: PGSliderBar:: PGSliderBar(const PGSliderBar ©) : PGItem(copy), - _value(copy._value), - _range(copy._range), - // _slider(copy._slider), - _left(copy._left), - _right(copy._right) + _min_value(copy._min_value), + _max_value(copy._max_value), + _scroll_value(copy._scroll_value), + _scroll_ratio(copy._scroll_ratio), + _page_value(copy._page_value), + _page_ratio(copy._page_ratio), + _ratio(copy._ratio), + _resize_thumb(copy._resize_thumb), + _manage_pieces(copy._manage_pieces), + _axis(copy._axis) { - _bar_state = -1; - _slider = NULL; + _needs_remanage = false; + _needs_recompute = true; + _scroll_button_held = NULL; + _mouse_button_page = false; + _dragging = false; } //////////////////////////////////////////////////////////////////// @@ -91,6 +103,71 @@ make_copy() const { return new PGSliderBar(*this); } +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::press +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button is depressed while the mouse +// is within the region. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +press(const MouseWatcherParameter ¶m, bool background) { + if (param.has_mouse()) { + _mouse_pos = param.get_mouse(); + } + if (get_active() && param.get_button() == MouseButton::one()) { + if (_needs_recompute) { + recompute(); + } + + if (_range_x != 0.0f) { + _mouse_button_page = true; + _scroll_button_held = NULL; + advance_page(); + _next_advance_time = + ClockObject::get_global_clock()->get_frame_time() + scroll_initial_delay; + } + } + PGItem::press(param, background); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::release +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever a +// mouse or keyboard button previously depressed with +// press() is released. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +release(const MouseWatcherParameter ¶m, bool background) { + if (MouseButton::is_mouse_button(param.get_button())) { + _mouse_button_page = false; + } + if (_dragging) { + end_drag(); + } + PGItem::release(param, background); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::move +// Access: Protected, Virtual +// Description: This is a callback hook function, called whenever a +// mouse is moved while within the region. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +move(const MouseWatcherParameter ¶m) { + _mouse_pos = param.get_mouse(); + if (_dragging) { + // We only get here if we the user originally clicked on the + // track, which caused the slider to move all the way to the mouse + // position, and then started dragging the mouse along the track. + // In this case, we start moving the thumb as if the user had + // started by dragging the thumb directly. + continue_drag(); + } +} + //////////////////////////////////////////////////////////////////// // Function: PGSliderBar::has_cull_callback // Access: Protected, Virtual @@ -126,161 +203,599 @@ has_cull_callback() const { //////////////////////////////////////////////////////////////////// bool PGSliderBar:: cull_callback(CullTraverser *trav, CullTraverserData &data) { - update(); + if (_manage_pieces && _needs_remanage) { + remanage(); + } + if (_needs_recompute) { + recompute(); + } + + if (_scroll_button_held != (PGItem *)NULL && + _next_advance_time <= ClockObject::get_global_clock()->get_frame_time()) { + advance_scroll(); + } + + if (_mouse_button_page && + _next_advance_time <= ClockObject::get_global_clock()->get_frame_time()) { + advance_page(); + } + + if (_needs_reposition) { + reposition(); + } + return PGItem::cull_callback(trav, data); } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::setup -// Access: Public -// Description: Creates a PGSliderBar with the indicated dimensions, -// with the indicated maximum range. +// Function: PGSliderBar::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. //////////////////////////////////////////////////////////////////// void PGSliderBar:: -setup(float width, float height, float range) { - //set_state(0); - //clear_state_def(0); +xform(const LMatrix4f &mat) { + PGItem::xform(mat); + _axis = _axis * mat; - _width = 0.5 * width; // quick reference to find the left and right max points - //set_frame(-0.5f * width, 0.5f * width, -0.5f * height, 0.5f * height); - set_range(range); - - NodePath current = NodePath(this); - if (!_slider) { - _slider = new PGSliderButton("slider"); - _slider_button = current.attach_new_node(_slider); + // Make sure we set the thumb to identity position first, so it + // won't be accidentally flattened. + if (_thumb_button != (PGButton *)NULL) { + _thumb_button->clear_transform(); } - - _slider->set_slider_bar(this); - //_slider->setup(_slider->get_name()); - _slider->setup(""); - _slider->set_drag_n_drop(true); - _slider->set_frame(-1,1,-1.5,1.5); - _slider_button.set_scale(_scale); - _slider_button.set_pos(0, 0, 0); // center it - // if left or right button to control slider desired, create them - if (!_slider_only) { - _left_button = current.attach_new_node(&_left); - _left.set_slider_bar(this); - _right_button = current.attach_new_node(&_right); - _right.set_slider_bar(this); - _left.setup(_left.get_name()); - _right.setup(_right.get_name()); - _left_button.set_scale(0.5); - _left_button.set_pos(-(_width+1), 0, -0.25); - _right_button.set_scale(0.5); - _right_button.set_pos((_width+0.5), 0, -0.25); + _needs_remanage = true; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::adjust +// Access: Public, Virtual +// Description: This is a callback hook function, called whenever the +// slider value is adjusted by the user or +// programmatically. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +adjust() { + string event = get_adjust_event(); + play_sound(event); + throw_event(event); + + if (has_notify()) { + get_notify()->slider_bar_adjust(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::setup_scroll_bar +// Access: Published +// Description: Creates PGSliderBar that represents a vertical or +// horizontal scroll bar (if vertical is true or false, +// respectively), with additional buttons for scrolling, +// and a range of 0 .. 1. +// +// length here is the measurement along the scroll bar, +// and width is the measurement across the scroll bar, +// whether it is vertical or horizontal (so for a +// horizontal scroll bar, the length is actually the x +// dimension, and the width is the y dimension). +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +setup_scroll_bar(bool vertical, float length, float width, float bevel) { + set_state(0); + clear_state_def(0); + + if (vertical) { + set_frame(-width / 2.0f, width / 2.0f, -length / 2.0f, length / 2.0f); + _axis = LVector3f::rfu(0.0f, 0.0f, -1.0f); + } else { + set_frame(-length / 2.0f, length / 2.0f, -width / 2.0f, width / 2.0f); + _axis = LVector3f::rfu(1.0f, 0.0f, 0.0f); } PGFrameStyle style; - style.set_width(0.3f, 0.3f); - - /* style.set_color(0.6f, 0.6f, 0.6f, 1.0f); - style.set_type(PGFrameStyle::T_bevel_in); - //set_frame_style(0, style); - */ + style.set_type(PGFrameStyle::T_flat); + set_frame_style(0, style); style.set_color(0.8f, 0.8f, 0.8f, 1.0f); style.set_type(PGFrameStyle::T_bevel_out); - _slider->set_frame_style(PGButton::S_ready, style); - style.set_type(PGFrameStyle::T_bevel_in); - _slider->set_frame_style(PGButton::S_depressed, style); + style.set_width(bevel, bevel); + + // Remove the button nodes created by a previous call to setup(), if + // any. + if (_thumb_button != (PGButton *)NULL) { + remove_child(_thumb_button); + set_thumb_button(NULL); + } + if (_left_button != (PGButton *)NULL) { + remove_child(_left_button); + set_left_button(NULL); + } + if (_right_button != (PGButton *)NULL) { + remove_child(_right_button); + set_right_button(NULL); + } + + PT(PGButton) thumb = new PGButton("thumb"); + thumb->setup("", bevel); + thumb->set_frame(-width / 2.0f, width / 2.0f, + -width / 2.0f, width / 2.0f); + add_child(thumb); + set_thumb_button(thumb); + + PT(PGButton) left = new PGButton("left"); + left->setup("", bevel); + left->set_frame(-width / 2.0f, width / 2.0f, + -width / 2.0f, width / 2.0f); + left->set_transform(TransformState::make_pos(((width - length) / 2.0f) * _axis)); + add_child(left); + set_left_button(left); + + PT(PGButton) right = new PGButton("right"); + right->setup("", bevel); + right->set_frame(-width / 2.0f, width / 2.0f, + -width / 2.0f, width / 2.0f); + right->set_transform(TransformState::make_pos(((length - width) / 2.0f) * _axis)); + add_child(right); + set_right_button(right); + + set_resize_thumb(true); + set_manage_pieces(true); } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::press -// Access: Public, Virtual -// Description: This is a callback hook function, called whenever a -// mouse or keyboard button is depressed while the mouse -// is within the region. +// Function: PGSliderBar::setup_slider +// Access: Published +// Description: Creates PGSliderBar that represents a slider that the +// user can use to control an analog quantity. +// +// This is functionally the same as a scroll bar, but it +// has a distinctive look. //////////////////////////////////////////////////////////////////// void PGSliderBar:: -press(const MouseWatcherParameter ¶m, bool background) { - PGItem::press(param, background); - //pgui_cat.info() << get_name() << "::" << param << endl; - //pgui_cat.info() << _slider->get_name() << "::press:x" << _slider_button.get_x() - // << " press:y" << _slider_button.get_y() << endl; +setup_slider(bool vertical, float length, float width, float bevel) { + set_state(0); + clear_state_def(0); - // translate the mouse param position into frame space - LPoint2f mouse_point = param.get_mouse(); - LVector3f result(mouse_point[0], mouse_point[1], 0); - result = get_frame_inv_xform().xform_point(result); - _update_slider = true; - _update_x = result[0]; - _update_y = result[1]; - //_slider_button.set_x(result[0]); + if (vertical) { + set_frame(-width / 2.0f, width / 2.0f, -length / 2.0f, length / 2.0f); + _axis = LVector3f::rfu(0.0f, 0.0f, -1.0f); + } else { + set_frame(-length / 2.0f, length / 2.0f, -width / 2.0f, width / 2.0f); + _axis = LVector3f::rfu(1.0f, 0.0f, 0.0f); + } + + PGFrameStyle style; + style.set_color(0.6f, 0.6f, 0.6f, 1.0f); + style.set_type(PGFrameStyle::T_flat); + style.set_visible_scale(1.0f, 0.25f); + style.set_width(bevel, bevel); + set_frame_style(0, style); + + // Remove the button nodes created by a previous call to setup(), if + // any. + if (_thumb_button != (PGButton *)NULL) { + remove_child(_thumb_button); + set_thumb_button(NULL); + } + if (_left_button != (PGButton *)NULL) { + remove_child(_left_button); + set_left_button(NULL); + } + if (_right_button != (PGButton *)NULL) { + remove_child(_right_button); + set_right_button(NULL); + } + + PT(PGButton) thumb = new PGButton("thumb"); + thumb->setup(" ", bevel); + thumb->set_frame(-width / 4.0f, width / 4.0f, + -width / 2.0f, width / 2.0f); + add_child(thumb); + set_thumb_button(thumb); + + set_resize_thumb(false); + set_manage_pieces(true); } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::drag -// Access: Public, Virtual -// Description: This is a hook function, called when the user -// is trying to drag the slider button +// Function: PGSliderBar::set_active +// Access: Published, Virtual +// Description: Sets whether the PGItem is active for mouse watching. +// This is not necessarily related to the +// active/inactive appearance of the item, which is +// controlled by set_state(), but it does affect whether +// it responds to mouse events. //////////////////////////////////////////////////////////////////// void PGSliderBar:: -drag(const MouseWatcherParameter ¶m) { - //pgui_cat.info() << get_name() << "::" << param << endl; - //pgui_cat.info() << _slider->get_name() << "::drag:x" << _slider_button.get_x() - // <<" drag:y" << _slider_button.get_y() << endl; +set_active(bool active) { + PGItem::set_active(active); - // translate the mouse param position into frame space - LPoint2f mouse_point = param.get_mouse(); - LVector3f result(mouse_point[0], mouse_point[1], 0); - result = get_frame_inv_xform().xform_point(result); - // keep the slider button within slider bar - if (result[0] < -_width) - result[0] = -_width; - if (result[0] > _width) - result[0] = _width; - _update_slider = true; - _update_x = result[0]; - _update_y = result[1]; - //_slider_button.set_x(result[0]); + // This also implicitly sets the managed pieces. + if (_thumb_button != (PGButton *)NULL) { + _thumb_button->set_active(active); + } + if (_left_button != (PGButton *)NULL) { + _left_button->set_active(active); + } + if (_right_button != (PGButton *)NULL) { + _right_button->set_active(active); + } } //////////////////////////////////////////////////////////////////// -// Function: PGSliderBar::update -// Access: Private -// Description: Computes the appropriate size of the bar frame -// according to the percentage completed. +// Function: PGSliderBar::remanage +// Access: Published +// Description: Manages the position and size of the scroll bars and +// the thumb. Normally this should not need to be +// called directly. //////////////////////////////////////////////////////////////////// -void PGSliderBar:: -update() { - // int state = get_state(); +void PGSliderBar:: +remanage() { + _needs_remanage = false; - // need left and right button input if they exist - if (!_slider_only) { - // Handle left and right button presses - if (_left.is_button_down()) { - // move the slider to the left - float x = _slider_button.get_x() - _speed; - _update_slider = true; - _update_x = max(x, -_width); - //_slider_button.set_x(max(x, -_width)); - } + const LVecBase4f &frame = get_frame(); + + float width, length; + if (fabs(_axis[0]) > fabs(_axis[1] + _axis[2])) { + // The slider is X-dominant. + width = frame[3] - frame[2]; + length = frame[1] - frame[0]; - if (_right.is_button_down()) { - // move the slider to the right - float x = _slider_button.get_x() + _speed; - _update_slider = true; - _update_x = min(x, _width); - //_slider_button.set_x(min(x, _width)); - } + } else { + // The slider is Y-dominant. + width = frame[1] - frame[0]; + length = frame[3] - frame[2]; } - // press() and drag() update schedules this values that need to be - // applied here so that value of current slider position as a ratio - // of range can be updated - if (_update_slider) { - //pgui_cat.info() << "mouse_point:x " << _update_x - // << " mouse_point:y " << _update_y << endl; - if (!_slider_button.is_empty()) - _slider_button.set_x(_update_x); - _mapped_value = (_update_x + _width)/(2*_width); - _value = _negative_mapping ? ((_mapped_value-0.5)*2) : _mapped_value; - _update_slider = false; - throw_event(get_click_event()); + LVector3f center = LVector3f::rfu((frame[0] + frame[1]) / 2.0f, + 0.0f, + (frame[2] + frame[3]) / 2.0f); + + if (_left_button != (PGButton *)NULL) { + _left_button->set_frame(-width / 2.0f, width / 2.0f, + -width / 2.0f, width / 2.0f); + _left_button->xform(LMatrix4f::translate_mat(center + ((width - length) / 2.0f) * _axis)); + _left_button->clear_transform(); + } + + if (_right_button != (PGButton *)NULL) { + _right_button->set_frame(-width / 2.0f, width / 2.0f, + -width / 2.0f, width / 2.0f); + _right_button->xform(LMatrix4f::translate_mat(center + ((length - width) / 2.0f) * _axis)); + _right_button->clear_transform(); + } + + if (_thumb_button != (PGButton *)NULL) { + _thumb_button->set_frame(-width / 2.0f, width / 2.0f, + -width / 2.0f, width / 2.0f); + _thumb_button->set_transform(TransformState::make_pos(center)); + } + + recompute(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::recompute +// Access: Published +// Description: Recomputes the position and size of the thumb. +// Normally this should not need to be called directly. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +recompute() { + _needs_recompute = false; + + if (_min_value != _max_value) { + _scroll_ratio = fabs(_scroll_value / (_max_value - _min_value)); + _page_ratio = fabs(_page_value / (_max_value - _min_value)); + + } else { + _scroll_ratio = 0.0f; + _page_ratio = 0.0f; + } + + LVecBase4f frame = get_frame(); + reduce_region(frame, _left_button); + reduce_region(frame, _right_button); + + if (fabs(_axis[0]) > fabs(_axis[1] + _axis[2])) { + // The slider is X-dominant. + + _min_x = frame[0]; + _max_x = frame[1]; + + float trough_width = _max_x - _min_x; + + if (_thumb_button != (PGButton *)NULL) { + const LVecBase4f &thumb_frame = _thumb_button->get_frame(); + + if (_resize_thumb) { + // If we're allowed to adjust the thumb's size, we don't need to + // find out how wide it is. + _thumb_width = trough_width * min(1.0f, _page_ratio); + _thumb_button->set_frame(-_thumb_width / 2.0f, _thumb_width / 2.0f, + thumb_frame[2], thumb_frame[3]); + } else { + // If we're not adjusting the thumb's size, we do need to know + // its current width. + _thumb_width = thumb_frame[1] - thumb_frame[0]; + } + } + + _range_x = trough_width - _thumb_width; + + const LVecBase4f &thumb_frame = _thumb_button->get_frame(); + if (_axis[0] >= 0.0f) { + // The slider runs forwards, left to right. + _thumb_start = (_min_x - thumb_frame[0]) * _axis; + } else { + // The slider runs backwards: right to left. + _thumb_start = (thumb_frame[1] - _max_x) * _axis; + } + _thumb_start += LVector3f::rfu(0.0f, 0.0f, (frame[2] + frame[3]) / 2.0f); + + } else { + // The slider is Y-dominant. We call it X in the variable names, + // but it's really Y (or even Z). + + _min_x = frame[2]; + _max_x = frame[3]; + + float trough_width = _max_x - _min_x; + + if (_thumb_button == (PGButton *)NULL) { + _thumb_width = 0.0f; + _range_x = 0.0f; + _thumb_start.set(0.0f, 0.0f, 0.0f); + + } else { + const LVecBase4f &thumb_frame = _thumb_button->get_frame(); + + if (_resize_thumb) { + // If we're allowed to adjust the thumb's size, we don't need to + // find out how wide it is. + _thumb_width = trough_width * min(1.0f, _page_ratio); + _thumb_button->set_frame(thumb_frame[0], thumb_frame[1], + -_thumb_width / 2.0f, _thumb_width / 2.0f); + } else { + // If we're not adjusting the thumb's size, we do need to know + // its current width. + _thumb_width = thumb_frame[3] - thumb_frame[2]; + } + + _range_x = trough_width - _thumb_width; + + if (_axis[1] >= 0.0f && _axis[2] >= 0.0f) { + // The slider runs forwards, bottom to top. + _thumb_start = (_min_x - thumb_frame[2]) * _axis; + } else { + // The slider runs backwards: top to bottom. + _thumb_start = (thumb_frame[3] - _max_x) * _axis; + } + _thumb_start += LVector3f::rfu((frame[0] + frame[1]) / 2.0f, 0.0f, 0.0f); + } + } + + reposition(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::frame_changed +// Access: Protected, Virtual +// Description: Called when the user changes the frame size. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +frame_changed() { + PGItem::frame_changed(); + _needs_remanage = true; + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::item_transform_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's local transform +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +item_transform_changed(PGItem *) { + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::item_frame_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's frame +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +item_frame_changed(PGItem *) { + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::item_draw_mask_changed +// Access: Protected, Virtual +// Description: Called whenever a watched PGItem's draw_mask +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +item_draw_mask_changed(PGItem *) { + _needs_recompute = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::item_press +// Access: Protected, Virtual +// Description: Called whenever the "press" event is triggered on a +// watched PGItem. See PGItem::press(). +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +item_press(PGItem *item, const MouseWatcherParameter ¶m) { + if (param.has_mouse()) { + _mouse_pos = param.get_mouse(); + } + if (item == _left_button || item == _right_button) { + _scroll_button_held = item; + _mouse_button_page = false; + advance_scroll(); + _next_advance_time = + ClockObject::get_global_clock()->get_frame_time() + scroll_initial_delay; + + } else if (item == _thumb_button) { + _scroll_button_held = NULL; + begin_drag(); } } + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::item_release +// Access: Protected, Virtual +// Description: Called whenever the "release" event is triggered on a +// watched PGItem. See PGItem::release(). +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +item_release(PGItem *item, const MouseWatcherParameter &) { + if (item == _scroll_button_held) { + _scroll_button_held = NULL; + + } else if (item == _thumb_button) { + _scroll_button_held = NULL; + if (_dragging) { + end_drag(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::item_move +// Access: Protected, Virtual +// Description: Called whenever the "move" event is triggered on a +// watched PGItem. See PGItem::move(). +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +item_move(PGItem *item, const MouseWatcherParameter ¶m) { + _mouse_pos = param.get_mouse(); + if (item == _thumb_button) { + if (_dragging) { + continue_drag(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::reposition +// Access: Private +// Description: A lighter-weight version of recompute(), this just +// moves the thumb, assuming all other properties are +// unchanged. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +reposition() { + _needs_reposition = false; + + float t = get_ratio(); + + if (_thumb_button != (PGButton *)NULL) { + _thumb_button->set_transform(TransformState::make_pos((t * _range_x) * _axis + _thumb_start)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::advance_scroll +// Access: Private +// Description: Advances the scroll bar by one unit in the left or +// right direction while the user is holding down the +// left or right scroll button. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +advance_scroll() { + if (_scroll_button_held == _left_button) { + set_ratio(max(_ratio - _scroll_ratio, 0.0f)); + + } else if (_scroll_button_held == _right_button) { + set_ratio(min(_ratio + _scroll_ratio, 1.0f)); + } + + _next_advance_time = + ClockObject::get_global_clock()->get_frame_time() + scroll_continued_delay; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::advance_page +// Access: Private +// Description: Advances the scroll bar by one page in the left or +// right direction while the user is holding down the +// mouse button on the track. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +advance_page() { + // Is the mouse position left or right of the current thumb + // position? + LPoint3f mouse = mouse_to_local(_mouse_pos) - _thumb_start; + float target_ratio = mouse.dot(_axis) / _range_x; + + float t; + if (target_ratio < _ratio) { + t = max(_ratio - _page_ratio + _scroll_ratio, target_ratio); + + } else { + t = min(_ratio + _page_ratio - _scroll_ratio, target_ratio); + } + set_ratio(t); + if (t == target_ratio) { + // We made it; begin dragging from now on until the user releases + // the mouse. + begin_drag(); + } + + _next_advance_time = + ClockObject::get_global_clock()->get_frame_time() + scroll_continued_delay; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::begin_drag +// Access: Private +// Description: Called when the user clicks down on the thumb button, +// possibly to begin dragging. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +begin_drag() { + if (_needs_recompute) { + recompute(); + } + if (_range_x != 0.0f) { + float current_x = mouse_to_local(_mouse_pos).dot(_axis); + _drag_start_x = current_x - get_ratio() * _range_x; + _dragging = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::continue_drag +// Access: Private +// Description: Called as the user moves the mouse while still +// dragging on the thumb button. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +continue_drag() { + if (_needs_recompute) { + recompute(); + } + if (_range_x != 0.0f) { + float current_x = mouse_to_local(_mouse_pos).dot(_axis); + set_ratio((current_x - _drag_start_x) / _range_x); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBar::end_drag +// Access: Private +// Description: Called as the user releases the mouse after dragging. +//////////////////////////////////////////////////////////////////// +void PGSliderBar:: +end_drag() { + _dragging = false; +} diff --git a/panda/src/pgui/pgSliderBar.h b/panda/src/pgui/pgSliderBar.h index 7426d767d4..c471395c6c 100755 --- a/panda/src/pgui/pgSliderBar.h +++ b/panda/src/pgui/pgSliderBar.h @@ -22,15 +22,20 @@ #include "pandabase.h" #include "pgItem.h" -#include "pgSliderButton.h" +#include "pgSliderBarNotify.h" +#include "pgButtonNotify.h" +#include "pgButton.h" //////////////////////////////////////////////////////////////////// // Class : PGSliderBar // Description : This is a particular kind of PGItem that draws a // little bar with a slider that moves from left to -// right indicating a value between the ranges +// right indicating a value between the ranges. +// +// This is used as an implementation for both +// DirectSlider and for DirectScrollBar. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA PGSliderBar : public PGItem { +class EXPCL_PANDA PGSliderBar : public PGItem, public PGButtonNotify { PUBLISHED: PGSliderBar(const string &name = ""); virtual ~PGSliderBar(); @@ -42,71 +47,110 @@ public: virtual PandaNode *make_copy() const; virtual bool has_cull_callback() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); + virtual void xform(const LMatrix4f &mat); virtual void press(const MouseWatcherParameter ¶m, bool background); - virtual void drag(const MouseWatcherParameter ¶m); + virtual void release(const MouseWatcherParameter ¶m, bool background); + virtual void move(const MouseWatcherParameter ¶m); + + virtual void adjust(); + + INLINE void set_notify(PGSliderBarNotify *notify); + INLINE PGSliderBarNotify *get_notify() const; PUBLISHED: - void setup(float width, float height, float range); + void setup_scroll_bar(bool vertical, float length, float width, float bevel); + void setup_slider(bool vertical, float length, float width, float bevel); - INLINE void set_range(float range); - INLINE float get_range() const; + INLINE void set_axis(const LVector3f &axis); + INLINE const LVector3f &get_axis() const; - //INLINE void set_magnifier(float value); - //INLINE float get_magnifier() const; + INLINE void set_range(float min_value, float max_value); + INLINE float get_min_value() const; + INLINE float get_max_value() const; + + INLINE void set_scroll_size(float scroll_size); + INLINE float get_scroll_size() const; + + INLINE void set_page_size(float page_size); + INLINE float get_page_size() const; INLINE void set_value(float value); INLINE float get_value() const; - INLINE float get_mapped_value() const; - INLINE float get_update_x() const; - INLINE float get_update_y() const; - INLINE void set_speed(float speed); - INLINE float get_speed() const; + INLINE void set_ratio(float ratio); + INLINE float get_ratio() const; - INLINE void set_scale(float speed); - INLINE float get_scale() const; + INLINE void set_resize_thumb(bool resize_thumb); + INLINE bool get_resize_thumb() const; - INLINE void set_slider_only(bool value); - INLINE bool get_slider_only() const ; - INLINE void set_negative_mapping(bool value); - INLINE bool get_negative_mapping() const ; + INLINE void set_manage_pieces(bool manage_pieces); + INLINE bool get_manage_pieces() const; - INLINE void set_bar_style(const PGFrameStyle &style); - INLINE PGFrameStyle get_bar_style() const; - INLINE string get_click_event() const; + INLINE void set_thumb_button(PGButton *thumb_button); + INLINE void clear_thumb_button(); + INLINE PGButton *get_thumb_button() const; - INLINE void set_slider_button(NodePath &np, PGSliderButton *button); + INLINE void set_left_button(PGButton *left_button); + INLINE void clear_left_button(); + INLINE PGButton *get_left_button() const; - INLINE NodePath get_slider_button() const; - INLINE NodePath get_left_button() const; - INLINE NodePath get_right_button() const; + INLINE void set_right_button(PGButton *right_button); + INLINE void clear_right_button(); + INLINE PGButton *get_right_button() const; + + INLINE static string get_adjust_prefix(); + INLINE string get_adjust_event() const; + + virtual void set_active(bool active); + + void remanage(); + void recompute(); + +protected: + virtual void frame_changed(); + + virtual void item_transform_changed(PGItem *item); + virtual void item_frame_changed(PGItem *item); + virtual void item_draw_mask_changed(PGItem *item); + virtual void item_press(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_release(PGItem *item, const MouseWatcherParameter ¶m); + virtual void item_move(PGItem *item, const MouseWatcherParameter ¶m); private: - void update(); + void reposition(); + void advance_scroll(); + void advance_page(); + void begin_drag(); + void continue_drag(); + void end_drag(); - bool _slider_only; - bool _negative_mapping; - bool _update_slider; +private: + bool _needs_remanage; + bool _needs_recompute; + bool _needs_reposition; - // These 3 variables control slider range - float _value; - float _mapped_value; - float _update_x; - float _update_y; + float _min_value, _max_value; + float _scroll_value, _scroll_ratio; + float _page_value, _page_ratio; + float _ratio; + bool _resize_thumb; + bool _manage_pieces; - float _range; - float _speed, _width; - float _scale; - int _bar_state; - PGFrameStyle _bar_style; - PGSliderButton *_slider; - PGSliderButton _left; - PGSliderButton _right; - NodePath _bar; - NodePath _slider_button; - NodePath _left_button; - NodePath _right_button; + LVector3f _axis; + + PT(PGButton) _thumb_button; + PT(PGButton) _left_button; + PT(PGButton) _right_button; + + float _min_x, _max_x, _thumb_width, _range_x; + LPoint3f _thumb_start; + PGItem *_scroll_button_held; + bool _mouse_button_page; + LPoint2f _mouse_pos; + double _next_advance_time; + bool _dragging; + float _drag_start_x; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgui/pgSliderBarNotify.I b/panda/src/pgui/pgSliderBarNotify.I new file mode 100644 index 0000000000..2d9913556a --- /dev/null +++ b/panda/src/pgui/pgSliderBarNotify.I @@ -0,0 +1,27 @@ +// Filename: pgSliderBarNotify.I +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBarNotify::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PGSliderBarNotify:: +PGSliderBarNotify() { +} diff --git a/panda/src/pgui/pgSliderBarNotify.cxx b/panda/src/pgui/pgSliderBarNotify.cxx new file mode 100644 index 0000000000..a8b695d0f3 --- /dev/null +++ b/panda/src/pgui/pgSliderBarNotify.cxx @@ -0,0 +1,40 @@ +// Filename: pgSliderBarNotify.cxx +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "pgSliderBarNotify.h" +#include "pgSliderBar.h" + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBarNotify::slider_bar_adjust +// Access: Protected, Virtual +// Description: Called whenever a watched PGSliderBar's value +// has been changed by the user or programmatically. +//////////////////////////////////////////////////////////////////// +void PGSliderBarNotify:: +slider_bar_adjust(PGSliderBar *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGSliderBarNotify::slider_bar_set_range +// Access: Protected, Virtual +// Description: Called whenever a watched PGSliderBar's overall range +// has been changed. +//////////////////////////////////////////////////////////////////// +void PGSliderBarNotify:: +slider_bar_set_range(PGSliderBar *) { +} diff --git a/panda/src/pgui/pgSliderBarNotify.h b/panda/src/pgui/pgSliderBarNotify.h new file mode 100644 index 0000000000..23e4698115 --- /dev/null +++ b/panda/src/pgui/pgSliderBarNotify.h @@ -0,0 +1,46 @@ +// Filename: pgSliderBarNotify.h +// Created by: drose (18Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGSLIDERBARNOTIFY_H +#define PGSLIDERBARNOTIFY_H + +#include "pandabase.h" +#include "pgItemNotify.h" + +class PGSliderBar; + +//////////////////////////////////////////////////////////////////// +// Class : PGSliderBarNotify +// Description : Objects that inherit from this class can receive +// notify messages when a slider bar moves or otherwise +// is reconfigured. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGSliderBarNotify : public PGItemNotify { +public: + INLINE PGSliderBarNotify(); + +protected: + virtual void slider_bar_adjust(PGSliderBar *slider_bar); + virtual void slider_bar_set_range(PGSliderBar *slider_bar); + + friend class PGSliderBar; +}; + +#include "pgSliderBarNotify.I" + +#endif diff --git a/panda/src/pgui/pgSliderButton.cxx b/panda/src/pgui/pgSliderButton.cxx deleted file mode 100755 index df19df2eb6..0000000000 --- a/panda/src/pgui/pgSliderButton.cxx +++ /dev/null @@ -1,94 +0,0 @@ -// Filename: pgSliderButton.cxx -// Created by: masad (21Oct04) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . -// -// To contact the maintainers of this program write to -// panda3d-general@lists.sourceforge.net . -// -//////////////////////////////////////////////////////////////////// - -#include "pgSliderButton.h" -#include "pgSliderBar.h" -#include "dcast.h" -#include "pgMouseWatcherParameter.h" - -#include "throw_event.h" -#include "mouseButton.h" -#include "mouseWatcherParameter.h" -#include "colorAttrib.h" -#include "transformState.h" - -TypeHandle PGSliderButton::_type_handle; - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::Constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -PGSliderButton:: -PGSliderButton(const string &name) : PGButton(name) -{ - _drag_n_drop = false; - _slider_bar = NULL; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::Destructor -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -PGSliderButton:: -~PGSliderButton() { -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::Copy Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -PGSliderButton:: -PGSliderButton(const PGSliderButton ©) : - PGButton(copy), - _drag_n_drop(copy._drag_n_drop) - // _slider_bar(copy._slider_bar) -{ - _slider_bar = NULL; -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::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 *PGSliderButton:: -make_copy() const { - return new PGSliderButton(*this); -} - -//////////////////////////////////////////////////////////////////// -// Function: PGSliderButton::move -// Access: Public, Virtual -// Description: This is a callback hook function, called whenever the -// button is dragged left or right by the user normally. -//////////////////////////////////////////////////////////////////// -void PGSliderButton:: -move(const MouseWatcherParameter ¶m) { - PGButton::move(param); - if (_drag_n_drop && is_button_down()) { - PGSliderBar *slider = DCAST(PGSliderBar, _slider_bar); - slider->drag(param); - //pgui_cat.info() << get_name() << "::move()" << endl; - } -} diff --git a/panda/src/pgui/pgSliderButton.h b/panda/src/pgui/pgSliderButton.h deleted file mode 100755 index 6f70c82034..0000000000 --- a/panda/src/pgui/pgSliderButton.h +++ /dev/null @@ -1,77 +0,0 @@ -// Filename: pgSliderButton.h -// Created by: masad (21Oct04) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . -// -// To contact the maintainers of this program write to -// panda3d-general@lists.sourceforge.net . -// -//////////////////////////////////////////////////////////////////// - -#ifndef PGSLIDERBUTTON_H -#define PGSLIDERBUTTON_H - -#include "pandabase.h" - -#include "pgButton.h" -#include "nodePath.h" -#include "pset.h" - -//////////////////////////////////////////////////////////////////// -// Class : PGSliderButton -// Description : This is a particular kind of PGItem that is -// specialized to behave like a normal button object. -// It keeps track of its own state, and handles mouse -// events sensibly. -//////////////////////////////////////////////////////////////////// -class EXPCL_PANDA PGSliderButton : public PGButton { -PUBLISHED: - PGSliderButton(const string &name); - virtual ~PGSliderButton(); - -public: - - virtual void move(const MouseWatcherParameter ¶m); - PGSliderButton(const PGSliderButton ©); - -PUBLISHED: - - INLINE bool is_drag_n_drop(); - INLINE void set_drag_n_drop(bool value); - INLINE void set_slider_bar(PGItem *item); - -public: - virtual PandaNode *make_copy() const; - - bool _drag_n_drop; - PGItem *_slider_bar; - - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - PGButton::init_type(); - register_type(_type_handle, "PGSliderButton", - PGButton::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 "pgSliderButton.I" - -#endif diff --git a/panda/src/pgui/pgTop.I b/panda/src/pgui/pgTop.I index 2dd4c58a40..b9cea8036a 100644 --- a/panda/src/pgui/pgTop.I +++ b/panda/src/pgui/pgTop.I @@ -42,6 +42,18 @@ get_mouse_watcher() const { return _watcher; } +//////////////////////////////////////////////////////////////////// +// Function: PGTop::get_group +// Access: Published +// Description: Returns the MouseWatcherGroup pointer that the PGTop +// object registers its PG items with, or NULL if the +// MouseWatcher has not yet been set. +//////////////////////////////////////////////////////////////////// +INLINE MouseWatcherGroup *PGTop:: +get_group() const { + return _watcher_group; +} + //////////////////////////////////////////////////////////////////// // Function: PGTop::set_start_sort // Access: Published @@ -83,15 +95,12 @@ get_start_sort() const { // Function: PGTop::add_region // Access: Public // Description: Adds the indicated region to the set of regions in -// the group. Returns true if it was successfully -// added, or false if it was already on the list. +// the group. //////////////////////////////////////////////////////////////////// -INLINE bool PGTop:: +INLINE void PGTop:: add_region(MouseWatcherRegion *region) { - if (_watcher_group == (PGMouseWatcherGroup *)NULL) { - return false; - } - return _watcher_group->add_region(region); + nassertv(_watcher_group != (PGMouseWatcherGroup *)NULL); + _watcher_group->add_region(region); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgui/pgTop.h b/panda/src/pgui/pgTop.h index da350ced97..f7b0d695fc 100644 --- a/panda/src/pgui/pgTop.h +++ b/panda/src/pgui/pgTop.h @@ -60,13 +60,14 @@ public: PUBLISHED: void set_mouse_watcher(MouseWatcher *watcher); INLINE MouseWatcher *get_mouse_watcher() const; + INLINE MouseWatcherGroup *get_group() const; INLINE void set_start_sort(int start_sort); INLINE int get_start_sort() const; public: // These methods duplicate the functionality of MouseWatcherGroup. - INLINE bool add_region(MouseWatcherRegion *region); + INLINE void add_region(MouseWatcherRegion *region); INLINE void clear_regions(); private: diff --git a/panda/src/pgui/pgVirtualFrame.I b/panda/src/pgui/pgVirtualFrame.I new file mode 100644 index 0000000000..659a91ec72 --- /dev/null +++ b/panda/src/pgui/pgVirtualFrame.I @@ -0,0 +1,104 @@ +// Filename: pgVirtualFrame.I +// Created by: drose (17Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::set_clip_frame +// Access: Published +// Description: Sets the bounding rectangle of the clip frame. +// This is the size of the small window through which we +// can see the virtual canvas. Normally, this is the +// same size as the actual frame or smaller (typically +// it is smaller by the size of the bevel, or to make +// room for scroll bars). +//////////////////////////////////////////////////////////////////// +INLINE void PGVirtualFrame:: +set_clip_frame(float left, float right, float bottom, float top) { + set_clip_frame(LVecBase4f(left, right, bottom, top)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::get_clip_frame +// Access: Published +// Description: Returns the bounding rectangle of the clip frame. +// See set_clip_frame(). If has_clip_frame() is +// false, this returns the item's actual frame. +//////////////////////////////////////////////////////////////////// +INLINE const LVecBase4f &PGVirtualFrame:: +get_clip_frame() const { + return _has_clip_frame ? _clip_frame : get_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::has_clip_frame +// Access: Published +// Description: Returns true if the clip frame has been set; see +// set_clip_frame(). If it has not been set, objects in +// the virtual frame will not be clipped. +//////////////////////////////////////////////////////////////////// +INLINE bool PGVirtualFrame:: +has_clip_frame() const { + return _has_clip_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::set_canvas_transform +// Access: Published +// Description: Changes the transform of the virtual canvas. This +// transform is applied to all child nodes of the +// canvas_node. +//////////////////////////////////////////////////////////////////// +INLINE void PGVirtualFrame:: +set_canvas_transform(const TransformState *transform) { + _canvas_node->set_transform(transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::get_canvas_transform +// Access: Published +// Description: Returns the transform of the virtual canvas. This +// transform is applied to all child nodes of the +// canvas_node. +//////////////////////////////////////////////////////////////////// +INLINE const TransformState *PGVirtualFrame:: +get_canvas_transform() const { + return _canvas_node->get_transform(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::get_canvas_node +// Access: Published +// Description: Returns the special node that holds all of the +// children that appear in the virtual canvas. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *PGVirtualFrame:: +get_canvas_node() const { + return _canvas_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::get_clip_plane_node +// Access: Published +// Description: Returns the special node that holds all of PlaneNodes +// that are used for applying the clip planes to the +// canvas_node. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *PGVirtualFrame:: +get_clip_plane_node() const { + return _clip_plane_node; +} diff --git a/panda/src/pgui/pgVirtualFrame.cxx b/panda/src/pgui/pgVirtualFrame.cxx new file mode 100644 index 0000000000..6ea8914fab --- /dev/null +++ b/panda/src/pgui/pgVirtualFrame.cxx @@ -0,0 +1,241 @@ +// Filename: pgVirtualFrame.cxx +// Created by: drose (17Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "pgVirtualFrame.h" +#include "clipPlaneAttrib.h" +#include "planeNode.h" +#include "sceneGraphReducer.h" + +TypeHandle PGVirtualFrame::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +PGVirtualFrame:: +PGVirtualFrame(const string &name) : PGItem(name) +{ + _has_clip_frame = false; + _clip_frame.set(0.0f, 0.0f, 0.0f, 0.0f); + + setup_child_nodes(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PGVirtualFrame:: +~PGVirtualFrame() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +PGVirtualFrame:: +PGVirtualFrame(const PGVirtualFrame ©) : + PGItem(copy), + _has_clip_frame(copy._has_clip_frame), + _clip_frame(copy._clip_frame) +{ + setup_child_nodes(); + + // Reassign the clip planes according to the clip frame. + if (_has_clip_frame) { + set_clip_frame(_clip_frame); + } else { + clear_clip_frame(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::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 *PGVirtualFrame:: +make_copy() const { + return new PGVirtualFrame(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::r_copy_children +// Access: Protected, Virtual +// Description: This is called by r_copy_subgraph(); the copy has +// already been made of this particular node (and this +// is the copy); this function's job is to copy all of +// the children from the original. +// +// Note that it includes the parameter inst_map, which +// is a map type, and is not (and cannot be) exported +// from PANDA.DLL. Thus, any derivative of PandaNode +// that is not also a member of PANDA.DLL *cannot* +// access this map, and probably should not even +// override this function. +//////////////////////////////////////////////////////////////////// +void PGVirtualFrame:: +r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) { + PandaNode::r_copy_children(from, inst_map); + + // Reassign the canvas_node to point to the new copy, if it's there. + const PGVirtualFrame *from_frame = DCAST(PGVirtualFrame, from); + PandaNode *from_canvas_node = from_frame->get_canvas_node(); + PandaNode *from_clip_plane_node = from_frame->get_clip_plane_node(); + + InstanceMap::const_iterator ci; + ci = inst_map.find(from_canvas_node); + if (ci != inst_map.end()) { + remove_child(_canvas_node); + _canvas_node = DCAST(ModelNode, (*ci).second); + } + + ci = inst_map.find(from_clip_plane_node); + if (ci != inst_map.end()) { + remove_child(_clip_plane_node); + _clip_plane_node = (*ci).second; + } + + // Reassign the clip planes according to the clip frame. + if (_has_clip_frame) { + set_clip_frame(_clip_frame); + } else { + clear_clip_frame(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::setup +// Access: Published +// Description: Creates a PGVirtualFrame with the indicated +// dimensions. +//////////////////////////////////////////////////////////////////// +void PGVirtualFrame:: +setup(float width, float height) { + set_state(0); + clear_state_def(0); + + set_frame(0, width, 0, height); + + float bevel = 0.05f; + + PGFrameStyle style; + style.set_width(bevel, bevel); + + style.set_color(0.8f, 0.8f, 0.8f, 1.0f); + style.set_type(PGFrameStyle::T_bevel_out); + set_frame_style(0, style); + + set_clip_frame(bevel, width - 2 * bevel, + bevel, height - 2 * bevel); +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::set_clip_frame +// Access: Published +// Description: Sets the bounding rectangle of the clip frame. +// This is the size of the small window through which we +// can see the virtual canvas. Normally, this is the +// same size as the actual frame or smaller (typically +// it is smaller by the size of the bevel, or to make +// room for scroll bars). +//////////////////////////////////////////////////////////////////// +void PGVirtualFrame:: +set_clip_frame(const LVecBase4f &frame) { + if (!_has_clip_frame || _clip_frame != frame) { + _has_clip_frame = true; + _clip_frame = frame; + + const LVector3f r = LVector3f::right(); + const LVector3f u = LVector3f::up(); + + PT(PlaneNode) left_clip = + new PlaneNode("left_clip", Planef(r, r * _clip_frame[0])); + PT(PlaneNode) right_clip = + new PlaneNode("right_clip", Planef(-r, r * _clip_frame[1])); + + PT(PlaneNode) bottom_clip = + new PlaneNode("bottom_clip", Planef(u, u * _clip_frame[2])); + PT(PlaneNode) top_clip = + new PlaneNode("top_clip", Planef(-u, u * _clip_frame[3])); + + _clip_plane_node->remove_all_children(); + _clip_plane_node->add_child(left_clip); + _clip_plane_node->add_child(right_clip); + _clip_plane_node->add_child(bottom_clip); + _clip_plane_node->add_child(top_clip); + + CPT(ClipPlaneAttrib) plane_attrib = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make()); + plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(left_clip))); + plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(right_clip))); + plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(bottom_clip))); + plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib->add_on_plane(NodePath::any_path(top_clip))); + + _canvas_node->set_attrib(plane_attrib); + clip_frame_changed(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::clear_clip_frame +// Access: Published +// Description: Removes the clip frame from the item. This +// disables clipping. +//////////////////////////////////////////////////////////////////// +void PGVirtualFrame:: +clear_clip_frame() { + if (_has_clip_frame) { + _has_clip_frame = false; + + _canvas_node->clear_attrib(ClipPlaneAttrib::get_class_type()); + clip_frame_changed(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::clip_frame_changed +// Access: Protected, Virtual +// Description: Called when the user changes the clip_frame size. +//////////////////////////////////////////////////////////////////// +void PGVirtualFrame:: +clip_frame_changed() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PGVirtualFrame::setup_child_nodes +// Access: Private +// Description: Creates the special canvas_node and clip_plane_node +// for this object. +//////////////////////////////////////////////////////////////////// +void PGVirtualFrame:: +setup_child_nodes() { + _canvas_node = new ModelNode("canvas"); + _canvas_node->set_preserve_transform(ModelNode::PT_local); + // We have to preserve the clip plane attribute. + _canvas_node->set_preserve_attributes(SceneGraphReducer::TT_other); + add_child(_canvas_node); + + _clip_plane_node = new PandaNode("clip_planes"); + add_child(_clip_plane_node); +} diff --git a/panda/src/pgui/pgVirtualFrame.h b/panda/src/pgui/pgVirtualFrame.h new file mode 100644 index 0000000000..fc2c860067 --- /dev/null +++ b/panda/src/pgui/pgVirtualFrame.h @@ -0,0 +1,112 @@ +// Filename: pgVirtualFrame.h +// Created by: drose (17Aug05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PGVIRTUALFRAME_H +#define PGVIRTUALFRAME_H + +#include "pandabase.h" + +#include "pgItem.h" +#include "modelNode.h" + +class TransformState; + +//////////////////////////////////////////////////////////////////// +// Class : PGVirtualFrame +// Description : This represents a frame that is rendered as a window +// onto another (possibly much larger) canvas. You can +// only see the portion of the canvas that is below the +// window at any given time. +// +// This works simply by automatically defining clipping +// planes to be applied to a special child node, called +// the canvas_node, of the PGVirtualFrame node. Every +// object that is parented to the canvas_node will be +// clipped by the clip_frame. Also, you can modify the +// canvas_transform through convenience methods here, +// which actually modifies the transform on the +// canvas_node. +// +// The net effect is that the virtual canvas is +// arbitrarily large, and we can peek at it through the +// clip_frame, and scroll through different parts of it +// by modifying the canvas_transform. +// +// See PGScrollFrame for a specialization of this class +// that handles the traditional scrolling canvas, with +// scroll bars. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PGVirtualFrame : public PGItem { +PUBLISHED: + PGVirtualFrame(const string &name = ""); + virtual ~PGVirtualFrame(); + +protected: + PGVirtualFrame(const PGVirtualFrame ©); + virtual PandaNode *make_copy() const; + virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map); + +PUBLISHED: + void setup(float width, float height); + + INLINE void set_clip_frame(float left, float right, float bottom, float top); + void set_clip_frame(const LVecBase4f &clip_frame); + INLINE const LVecBase4f &get_clip_frame() const; + INLINE bool has_clip_frame() const; + void clear_clip_frame(); + + INLINE void set_canvas_transform(const TransformState *transform); + INLINE const TransformState *get_canvas_transform() const; + + INLINE PandaNode *get_canvas_node() const; + INLINE PandaNode *get_clip_plane_node() const; + +protected: + virtual void clip_frame_changed(); + +private: + void setup_child_nodes(); + +private: + bool _has_clip_frame; + LVecBase4f _clip_frame; + + PT(ModelNode) _canvas_node; + PT(PandaNode) _clip_plane_node; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PGItem::init_type(); + register_type(_type_handle, "PGVirtualFrame", + PGItem::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 "pgVirtualFrame.I" + +#endif diff --git a/panda/src/pgui/pgWaitBar.cxx b/panda/src/pgui/pgWaitBar.cxx index ca53cac48d..6a28c4d244 100644 --- a/panda/src/pgui/pgWaitBar.cxx +++ b/panda/src/pgui/pgWaitBar.cxx @@ -113,7 +113,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { //////////////////////////////////////////////////////////////////// // Function: PGWaitBar::setup -// Access: Public +// Access: Published // Description: Creates a PGWaitBar with the indicated dimensions, // with the indicated maximum range. //////////////////////////////////////////////////////////////////// @@ -124,8 +124,10 @@ setup(float width, float height, float range) { set_frame(-0.5f * width, 0.5f * width, -0.5f * height, 0.5f * height); + float bevel = 0.05f; + PGFrameStyle style; - style.set_width(0.05f, 0.05f); + style.set_width(bevel, bevel); style.set_color(0.6f, 0.6f, 0.6f, 1.0f); style.set_type(PGFrameStyle::T_bevel_in); diff --git a/panda/src/pgui/pgui_composite1.cxx b/panda/src/pgui/pgui_composite1.cxx index b97fdbd8c5..7dbbcbec02 100644 --- a/panda/src/pgui/pgui_composite1.cxx +++ b/panda/src/pgui/pgui_composite1.cxx @@ -1,9 +1,10 @@ - #include "config_pgui.cxx" #include "pgButton.cxx" -#include "pgSliderButton.cxx" +#include "pgButtonNotify.cxx" #include "pgCullTraverser.cxx" #include "pgEntry.cxx" #include "pgMouseWatcherGroup.cxx" #include "pgMouseWatcherParameter.cxx" - +#include "pgFrameStyle.cxx" +#include "pgItem.cxx" +#include "pgItemNotify.cxx" diff --git a/panda/src/pgui/pgui_composite2.cxx b/panda/src/pgui/pgui_composite2.cxx index 2150101e14..16cd6ecfac 100644 --- a/panda/src/pgui/pgui_composite2.cxx +++ b/panda/src/pgui/pgui_composite2.cxx @@ -1,9 +1,9 @@ - -#include "pgFrameStyle.cxx" -#include "pgItem.cxx" #include "pgMouseWatcherBackground.cxx" #include "pgMouseWatcherRegion.cxx" -#include "pgTop.cxx" -#include "pgWaitBar.cxx" +#include "pgScrollFrame.cxx" #include "pgSliderBar.cxx" +#include "pgSliderBarNotify.cxx" +#include "pgTop.cxx" +#include "pgVirtualFrame.cxx" +#include "pgWaitBar.cxx" diff --git a/panda/src/putil/bam.h b/panda/src/putil/bam.h index 5ad9f35ddd..a4ff103621 100644 --- a/panda/src/putil/bam.h +++ b/panda/src/putil/bam.h @@ -35,9 +35,10 @@ static const unsigned short _bam_major_ver = 5; // Bumped to major version 4 on 4/10/02 to store new scene graph. // Bumped to major version 5 on 5/6/05 for new Geom implementation. -static const unsigned short _bam_minor_ver = 2; +static const unsigned short _bam_minor_ver = 3; // Bumped to minor version 1 on 7/14/05 to add TextureStage::_saved_result. // Bumped to minor version 2 on 7/21/05 to add TransformState::is_2d. +// Bumped to minor version 3 on 8/25/05 to add ModelNode::_preserve_attributes. #endif diff --git a/panda/src/tform/Sources.pp b/panda/src/tform/Sources.pp index 090b0ab592..d52422327d 100644 --- a/panda/src/tform/Sources.pp +++ b/panda/src/tform/Sources.pp @@ -4,7 +4,7 @@ #begin lib_target #define TARGET tform #define LOCAL_LIBS \ - dgraph pgraph linmath display event putil gobj gsgbase \ + grutil dgraph pgraph linmath display event putil gobj gsgbase \ mathutil device #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index e50a765859..026b0ee540 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -29,6 +29,7 @@ #include "transformState.h" #include "displayRegion.h" #include "dcast.h" +#include "indent.h" #include @@ -122,58 +123,9 @@ get_over_region(const LPoint2f &pos) const { return get_preferred_region(regions); } - -//////////////////////////////////////////////////////////////////// -// Function: MouseWatcher::output -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -void MouseWatcher:: -output(ostream &out) const { - DataNode::output(out); - - int count = _regions.size(); - Groups::const_iterator gi; - for (gi = _groups.begin(); gi != _groups.end(); ++gi) { - MouseWatcherGroup *group = (*gi); - count += group->_regions.size(); - } - - out << " (" << count << " regions)"; -} - -//////////////////////////////////////////////////////////////////// -// Function: MouseWatcher::write -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -void MouseWatcher:: -write(ostream &out, int indent_level) const { - indent(out, indent_level) - << "MouseWatcher " << get_name() << ":\n"; - Regions::const_iterator ri; - for (ri = _regions.begin(); ri != _regions.end(); ++ri) { - MouseWatcherRegion *region = (*ri); - region->write(out, indent_level + 2); - } - - if (!_groups.empty()) { - Groups::const_iterator gi; - for (gi = _groups.begin(); gi != _groups.end(); ++gi) { - MouseWatcherGroup *group = (*gi); - indent(out, indent_level + 2) - << "Subgroup:\n"; - for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { - MouseWatcherRegion *region = (*ri); - region->write(out, indent_level + 4); - } - } - } -} - //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::add_group -// Access: Public +// Access: Published // Description: Adds the indicated group of regions to the set of // regions the MouseWatcher will monitor each frame. // @@ -207,7 +159,7 @@ add_group(MouseWatcherGroup *group) { //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::remove_group -// Access: Public +// Access: Published // Description: Removes the indicated group from the set of extra // groups associated with the MouseWatcher. Returns // true if successful, or false if the group was already @@ -237,6 +189,71 @@ remove_group(MouseWatcherGroup *group) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_num_groups +// Access: Published +// Description: Returns the number of separate groups added to the +// MouseWatcher via add_group(). +//////////////////////////////////////////////////////////////////// +int MouseWatcher:: +get_num_groups() const { + return _groups.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_group +// Access: Published +// Description: Returns the nth group added to the MouseWatcher via +// add_group(). +//////////////////////////////////////////////////////////////////// +MouseWatcherGroup *MouseWatcher:: +get_group(int n) const { + nassertr(n >= 0 && n < (int)_groups.size(), NULL); + return _groups[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +output(ostream &out) const { + DataNode::output(out); + + int count = _regions.size(); + Groups::const_iterator gi; + for (gi = _groups.begin(); gi != _groups.end(); ++gi) { + MouseWatcherGroup *group = (*gi); + count += group->_regions.size(); + } + + out << " (" << count << " regions)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << "MouseWatcher " << get_name() << ":\n"; + MouseWatcherGroup::write(out, indent_level + 2); + + if (!_groups.empty()) { + Groups::const_iterator gi; + for (gi = _groups.begin(); gi != _groups.end(); ++gi) { + MouseWatcherGroup *group = (*gi); + indent(out, indent_level + 2) + << "Subgroup:\n"; + group->write(out, indent_level + 4); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::get_over_regions // Access: Protected @@ -611,14 +628,14 @@ throw_event_pattern(const string &pattern, const MouseWatcherRegion *region, // being moved from last position. //////////////////////////////////////////////////////////////////// void MouseWatcher:: -move(ButtonHandle button) { +move() { MouseWatcherParameter param; - param.set_button(button); param.set_modifier_buttons(_mods); param.set_mouse(_mouse); - if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) + if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) { _preferred_button_down_region->move(param); + } } //////////////////////////////////////////////////////////////////// @@ -980,7 +997,6 @@ consider_keyboard_suppress(const MouseWatcherRegion *region) { //////////////////////////////////////////////////////////////////// void MouseWatcher:: do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { - bool mouse_moved = false; // Initially, we do not suppress any events to objects below us in // the data graph. _internal_suppress = 0; @@ -1002,7 +1018,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { // Asad: determine if mouse moved from last position const LVecBase2f &last_f = _xy->get_value(); if (f != last_f) { - mouse_moved = true; + move(); } if (_display_region != (DisplayRegion *)NULL) { @@ -1083,10 +1099,6 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { if (_has_mouse && (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) { - if (mouse_moved) { - move(ButtonHandle::none()); - //tform_cat.info() << "do_transmit_data()::mouse_moved" << endl; - } // Transmit the mouse position. _xy->set_value(_mouse); output.set_data(_xy_output, EventParameter(_xy)); diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index 910963b812..da5037efaf 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -109,13 +109,15 @@ PUBLISHED: INLINE DisplayRegion *get_display_region() const; INLINE bool has_display_region() const; + bool add_group(MouseWatcherGroup *group); + bool remove_group(MouseWatcherGroup *group); + int get_num_groups() const; + MouseWatcherGroup *get_group(int n) const; + public: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; - bool add_group(MouseWatcherGroup *group); - bool remove_group(MouseWatcherGroup *group); - protected: typedef pvector< PT(MouseWatcherRegion) > VRegions; void get_over_regions(VRegions ®ions, const LPoint2f &pos) const; @@ -136,7 +138,7 @@ protected: const MouseWatcherRegion *region, const ButtonHandle &button); - void move(ButtonHandle button); + void move(); void press(ButtonHandle button); void release(ButtonHandle button); void keystroke(int keycode); diff --git a/panda/src/tform/mouseWatcherGroup.cxx b/panda/src/tform/mouseWatcherGroup.cxx index da3cbc6e0d..a597288a06 100644 --- a/panda/src/tform/mouseWatcherGroup.cxx +++ b/panda/src/tform/mouseWatcherGroup.cxx @@ -17,10 +17,24 @@ //////////////////////////////////////////////////////////////////// #include "mouseWatcherGroup.h" - +#include "lineSegs.h" +#include "indent.h" TypeHandle MouseWatcherGroup::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MouseWatcherGroup:: +MouseWatcherGroup() { +#ifndef NDEBUG + _show_regions = false; + _color.set(0.4f, 0.6f, 1.0f, 1.0f); +#endif // NDEBUG +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcherGroup::Destructor // Access: Public, Virtual @@ -34,31 +48,30 @@ MouseWatcherGroup:: // Function: MouseWatcherGroup::add_region // Access: Published // Description: Adds the indicated region to the set of regions in -// the group. Returns true if it was successfully -// added, or false if it was already on the list. +// the group. It is an error to add the same region to +// the set more than once. //////////////////////////////////////////////////////////////////// -bool MouseWatcherGroup:: +void MouseWatcherGroup:: add_region(MouseWatcherRegion *region) { - //return _regions.insert(region).second; - PT(MouseWatcherRegion) pt = region; // We will only bother to check for duplicates in the region list if - // we are building opt 1 or 2. The overhead for doing this may be - // too high if we have many regions. -#ifdef _DEBUG + // we are building a development Panda. The overhead for doing this + // may be too high if we have many regions. +#ifndef NDEBUG // See if the region is in the set/vector already Regions::const_iterator ri = find(_regions.begin(), _regions.end(), pt); - if (ri != _regions.end()) { - // Already in the set, return false - return false; - } -#endif + nassertv(ri == _regions.end()); + + // Also add it to the vizzes if we have them. + if (_show_regions) { + nassertv(_vizzes.size() == _regions.size()); + _vizzes.push_back(make_viz_region(pt)); + } +#endif // NDEBUG - // Not in the set, add it and return true _regions.push_back(pt); - return true; } //////////////////////////////////////////////////////////////////// @@ -69,7 +82,7 @@ add_region(MouseWatcherRegion *region) { //////////////////////////////////////////////////////////////////// bool MouseWatcherGroup:: has_region(MouseWatcherRegion *region) const { - // See if the region is in the set/vector + // See if the region is in the vector. PT(MouseWatcherRegion) pt = region; Regions::const_iterator ri = find(_regions.begin(), _regions.end(), pt); @@ -90,14 +103,23 @@ has_region(MouseWatcherRegion *region) const { //////////////////////////////////////////////////////////////////// bool MouseWatcherGroup:: remove_region(MouseWatcherRegion *region) { - //return _regions.erase(region) != 0; - - // See if the region is in the set/vector + // See if the region is in the vector. PT(MouseWatcherRegion) pt = region; Regions::iterator ri = find(_regions.begin(), _regions.end(), pt); if (ri != _regions.end()) { // Found it, now erase it +#ifndef NDEBUG + // Also remove it from the vizzes. + if (_show_regions) { + nassertr(_vizzes.size() == _regions.size(), false); + size_t index = ri - _regions.begin(); + Vizzes::iterator vi = _vizzes.begin() + index; + _show_regions_root.node()->remove_child(*vi); + _vizzes.erase(vi); + } +#endif // NDEBUG + _regions.erase(ri); return true; } @@ -134,4 +156,152 @@ find_region(const string &name) const { void MouseWatcherGroup:: clear_regions() { _regions.clear(); + +#ifndef NDEBUG + if (_show_regions) { + _show_regions_root.node()->remove_all_children(); + _vizzes.clear(); + } +#endif // NDEBUG } + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::get_num_regions +// Access: Published +// Description: Returns the number of regions in the group. +//////////////////////////////////////////////////////////////////// +int MouseWatcherGroup:: +get_num_regions() const { + return _regions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::get_region +// Access: Published +// Description: Returns the nth regions in the group. +//////////////////////////////////////////////////////////////////// +MouseWatcherRegion *MouseWatcherGroup:: +get_region(int n) const { + nassertr(n >= 0 && n < (int)_regions.size(), NULL); + return _regions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +output(ostream &out) const { + out << "MouseWatcherGroup (" << _regions.size() << " regions)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::write +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +write(ostream &out, int indent_level) const { + Regions::const_iterator ri; + for (ri = _regions.begin(); ri != _regions.end(); ++ri) { + MouseWatcherRegion *region = (*ri); + region->write(out, indent_level); + } +} + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::show_regions +// Access: Published +// Description: Enables the visualization of all of the regions +// handled by this MouseWatcherGroup. The supplied +// NodePath should be the root of the 2-d scene graph +// for the window. +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +show_regions(const NodePath &render2d) { + _show_regions = true; + _show_regions_root = render2d.attach_new_node("show_regions"); + _show_regions_root.set_bin("unsorted", 0); + update_regions(); +} +#endif // NDEBUG + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::set_color +// Access: Published +// Description: Specifies the color used to draw the region +// rectangles for the regions visualized by +// show_regions(). +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +set_color(const Colorf &color) { + _color = color; + update_regions(); +} +#endif // NDEBUG + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::hide_regions +// Access: Published +// Description: Stops the visualization created by a previous call to +// show_regions(). +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +hide_regions() { + _show_regions_root.remove_node(); + _show_regions = false; + _vizzes.clear(); +} +#endif // NDEBUG + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::update_regions +// Access: Private +// Description: Internally regenerates the show_regions() +// visualization. +//////////////////////////////////////////////////////////////////// +void MouseWatcherGroup:: +update_regions() { + _show_regions_root.node()->remove_all_children(); + _vizzes.clear(); + _vizzes.reserve(_regions.size()); + + Regions::const_iterator ri; + for (ri = _regions.begin(); ri != _regions.end(); ++ri) { + _vizzes.push_back(make_viz_region(*ri)); + } +} +#endif // NDEBUG + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherGroup::make_viz_region +// Access: Private +// Description: Creates a node to represent the indicated region, and +// attaches it to the _show_regions_root. Does not add +// it to _vizzes. +//////////////////////////////////////////////////////////////////// +PandaNode *MouseWatcherGroup:: +make_viz_region(MouseWatcherRegion *region) { + LineSegs ls("show_regions"); + ls.set_color(_color); + + const LVecBase4f &f = region->get_frame(); + + ls.move_to(LVector3f::rfu(f[0], 0.0f, f[2])); + ls.draw_to(LVector3f::rfu(f[1], 0.0f, f[2])); + ls.draw_to(LVector3f::rfu(f[1], 0.0f, f[3])); + ls.draw_to(LVector3f::rfu(f[0], 0.0f, f[3])); + ls.draw_to(LVector3f::rfu(f[0], 0.0f, f[2])); + + PT(PandaNode) node = ls.create(); + _show_regions_root.attach_new_node(node); + + return node; +} +#endif // NDEBUG diff --git a/panda/src/tform/mouseWatcherGroup.h b/panda/src/tform/mouseWatcherGroup.h index dcbc1dfc5d..e56ed6368a 100644 --- a/panda/src/tform/mouseWatcherGroup.h +++ b/panda/src/tform/mouseWatcherGroup.h @@ -25,6 +25,7 @@ #include "pointerTo.h" #include "referenceCount.h" #include "pvector.h" +#include "nodePath.h" //////////////////////////////////////////////////////////////////// // Class : MouseWatcherGroup @@ -33,19 +34,45 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA MouseWatcherGroup : virtual public ReferenceCount { public: + MouseWatcherGroup(); virtual ~MouseWatcherGroup(); PUBLISHED: - bool add_region(MouseWatcherRegion *region); + void add_region(MouseWatcherRegion *region); bool has_region(MouseWatcherRegion *region) const; bool remove_region(MouseWatcherRegion *region); MouseWatcherRegion *find_region(const string &name) const; void clear_regions(); + int get_num_regions() const; + MouseWatcherRegion *get_region(int n) const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +#ifndef NDEBUG + void show_regions(const NodePath &render2d); + void set_color(const Colorf &color); + void hide_regions(); +#endif // NDEBUG + protected: typedef pvector< PT(MouseWatcherRegion) > Regions; Regions _regions; +private: +#ifndef NDEBUG + void update_regions(); + PandaNode *make_viz_region(MouseWatcherRegion *region); + + typedef pvector< PT(PandaNode) > Vizzes; + Vizzes _vizzes; + + bool _show_regions; + NodePath _show_regions_root; + Colorf _color; +#endif // NDEBUG + public: static TypeHandle get_class_type() { ReferenceCount::init_type();