| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- """
- 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()
|