Getting started with H3DAPI
Loading a Scene
H3DAPI is distributed with two small programs written in C++. These are called H3DLoad and H3DViewer and can be used to load x3d-files. To load a scene with H3DLoad open a terminal and write:
If the filename is omitted H3DLoad will output information on options that can be set when loading a scene.
H3DViewer is a GUI based loader. Simply start the program and use the menu to load a file and change properties of how to render the scene.
In Windows it is also possible to drag an x3d-file and drop it on H3DLoad.exe or H3DViewer.exe to load the scene defined in the file.
H3D extends X3D with the cabability of adding haptics to a scene. A simple X3D scene displaying a red Sphere with radius 0.05 could look like below. That scene can be loaded in any X3D-complient browser.
<X3D profile='Immersive' version='3.0'> <Scene> <Group> <Viewpoint position="0 0 0.6"/> <Shape> <Appearance> <Material diffuseColor="1 0 0"/> </Appearance> <Sphere radius="0.05"/> </Shape> </Group> </Scene> </X3D>
Adding haptics to this scene using H3DAPI is simple. Just add a surface to the Appearance node. The reason for having it put in appearance is because properties of the surface decides how it feels, appear to feel, for the user. In the example below the only property of the surface that can be set is how hard (stiff) it feels. There is no friction parameters for the SmoothSurface node. The scene below will not load in any X3D-based viewer except those supporting H3D.
<X3D profile='Immersive' version='3.0'> <Scene> <Group> <Viewpoint position="0 0 0.6"/> <Shape> <Appearance> <Material diffuseColor="1 0 0"/> <SmoothSurface stiffness="0.4"/> </Appearance> <Sphere radius="0.05"/> </Shape> </Group> </Scene> </X3D>
One of the goals for H3DAPI is to fully support X3D. Since X3D is an ongoing project new features are added and revised and H3DAPI still does not fully support the entire X3D standard. H3DAPI is an API that makes it easy to add haptics to the scene and therefore the haptics part of H3DAPI is prioritized. For example, H3D does not enforce the <X3D> and <Scene> tags so they could be omitted in the examples above.
To find out what can be done with X3D see the homepage of Web3D Consortium.
Python can be used to define special fields, set up routes, make calculations that X3D can not handle and a lot more. This section is divided into two parts. The first part shows how to define and use a field in python. The second part shows some useful features of the X3D/python connection. See H3DAPI manual for more information about python programming with H3D.
Custom defined fields
In python it is easy to define fields to add some new behaviour to a scene. We will modify the example in the Using X3D section. The field defined in python will be used to change the color of the sphere when the sphere is touched by a haptics device. We will do this by setting up routes. Route connects field to each other. We have to know the types of the fields that are routed to our new field, and the type of the field the new field will route to. The field isTouched of a geometry (in our case the sphere) tells whether a geometry is touched by any of the connected haptics devices. This field is an MFBool, which means that this is what the field we define in python will take as input. It will output an SFColor since this is the type of the diffuseColor field in a Material node. Input and output types of a field are by default the same but this can be changed by using the TypedField template. The python code for this field looks like below:
# Import H3D specific functions. from H3DInterface import * # ChangeColor is a field that takes and MFBool as input and outputs an SFColor # The field routed to this class is the isTouched field of X3DGeometryNode class ChangeColor( TypedField( SFColor, MFBool ) ): # Only using routes for this field so only override the update function. def update( self, event ): # The inputed field is initially zero length. if len(event.getValue()) > 0 and event.getValue(): # If touched return green color return RGB(0, 1, 0) else: # If not touched return red color return RGB(1, 0, 0) # Create an instance of our field. This name is what will be used # when routing to and from the field. changeColor = ChangeColor()
To set up the routes we need to modify the X3D-example in the Using X3D section. We need to add a PythonScript node and some DEF statements to be able to set up routes correctly. In this example the python-file containing the code above is called mypythonfile.py.
<X3D profile='Immersive' version='3.0'> <Scene> <Group> <Viewpoint position="0 0 0.6"/> <Shape> <Appearance> <Material DEF="MyMaterial" diffuseColor="1 0 0"/> <SmoothSurface stiffness="0.4"/> </Appearance> <Sphere DEF="MySphere" radius="0.05"/> </Shape> </Group> <PythonScript DEF="PS" url="mypythonfile.py" /> <ROUTE fromNode="MySphere" fromField="isTouched" toNode="PS" toField="changeColor" /> <ROUTE fromNode="PS" fromField="changeColor" toNode="MyMaterial" toField="diffuseColor" /> </Scene> </X3D>
To find out more about what features exist when defining fields in python see H3DAPI manual and the source code.
Accessing an X3D scene node from python
Sometimes it is needed to access a node in the scenegraph from python. This can be accomplished by using the containerField statement to change the default container field to "references". In the example in the section Custom defined fields we could set up the routes in python instead if we could just access the nodes MySphere and MyMaterial. This can be accomplished by removing the ROUTE statements in the X3D file and modify the PythonScript part like this:
<PythonScript DEF="PS" url="mypythonfile.py" > <Material USE="MyMaterial" containerField="references" /> <Sphere USE="MySphere" containerField="references" /> </PythonScript>
The nodes can now be accessed in python and the routes between the fields can be set up there by adding the following code to the end of the python-code in the section Custom defined fields:
# Get the references to the Material and Sphere node. material, sphere, = references.getValue() # Set up routes. sphere.isTouched.route( changeColor ) changeColor.route( material.diffuseColor )
In the example above it is quite useless to set up routes this way since it is more straightforward to set up the routes in the X3D-file without having to go to the trouble of sending nodes to the python script. It is however useful to give python access to nodes sometimes, for example in the DeformableShape example provided with an installation of H3DAPI. In that example the triangles of the geometry is calculated in python and in order to add the created geometry to the scenegraph the containerField is used to let python access the shape in which the geometry belongs.
List of useful python functions
- initialize() - If defined called once at initialization of the PythonScript node. Define by writing the following in the python file.
- traverseSG - If defined called once each scene graph loop (graphics frame) if the PythonScript node is part of the scenegraph. Define by writing the following in a python file.
- createX3DFromString( string ) - Used to create X3D code from a string. Returns a Group node containing all nodes in the string as children and also returns a list of named nodes. Named nodes are nodes that have the DEF statement.
- n, dn = createX3DNodeFromString( string ) - Used to create X3D code from a string. Returns the first node in the string and also returns a list of named nodes. Named nodes are nodes that have the DEF statement.
- g, dn = createX3DFromURL( url ) - Used to create X3D code from an url which points to a file containing X3D code. Returns a Group node containing all nodes in the file read as children and also returns a list of named nodes. Named nodes are nodes that have the DEF statement.
- n. dn = createX3DNodeFromURL( url ) - Used to create X3D code from an url which points to a file containing X3D code. Returns the first node in the file and also returns a list of named nodes. Named nodes are nodes that have the DEF statement.
Including the H3DUtils python module will give some useful templates for creating fields that might be needed in python sometimes to debug an application. These are:
- PrintFieldValue( base_class ) - Prints the value of the field. base_class should be a previously defined field type.
- FieldValue2String( base_class ) - Converts a field value to a string. base_class should be a previously defined field type.
- FieldValue2StringList( base_class ) - Converts a field value to a list of string, useful for MF-fields. base_class should be a previously defined field type.
- FieldValue2Int( base_class ) - Converts a field value to an integer. base_class should be a previously defined field type.
- SField2MField( sfield, mfield ) - Converts an sfield to an mfield.
- TimerCallback( AutoUpdate( SFTime ) ) - A field in which in which you can set callback functions to be called at a later time that you specify.
For an extensive list of useful python functions see the H3DAPI manual.
Check out H3DAPI on Youtube. It contains basic instructions videos.