| OGRE 1.11.1 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 we ship a small module called `OgreNumpy` that helps you converting between Ogre and NumPy.

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 need a pointer that we can pass to Ogre.Image. For this we copy the contents of our array into an Ogre.MemoryDataStream. Here OgreNumpy.AsDataStream automatically allocates the MemoryDataStream of the according size and takes care of the copying.

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

While this is not the fastest option, we now have an Ogre data type, that we can use as usual and upload to an Texture.

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 = Ogre.Rectangle2D(True)
rect.setCorners(-0.5, 0.5, 0.5, -0.5) # in normalized screen space
rect.setMaterial(mat)
rect.setBoundingBox(Ogre.AxisAlignedBox(Ogre.AxisAlignedBox.BOX_INFINITE))
scn_mgr.getRootSceneNode().createChildSceneNode().attachObject(rect)

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

gray = (0.3, 0.3, 0.3)
vp.setBackgroundColour(Ogre.ColourValue(*gray))

Here we use the normal python iterable unpacking. Again this is copying the data.

Finally we want to store the rendered image into a file. This time we manually allocate the memory with Ogre.MemoryDataStream to store the pixel values.

mem = Ogre.MemoryDataStream(win.getWidth() * win.getHeight() * 3)
pb = Ogre.PixelBox(win.getWidth(), win.getHeight(), 1, Ogre.PF_BYTE_RGB, mem.getPtr())
win.copyContentsToMemory(pb, pb)

Finally we store the image to disk using pyplot.

pyplot.imsave("screenshot.png", OgreNumpy.view(pb))

To convert the Ogre.PixelBox to a NumPy array we use OgreNumpy.view. This function does not copy the data, but as the name suggests just creates a NumPy view on the underlying Ogre.MemoryDataStream. Using this pattern we could also have avoided the copy when loading the numpy array as a Texture.

Note
  There is also Ogre::RenderTarget::writeContentsToFile if you do not need the pixel data in Python.