In addition to rendering a scene, your application will likely require some way for the user to interact with it. While you may choose to handle user input independently, HOOPS Visualize provides operators for this purpose. An operator is a hook into the user input event loop present on all supported GUI platforms. Visualize has a set of pre-built, standard operators to handle zooming, panning, and rotation, and also offers the ability to subclass these for custom functionality. When using operators in C++, include the header sprk_ops.h. All operators must derive from HPS::Operator.
The purpose of operators is to process input events. The three primary types of events that can be processed are HPS::KeyboardEvent, HPS::MouseEvent, and HPS::TouchEvent. Touch events are triggered in response to the action of the user's fingers on a touch-screen. Additionally, your operator can overload a function to intercept a timer tick event.
Visualize's standard operators provide common functionality for interacting with a 3D scene; many of these operators can be combined:
Camera manipulation operators:
Please note that while HOOPS Visualize's standard operators handle many common situations, they do not attempt to address every possible application-specific GUI behavior. Source code is provided to allow developers to easily customize the operators for their own needs.
After the code above executes, you should be able to orbit the camera around your scene by dragging the middle mouse button.
Unlike most other HOOPS Visualize classes, C++ developers must use a pointer to instantiate and use the operator. However, after the call to Push, Visualize will take ownership of the pointer and you no longer have to keep a reference to it or worry about its deletion. Visualize will free the memory automatically.
In the code sample above, the parameter to the HPS::OrbitOperator constructor is called the operator trigger. You can combine the mouse button with a keyboard button to create a complex trigger. For example, if you want to orbit the scene when the user holds down CTRL and the right mouse button, you can add a HPS::ModifierKeys parameter, like so:
If you require more complex logic than trapping a single modifier key, you should create your own operator, as the standard operators do not include logic to handle all possible keyboard combinations.
To disable the operator, detach it from the view.
This push-and-detach mechanic is identical for all the standard operators.
Propagation of Events
Generally speaking, an operator will consume an event if there's something the operator can do with it. For example, the HPS::PanOperator, which pans the scene when the user drags the mouse, will consume a MouseEvent but pass on a KeyboardEvent.
In order for an operator to respond to an event, it has to overload the appropriate function. For example, the Pan operator will respond to MouseDown events, so it overloads the OnMouseDown() function.
Since all operators must derive from the HPS::Operator base class, you can look at that class to see what functions are available to be overloaded, and therefore which events the operators can respond to. Here is a sample of events that can be overloaded:
|OnMouse[Down|Up|Move]||Handle mouse clicks|
|OnMouseWheel||Handle the mouse wheel|
|OnMouse[Enter|Leave]||Handle the mouse cursor entering/leaving the active window|
|OnKey[Down|Up]||Handle keyboard presses|
|OnTextInput||A special case, used to handle text input from software keyboards, like those on smartphones|
|OnTimerTick||Called on each tick of the HPS clock|
All of these functions return a Boolean. Returning TRUE means that the event which caused this function to be called should be destroyed.
Returning FALSE means that the event which caused this function to be called should be allowed to propagate to other operators. This diagram summarizes how operator events are propagated:
Adding Operators to a View
Now that we've covered the basic characteristics of operators, let's discuss how to incorporate them into a scene.
Each instance of an operator can only be associated with a single HPS::View. However, one HPS::View can have multiple operators associated simultaneously in one of three operator stacks, with each operator stack representing an operator priority: High, Default, or Low. When attaching an operator to a view, you can decide which priority to give it. If no priority is specified, the Default priority is used. Here's an example:
Why are these priority stacks necessary? Let's say we have three operators on the Default stack. They all respond to the Left Mouse event:
Now let's say that you want OP C to intercept the event, but with this current configuration it won't work because OP A will intercept the event first and discard it. Because the operator that was attached last receives the input event first, one potential workaround is to detach OP C from the view and then push it onto the view again so that it will land on top of OP A. This can be problematic, though, since detaching the operator will delete the operator along with any state information stored within the operator.
To get around this problem, we can use the High Priority stack. Operators added to the High Priority stack will receive events before operators in the Default stack.
If we move OP C to the High Priority stack, then our Operator Priority stacks will look like this:
With this stack configuration, events will always go through OP C first. If OP C does not discard them, the events will be passed on to OP A. If OP A does not discard them, the events will be passed on to OP B.
To change the Priority stack of an operator, use the Set function:
Please note, however, that the Set function will remove any operators already on the stack; to preserve your existing operators, add them to the Set Operator array parameter (using the overloaded version of Set that takes an Operator array as a parameter).
You can use the operator stack to combine operators, giving your scene the ability to respond to multiple types of input events. For example, you could use the mouse wheel input to zoom the scene, right button to pan, and left button to orbit. The code below demonstrates how you could set that up:
The HPS::HandlesOperator combines several manipulator handles into a single operator. The following handle types are available:
- Plane handles: Allow user to translate geometry anywhere on the plane represented by the selected handle.
- Center handle: Allows user to translate geometry anywhere in the view plane.
- Rotation handles: Allow user to rotate the model around the associated axis.
- Axis handles: Allow user to move the model along the associated axis.
The handles operator is enabled with the same method used to enable the other operators:
Once the operator is enabled, the manipulator handles will appear when any geometry is double-clicked. Clicking anywhere on the background makes the handles disappear.
The Handles operator can be constrained to suit the needs of your application. Rotation, axis translation, or plane translation handles can be removed individually or as a group by calling RemoveHandles with the appropriate parameter. For example, if you wanted to set up some handles which made sense for a 2D drawing which lies on the XY plane, you could do the following:
When using the HPS::HandlesOperator on models loaded through the Exchange, Parasolid, or ExchangeParasolid APIs, you will need to call HandlesOperator::SetCADModel(), passing it the CADModel that is attached to your view. This should be done before the operator is used.
There are a number of options for customizing the handles' appearance. Please see the API Reference for more details about these options as well as the limitations of the operator.
The SpaceMouseOperator is a special operator that is designed to work with the proprietary 3DConnexion Space Mouse. The Space Mouse allows for 6 degrees of freedom - it can be used to pan, orbit, zoom, and select all at once. Use of this operator has additional requirements as noted below.
- Acquire XInput.lib (this is part of DirectX) and link your application to it
- Add sprk_space_mouse_op.h and sprk_space_mouse_op.cpp to your project's build environment
- If the project is using precompiled headers, set sprk_space_mouse_op.cpp to NOT use them (or add the precompiled header to it)
You are now ready to use the operator. It can be enabled similarly to the other operators by pushing an instance onto the HPS::OperatorControl: