OGRE  14.2
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.

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]

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)
Class representing an image file.
Definition: OgreImage.h:61
static TextureManager & getSingleton(void)
Get the singleton instance.

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]
rect = scn_mgr.createScreenSpaceRect(True)
rect.setCorners(-0.5, 0.5, 0.5, -0.5) # in normalized screen space
static MaterialManager & getSingleton(void)
Get the singleton instance.

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

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

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)
A primitive describing a volume (3D), image (2D) or line (1D) of pixels in memory.
Definition: OgrePixelFormat.h:349

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)
There is also Ogre::RenderTarget::writeContentsToFile if you do not need the pixel data in Python.