This tech tip shows how to create a visual indicator of world orientation which is always in view. A compass of sorts, effectively setup as a “Heads-up display” or HUD. Two options are provided:

  • The first sets up the HUD as a group node which is linked to the view.
  • The second sets up a GUI Canvas object, a new feature in Vizard 5 providing a flat render node designed for displaying GUI elements or other nodes. In this example the canvas uses the “WorldOverlay” render mode.

For both of these examples, the HUD is a node in the world (as opposed to being in an ortho window, or screen space). While it is possible to set up a HUD in window or screen space, an advantage to having a HUD in world space is that rendering can be simplified as there’s no need for special treatment for such aspects as lighting, depth testing, or stereo. Set the method used for displaying the axes model in line 13 of the script:

“””
Set the method used for displaying the axis in line 13
“””

import viz
import vizshape

viz.setMultiSample(4)
viz.go()
maze = viz.addChild(‘maze.osgb’)

# Set to 1 or 2 as desired
example = 2

if example == 1:
# Set up a group node to be the HUD.
# Other geometry will be parented to this node.
viewGroup = viz.addGroup()

# Link the HUD to the main view so it’s always in view
viewGroupLink = viz.link(viz.MainView, viewGroup)

# Shift the HUD a meter in front and down and to the left a bit
viewGroupLink.preTrans([-0.3, 0.2, 1])

# Add a small 20x20cm grid to provide a bit of frame of reference
grid = vizshape.addGrid(size=(0.2, 0.2), step=0.05, parent=viewGroup)

# Add small (10cm long) axes which will serve as the indicator
axes = vizshape.addAxes(length=0.1, parent=viewGroup)

if example == 2:
# Setup a GUI canvas to serve as the HUD
# One advantage of the GUI canvas is that, as a separate render
# node, it is always drawn on top. So the HUD won’t be occluded
# when the view gets up close to an object in the scene.
canvas = viz.addGUICanvas()

# Define the dimensions of the canvas
canvasDimensions = [800, 600]  # width, height in (virtual) pixels

# Canvas field of view will be slightly smaller than the window’s
canvasFOV = viz.MainWindow.getVerticalFOV()  15

# Depth of 1m
canvasDepth = 1  # distance out in front (meters)

# Set up the canvas render parameters
canvas.setRenderWorldOverlay(canvasDimensions, canvasFOV, canvasDepth)

# Because the canvas-space has units of pixels, added nodes must
# be greatly scaled up to appear acceptably-sized
canvasUpscale = [100, 100, 100]

# Position the HUD on the lower right corner of the canvas with offset
anchorPos = [canvas.getRenderResolution()[0] + 80, 40, 0]

# Add a grid to provide a bit of frame of reference
grid = vizshape.addGrid(size=(2, 2), step=0.5, scale=canvasUpscale, pos=anchorPos, parent=canvas)

# Add axes which will serve as the indicator
# The axes are parented to the canvas, placing them at the origin in
# canvas-space, which is the lower-left corner of the canvas.
axes = vizshape.addAxes(scale=canvasUpscale, enable=viz.DEPTH_TEST, pos=anchorPos, parent=canvas)

# Add 2D text nodes to serve as labels
= viz.addText(‘X’,pos=[1.1,0,0],color=viz.RED,scale=[0.3,0.3,0.3],parent=axes)
= viz.addText(‘Y’,pos=[0,1.1,0],color=viz.GREEN,scale=[0.3,0.3,0.3],align=viz.ALIGN_CENTER_BASE,parent=axes)
= viz.addText(‘Z’,pos=[0,0,1.1],color=viz.BLUE,scale=[0.3,0.3,0.3],align=viz.ALIGN_CENTER_BASE,parent=axes)

# Billboard each text label so it always faces the user
[label.billboard() for label in [X, Y, Z]]

# Set a backdrop for each label so it is more visible over the scene
[label.setBackdrop(viz.BACKDROP_RIGHT_BOTTOM) for label in [X, Y, Z]]

# Enable depth test on each label for proper occlusion behavior
[label.enable(viz.DEPTH_TEST) for label in [X, Y, Z]]

# Setup a link in orientation between the maze (world coordinates)
# and the axes (the indicator of world orientation)
link = viz.link(maze, axes, mask=viz.LINK_ORI)

# Because the axes are linked to the main view, any orientation of the
# main view needs to be negated in order that the axes align properly
# with the world coordinates. Setup a link operator wherein the link
# is post-multiplied with the inverse matrix of the main view.
link.postMultInverseLinkable(viz.MainView)