H3D allows user access and manipulation of haptics device properties in X3D and Python via H3DHapticsDevice node. The list of fields and other nodes that inherit from H3DHapticsDevice can be found at the H3D reference page. In this example we will use and modify five properties of the haptics device, namely:
- trackerPosition - gives the position of the device tracker in world coordinates
- trackerOrientation - gives the orientation of the device tracker in world coordinates
- mainButton - gives True if the main button is pressed, False otherwise
- secondaryButton - gives True if the secondary button is pressed, False otherwise
- stylus - inputOutput field of which value is used as the graphical representation of the haptics device
The first four fields are outputOnly, thus we may only get their values but not set them e.g. in Python, we may not use the setValue method on these fields.
- getting reference to active haptics device in Python
- using and manipulating H3DHapticsDevice fields
Setting the scene
In this tutorial, we will create a very simple pistol-like object, that shoots bullets of different colours when the buttons of the haptics device are pressed.
<Group> <Group DEF="G"> <SpotLight location="0 2 0" direction="0 -1 0" attenuation="3 0 0"/> </Group> <PythonScript DEF="P" url="script.py"> <Group USE="G" containerField="references" /> </PythonScript> </Group>
In our X3D file, we have a Group node as the topmost node. We also define a SpotLight and a PythonScript node to the scene graph. Reference to the Group node is passed to the script. In this script we will: change the stylus of the haptics device, create the "bullets" in our scene, and define the behaviour of our "gun" with some routing.
Changing the look of the stylus
# script.py from H3DInterface import * root, = references.getValue() di = getActiveDeviceInfo() if di: hd = di.device.getValue() hd.stylus.setValue( createX3DNodeFromString(" <Group> \ <Shape> \ <Appearance><Material /></Appearance> \ <Sphere DEF=\"PROXY\" radius=\"0.00025\"/> \ </Shape> \ <Transform DEF=\"T\" translation=\"0 0 0.025\" rotation=\"1 0 0 -1.570796\" > \ <Shape> \ <Appearance><Material /></Appearance> \ <Cone DEF=\"PROXY\" bottomRadius=\"0.01\" height=\"0.05\"/> \ </Shape> \ </Transform> \ </Group>") )
In the Python script we first import the H3Dinterface module as usual. The Group node reference is obtained and stored in the variable root.
We call getActiveDefineInfo to obtain the DeviceInfo of our scene. DeviceInfo has a device field that contains a list of the current active haptics devices. We get the first active haptics device and refer to it with hd. hd is of course an H3DHapticsDevice object; the value of its SFNode-typed stylus field is used as the visual representation of the haptics device. In this example, we create a node containing a Shape of Cone geometry and set it as the stylus value.
Creating the bullets
# script.py def bullets(): l =  for x in range(2): l.append( createX3DNodeFromString("\ <ParticleSystem colorKey=\"0\" geometryType=\"LINE\" \ particleLifetime=\"2\" enabled=\"true\" \ createParticles=\"false\" maxParticles=\"20\" \ particleSize=\"0.05 0.05\" lifetimeVariation=\"0\"> \ <Color containerField=\"colorRamp\" color=\"0 0.5 0.8\" /> \ <PointEmitter speed=\"0.6\" mass=\"0.00005\" /> \ </ParticleSystem>") ) l.colorRamp.getValue().color.setValue( [RGB(0.8, 0.5, 0)] ) return l bullets = bullets() em0 = bullets.emitter.getValue() em1 = bullets.emitter.getValue()
We will model the bullets using a ParticleSystem with a PointEmitter. The bullets function returns a list which contains a ParticleSystem node as each of its element. The two ParticleSystem nodes differ only in the colour of the particles. The first node is created with the color RGB(0, 0.5, 0.8) as specified in the X3D string. The color of the second ParticleSystem is set using the setValue method.
We could have easily added the two ParticleSystems in the X3D file. Generating the nodes in Python prevents repetitive code. It is of course a matter of developer preference.
With the bullets function, we create the ParticleSystems and get the references to their emitters in em0 em1.
# script.py class BulPos( TypedField(SFVec3f, SFRotation) ): def update( self, event ): return event.getValue()*Vec3f(0,0,-1) bulpos1 = BulPos() bulpos2 = BulPos()
We also define a field class that takes an incoming route of type SFRotation and returns a product of its value with Vec(0, 0, -1). This field will be used to ensure that the bullets always move towards the direction that the haptics device points to. We will route the haptics device's trackerOrientation to this field, and route the resulting vector of this field to the direction field of the "bullets".
We then create two instances of this field, bulpos1 and bulpos2
# script.py hd.trackerPosition.route( em0.position ) hd.trackerPosition.route( em1.position ) hd.trackerOrientation.route( bulpos1 ) bulpos1.route( em0.direction ) hd.trackerOrientation.route( bulpos2 ) bulpos2.route( em1.direction ) hd.mainButton.route( bullets.createParticles ) hd.secondaryButton.route( bullets.createParticles ) root.addChildren.setValue( bullets )
As mentioned, we will need to set up a few routes to define the behaviour of the "bullets". First, we want the bullets to be emitted from the haptics device. To do that, the device's trackerPosition is routed to both the ParticleSystems.
Second, to ensure that the bullets travel in the direction where the haptics device is pointing, we route trackerOrientation to each of the two BulPos field instances and each of them are then routed to each ParticleSystem emitter's direction.
Finally, we route the mainButton and secondaryButton to each createParticles field. Hence, bullets are only emitted when any of the buttons is pressed.
- H3D::H3DHapticsDevice class reference
- H3D::ParticleSystem class reference
- H3D::X3DParticleEmitterNode class reference
More examples using haptics device properties: