better support for multiple windows, multiple trackballs, one camera

This commit is contained in:
David Rose
2012-01-13 00:22:05 +00:00
parent 9eea663587
commit 50e53a4d4e
5 changed files with 135 additions and 92 deletions
+1 -1
View File
@@ -1184,7 +1184,7 @@ class DisplayRegionContext(DirectObject):
# Values for this frame
# This ranges from -1 to 1
if (base.mouseWatcherNode.hasMouse()):
if base.mouseWatcherNode and base.mouseWatcherNode.hasMouse():
self.mouseX = base.mouseWatcherNode.getMouseX()
self.mouseY = base.mouseWatcherNode.getMouseY()
self.mouseX = (self.mouseX-self.originX)*self.scaleX
+1 -1
View File
@@ -14,7 +14,7 @@ from ObjectPaletteBase import ObjectGen
# python wrapper around a panda.NodePath object
class PythonNodePath(NodePath):
def __init__(self,node):
NodePath.NodePath.__init__(self,node)
NodePath.__init__(self,node)
class ObjectMgrBase:
""" ObjectMgr will create, manage, update objects in the scene """
+123 -79
View File
@@ -246,6 +246,13 @@ class ShowBase(DirectObject.DirectObject):
props.setRawMice(1)
self.openDefaultWindow(startDirect = False, props=props)
# The default is trackball mode, which is more convenient for
# ad-hoc development in Python using ShowBase. Applications
# can explicitly call base.useDrive() if they prefer a drive
# interface.
self.mouseInterface = self.trackball
self.useTrackball()
self.loader = Loader.Loader(self)
self.graphicsEngine.setDefaultLoader(self.loader.loader)
@@ -566,19 +573,102 @@ class ShowBase(DirectObject.DirectObject):
def openWindow(self, props = None, pipe = None, gsg = None,
type = None, name = None, size = None, aspectRatio = None,
makeCamera = 1, keepCamera = 0,
scene = None, stereo = None, rawmice = 0,
callbackWindowDict = None):
makeCamera = True, keepCamera = False,
scene = None, stereo = None,
callbackWindowDict = None, requireWindow = None):
"""
Creates a window and adds it to the list of windows that are
to be updated every frame.
props is the WindowProperties that describes the window.
type is either 'onscreen', 'offscreen', or 'none'.
If keepCamera is true, the existing base.cam is set up to
render into the new window.
If keepCamera is false but makeCamera is true, a new camera is
set up to render into the new window.
If callbackWindowDict is not None, a CallbackGraphicWindow is
created instead, which allows the caller to create the actual
window with its own OpenGL context, and direct Panda's
rendering into that window.
If requireWindow is true, it means that the function should
raise an exception if the window fails to open correctly.
"""
# Save this lambda here for convenience; we'll use it to call
# down to the underlying _doOpenWindow() with all of the above
# parameters.
func = lambda : self._doOpenWindow(
props = props, pipe = pipe, gsg = gsg,
type = type, name = name, size = size, aspectRatio = aspectRatio,
makeCamera = makeCamera, keepCamera = keepCamera,
scene = scene, stereo = stereo,
callbackWindowDict = callbackWindowDict)
if self.win:
# If we've already opened a window before, this is just a
# pass-through to _doOpenWindow().
win = func()
self.graphicsEngine.openWindows()
return win
if type is None:
type = self.windowType
if requireWindow is None:
requireWindow = self.requireWindow
win = func()
# Give the window a chance to truly open.
self.graphicsEngine.openWindows()
if win != None and not win.isValid():
self.notify.info("Window did not open, removing.")
self.closeWindow(win)
win = None
if win == None and pipe == None:
# Try a little harder if the window wouldn't open.
self.makeAllPipes()
try:
self.pipeList.remove(self.pipe)
except ValueError:
pass
while self.win == None and self.pipeList:
self.pipe = self.pipeList[0]
self.notify.info("Trying pipe type %s (%s)" % (
self.pipe.getType(), self.pipe.getInterfaceName()))
win = func()
self.graphicsEngine.openWindows()
if win != None and not win.isValid():
self.notify.info("Window did not open, removing.")
self.closeWindow(win)
win = None
if win == None:
self.pipeList.remove(self.pipe)
if win == None:
self.notify.warning("Unable to open '%s' window." % (type))
if requireWindow:
# Unless require-window is set to false, it is an
# error not to open a window.
raise StandardError, 'Could not open window.'
else:
self.notify.info("Successfully opened window of type %s (%s)" % (
win.getType(), win.getPipe().getInterfaceName()))
return win
def _doOpenWindow(self, props = None, pipe = None, gsg = None,
type = None, name = None, size = None, aspectRatio = None,
makeCamera = True, keepCamera = False,
scene = None, stereo = None,
callbackWindowDict = None):
if pipe == None:
pipe = self.pipe
@@ -769,69 +859,13 @@ class ShowBase(DirectObject.DirectObject):
if 'startDirect' in kw:
del kw['startDirect']
if self.win:
# If we've already opened a window before, this does
# little more work than openMainWindow() alone.
self.openMainWindow(*args, **kw)
self.graphicsEngine.openWindows()
return
self.openMainWindow(*args, **kw)
# Give the window a chance to truly open.
self.graphicsEngine.openWindows()
if self.win != None and not self.isMainWindowOpen():
self.notify.info("Window did not open, removing.")
self.closeWindow(self.win)
if self.win == None and not kw.get('pipe', None):
# Try a little harder if the window wouldn't open.
self.makeAllPipes()
try:
self.pipeList.remove(self.pipe)
except ValueError:
pass
while self.win == None and self.pipeList:
self.pipe = self.pipeList[0]
self.notify.info("Trying pipe type %s (%s)" % (
self.pipe.getType(), self.pipe.getInterfaceName()))
self.openMainWindow(*args, **kw)
self.graphicsEngine.openWindows()
if self.win != None and not self.isMainWindowOpen():
self.notify.info("Window did not open, removing.")
self.closeWindow(self.win)
if self.win == None:
self.pipeList.remove(self.pipe)
if self.win == None:
self.notify.warning("Unable to open '%s' window." % (
self.windowType))
if self.requireWindow:
# Unless require-window is set to false, it is an
# error not to open a window.
raise StandardError, 'Could not open window.'
else:
self.notify.info("Successfully opened window of type %s (%s)" % (
self.win.getType(), self.win.getPipe().getInterfaceName()))
# The default is trackball mode, which is more convenient for
# ad-hoc development in Python using ShowBase. Applications
# can explicitly call base.useDrive() if they prefer a drive
# interface.
self.mouseInterface = self.trackball
self.useTrackball()
if startDirect:
self.__doStartDirect()
return self.win != None
def isMainWindowOpen(self):
if self.win != None:
return self.win.isValid()
return 0
def openMainWindow(self, *args, **kw):
"""
Creates the initial, main window for the application, and sets
@@ -1148,11 +1182,7 @@ class ShowBase(DirectObject.DirectObject):
win = self.win
if win != None and win.hasSize():
# Temporary hasattr for old Pandas
if not hasattr(win, 'getSbsLeftXSize'):
aspectRatio = float(win.getXSize()) / float(win.getYSize())
else:
aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
else:
if win == None or not hasattr(win, "getRequestedProperties"):
@@ -1216,6 +1246,8 @@ class ShowBase(DirectObject.DirectObject):
self.camera.node().setPreserveTransform(ModelNode.PTLocal)
__builtin__.camera = self.camera
self.mouse2cam.node().setNode(self.camera.node())
if useCamera:
# Use the existing camera node.
cam = useCamera
@@ -1381,7 +1413,13 @@ class ShowBase(DirectObject.DirectObject):
self.dataRoot = NodePath('dataRoot')
# Cache the node so we do not ask for it every frame
self.dataRootNode = self.dataRoot.node()
self.dataUnused = NodePath('dataUnused')
# Now we have the main trackball & drive interfaces.
# useTrackball() and useDrive() switch these in and out; only
# one is in use at a given time.
self.trackball = NodePath(Trackball('trackball'))
self.drive = NodePath(DriveInterface('drive'))
self.mouse2cam = NodePath(Transform2SG('mouse2cam'))
# [gjeon] now you can create multiple mouse watchers to support multiple windows
def setupMouse(self, win, fMultiWin=False):
@@ -1389,6 +1427,15 @@ class ShowBase(DirectObject.DirectObject):
Creates the structures necessary to monitor the mouse input,
using the indicated window. If the mouse has already been set
up for a different window, those structures are deleted first.
The return value is the ButtonThrower NodePath created for
this window.
If fMultiWin is true, then the previous mouse structures are
not deleted; instead, multiple windows are allowed to monitor
the mouse input. However, in this case, the trackball
controls are not set up, and must be set up by hand if
desired.
"""
if not fMultiWin and self.buttonThrowers != None:
for bt in self.buttonThrowers:
@@ -1409,6 +1456,9 @@ class ShowBase(DirectObject.DirectObject):
self.mouseWatcher = self.buttonThrowers[0].getParent()
self.mouseWatcherNode = self.mouseWatcher.node()
if self.mouseInterface:
self.mouseInterface.reparentTo(self.mouseWatcher)
if self.recorder:
# If we have a recorder, the mouseWatcher belongs under a
# special MouseRecorder node, which may intercept the
@@ -1420,14 +1470,6 @@ class ShowBase(DirectObject.DirectObject):
np = mw.getParent().attachNewNode(mouseRecorder)
mw.reparentTo(np)
# Now we have the main trackball & drive interfaces.
# useTrackball() and useDrive() switch these in and out; only
# one is in use at a given time.
self.trackball = self.dataUnused.attachNewNode(Trackball('trackball'))
self.drive = self.dataUnused.attachNewNode(DriveInterface('drive'))
self.mouse2cam = self.dataUnused.attachNewNode(Transform2SG('mouse2cam'))
self.mouse2cam.node().setNode(self.camera.node())
# A special ButtonThrower to generate keyboard events and
# include the time from the OS. This is separate only to
# support legacy code that did not expect a time parameter; it
@@ -1445,6 +1487,8 @@ class ShowBase(DirectObject.DirectObject):
self.pixel2dp.node().setMouseWatcher(mw.node())
mw.node().addRegion(PGMouseWatcherBackground())
return self.buttonThrowers[0]
# [gjeon] this function is seperated from setupMouse to allow multiple mouse watchers
def setupMouseCB(self, win):
# For each mouse/keyboard device, we create
@@ -1471,8 +1515,7 @@ class ShowBase(DirectObject.DirectObject):
mk = self.dataRoot.attachNewNode(MouseAndKeyboard(win, i, name))
mw = mk.attachNewNode(MouseWatcher("watcher%s" % (i)))
# Temporary hasattr for old Pandas
if hasattr(win, 'getSideBySideStereo') and win.getSideBySideStereo():
if win.getSideBySideStereo():
# If the window has side-by-side stereo enabled, then
# we should constrain the MouseWatcher to the window's
# DisplayRegion. This will enable the MouseWatcher to
@@ -1991,7 +2034,7 @@ class ShowBase(DirectObject.DirectObject):
# object out of there, so we won't be updating the camera any
# more.
if self.mouse2cam:
self.mouse2cam.reparentTo(self.dataUnused)
self.mouse2cam.detachNode()
def enableMouse(self):
"""
@@ -2029,12 +2072,13 @@ class ShowBase(DirectObject.DirectObject):
Switch mouse action
"""
# Get rid of the prior interface:
self.mouseInterface.reparentTo(self.dataUnused)
self.mouseInterface.detachNode()
# Update the mouseInterface to point to the drive
self.mouseInterface = changeTo
self.mouseInterfaceNode = self.mouseInterface.node()
# Hookup the drive to the camera.
self.mouseInterface.reparentTo(self.mouseWatcher)
if self.mouseWatcher:
self.mouseInterface.reparentTo(self.mouseWatcher)
if self.mouse2cam:
self.mouse2cam.reparentTo(self.mouseInterface)
@@ -2157,7 +2201,7 @@ class ShowBase(DirectObject.DirectObject):
self.oobeLens.setNearFar(0.1, 10000.0)
self.oobeLens.setMinFov(40)
self.oobeTrackball = self.dataUnused.attachNewNode(Trackball('oobeTrackball'), 1)
self.oobeTrackball = NodePath(Trackball('oobeTrackball'), 1)
self.oobe2cam = self.oobeTrackball.attachNewNode(Transform2SG('oobe2cam'))
self.oobe2cam.node().setNode(self.oobeCameraTrackball.node())
+1 -6
View File
@@ -35,12 +35,7 @@ class EmbeddedPandaWindow(wx.Window):
if platform.system() != 'Darwin':
wp.setParentWindow(self.GetHandle())
if base.win:
self.win = base.openWindow(props = wp, gsg = gsg, type = 'onscreen')
else:
base.openDefaultWindow(props = wp, gsg = gsg, type = 'onscreen')
self.win = base.win
self.win = base.openWindow(props = wp, gsg = gsg, type = 'onscreen')
self.Bind(wx.EVT_SIZE, self.onSize)
# This doesn't actually do anything, since wx won't call
+9 -5
View File
@@ -52,7 +52,9 @@ transmit_data(DataGraphTraverser *trav,
const DataConnection &connect = (*ci);
const EventParameter &data =
inputs[connect._parent_index].get_data(connect._output_index);
new_input.set_data(connect._input_index, data);
if (!data.is_empty()) {
new_input.set_data(connect._input_index, data);
}
}
#ifndef NDEBUG
@@ -308,7 +310,7 @@ reconnect() {
dgraph_cat.warning()
<< "Ignoring mismatched type for connection " << name
<< " between " << *data_node << " and " << *this << "\n";
} else if (num_found == 1) {
} else {
DataConnection dc;
dc._parent_index = i;
dc._output_index = output_def._index;
@@ -320,9 +322,11 @@ reconnect() {
}
if (num_found > 1) {
dgraph_cat.warning()
<< "Multiple connections found for " << name << " into " << *this
<< "\n";
if (dgraph_cat.is_debug()) {
dgraph_cat.debug()
<< "Multiple connections found for " << name << " into " << *this
<< "\n";
}
}
}