Not a dumb question at all — and actually, looking at it more carefully, I owe you a correction on my previous post.
For morph targets / blendshapes specifically, Babylon’s glTF loader does not use InstancedMesh when a mesh has morph targets. The relevant check in glTFLoader.ts is roughly:
const shouldInstance =
this._disableInstancedMesh === 0 &&
this._parent.createInstances &&
node.skin == undefined &&
!mesh.primitives[0].targets;
So if a mesh has morph targets, every node that references it gets a full Mesh with its own MorphTargetManager. The morph target data (positions/normals/…) is reloaded per copy, but the weights are independent per node.
That means your idea is right: adding extra animation channels in the glTF JSON, each targeting a different node’s weights path, would animate each algae independently. The reason only the first one animated in the Sandbox is almost certainly that your exporter only produced one channel (targeting the original node) — the other nodes have their own MorphTargetManager sitting there with weights stuck at 0.
Concretely, in the glTF you’d want something like:
"animations": [{
"channels": [
{ "sampler": 0, "target": { "node": 5, "path": "weights" } },
{ "sampler": 0, "target": { "node": 6, "path": "weights" } },
{ "sampler": 0, "target": { "node": 7, "path": "weights" } }
],
"samplers": [ { "input": ..., "output": ..., "interpolation": "LINEAR" } ]
}]
All three channels can share the same sampler (same keyframes / output buffer), so it costs almost nothing in file size — just three extra channel entries per algae. You can script that as a small post-process on the exported .gltf (or .glb after unpacking).
Two caveats so you don’t get bitten:
- This trick only works for morph targets, not for the skinned case. For nodes with a
skin, the loader still creates separate Mesh objects, but they all share the same Skeleton (because the skin is shared by index). Animating bones still moves them once, and every copy that uses that skeleton deforms identically. So for the skinned algae you still need either instantiateModelsToScene() (which clones the skeleton) or VAT, or duplicating the skin in the glTF.
- If you ever switch the algae to a skinned + morphed mesh, only the morph part will be independent via this trick; the skin part will still be shared.
So a more accurate option list, just for the morph-only case, would be:
AssetContainer.instantiateModelsToScene() — clones the rig + morph manager per copy.
- VAT — fully bakes the deformation, scales to thousands of copies.
- Duplicate the mesh/skin in Maya — heaviest in size, lightest in code.
- Your idea: keep one shared mesh + multiple nodes, but add per-node
weights animation channels in the glTF JSON. Cheap on size, no runtime code, works because the loader gives each morphed-mesh node its own MorphTargetManager.
Sorry for the confusion in the previous answer — I’d conflated the skinned case (where the original “explode” symptom came from) with the morph-target case, but Babylon handles those two differently inside the loader.