Radium Engine  1.5.20
Loading...
Searching...
No Matches
Camera manipulation in Radium

The Radium Engine provides some classes that allow to extend the way one might interact with a camera in an application.

The class Ra::Gui::CameraManipulator defines the general interface for camera manipulator. All manipulators have common properties that allow to switch from one manipulator to one other without disturbing the visual and interactive behavior of the application.

Implementing a CameraManipulator

In order to extend the set of Ra::Gui::CameraManipulator available in Radium or to develop a dedicated Ra::Gui::CameraManipulator for a Ra::Gui::Viewer-based application, programmers are intended to do the following, demonstrated by the class FlightCameraManipulator.

  1. Define the class that must inherits from Ra::Gui::CameraManipulator and, in order to receive interaction events, from Ra::Gui::KeyMappingManageable. Note that a CameraManipulator is a Q_OBJECT
    class RA_GUI_API FlightCameraManipulator : public CameraManipulator,
    public KeyMappingManageable<FlightCameraManipulator>
    {
    Q_OBJECT
  2. Implement the constructors (default, copy). Note that it is also very important to implement a constructor that will take any Ra::Gui::CameraManipulator and will copy the base class before initializing the current manipulator.
    FlightCameraManipulator::FlightCameraManipulator( const CameraManipulator& other ) :
    CameraManipulator( other ), m_keyMappingCallbackManager { KeyMapping::getContext() } {
    m_flightSpeed = ( m_target - m_camera->getPosition() ).norm() / 10_ra;
    initializeFixedUpVector();
    setupKeyMappingCallbacks();
    m_cameraSensitivity = 2_ra;
    }
  3. Implement the Ra::Gui::KeyMappingManageable part of the class. This implies defining the method void FlightCameraManipulator::configureKeyMapping_impl() according to the semantic imposed by Ra::Gui::KeyMappingManageable. It is recommended that, when implementing this keymapping initialisation callback, a default configuration is defined and saved to the xml keymapping configuration file if this later does not already contains a configuration. This will allow the users to edit and customize the proposed keymapping configuration.
    #define KMA_VALUE( XX ) Gui::KeyMappingManager::KeyMappingAction Gui::FlightCameraManipulator::XX;
    KeyMappingFlightManipulator
    #undef KMA_VALUE
    void FlightCameraManipulator::configureKeyMapping_impl() {
    FlightCameraKeyMapping::setContext(
    Gui::KeyMappingManager::getInstance()->getContext( "FlightManipulatorContext" ) );
    if ( KeyMapping::getContext().isInvalid() ) {
    LOG( logWARNING )
    << "CameraContext not defined (maybe the configuration file do not contains "
    "it). Add it for default key mapping.";
    FlightCameraKeyMapping::setContext(
    Gui::KeyMappingManager::getInstance()->addContext( "FlightManipulatorContext" ) );
    }
    #define KMA_VALUE( XX ) \
    XX = Gui::KeyMappingManager::getInstance()->getAction( FlightCameraKeyMapping::getContext(), \
    #XX );
    KeyMappingFlightManipulator
    #undef KMA_VALUE
    auto mgr = Gui::KeyMappingManager::getInstance();
    auto context = FlightCameraKeyMapping::getContext();
    // use wrapper to have reference in pair
    KeyMappingManager::EventBinding>;
    // don't use [] since reference don't have a default value. use at and insert instead.
    defaultBinding.insert(
    std::make_pair( std::string { "FLIGHTMODECAMERA_PAN" },
    ActionBindingPair { FLIGHTMODECAMERA_PAN,
    mgr->createEventBindingFromStrings(
    "LeftButton", "ShiftModifier" ) } ) );
    defaultBinding.insert( std::make_pair(
    std::string { "FLIGHTMODECAMERA_ROTATE" },
    ActionBindingPair { FLIGHTMODECAMERA_ROTATE,
    mgr->createEventBindingFromStrings( "LeftButton" ) } ) );
    defaultBinding.insert(
    std::make_pair( std::string { "FLIGHTMODECAMERA_ZOOM" },
    ActionBindingPair { FLIGHTMODECAMERA_ZOOM,
    mgr->createEventBindingFromStrings(
    "LeftButton", "ControlModifier" ) } ) );
    for ( auto& [actionName, actionBinding] : defaultBinding ) {
    if ( actionBinding.first.get().isInvalid() ) {
    LOG( logWARNING ) << "FlightManipulator action " << actionName
    << " not defined in configuration file. Adding default keymapping.";
    actionBinding.first.get() = mgr->addAction( context, actionBinding.second, actionName );
    }
    }
    }
  4. Implement the inherited abstract method according to the wanted behavior of the Ra::Gui::CameraManipulator

Extending/Specializing an existing CameraManipulator

The example application CustomCameraManipulator Demonstrate how to extend an existing manipulator and specialize its behavior for a given context. In the following code, the Ra::Gui::TrackballCameraManipulator class is used to define a simple pan and zoom manipulator, simply by ignoring rotation events:

// Add simple Camera Manipulator with only translation and zoom
class CameraManipulator2D : public Ra::Gui::TrackballCameraManipulator
{
public:
inline CameraManipulator2D() : Ra::Gui::TrackballCameraManipulator() {
m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_ROTATE, []( QEvent* ) {} );
}
inline explicit CameraManipulator2D( const CameraManipulator& other ) :
Ra::Gui::TrackballCameraManipulator( other ) {
m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_ROTATE, []( QEvent* ) {} );
}
};

Using a CameraManipulator

Using a Ra::Gui::CameraManipulator in a Ra::Gui::Viewer-based application is quite straightforward.

If one wants to set a first camera manipulator to a viewer

myViewer->setCameraManipulator(
new Ra::Gui::FlightCameraManipulator( width, height );

If one wants to change the manipulator while keeping the actual visual state

myViewer->setCameraManipulator(
new Ra::Gui::FlightCameraManipulator( *( m_viewer->getCameraManipulator() ) ) );