I have successfully ran a modified multi-threaded version of this sample:
https://github.com/OGRECave/ogre/blob/m ... /sample.py
It was not easy, and OGRE python binding needed to be modified.
First Attempt
Instead of the main() function, I put the code in a Thread::run().
Code: Select all
import Ogre
import Ogre.Bites
import Ogre.RTShader
import threading
class KeyListener(Ogre.Bites.InputListener):
def keyPressed(self, evt):
if evt.keysym.sym == Ogre.Bites.SDLK_ESCAPE:
Ogre.Root.getSingleton().queueEndRendering()
return True
class App(threading.Thread):
def run(self):
ctx = Ogre.Bites.ApplicationContext("PySample")
#
ctx.initApp()
#
root = ctx.getRoot()
scn_mgr = root.createSceneManager()
#
# register for input events
klistener = KeyListener() # must keep a reference around
ctx.addInputListener(klistener)
#
shadergen = Ogre.RTShader.ShaderGenerator.getSingleton()
shadergen.addSceneManager(scn_mgr) # must be done before we do anything with the scene
#
# without light we would just get a black screen
scn_mgr.setAmbientLight((.1, .1, .1))
#
light = scn_mgr.createLight("MainLight")
lightnode = scn_mgr.getRootSceneNode().createChildSceneNode()
lightnode.setPosition(0, 10, 15)
lightnode.attachObject(light)
#
# create the camera
cam = scn_mgr.createCamera("myCam")
cam.setNearClipDistance(5)
cam.setAutoAspectRatio(True)
camnode = scn_mgr.getRootSceneNode().createChildSceneNode()
camnode.attachObject(cam)
#
# map input events to camera controls
camman = Ogre.Bites.CameraMan(camnode)
camman.setStyle(Ogre.Bites.CS_ORBIT)
camman.setYawPitchDist(0, 0.3, 15)
ctx.addInputListener(camman)
#
# and tell it to render into the main window
vp = ctx.getRenderWindow().addViewport(cam)
vp.setBackgroundColour((.3, .3, .3))
#
# finally something to render
ent = scn_mgr.createEntity("Sinbad.mesh")
node = scn_mgr.getRootSceneNode().createChildSceneNode()
node.attachObject(ent)
#
root.startRendering() # blocks until queueEndRendering is called
ctx.closeApp()
Then, I executed python3, loaded sample.py, instantiated App and executed the thread:
Code: Select all
$ python3
import sample
app = sample.App()
app.start()
.... lots of output from OGRE ....
.... But no command prompt :-( ....
Unfortunately, when root.startRendering() is executed, the so called GIL is locked during the whole call. This is because we are not supposed to access python data concurrently. So, I created a method that releases the lock before calling startRendering in C++.
Of course, this caused a crash!
The reason for the crash is the "KeyListener" python class that is registered as an InputListener callback. When SWIG executes the KeyListener keyPressed() method, it does not lock the GIL.
I do not know anything about SWIG. I am using pybind11 in my code. In pybind11, virtual methods that are overridden by python code, they do lock the GIL. So, at the end what was needed (in C++) was:
- A python bound method that releases the GIL before calling Root::startRendering()
- An InputListener wrap that does lock the GIL before calling the python code.
The final python code became:
Code: Select all
import Ogre
import Ogre.Bites
import Ogre.RTShader
import my_bindings as mb
import threading
#class KeyListener(Ogre.Bites.InputListener):
class KeyListener(mb.InputListener):
# changed the python method name just to make it easier...
def escPressed(self, evt):
Ogre.Root.getSingleton().queueEndRendering()
return True
class App(threading.Thread):
def run(self):
ctx = Ogre.Bites.ApplicationContext("PySample")
#
ctx.initApp()
#
root = ctx.getRoot()
scn_mgr = root.createSceneManager()
#
# register for input events
klistener = KeyListener() # must keep a reference around
# ctx.addInputListener(klistener)
mb.addInputListener(ctx, klistener)
#
shadergen = Ogre.RTShader.ShaderGenerator.getSingleton()
shadergen.addSceneManager(scn_mgr) # must be done before we do anything with the scene
#
# without light we would just get a black screen
scn_mgr.setAmbientLight((.1, .1, .1))
#
light = scn_mgr.createLight("MainLight")
lightnode = scn_mgr.getRootSceneNode().createChildSceneNode()
lightnode.setPosition(0, 10, 15)
lightnode.attachObject(light)
#
# create the camera
cam = scn_mgr.createCamera("myCam")
cam.setNearClipDistance(5)
cam.setAutoAspectRatio(True)
camnode = scn_mgr.getRootSceneNode().createChildSceneNode()
camnode.attachObject(cam)
#
# map input events to camera controls
camman = Ogre.Bites.CameraMan(camnode)
camman.setStyle(Ogre.Bites.CS_ORBIT)
camman.setYawPitchDist(0, 0.3, 15)
ctx.addInputListener(camman)
#
# and tell it to render into the main window
vp = ctx.getRenderWindow().addViewport(cam)
vp.setBackgroundColour((.3, .3, .3))
#
# finally something to render
ent = scn_mgr.createEntity("Sinbad.mesh")
node = scn_mgr.getRootSceneNode().createChildSceneNode()
node.attachObject(ent)
#
# root.startRendering() # blocks until queueEndRendering is called
mb.no_gil_render(root) # blocks until queueEndRendering is called
ctx.closeApp()
Of course, we could have a loop and call the method to render only one frame. However, the GIL would be locked during each frame rendering, and I want python to be doing other things while rendering...
Are we interested in this kind of thing?
PS: I have written an SDL3 ApplicationContext for OgreBites. My changes, however, are too drastic and if ever merged cannot be done in the current release. Since I am doing "drastic" and "useless" changes, I could try to replace SWIG by pybind11. Is it welcome?