.. _debug_rendering_ref: =============== Debug rendering =============== AGX has an internal scheme for rendering rigid bodies, geometries and constraints (including contacts). It is handled by a class **agxRender::RenderManager**. When debug-rendering is enabled, this class will dispatch calls for rendering and create **agxRender::RenderProxy’s**. A render proxy is a placeholder for a class that can be rendered in the clients rendering system. By default if AGX is built with OpenSceneGraph, there is an implementation of render proxies for that scene graph. Each primitive has its own render proxy which need to be implemented at the client side. Responsible for creating these proxies is the **agxRender::RenderProxyFactory**, a class which is derived to a specialization for each rendering system. The class **agxOSG::RenderProxyFactory** is one such specialization for OSG. This rendering system should not be considered to be the rendering system for your simulations in simulators etc. It is merely a system for debugging your simulation, being able to see if bodies are enabled, static, kinematic, sleeping. To see where your constraints are attached to bodies etc. When you need a proper rendering engine, you should connect that directly through the geometries and rigid bodies. .. _table-23: .. csv-table:: :header: "Class", "Description" :widths: auto "**agxRender::RenderManager**", "Manages the debug rendering system. Called from agxSDK::Simulation::stepForward()" "***agxRender::RenderProxyFactory**", "Abstract base class responsible for creating agxRender::RenderProxy for rendering in a specific rendering system." "**agxRender::RenderProxy**", "Abstract base class for all renderable objects. Derived down to various primitives (sphere, cylinder, etc.)" "***SphereProxy, *CylinderProxy, *LineProxy, *TextProxy, *PlaneProxy, *CapsuleProxy, *BoxProxy, *HeightfieldProxy, *TrimeshProxy**", "Specialization of a RenderProxy for each supported shape." "**agxRender::GraphRenderer**", "An abstract class for rendering graphs related to statistics information." :numref:`Table %s: Classes for rendering. (*) indicates classes that need to be implemented and adopted for your specific rendering engine. ` Objects with different properties are rendered differently: geometries belonging to static bodies are rendered in blue, whereas geometries associated to dynamic bodies are rendered in green. Sensors are rendered with a lighter blue. ------------- RenderManager ------------- agxRender::RenderManager. This class is responsible for updating all the RenderProxy instances that are created during debug rendering. Each render manager need a reference to a RenderProxyFactory, specific for a rendering engine. Several render managers can use the same RenderProxyFactory, so if you are using several agxSDK::Simulation, you can assign the same RenderProxyFactory to those simulations. .. code-block:: c++ // Create a render proxy factory, specific for OpenSceneGraph agxRender::RenderProxyFactoryRef factory = new agxOSG::RenderProxyFactory; // Create a simulation agxSDK::SimulationRef sim = new agxSDK::Simulation; // Tell this simulation's render manager to use our factory sim->getRendermanager()->setProxyFactory( factory ); // Create another simulation, use the same factory for the debug rendering agxSDK::SimulationRef sim2 = new agxSDK::Simulation; sim2->getRendermanager()->setProxyFactory( factory ); During a Simulation::stepForward(), a call to RenderManager::update() will be called which in turn will query the render proxy factory for spheres, lines, cylinders etc. There is no cache functionality implemented in the RenderManager, that is, when a render proxy is required, a call to the render proxy factory will be executed. There will be no storage of unused render proxies in the render manager. If caching is required (to achieve better performance for scenes where the number of proxies varies a lot), it has to be done in the render proxy factory implementation ------------ Scale factor ------------ By default the overall scaling of debug rendering is configured to fit a scene where objects are around 1 m. For a very small or very large scene it might be useful to increase/decrease the size of the rendered constraints, contacts etc. This can be done using the `setScaleFactor` method: .. code-block:: c++ simulation->getRenderManager()->setScaleFactor(0.1f); The scale factor can also be controlled with the keyboard in an application based on the :code:`agxOSG::ExampleApplication` class such as :ref:`agxViewer `. *Note that the application/script might have changed the mapping for those keys.* For more information see :ref:`keybindings ` ------------ Render flags ------------ There are three methods that control what should be rendered by the render manager: .. code-block:: c++ void setFlags( unsigned int flags ); void enableFlags( unsigned int flags ); void disableFlags( unsigned int flags ); *setFlags* will override the current set of flags with the specified one. *enableFlags* will enable the specified flags and leave the rest untouched. *disableFlags* will disable the specific flags and leave the rest untouched. As an example, to specify that the default set of flags should be used, plus the rendering of bounding volumes (which is not enabled by default) the following call can be done: .. code-block:: c++ // Render default, plus the bounding volumes simulation->getRenderManager()->setFlags(agxRender::RENDER_DEFAULT| agxRender::RENDER_BOUNDING_VOLUMES); ------------------ Renderable objects ------------------ There are various types of objects that will be rendered in the debug rendering: ^^^^^^ Shapes ^^^^^^ All shapes (agxCollide::Shape) for all enabled geometries will be rendered. Every time a new shape is added/removed from a geometry part of the simulation, it will be added/removed from the debug rendering system (if it is enabled). A proxy will be associated for each shape. When the shape is deleted/removed, the proxy will get a call to onChange(REMOVE) (see below). For each time step, the state (color, alpha) and shape (size/form) will be synchronized with the corresponding AGX shape. For most of the time, only transform changes will be updated for these shapes, which should be efficiently handled by the rendering engine. The rendering of shapes will occur of the flag RENDER_GEOMETRIES is enabled in the render manager. ^^^^^^^^^^^ Constraints ^^^^^^^^^^^ The constraints in AGX has a virtual *render* method. This method will be called whenever the RENDER_CONSTRAINTS flag is enabled in the render manager. These render methods, will query the render manager for various shapes such as cylinder, spheres or lines. These queries will be dispatched to the specific RenderProxyFactory. ^^^^^^^^^^^ Renderables ^^^^^^^^^^^ There is a special class, agx::Renderable, which can be used for some specific rendering. It has a virtual method *render* which will be called from the RenderManager if the RENDER_RENDERABLES flag is enabled in the render manager. ^^^^^^ Bodies ^^^^^^ For each enabled RigidBody a sphere proxy will be rendered. These sphere proxies will be acquired via the render manager to the specific render proxy factory. This will occur if the RENDER_BODIES flag is enabled. ^^^^^^^^ Contacts ^^^^^^^^ Contacts will be rendered analogous to bodies. A sphere and a line will be acquired from the render manager. This occurs if the flag RENDER_CONTACTS is enabled. ^^^^^^^^^^ Statistics ^^^^^^^^^^ Statistics involve both the textual information (as collected by the agx::Statistics class) and graph drawing, showing some of the statistics as visual graphs on the screen. This occurs if the RENDER_STATISTICS is enabled. ---------------------------------------- Implementation of custom debug rendering ---------------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ agxRender::RenderProxyFactory ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This class need to be specialized for any specific rendering engine. In an implementation for OpenSceneGraph can be found. The virtual methods that need to be implemented are: .. code-block:: c++ /// Interface for creating and returning a SphereProxy virtual SphereProxy *createSphere( float radius ) = 0; /// Interface for creating and returning a BoxProxy virtual BoxProxy *createBox( const agx::Vec3& halfExtents ) = 0; /// Interface for creating and returning a LineProxy virtual LineProxy *createLine( const agx::Vec3& p1, const agx::Vec3& p2 ) = 0; /// Interface for creating and returning a CylinderProxy virtual CylinderProxy *createCylinder( float radius, float height ) = 0; /// Interface for creating and returning a ConeProxy virtual ConeProxy *createCone( float radius, float height ) = 0; /// Interface for creating and returning a CapsuleProxy virtual CapsuleProxy *createCapsule( float radius, float height ) = 0; /// Interface for creating and returning TextProxy virtual TextProxy *createText( const agx::String& text, const agx::Vec3& pos ) = 0; /// Interface for creating and returning PlaneProxy virtual PlaneProxy *createPlane( const agx::Vec3& normal, agx::Real distance ) = 0; /// Interface for creating and returning HeightfieldProxy virtual HeightFieldProxy *createHeightfield( const agxCollide::HeightField *hf ) = 0; /// Interface for creating and returning TrimeshProxy virtual TrimeshProxy *createTrimesh( const agxCollide::Trimesh *mesh ) = 0; Each of these methods are responsible for returning a RenderProxy of a specific type based on the in-data. The RenderProxy is then responsible for holding this renderable reference into the render system in use. As an example, the code for creating a sphere OpenSceneGraph looks like this: .. code-block:: c++ agxRender::SphereProxy* RenderProxyFactory::createSphere( float radius ) { ShapeData sphereData; sphereData.shape = new osg::Sphere(); sphereData.shape->setRadius( radius ); sphereData.geode = createGeode( sphereData.shape, &sphereData.shapeDrawable ); sphereData.geode->setName("SphereGeode"); // Create a new Sphere proxy including the rendering representation in OSG agxOSG::SphereProxy *proxy = new agxOSG::SphereProxy( radius, sphereData, this ); addChild( proxy->getNode() ); // Add the node to the scenegraph (hold by the factory) return proxy; // Return the proxy } The specific data required will differ between rendering engines. .. _agxrender-renderproxy: ---------------------- agxRender::RenderProxy ---------------------- Each subclass (primitive type) of RenderProxy need to be implemented for the specific render engine. The virtual methods that can/should be implemented are: ^^^^^^^^ onChange ^^^^^^^^ This method need to be implemented (pure virtual method) in your representation of a RenderProxy. It will receive calls from each of the set methods described below. Whenever a proxy is required to change color, shape, alpha or transform, this method will be called. Based on the event-type described in the enum agxRender::RenderProxy::EventType, you should update your rendering representation with the new current value. For the OpenSceneGraph implementation a code snipped looks like the following: .. code-block:: c++ void onChange( RenderProxy::EventType type) { switch( type ){ case (RenderProxy::ENABLE): setEnableOSG(getEnable()); break; case (RenderProxy::ALPHA): setAlphaOSG(getAlpha()); break; case (RenderProxy::TRANSFORM): setTransformOSG(getTransform()); break; ... So based on the event type different calls are done to the rendering API to reflect the changes into the rendering implementation. ^^^^^^^^^^^ updateShape ^^^^^^^^^^^ This method need to be implemented (pure virtual method) need to be implemented in your representation of a RenderProxy. It is specific for each shape. When this method is called (probably from your specific implementation of onChange(SHAPE)) the render proxy implementation need to update the rendering representation to reflect changes, such as changing radius for a sphere, change the triangle mesh structure or changing the size of a box. A code snippet from the OpenSceneGraph representation illustrate how it can be done: .. code-block:: c++ void SphereProxy::updateShape( ) { // Get the agx representation of the shape associated to this proxy const agxCollide::Sphere *sphere = getCastShape(); if (sphere) // If there IS a shape associated, then read the radius from that m_radius = sphere->getRadius(); // If radius is the same, then just dont do anything if (agx::_equivalent((float)m_radius, m_data.shape->getRadius())) return; // Change the radius of the osg-sphere m_data.shape->setRadius(m_radius); m_data.geode->getDrawable(0)->dirtyDisplayList(); } ^^^^^^^^^^^^ setTransform ^^^^^^^^^^^^ .. code-block:: c++ virtual void RenderProxy::setTransform( const agx::AffineMatrix4x4& transform ); This method will set the m_transform member of the RenderProxy, then it will call onChange( TRANSFORM ) which indicates that the transform is changed. If m_transform == transform, no call to onChange will occur (to avoid unnecessary state changes. If you override this method, store transformation in m_transform and call onChange(TRANSFORM). ^^^^^^^^ setColor ^^^^^^^^ Store a color in m_color and call onChange(COLOR). If color == m_color, no call to onChange will occur to reduce state changes. If you override this method, update m_color and call onChange(COLOR). ^^^^^^^^ getColor ^^^^^^^^ This method return the color of the proxy, the value in m_color. You can override this method to return the color as stored somewhere else, just make sure you sync with the m_color member attribute. ^^^^^^^^ setAlpha ^^^^^^^^ Set the transparency value of the proxy. The implementation will store alpha in m_alpha and call onChange(ALPHA). If you override this implementation, you need to update m_alpha, and call onChange(ALPHA). ^^^^^^^^ getAlpha ^^^^^^^^ Return the value of the m_alpha member attribute. If you override setAlpha you might want to also override this method. ------------------------ agxRender::GraphRenderer ------------------------ This class is specific for each rendering engine. An implementation for OpenSceneGraph can be found in and corresponding .cpp file. For more information, see the doxygen generated documentation for the class.