.. _adding_actor: Adding a new actor ================== Actors are the visual elements of a Met.3D scene. For a full description of the actor class hierarchy, rendering interface, resource lifecycle, and shader loading, see :doc:`/05_developer_manual/03_actors/index`. Choose a base class: * ``MActor`` (``src/gxfw/mactor.h``) — general base. Use for annotation, geometry, or any actor that does not read gridded data from the pipeline. * ``MNWPMultiVarActor`` (``src/gxfw/nwpmultivaractor.h``) — extends ``MActor`` with managed data variables. Use when the actor needs to load one or more NWP fields. Deriving from MActor --------------------- ``MExampleActor`` (``src/teaching/``) is a minimal, heavily commented example actor. It is the best starting point for a new ``MActor``-derived actor. Enable it with: .. code-block:: bash cmake -DTEACHING=ON .. The example actor covers constructor boilerplate, property setup, shader loading, geometry upload, and rendering. Read through it alongside the notes below. Required static methods ~~~~~~~~~~~~~~~~~~~~~~~~ Every actor must implement four static methods used by the registration system: .. code-block:: c++ static QString staticActorType() { return "MyActor"; } // session file key static QString staticDisplayName() { return "My actor"; } // Create actor menu static QString staticIconPath() { return ":/icons/actors/missing.png"; } static QString staticSettingsID() { return "MyActor"; } // config group name Properties ~~~~~~~~~~~ Declare properties as member variables (``MFloatProperty``, ``MColorProperty``, ``MEnumProperty``, ``MBoolProperty``, ``MPointFProperty``, ``MButtonProperty``, …), configure them in the constructor, and register them with ``actorPropertiesSupGroup.addSubProperty()``. Property-change callbacks either call ``&MActor::emitActorChangedSignal`` (redraw only) or a custom slot that requests data, rebuilds geometry, and so on. See :doc:`/05_developer_manual/01_system_modules/property_framework` for the property framework API. Shaders ~~~~~~~~ GLSL effect files (``*.fx.glsl``) live in ``src/glsl/``. Compile them in ``reloadShaderEffects()`` and register the result in ``initializeActorResources()``. See :ref:`doc_dev_actors` → *Shader loading* for the full pattern. Render methods ~~~~~~~~~~~~~~~ Override whichever render method applies to your actor (see :ref:`doc_dev_actors` → *Rendering interface* for when each is called): * ``renderToCurrentContext()`` — 3-D geometry in world space (lon/lat/pressure). * ``renderToCurrentFullScreenContext()`` — full-screen 2-D overlay; enable with ``setActorSupportsFullScreenVisualisation(true)`` in the constructor. * ``renderToCurrentContextUiLayer()`` — 2-D UI elements on top of the 3-D view. Registration ~~~~~~~~~~~~~ In ``src/system/applicationconfiguration.cpp``, ``MApplicationConfigurationManager::registerActorFactories()``: .. code-block:: c++ #include "actors/myactor.h" // inside registerActorFactories(): glRM->registerActorFactory("Base"); Available groups: ``"Base"``, ``"Sections && volumes"``, ``"Atmospheric features"``, ``"Flow visualization"``, ``"Diagrams"``, ``"Annotation"``. Groups map to the context-menu categories in the *Create actor* dialog. Deriving from MNWPMultiVarActor --------------------------------- ``MNWPMultiVarActor`` adds a managed list of ``MNWPActorVariable`` objects to ``MActor``. The base class handles the *Add variable* / *Delete variable* UI, data requests, time/member synchronisation, and transfer function wiring. See :ref:`doc_dev_actors` → *MNWPMultiVarActor* for a description of actor variables and the data flow. Pure virtual methods ~~~~~~~~~~~~~~~~~~~~~ ``supportedLevelTypes()`` returns the level types the actor can handle: .. code-block:: c++ QList MMyActor::supportedLevelTypes() { return { PRESSURE_LEVELS_3D, HYBRID_SIGMA_PRESSURE_3D, AUXILIARY_PRESSURE_3D, SINGLE_LEVEL }; } ``createActorVariable()`` is a factory called whenever the user adds a variable. Return a new instance of the appropriate typed variable class: .. code-block:: c++ MNWPActorVariable* MMyActor::createActorVariable( const MSelectableDataVariable& dataSource) { auto* var = new MNWP3DVolumeActorVariable(this); var->dataSourceID = dataSource.dataSourceID; var->levelType = dataSource.levelType; var->variableName = dataSource.variableName; return var; } Reacting to new data ~~~~~~~~~~~~~~~~~~~~~ Override ``dataFieldChangedEvent()`` to update your actor whenever a new data field arrives: .. code-block:: c++ void MMyActor::dataFieldChangedEvent() { // rebuild vertex buffers, recompute derived state, … emitActorChangedSignal(); // schedule a redraw } Reacting to variable list changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Override these slots to update internal structures when variables are added, changed, or removed. Always call the base implementation first: * ``onAddActorVariable(MNWPActorVariable *var)`` * ``onChangeActorVariable(MNWPActorVariable *var)`` * ``onDeleteActorVariable(MNWPActorVariable *var)`` Accessing data in the render method ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Iterate over ``variables`` and guard every grid access with ``hasData()``: .. code-block:: c++ void MMyActor::renderToCurrentContext(MSceneViewGLWidget *sceneView) { for (MNWPActorVariable *baseVar : variables) { if (!baseVar->hasData()) continue; auto *var = static_cast(baseVar); // var->grid — current MStructuredGrid // var->transferFunction — active transfer function // … bind shader, upload grid to texture, draw … } } Registration is the same as for ``MActor``. Add the include and factory call in ``applicationconfiguration.cpp``. .. note:: If you think your new actor would be useful to other Met.3D users, we would be happy to include it in the main codebase. Please consider submitting a `merge request on GitLab`_. .. _`merge request on GitLab`: https://gitlab.com/wxmetvis/met.3d/-/merge_requests .. _actor_icon: Adding an icon ~~~~~~~~~~~~~~~ If you would like to create a new icon for your actor, make sure it is: * grayscale, * 112 × 112 pixels, * free of text, letters, and fine structures, * simple and conveying the functionality of the actor in an abstract way. We recommend creating the icon as a vector graphic using `Inkscape `_ and exporting it as a PNG with an optional alpha channel. The icons live in ``resources/icons/actors/``. Add the PNG file there and update ``staticIconPath()``: .. code-block:: c++ static QString staticIconPath() { return ":/icons/actors/myactor.png"; }