High Level 3D Graphics Programming in Python

Pivy logo

General Information:

Authors:Tamer Fahmy, Dieter Schmalstieg
Contact:fahmy@ims.tuwien.ac.at, schmalstieg@ims.tuwien.ac.at
Background:Tamer Fahmy is a Research Assistant at the Virtual Reality Group of the Interactive Media Systems Institute at the Vienna University of Technology.
Abstract:Pivy is a Python binding for the popular object-oriented 3D C++ toolkit Open Inventor which presents a programming model based on a 3D scene database. We describe the benefits of using Python for Open Inventor programming.
Keywords:Python, Pivy, Coin, Open Inventor, 3D Graphics

1. Introduction

Currently OpenGL 1 is the primary choice for cross platform 3D graphics application development. OpenGL provides so-called immediate mode access to the frame buffer where the application itself has to maintain the data that describe the model.

OpenGL, designed as a low-level API, therefore provides no out of the box facilities for user interaction such as moving objects to a different location or selecting them for further manipulations. Additional complicated code needs to be implemented by the programmer to fulfill these tasks.

High-level libraries such as Open Inventor 2, Coin 3 or Performer 4 built on top of OpenGL have been developed to facilitate and speed up the development process. They allow the creation of otherwise hard to implement or involved 3D graphics applications.

Unlike OpenGL these libraries focus on creating 3D objects. Object information such as shape, size, location in 3D space, is stored in a scene database. In contrast to OpenGL they provide the necessary functionality to interact with objects and to change the objects in the scene.

Those libraries are referred to as operating in retained mode where all the data describing a model needs to be specified in advance using predefined data structures. They internally organize the data in a hierarchical database.

Another important distinction is made in this context between application- and data-driven scene graph APIs. Data-driven toolkits only re-render when something changes in the scene, for example the user changing the viewpoint of the scene. Application-driven toolkits re-render the scene continuously in an application loop, using up all CPU resources available. The latter case is used for games and simulation software such as flight simulators where high and constant frame rates are desirable. In general a data-driven approach fits better for a general purpose 3D API where constant frame rate is not the main concern. More importantly resources should be available for other computational tasks. Typical examples benefiting from this approach are applications that visualize results of numerical simulations or 3D editors (level editor for games). Examples of application-driven APIs are Performer or OpenSG 5, whereas Open Inventor or Coin offer a data-driven API.

Performance is a key problem, hence these libraries are usually implemented in a compiled language such as C++. However, the use of C++, a statically typed language with a heavy and complicated syntax, tends to be error-prone and cumbersome. A dynamically typed and bound language with an intuitive syntax like Python provides a more natural interface.

Consequently, bindings have been created to interface with those libraries in order to make them accessible from within the Python interpreter allowing true Rapid Application Development. We are presenting the benefits of using Python for high-level 3D graphics programming by presenting Pivy 6, a Python binding for the popular object-oriented 3D C++ toolkit Open Inventor.

2. Open Inventor Overview

Open Inventor was originally developed by Silicon Graphics, Inc. as the IRIS Inventor library. It has long since become the de facto standard graphics library for 3D visualization and visual simulation software in the scientific and engineering community. It has proved its value over a period of more than 10 years, its maturity contributing to its success as a major building block in thousands of large-scale engineering applications around the world.

Open Inventor is an object-oriented 3D toolkit offering a comprehensive solution to interactive graphics programming problems. It presents a programming model based on a 3D scene database that dramatically simplifies graphics programming. It includes a rich set of objects such as cubes, polygons, text, materials, cameras, lights, track balls and handle boxes.

Open Inventor also defines a standard 3D file format (ASCII and binary) for scene data interchange. This allows the construction of scene graphs in ASCII files without the need to program a single line. Those ASCII files can then be viewed by using the provided viewers from Open Inventor or any common modelling tool.

The significant characteristics of Open Inventor:

We chose to bind Pivy against Coin, which implements the SGI Open Inventor 2.1 API. Coin is portable over a wide range of platforms (any UNIX / Linux / *BSD platform, all Microsoft Windows operating systems, and Mac OS X) and adds additional features missing in the original SGI Open Inventor API such as VRML97 support, 3D Sound, 3D Textures, Multi-threading and parallel rendering. Additionally GUI bindings implementing viewer widgets for several GUI toolkits (Qt, Gtk, Xt, Cocoa, Win32) are available. Coin is Open Source and has an active and growing community.

3. Pivy

3.1 Overview

Pivy is a Python binding for Coin where the interface is implemented using SWIG. Pivy allows:

  • development of Coin applications in Python
  • interactive modification of Coin programs from within the Python interpreter at runtime
  • incorporation of Scripting Nodes into the scene graph which are capable of executing Python code and callback functions

3.2 Open Inventor Programming in Python

Pivy offers the ability to inspect and modify scene graphs or even Python code from within the Python interpreter making Rapid Application Development possible. It allows the use of numerous GUI toolkits for which Python bindings exist in a transparent fashion by providing bridges. Additionally it allows GUI toolkit agnostic programming by providing an SoGui binding implemented as a proxy class. This proxy class probes any known SoGui binding and picks the first available unless one has been directly specified.

As mentioned above, Open Inventor also features an extensible text-based file format. However, there is no facility for procedural scripting. Pivy provides a powerful and easy-to-use scripting interface, unlike other Open Inventor bindings such as those for Java 7 and Scheme 8.

Additionally, existing C++ extensions can be easily wrapped using SWIG and used within Pivy. New Open Inventor Nodes and NodeKits can also be developed solely in Python.

In the same fashion that Open Inventor allows C++ programmers to make use of direct OpenGL calls, the same functionality is available for Python programmers through the existing PyOpenGL 9 binding in Pivy.

3.3 Python Scripting Node

In addition to the general benefits of using Python for Open Inventor development outlined above, the Scripting Node is one of the most prominent features of Pivy. The Scripting Node works similarly to the JavaScript facilities in VRML: small reusable applications can be developed by embedding Python code in Open Inventor files. Other applications and frameworks that create and use Open Inventor scene graphs can thus be scripted and extended at runtime using Python code. We are using the Pivy Scripting node in the Augmented Reality framework Studierstube 10 developed at our group.

4. A Pivy Example

Hello Cone screenshot

The following code example creates a red Cone in a so-called 3D-model examination viewer:

from sogui import *
from pivy import *
import sys

def main():
    # Initialize Coin. This returns a main window to use.
    # If unsuccessful, exit.
    myWindow = SoGui.init(sys.argv[0])
    if myWindow == None: sys.exit(1)

    myMaterial = SoMaterial()
    myMaterial.diffuseColor(1.0, 0.0, 0.0) # Red

    # Make a scene containing a red cone
    scene = SoSeparator()
    scene.ref()
    scene.addChild(myMaterial)
    scene.addChild(SoCone())

    # Create a viewer in which to see our scene graph.
    viewer = SoGuiExaminerViewer(myWindow)

    # Put our scene into viewer, change the title
    viewer.setSceneGraph(scene)
    viewer.setTitle("Hello Cone")
    viewer.show()

    SoGui.show(myWindow) # Display main window
    SoGui.mainLoop()     # Main Coin event loop

if __name__ == "__main__":
    main()

First the sogui module which contains the classes and definitions relevant for the viewer is imported. In the next line the pivy module is imported, which contains the actual Coin binding. In the main function SoGui.init() initializes the Coin scene database and returns a widget. If SoQt (the Coin GUI binding for the Qt toolkit) is used, a widget is returned that can be used from within PyQt (the Python binding for the Qt toolkit). This allows Coin to be embedded in PyQt applications, similar to what can be done in C++ for Qt applications. A material node is then created and its diffuse color field is set to red. An SoSeparator instance, which represents the root node in the scene then gets two child nodes added: the newly created material node and the SoCone shape node. The order in which the child nodes are added is important as the scene graph is traversed from top to bottom and left to right. If we had reversed the order of the material and the cone node, the cone would have been rendered in its default color as it is not affected by the material node. After this the viewer instance is created, the window title is set and the viewer is displayed. Once the mainLoop() method of the SoGui toolkit is called, the viewer appears and shows the rendered image.

The examination viewer allows manipulation of settings, such as the drawstyle of the object or viewing position of the camera.

Alternatively the scene can be described using the Open Inventor file format by specifying it in a separate file.

hellocone.iv contains:

#Inventor V2.1 ascii

Separator {
   Material { diffuseColor 1 0 0 }
   Cone {}
}

In order to load the file the application needs to be modified accordingly:

from sogui import *
from pivy import *
import sys

def main():
    # Initialize Coin. This returns a main window to use.
    # If unsuccessful, exit.
    myWindow = SoGui.init(sys.argv[0])
    if myWindow == None: sys.exit(1)

    input = SoInput()
    input.openFile("hellocone.iv")

    # read in hellocone inventor file
    scene = SoDB.readAll(input)
  
    # Create a viewer in which to see our scene graph.
    viewer = SoGuiExaminerViewer(myWindow)

    # Put our scene in viewer, change the title
    viewer.setSceneGraph(scene)
    viewer.setTitle("Hello Cone")
    viewer.show()

    SoGui.show(myWindow) # Display main window
    SoGui.mainLoop()     # Main Coin event loop

if __name__ == "__main__":
    main()

5. Conclusions

Pivy offers access to a powerful, easy to use and general purpose 3D graphics high-level API. Python projects looking for an effective way to visualize their results in 3D, while simultaneously providing interactive features out of the box, could benefit by its usage. Apart from scientific applications also games could be easily developed using Pivy.

The combination of Coin and Pivy with its scripting node makes it useful for existing C++ Open Inventor applications which are looking for a possibility to enhance their applications through a scripting facility.

[1]OpenGL - high performance 2D/3D graphics (http://www.opengl.org/)
[2]SGI Open Inventor (http://oss.sgi.com/projects/inventor/)
[3]Coin - a high-level 3D graphics library (http://www.coin3d.org/)
[4]OpenGL Performer - (http://www.sgi.com/software/performer/)
[5]OpenSG - a portable scenegraph system (http://www.opensg.org/)
[6]Python binding for Coin (http://pivy.tammura.at/)
[7]Java wrapper for Coin (http://www.coin3d.org/Coin/Java/)
[8]Scheme binding for Coin (http://www.coin3d.org/ivy.php)
[9]PyOpenGL - the Python OpenGL binding (http://pyopengl.sourceforge.net/)
[10]The Studierstube Augmented Reality Project (http://www.studierstube.org/)