FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
0021 — visual::FlowPlane sets its colour uniforms on the shared material, so two VisualFlowPlane instances corrupt each other
ID 0021
Class BUG
Severity 2
Status ready
Models VisualFlowPlane
Found 2026-09-23 during the design of the baked scalar-field visualisers

Evidence

Every FlowPlane builds its mesh against the one material named FlowPlaneHeatmap (src/environment/visual/FlowPlane.cpp:44), then writes its own ColorRange, VisibleRange and AlphaRange into that material's vertex-program parameters (src/environment/visual/FlowPlane.cpp:56-60):

auto pass = m_entity->getSubEntity(0)->getMaterial()->getTechnique(0)->getPass(0);
auto params = pass->getVertexProgramParameters();
params->setNamedConstant("colorRange", Ogre::Vector2(settings.colorRange[0], settings.colorRange[1]));
params->setNamedConstant("visibleRange", Ogre::Vector2(settings.visibleRange[0], settings.visibleRange[1]));
params->setNamedConstant("alphaRange", Ogre::Vector2(settings.alphaRange[0], settings.alphaRange[1]));

Ogre::SubEntity::getMaterial() hands back the MaterialPtr the submesh was created with; nothing clones it. The mesh name is per-instance ("FlowPlaneMesh" + instanceString, FlowPlane.cpp:44), but the material name in that same call is the literal "FlowPlaneHeatmap", declared once in data/resources/ogre/assets/other/FlowPlane.material:18. The three uniforms are param_named defaults of the shared FlowPlaneVP program (FlowPlane.material:7-9), not per-renderable constants, so the write is global.

Each VisualFlowPlane SimObject constructs exactly one FlowPlane (src/environment/VisualFlowPlane.cpp:197), so two such SimObjects in one input file are two writers of the same three uniforms.

Effect

With two or more VisualFlowPlane objects in a scene, the last one constructed wins: every plane is then coloured and thresholded by that plane's ColorRange, VisibleRange and AlphaRange, whatever its own XML said. A user who gives a temperature plane ColorRange 4,12 and a current plane ColorRange 0,1 sees both drawn with 0,1 — the temperature plane saturates to a single colour, and with a mismatched VisibleRange a plane can vanish entirely. Nothing is logged; the parameters are read and accepted, they simply have no lasting effect.

The severity is limited to the picture: no state, no output port and no recorded result is affected, and a single-plane scene — which is every test and example in the repository — is correct.

Possible fix

  1. Clone the material per instance in the FlowPlane constructor: material->clone("FlowPlaneHeatmap_" + instanceString), assign the clone to the sub-entity with m_entity->getSubEntity(0)->setMaterial(clone), and set the uniforms on the clone. The destructor then removes the clone via Ogre::MaterialManager::getSingleton().remove(...) — which depends on ENV-0022, since FlowPlane has no destructor body today. This is the option the new baked visualisers are specified to use.
  2. Move the three values out of the program's default_params and into per-renderable custom parameters (Renderable::setCustomParameter plus param_named_auto custom), which avoids a material per plane. Cheaper in material count, but it changes the shader and the .material file, and the same change would have to be made for any future visualiser sharing the program.

Option 1 is the smaller, more local change and matches the precedent being set elsewhere.

Test that would prove it

An Ogre-backed (_NEEDS_GUI) test that constructs two FlowPlane objects with different colorRange values and then reads the named constant back from each sub-entity's material pass: today both report the second plane's range, and the two MaterialPtr values compare equal. After the fix the pointers differ and each reports its own range.

Risk

Cloning a material per plane costs one material and one GPU program instance per VisualFlowPlane; with the handful of planes a scene realistically carries this is negligible, but the clone must be removed on destruction or it becomes a per-run leak in a long session that creates and destroys planes. Any out-of-repo material that overrode FlowPlaneHeatmap by name would still be picked up, since the clone is taken from the looked-up material rather than built from scratch.