Visualising Agents

The main purpose of FLAME GPU visualisations is to display the agents. By default no agents are visualised, and each agent to be visualised must be configured.

// Configure the visualisation
flamegpu::visualiser::ModelVis visualisation = cudaSimulation.getVisualisation();
// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");
...

Agent Model

Currently FLAME GPU only supports loading models from .obj (wavefront) format files.

It is suggested that low-poly models are used, as each model will be drawn once per agent. Which potentially multiplies the polygon count by millions, which can significantly impact the performance of the visualiser.

Several static stock models are provided alongside FLAME GPU.

Model Name

Description

SPHERE

A sphere constructed from slices and segments

ICOSPHERE

A lower-poly sphere constructed from triangles

CUBE

A simple 12-poly cube

TEAPOT

The traditional graphics example model, Utah teapot

STUNTPLANE

A low-poly model of a plane, useful for representing agents with rotation in all 3 planes

PYRAMID

A 4-poly equilateral triangular pyramid

ARROWHEAD

A 4-poly triangular pyramid for indicating 3D orientation

Examples of setting the agent model are shown below

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");
// Configure the agent to use the stock icosphere model
boid_agt.setModel(flamegpu::visualiser::Stock::Models::ICOSPHERE);
// Or, configure the agent to use a custom model
boid_agt.setModel("my_files/person.obj");

KeyFrame Animated Models

FLAME GPU also supports the use of model pairs, to provide basic animation by interpolating between the two models. Both models must have the same number of vertices/faces in the same order for this to work.

A stock keyframe model pair PEDESTRIAN is avaiable, which represents a low-poly pedestrian.

In order to use a keyframe model pair, it is also necessary to specify a float agent variable, which contains the interpolation position in the inclusive range [0, 1] which controls the contribution of each model to that which is rendered by the agent.

Examples of setting a keyframe agent model are shown below

// Add agent 'pedestrian' to the visualisation
flamegpu::visualiser::AgentVis ped_agt = visualisation.addAgent("pedestrian");
// Configure the agent to use the stock pedestrian model, and "animate" variable to control animation
ped_agt.setKeyFrameModel(flamegpu::visualiser::Stock::Models::PEDESTRIAN, "animate");
// Or, configure the agent to use a custom model pair, and "animate" variable to control animation
ped_agt.setKeyFrameModel("my_files/ped_a.obj", "my_files/ped_a.obj", "animate");

The video below shows the Pedestrian Navigation example which demonstrates how keyframe animated agents appear.

Agent Position

Agent variables can be used to control an agent’s position with the visualisation. It is required that atleast one component is set, otherwise agents will all remain at the origin.

By default, if an agent has float variables with names x, y and z, these will be used for the agent’s position.

However, you can use other variables if you have used different variable names or an array variable.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Set agent position with upto 3 individual float variables
boid_agt.setXVariable("pos_x");
boid_agt.setYVariable("pos_y");
boid_agt.setZVariable("pos_z");
// Or, set agent position with a single float[2] variable
boid_agt.setXYVariable("pos_xy");
// Or, set agent position with a single float[3] variable
boid_agt.setXYZVariable("pos_xyz");

Agent Direction

Agent direction or rotation can be linked to agent variables similar to that of agent position, this will cause the model used for the agent to rotate towards the given direction. However, there are several options to choose from depending on how direction is stored within your model.

You can specify the forward vector of the agent (e.g. it’s velocity), which are used to derive the agent’s yaw and pitch. This can optionally be extended by specifying an up vector, which will be used to derive agent’s roll.

Alternatively, you can directly specify the yaw, pitch and roll variables.

A few examples are provided below, for a complete understanding of the combinations permitted view the API documentation for AgentVis.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Set agent forward and up vectors (yaw, pitch and roll) with individual float variables
boid_agt.setForwardXVariable("velocity_x");
boid_agt.setForwardYVariable("velocity_y");
boid_agt.setForwardZVariable("velocity_z");
boid_agt.setUpXVariable("up_x");
boid_agt.setUpYVariable("up_y");
boid_agt.setUpZVariable("up_z");
// Or, set agent forward vector xz (yaw) with a single float[2] variable
boid_agt.setForwardXZVariable("velocity_xz");
// Or, set agent forward vector (yaw and pitch) with a single float[3] variable
boid_agt.setForwardXYZVariable("velocity_xyz");
// Or, set agent yaw, pitch and roll angles with individual float variables
boid_agt.setYawVariable("yaw");
boid_agt.setPitchVariable("pitch");
boid_agt.setRollVariable("roll");
// Or, set agent yaw, pitch with a single float[2] variable
boid_agt.setDirectionYPVariable("angles");
// Or, set agent yaw, pitch, roll with a single float[3] variable
boid_agt.setDirectionYPRVariable("angles");

Agent Scale

Similar to agent position and direction, agent variables can be used to set a scale multiplier for the model used.

First you should set the base model scale (with setModelScale()), and then the multiplier used will multiply by this value (so an agent variable with value 1, would be at the base scale). If you would prefer to provide the absolute model scale via agent variables, then specify a model scale of 1.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");
// Uniformly scale the default model so that it is 1.5 units in it's longest axis
boid_agt.setModelScale(1.5f);
// Or, scale each axis of the default model individually
boid_agt.setModelScale(1.0f, 1.5f, 1.0f);

// Uniformly scale the model with a single float variable
boid_agt.setUniformScaleVariable("scale_xyz");
// Or, set agent scale multiplier with upto 3 individual float variables
boid_agt.setScaleXVariable("scale_x");
boid_agt.setScaleYVariable("scale_y");
boid_agt.setScaleZVariable("scale_z");
// Or, set agent scale multiplier with a single float[2] variable
boid_agt.setScaleXYVariable("scale_xy");
// Or, set agent scale multiplier with a single float[3] variable
boid_agt.setScaleXYZVariable("scale_xyz");

Agent Color

There are a wide range of options for controlling the color of agent models.

Static Colors

Agent colors can be specified as a static RGB color.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Set the agent's color to a stock color
boid_agt.setColor(flamegpu::visualiser::Stock::Colors::RED);
// Set the agent's color with a HEX color code
boid_agt.setColor(flamegpu::visualiser::Color{"#ff0000"});
// Set the agent's color with floating point components
boid_agt.setColor(flamegpu::visualiser::Color{1.0f, 0.0f, 0.0f});

Discrete Color Selection

An integer or unsigned integer agent variable can map to a palette of colors.

A DiscreteColor instance is simply a map between integer keys, and Color values. An additional fall-back color must also be specified, this will be used if the value an agent holds falls outside of the keys found within the map.

A predefined stock Palette can be used to automatically fill, the map. Keys will be assigned 0,1,2 etc, however this can be changed by overriding the offset and stride.

The stock palettes available are:

Qualitative

Sequential

Diverging

Set1 (from Colorbrewer)

YlOrRd (from Colorbrewer)

RdYlBu (from Colorbrewer)

Set2 (from Colorbrewer)

YlGn (from Colorbrewer)

PiYG (from Colorbrewer)

Dark2 (from Colorbrewer)

Greys (from Colorbrewer)

Pastel (from seaborn)

Viridis (from BIDS/MatPlotLib)

Note

The Viridis palette is dynamic, it uniformly samples the number of specified values from the continuous Viridis palette.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Map the agent's color to the value of 'i' as selected from a discrete stock palette
boid_agt.setColor(flamegpu::visualiser::DiscreteColor("i", flamegpu::visualiser::Stock::Palette::DARK2, flamegpu::visualiser::Stock::Colors::WHITE));
// Or, map the agent's color to the value of 'i', as selected from a dynamic stock palette, using 10 uniformly spaced values from Viridis
// Override the offset to 1, and stride to 2 (1,3,5,7..)
boid_agt.setColor(flamegpu::visualiser::DiscreteColor("i", flamegpu::visualiser::Stock::Palette::Viridis(10), flamegpu::visualiser::Stock::Colors::WHITE,
1, 2));

Alternatively, you can construct a bespoke palette of discrete colors

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Construct a DiscreteColor map for integer variable "i" with fall-back White
flamegpu::visualiser::iDiscreteColor cmap("i", flamegpu::visualiser::Stock::Colors::WHITE);
// Add desired key:color mappings
cmap[0] = flamegpu::visualiser::Stock::Colors::RED;
cmap[1] = flamegpu::visualiser::Color{"#00ff00"};
cmap[2] = flamegpu::visualiser::Color{0.0, 0.0, 1.0};
// Bind cmap to the 'boid' agent
boid_agt.setColor(cmap);

Color Interpolation

A float agent variable can also be mapped to colors, by interpolating through a specified palette.

Currently there are two options available for this; HSVInterpolation and ViridisInterpolation.

By default interpolation clamps variables to the inclusive range [0, 1]. This can be overridden by setting the minimum and maximum bounds.

HSVInterpolation allows you to interpolate around the hues of the HSV color wheel. This can be used to provide a simple heatmap.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Use the default red-green HSV interpolation over the agent variable i
// With custom min/max bounds [0,100]
boid_agt.setColor(flamegpu::visualiser::HSVInterpolation::REDGREEN("i", 0.0f, 100.0f));
// Or, use a custom HSV interpolation over the agent variable i
boid_agt.setColor(flamegpu::visualiser::HSVInterpolation("i", 0.0f, 360.0f));

ViridisInterpolation works similarly, but interpolates over the fixed Viridis heatmap from BIDS/MatPlotLib.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");

// Use Viridis interpolation over the agent variable i
boid_agt.setColor(flamegpu::visualiser::ViridisInterpolation("i"));

Agent States

Additional configurations are possible, to differentiate multi-state agents in each of their states.

First an AgentStateVis for specialising the agent state must be retrieved using State().

By default, this new agent will be assigned a different color from the default palette.

AgentStateVis has the same methods as AgentVis to allow agent states to be differentiated by their: model, model scale and color, using the same methods as described in the above sections. It is not possible, to specify a different position or direction configuration for each agent state.

Additionally, all agents in a particular state can be hidden using setVisble().

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");
...
// Specialise the 'active' agent state
flamegpu::visualiser::AgentStateVis active_boid_agt = boid_agent.State("active");
...
// Hide agents in the 'inactive' agent state
boid_agent.State("inactive").setVisible(false);

Colors from Palettes

If you would rather use a palette to automatically assign agents in different states colors, rather than manual assignment outlined above, you can specify a pallet to be used with setAutoPalette(). Each agent state specialised (by calling State()) will then be assigned a different color from the selected palette.

// Add agent 'boid' to the visualisation
flamegpu::visualiser::AgentVis boid_agt = visualisation.addAgent("boid");
// Assign a palette to the boid agent
boid_agt.setAutoPalette(flamegpu::visualiser::Stock::Palette::DARK2);

// Specialise the 'active' agent state, assigning it a unique color from the DARK2 palette
boid_agent.State("active");
// Specialise the 'waiting' agent state, assigning it a unique color from the DARK2 palette
boid_agent.State("waiting");

These colors are not locked, and can be further overridden with the normal color methods.