OGRE  master
Object-Oriented Graphics Rendering Engine
Working with NumPy

Ogre includes a Python component which automatically generates Python bindings from the C++ headers. However, with Python, you most likely do not only want to just use Ogre, but connect it to other components. For this, the Component uses standard python protocols, that offer exposing the API in a pythonic way. In this tutorial, we will look how Ogre integrates with numpy.

Note
this tutorial can be run live in Google Colab here.

We start with a simple 3 channel python array representing a green gradient:

arr = np.zeros((256, 256, 3), dtype=np.uint8)
arr[:,:,1] = np.mgrid[0:256,0:256][1]
numpy_arr.png

To be able to load it into Ogre we now have to convert it to Ogre.Image. The underlying C++ API takes a raw uchar*. However, the python bindings accept any object implementing the Buffer Protocol. This means we can pass the numpy array as is.

ogre_img = Ogre.Image()
ogre_img.loadDynamicImage(arr, 256, 256, Ogre.PF_BYTE_RGB)
Ogre.TextureManager.getSingleton().loadImage("gradient", "General", ogre_img)

Note that Ogre.Image is merely a view on the underlying array and no data is copied. While this is efficient, it also means that you have to ensure that the array does not get out of scope manually. Otherwise the application will crash due to accessing an invalid pointer.

For completeness we also create a small scene where we map the texture on a screen-centred rectangle.

mat = Ogre.MaterialManager.getSingleton().create("gradient_mat", "General")
rpass = mat.getTechniques()[0].getPasses()[0]
rpass.setLightingEnabled(False)
rpass.createTextureUnitState("gradient")
rect = scn_mgr.createScreenSpaceRect(True)
rect.setCorners(-0.5, 0.5, 0.5, -0.5) # in normalized screen space
rect.setMaterial(mat)
scn_mgr.getRootSceneNode().createChildSceneNode().attachObject(rect)

As the rectangle does not cover the full scene, we also set a background colour

gray = np.array([0.3, 0.3, 0.3])
vp.setBackgroundColour(gray)

Here, the standard python sequence protocol is used. Therefore, the data is copied.

Finally, we want read-back rendered image into an array. To avoid superficial copies of the data, we again allocate the memory with numpy:

mem = np.empty((win.getHeight(), win.getWidth(), 3), dtype=np.uint8)
pb = Ogre.PixelBox(win.getWidth(), win.getHeight(), 1, Ogre.PF_BYTE_RGB, mem)
win.copyContentsToMemory(pb, pb)

Note, that the convention of specifying width and height is swapped between Ogre and numpy.

Now we can store the image to disk using pyplot.

pyplot.imsave("screenshot.png", mem)
Note
There is also Ogre::RenderTarget::writeContentsToFile if you do not need the pixel data in Python.
numpy_final.png