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 Actors.
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) — extendsMActorwith managed data variables. Use when the actor needs to load one or more NWP fields.
Important
Met.3D globs its source and resource files in CMake, so after adding a new
.h/.cpp file (or an icon under resources/) you must clear the
CMake cache and build files and re-run CMake for it to be compiled.
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:
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:
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
Constructor and boilerplate
The constructor takes the owning MSession and forwards it to the base class.
Add the one-line getSettingsID() override, and wire up the actor’s identity
at the top of the constructor before creating any properties:
explicit MMyActor(MSession *parent);
QString getSettingsID() override { return staticSettingsID(); }
MMyActor::MMyActor(MSession *parent)
: MActor(parent)
{
setActorType(staticActorType());
setName(staticDisplayName());
actorPropertiesSupGroup.setConfigGroup(getSettingsID());
// ... create and configure properties ...
}
Important
staticActorType() and each property’s setConfigKey(...) are written
into actor configuration and session files. Think carefully on how to name them
and keep them stable, as changing them later on breaks compatibility with old
configuration files.
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 Property Framework
for the property framework API.
Shaders
GLSL effect files (*.fx.glsl) live in src/glsl/. In
initializeActorResources() acquire the (shared) program with
glRM->generateEffectProgram(name, shader) and call reloadShaderEffects()
only when it reports the program is new. reloadShaderEffects() performs the
actual compilation. This method is also invoked on its own when shaders are reloaded
from the UI, so keep the compilation logic there.
With the shader effect held as a member
(std::shared_ptr<GL::MShaderEffect> shader;):
void MMyActor::initializeActorResources()
{
MGLResourcesManager *glRM = MGLResourcesManager::getInstance();
// Returns true if the program was newly created, false if a program
// of that name already exists and can be reused.
bool loadShaders = glRM->generateEffectProgram("myactor_shader", shader);
if (loadShaders)
{
reloadShaderEffects();
}
}
void MMyActor::reloadShaderEffects()
{
// Register one progress bar task per shader that is compiled below.
beginCompileShaders(1);
compileShadersFromFileWithProgressDialog(
shader, "src/glsl/myactor.fx.glsl");
endCompileShaders();
}
See Actors → Shader loading for the full pattern.
Cleanup
Release GPU items you created in the destructor via
MGLResourcesManager::getInstance()->releaseGPUItem(...) (e.g. vertex
buffers), and delete any other owned objects. Shader programs obtained through
generateEffectProgram() are owned and reference-counted by the resource
manager. These do not have to be released in the destructor.
Render methods
Override whichever render method applies to your actor (see 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 withsetActorSupportsFullScreenVisualisation(true)in the constructor.renderToCurrentContextUiLayer()— 2-D UI elements on top of the 3-D view.
Registration
In src/system/applicationconfiguration.cpp,
MApplicationConfigurationManager::registerActorFactories():
#include "actors/myactor.h"
// inside MApplicationConfigurationManager::registerActorFactories():
MActorTypeRegistry::registerActorType<MMyActor>("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
Actors → MNWPMultiVarActor for a description of actor variables and
the data flow.
MExampleMultiVarActor (src/teaching/, also enabled with
-DTEACHING=ON) is the corresponding minimal, commented example for a
data-driven actor. Use that actor as starting point, just as MExampleActor
for a plain MActor.
Pure virtual methods
supportedLevelTypes() returns the level types the actor can handle:
QList<MVerticalLevelType> 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:
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:
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():
void MMyActor::renderToCurrentContext(MSceneViewGLWidget *sceneView)
{
for (MNWPActorVariable *baseVar : variables)
{
if (!baseVar->hasData()) continue;
auto *var = static_cast<MNWP3DVolumeActorVariable*>(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.
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.
Line width should be 2px for normal lines and 3px for thicker lines.
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():
static QString staticIconPath() { return ":/icons/actors/myactor.png"; }
Everything under resources/ is compiled into the Qt resource automatically,
in Met.3D there is no .qrc file to edit. When adding a new resource, you
have to clear the CMake cache before re-building, otherwise the old resources
are re-used.