|
@@ -1,410 +0,0 @@
|
|
|
-"""
|
|
|
|
|
-Show how to use libRocket in Panda3D.
|
|
|
|
|
-"""
|
|
|
|
|
-import sys
|
|
|
|
|
-from panda3d.core import loadPrcFile, loadPrcFileData, Point3,Vec4, Mat4, LoaderOptions # @UnusedImport
|
|
|
|
|
-from panda3d.core import DirectionalLight, AmbientLight, PointLight
|
|
|
|
|
-from panda3d.core import Texture, PNMImage
|
|
|
|
|
-from panda3d.core import PandaSystem
|
|
|
|
|
-import random
|
|
|
|
|
-from direct.interval.LerpInterval import LerpHprInterval, LerpPosInterval, LerpFunc
|
|
|
|
|
-from direct.showbase.ShowBase import ShowBase
|
|
|
|
|
-
|
|
|
|
|
-# workaround: https://www.panda3d.org/forums/viewtopic.php?t=10062&p=99697#p99054
|
|
|
|
|
-#from panda3d import rocket
|
|
|
|
|
-import _rocketcore as rocket
|
|
|
|
|
-
|
|
|
|
|
-from panda3d.rocket import RocketRegion, RocketInputHandler
|
|
|
|
|
-
|
|
|
|
|
-loadPrcFileData("", "model-path $MAIN_DIR/assets")
|
|
|
|
|
-
|
|
|
|
|
-import console
|
|
|
|
|
-
|
|
|
|
|
-global globalClock
|
|
|
|
|
-
|
|
|
|
|
-class MyApp(ShowBase):
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self):
|
|
|
|
|
- ShowBase.__init__(self)
|
|
|
|
|
-
|
|
|
|
|
- self.win.setClearColor(Vec4(0.2, 0.2, 0.2, 1))
|
|
|
|
|
-
|
|
|
|
|
- self.disableMouse()
|
|
|
|
|
-
|
|
|
|
|
- self.render.setShaderAuto()
|
|
|
|
|
-
|
|
|
|
|
- dlight = DirectionalLight('dlight')
|
|
|
|
|
- alight = AmbientLight('alight')
|
|
|
|
|
- dlnp = self.render.attachNewNode(dlight)
|
|
|
|
|
- alnp = self.render.attachNewNode(alight)
|
|
|
|
|
- dlight.setColor((0.8, 0.8, 0.5, 1))
|
|
|
|
|
- alight.setColor((0.2, 0.2, 0.2, 1))
|
|
|
|
|
- dlnp.setHpr(0, -60, 0)
|
|
|
|
|
- self.render.setLight(dlnp)
|
|
|
|
|
- self.render.setLight(alnp)
|
|
|
|
|
-
|
|
|
|
|
- # Put lighting on the main scene
|
|
|
|
|
- plight = PointLight('plight')
|
|
|
|
|
- plnp = self.render.attachNewNode(plight)
|
|
|
|
|
- plnp.setPos(0, 0, 10)
|
|
|
|
|
- self.render.setLight(plnp)
|
|
|
|
|
- self.render.setLight(alnp)
|
|
|
|
|
-
|
|
|
|
|
- self.loadRocketFonts()
|
|
|
|
|
-
|
|
|
|
|
- self.loadingTask = None
|
|
|
|
|
-
|
|
|
|
|
- #self.startModelLoadingAsync()
|
|
|
|
|
- self.startModelLoading()
|
|
|
|
|
-
|
|
|
|
|
- self.inputHandler = RocketInputHandler()
|
|
|
|
|
- self.mouseWatcher.attachNewNode(self.inputHandler)
|
|
|
|
|
-
|
|
|
|
|
- self.openLoadingDialog()
|
|
|
|
|
-
|
|
|
|
|
- def loadRocketFonts(self):
|
|
|
|
|
- """ Load fonts referenced from e.g. 'font-family' RCSS directives.
|
|
|
|
|
-
|
|
|
|
|
- Note: the name of the font as used in 'font-family'
|
|
|
|
|
- is not always the same as the filename;
|
|
|
|
|
- open the font in your OS to see its display name.
|
|
|
|
|
- """
|
|
|
|
|
- rocket.LoadFontFace("modenine.ttf")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def startModelLoading(self):
|
|
|
|
|
- self.monitorNP = None
|
|
|
|
|
- self.keyboardNP = None
|
|
|
|
|
- self.loadingError = False
|
|
|
|
|
-
|
|
|
|
|
- self.taskMgr.doMethodLater(1, self.loadModels, 'loadModels')
|
|
|
|
|
-
|
|
|
|
|
- def loadModels(self, task):
|
|
|
|
|
- self.monitorNP = self.loader.loadModel("monitor")
|
|
|
|
|
- self.keyboardNP = self.loader.loadModel("takeyga_kb")
|
|
|
|
|
-
|
|
|
|
|
- def startModelLoadingAsync(self):
|
|
|
|
|
- """
|
|
|
|
|
- NOTE: this seems to invoke a few bugs (crashes, sporadic model
|
|
|
|
|
- reading errors, etc) so is disabled for now...
|
|
|
|
|
- """
|
|
|
|
|
- self.monitorNP = None
|
|
|
|
|
- self.keyboardNP = None
|
|
|
|
|
- self.loadingError = False
|
|
|
|
|
-
|
|
|
|
|
- # force the "loading" to take some time after the first run...
|
|
|
|
|
- options = LoaderOptions()
|
|
|
|
|
- options.setFlags(options.getFlags() | LoaderOptions.LFNoCache)
|
|
|
|
|
-
|
|
|
|
|
- def gotMonitorModel(model):
|
|
|
|
|
- if not model:
|
|
|
|
|
- self.loadingError = True
|
|
|
|
|
- self.monitorNP = model
|
|
|
|
|
-
|
|
|
|
|
- self.loader.loadModel("monitor", loaderOptions=options, callback=gotMonitorModel)
|
|
|
|
|
-
|
|
|
|
|
- def gotKeyboardModel(model):
|
|
|
|
|
- if not model:
|
|
|
|
|
- self.loadingError = True
|
|
|
|
|
- self.keyboardNP = model
|
|
|
|
|
-
|
|
|
|
|
- self.loader.loadModel("takeyga_kb", loaderOptions=options, callback=gotKeyboardModel)
|
|
|
|
|
-
|
|
|
|
|
- def openLoadingDialog(self):
|
|
|
|
|
- self.userConfirmed = False
|
|
|
|
|
-
|
|
|
|
|
- self.windowRocketRegion = RocketRegion.make('pandaRocket', self.win)
|
|
|
|
|
- self.windowRocketRegion.setActive(1)
|
|
|
|
|
-
|
|
|
|
|
- self.windowRocketRegion.setInputHandler(self.inputHandler)
|
|
|
|
|
-
|
|
|
|
|
- self.windowContext = self.windowRocketRegion.getContext()
|
|
|
|
|
-
|
|
|
|
|
- self.loadingDocument = self.windowContext.LoadDocument("loading.rml")
|
|
|
|
|
- if not self.loadingDocument:
|
|
|
|
|
- raise AssertionError("did not find loading.rml")
|
|
|
|
|
-
|
|
|
|
|
- self.loadingDots = 0
|
|
|
|
|
- el = self.loadingDocument.GetElementById('loadingLabel')
|
|
|
|
|
- self.loadingText = el.first_child
|
|
|
|
|
- self.stopLoadingTime = globalClock.getFrameTime() + 3
|
|
|
|
|
- self.loadingTask = self.taskMgr.add(self.cycleLoading, 'doc changer')
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- # note: you may encounter errors like 'KeyError: 'document'"
|
|
|
|
|
- # when invoking events using methods from your own scripts with this
|
|
|
|
|
- # obvious code:
|
|
|
|
|
- #
|
|
|
|
|
- # self.loadingDocument.AddEventListener('aboutToClose',
|
|
|
|
|
- # self.onLoadingDialogDismissed, True)
|
|
|
|
|
- #
|
|
|
|
|
- # A workaround is to define callback methods in standalone Python
|
|
|
|
|
- # files with event, self, and document defined to None.
|
|
|
|
|
- #
|
|
|
|
|
- # see https://www.panda3d.org/forums/viewtopic.php?f=4&t=16412
|
|
|
|
|
- #
|
|
|
|
|
-
|
|
|
|
|
- # Or, use this indirection technique to work around the problem,
|
|
|
|
|
- # by publishing the app into the context, then accessing it through
|
|
|
|
|
- # the document's context...
|
|
|
|
|
-
|
|
|
|
|
- self.windowContext.app = self
|
|
|
|
|
- self.loadingDocument.AddEventListener('aboutToClose',
|
|
|
|
|
- 'document.context.app.handleAboutToClose()', True)
|
|
|
|
|
-
|
|
|
|
|
- self.loadingDocument.Show()
|
|
|
|
|
-
|
|
|
|
|
- def handleAboutToClose(self):
|
|
|
|
|
- self.userConfirmed = True
|
|
|
|
|
- if self.monitorNP and self.keyboardNP:
|
|
|
|
|
- self.onLoadingDialogDismissed()
|
|
|
|
|
-
|
|
|
|
|
- def attachCustomRocketEvent(self, document, rocketEventName, pandaHandler, once=False):
|
|
|
|
|
- # handle custom event
|
|
|
|
|
-
|
|
|
|
|
- # note: you may encounter errors like 'KeyError: 'document'"
|
|
|
|
|
- # when invoking events using methods from your own scripts with this
|
|
|
|
|
- # obvious code:
|
|
|
|
|
- #
|
|
|
|
|
- # self.loadingDocument.AddEventListener('aboutToClose',
|
|
|
|
|
- # self.onLoadingDialogDismissed, True)
|
|
|
|
|
- #
|
|
|
|
|
- # see https://www.panda3d.org/forums/viewtopic.php?f=4&t=16412
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- # this technique converts Rocket events to Panda3D events
|
|
|
|
|
-
|
|
|
|
|
- pandaEvent = 'panda.' + rocketEventName
|
|
|
|
|
-
|
|
|
|
|
- document.AddEventListener(
|
|
|
|
|
- rocketEventName,
|
|
|
|
|
- "messenger.send('" + pandaEvent + "', [event])")
|
|
|
|
|
-
|
|
|
|
|
- if once:
|
|
|
|
|
- self.acceptOnce(pandaEvent, pandaHandler)
|
|
|
|
|
- else:
|
|
|
|
|
- self.accept(pandaEvent, pandaHandler)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def cycleLoading(self, task):
|
|
|
|
|
- """
|
|
|
|
|
- Update the "loading" text in the initial window until
|
|
|
|
|
- the user presses Space, Enter, or Escape or clicks (see loading.rxml)
|
|
|
|
|
- or sufficient time has elapsed (self.stopLoadingTime).
|
|
|
|
|
- """
|
|
|
|
|
- text = self.loadingText
|
|
|
|
|
-
|
|
|
|
|
- now = globalClock.getFrameTime()
|
|
|
|
|
- if self.monitorNP and self.keyboardNP:
|
|
|
|
|
- text.text = "Ready"
|
|
|
|
|
- if now > self.stopLoadingTime or self.userConfirmed:
|
|
|
|
|
- self.onLoadingDialogDismissed()
|
|
|
|
|
- return task.done
|
|
|
|
|
- elif self.loadingError:
|
|
|
|
|
- text.text = "Assets not found"
|
|
|
|
|
- else:
|
|
|
|
|
- count = 5
|
|
|
|
|
- intv = int(now * 4) % count # @UndefinedVariable
|
|
|
|
|
- text.text = "Loading" + ("." * (1+intv)) + (" " * (2 - intv))
|
|
|
|
|
-
|
|
|
|
|
- return task.cont
|
|
|
|
|
-
|
|
|
|
|
- def onLoadingDialogDismissed(self):
|
|
|
|
|
- """ Once a models are loaded, stop 'loading' and proceed to 'start' """
|
|
|
|
|
- if self.loadingDocument:
|
|
|
|
|
- if self.loadingTask:
|
|
|
|
|
- self.taskMgr.remove(self.loadingTask)
|
|
|
|
|
- self.loadingTask = None
|
|
|
|
|
-
|
|
|
|
|
- self.showStarting()
|
|
|
|
|
-
|
|
|
|
|
- def fadeOut(self, element, time):
|
|
|
|
|
- """ Example updating RCSS attributes from code
|
|
|
|
|
- by modifying the 'color' RCSS attribute to slowly
|
|
|
|
|
- change from solid to transparent.
|
|
|
|
|
-
|
|
|
|
|
- element: the Rocket element whose style to modify
|
|
|
|
|
- time: time in seconds for fadeout
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # get the current color from RCSS effective style
|
|
|
|
|
- color = element.style.color
|
|
|
|
|
- # convert to RGBA form
|
|
|
|
|
- prefix = color[:color.rindex(',')+1].replace('rgb(', 'rgba(')
|
|
|
|
|
-
|
|
|
|
|
- def updateAlpha(t):
|
|
|
|
|
- # another way of setting style on a specific element
|
|
|
|
|
- attr = 'color: ' + prefix + str(int(t)) +');'
|
|
|
|
|
- element.SetAttribute('style', attr)
|
|
|
|
|
-
|
|
|
|
|
- alphaInterval = LerpFunc(updateAlpha,
|
|
|
|
|
- duration=time,
|
|
|
|
|
- fromData=255,
|
|
|
|
|
- toData=0,
|
|
|
|
|
- blendType='easeIn')
|
|
|
|
|
-
|
|
|
|
|
- return alphaInterval
|
|
|
|
|
-
|
|
|
|
|
- def showStarting(self):
|
|
|
|
|
- """ Models are loaded, so update the dialog,
|
|
|
|
|
- fade out, then transition to the console. """
|
|
|
|
|
- self.loadingText.text = 'Starting...'
|
|
|
|
|
-
|
|
|
|
|
- alphaInterval = self.fadeOut(self.loadingText, 0.5)
|
|
|
|
|
- alphaInterval.setDoneEvent('fadeOutFinished')
|
|
|
|
|
-
|
|
|
|
|
- def fadeOutFinished():
|
|
|
|
|
- if self.loadingDocument:
|
|
|
|
|
- self.loadingDocument.Close()
|
|
|
|
|
- self.loadingDocument = None
|
|
|
|
|
- self.createConsole()
|
|
|
|
|
-
|
|
|
|
|
- self.accept('fadeOutFinished', fadeOutFinished)
|
|
|
|
|
-
|
|
|
|
|
- alphaInterval.start()
|
|
|
|
|
-
|
|
|
|
|
- def createConsole(self):
|
|
|
|
|
- """ Create the in-world console, which displays
|
|
|
|
|
- a RocketRegion in a GraphicsBuffer, which appears
|
|
|
|
|
- in a Texture on the monitor model. """
|
|
|
|
|
-
|
|
|
|
|
- self.monitorNP.reparentTo(self.render)
|
|
|
|
|
- self.monitorNP.setScale(1.5)
|
|
|
|
|
-
|
|
|
|
|
- self.keyboardNP.reparentTo(self.render)
|
|
|
|
|
- self.keyboardNP.setHpr(-90, 0, 15)
|
|
|
|
|
- self.keyboardNP.setScale(20)
|
|
|
|
|
-
|
|
|
|
|
- self.placeItems()
|
|
|
|
|
-
|
|
|
|
|
- self.setupRocketConsole()
|
|
|
|
|
-
|
|
|
|
|
- # re-enable mouse
|
|
|
|
|
- mat=Mat4(self.camera.getMat())
|
|
|
|
|
- mat.invertInPlace()
|
|
|
|
|
- self.mouseInterfaceNode.setMat(mat)
|
|
|
|
|
- self.enableMouse()
|
|
|
|
|
-
|
|
|
|
|
- def placeItems(self):
|
|
|
|
|
- self.camera.setPos(0, -20, 0)
|
|
|
|
|
- self.camera.setHpr(0, 0, 0)
|
|
|
|
|
- self.monitorNP.setPos(0, 0, 1)
|
|
|
|
|
- self.keyboardNP.setPos(0, -5, -2.5)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def setupRocketConsole(self):
|
|
|
|
|
- """
|
|
|
|
|
- Place a new rocket window onto a texture
|
|
|
|
|
- bound to the front of the monitor.
|
|
|
|
|
- """
|
|
|
|
|
- self.win.setClearColor(Vec4(0.5, 0.5, 0.8, 1))
|
|
|
|
|
-
|
|
|
|
|
- faceplate = self.monitorNP.find("**/Faceplate")
|
|
|
|
|
- assert faceplate
|
|
|
|
|
-
|
|
|
|
|
- mybuffer = self.win.makeTextureBuffer("Console Buffer", 1024, 512)
|
|
|
|
|
- tex = mybuffer.getTexture()
|
|
|
|
|
- tex.setMagfilter(Texture.FTLinear)
|
|
|
|
|
- tex.setMinfilter(Texture.FTLinear)
|
|
|
|
|
-
|
|
|
|
|
- faceplate.setTexture(tex, 1)
|
|
|
|
|
-
|
|
|
|
|
- self.rocketConsole = RocketRegion.make('console', mybuffer)
|
|
|
|
|
- self.rocketConsole.setInputHandler(self.inputHandler)
|
|
|
|
|
-
|
|
|
|
|
- self.consoleContext = self.rocketConsole.getContext()
|
|
|
|
|
- self.console = console.Console(self, self.consoleContext, 40, 13, self.handleCommand)
|
|
|
|
|
-
|
|
|
|
|
- self.console.addLine("Panda DOS")
|
|
|
|
|
- self.console.addLine("type 'help'")
|
|
|
|
|
- self.console.addLine("")
|
|
|
|
|
-
|
|
|
|
|
- self.console.allowEditing(True)
|
|
|
|
|
-
|
|
|
|
|
- def handleCommand(self, command):
|
|
|
|
|
- if command is None:
|
|
|
|
|
- # hack for Ctrl-Break
|
|
|
|
|
- self.spewInProgress = False
|
|
|
|
|
- self.console.addLine("*** break ***")
|
|
|
|
|
- self.console.allowEditing(True)
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- command = command.strip()
|
|
|
|
|
- if not command:
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- tokens = [x.strip() for x in command.split(' ')]
|
|
|
|
|
- command = tokens[0].lower()
|
|
|
|
|
-
|
|
|
|
|
- if command == 'help':
|
|
|
|
|
- self.console.addLines([
|
|
|
|
|
- "Sorry, this is utter fakery.",
|
|
|
|
|
- "You won't get much more",
|
|
|
|
|
- "out of this simulation unless",
|
|
|
|
|
- "you program it yourself. :)"
|
|
|
|
|
- ])
|
|
|
|
|
- elif command == 'dir':
|
|
|
|
|
- self.console.addLines([
|
|
|
|
|
- "Directory of C:\\:",
|
|
|
|
|
- "HELP COM 72 05-06-2015 14:07",
|
|
|
|
|
- "DIR COM 121 05-06-2015 14:11",
|
|
|
|
|
- "SPEW COM 666 05-06-2015 15:02",
|
|
|
|
|
- " 2 Files(s) 859 Bytes.",
|
|
|
|
|
- " 0 Dirs(s) 7333 Bytes free.",
|
|
|
|
|
- ""])
|
|
|
|
|
- elif command == 'cls':
|
|
|
|
|
- self.console.cls()
|
|
|
|
|
- elif command == 'echo':
|
|
|
|
|
- self.console.addLine(' '.join(tokens[1:]))
|
|
|
|
|
- elif command == 'ver':
|
|
|
|
|
- self.console.addLine('Panda DOS v0.01 in Panda3D ' + PandaSystem.getVersionString())
|
|
|
|
|
- elif command == 'spew':
|
|
|
|
|
- self.startSpew()
|
|
|
|
|
- elif command == 'exit':
|
|
|
|
|
- self.console.setPrompt("System is shutting down NOW!")
|
|
|
|
|
- self.terminateMonitor()
|
|
|
|
|
- else:
|
|
|
|
|
- self.console.addLine("command not found")
|
|
|
|
|
-
|
|
|
|
|
- def startSpew(self):
|
|
|
|
|
- self.console.allowEditing(False)
|
|
|
|
|
- self.console.addLine("LINE NOISE 1.0")
|
|
|
|
|
- self.console.addLine("")
|
|
|
|
|
-
|
|
|
|
|
- self.spewInProgress = True
|
|
|
|
|
-
|
|
|
|
|
- # note: spewage always occurs in 'doMethodLater';
|
|
|
|
|
- # time.sleep() would be pointless since the whole
|
|
|
|
|
- # UI would be frozen during the wait.
|
|
|
|
|
- self.queueSpew(2)
|
|
|
|
|
-
|
|
|
|
|
- def queueSpew(self, delay=0.1):
|
|
|
|
|
- self.taskMgr.doMethodLater(delay, self.spew, 'spew')
|
|
|
|
|
-
|
|
|
|
|
- def spew(self, task):
|
|
|
|
|
- # generate random spewage, just like on TV!
|
|
|
|
|
- if not self.spewInProgress:
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- def randchr():
|
|
|
|
|
- return chr(int(random.random() < 0.25 and 32 or random.randint(32, 127)))
|
|
|
|
|
-
|
|
|
|
|
- line = ''.join([randchr() for _ in range(40) ])
|
|
|
|
|
-
|
|
|
|
|
- self.console.addLine(line)
|
|
|
|
|
- self.queueSpew()
|
|
|
|
|
-
|
|
|
|
|
- def terminateMonitor(self):
|
|
|
|
|
- alphaInterval = self.fadeOut(self.console.getTextContainer(), 2)
|
|
|
|
|
-
|
|
|
|
|
- alphaInterval.setDoneEvent('fadeOutFinished')
|
|
|
|
|
-
|
|
|
|
|
- def fadeOutFinished():
|
|
|
|
|
- sys.exit(0)
|
|
|
|
|
-
|
|
|
|
|
- self.accept('fadeOutFinished', fadeOutFinished)
|
|
|
|
|
-
|
|
|
|
|
- alphaInterval.start()
|
|
|
|
|
-
|
|
|
|
|
-app = MyApp()
|
|
|
|
|
-app.run()
|
|
|