instantiateHierarchy for SolidParticleSystem?

I’m building a multiplayer game where there’s a large map and users can place things on the map. The things can be 3D models, photos (3D models with a MeshBuilder.Plane & a diffuseTexture pointing to a URL) or Minecraft-like voxels.

The map is chunked into segments of 32x32x32. Each chunk currently has up to three layers:
(1) Terrain (voxels only)
(2) “Surfaces” (3D models)
(3) “Foilage” (3D models which can optionally be placed above or alongside surfaces or terrain)

When there’s lots of content, the surfaces / foilage layers cause really slow:

  • getActiveMeshCandidates
  • graphics frame time

The scene is already using Octree. The meshes are instanced, have low polygon count, and their world matrices & materials are frozen.

When I say “really slow”, I mean the framerate hovers around 30 fps but the CPU usage of the tab is around 100%. I’m on a 2019 Macbook Pro (using Chrome) with a AMD Radeon Pro 5500M 8 GB.

So now, instead of instancing, I’m trying to batch render each chunk by wrapping the two layers into a single SolidParticleSystem for each chunk.

If I loop through the meshes and call addShape on each of them, the CPU usage drops to ~40% once it all loads! But the positions of the meshes are incorrect.

Is there an equivalent of Mesh#instantiateHierarchy but for SolidParticleSystem? I’m having trouble correctly calling addShape on the 3D models (which can contain TransformNode and/or empty meshes).

  • If I use Mesh.MergeMeshes, the materials end up rendering incorrectly or the colors change.
  • It’s tough to associate the parents correctly since the parent might be a TransformNode or an empty Mesh with no vertices. To “flatten” that, would I need to apply the transformation matrix to the positions of each child recursively? What would the right way to do that be?

This is the code I currently have for adding shapes. This is hacky and it doesn’t really work. There’s an off-by-one error somewhere and I don’t think it handles parenting correctly.

sps.vars.opts = {
  positionFunction: function (particle: SolidParticle, i: number) {
    const parent = sps.vars.particleParents[i];
    const position = sps.vars.positions[i];

    if (isFinite(parent)) {
      particle.parentId = parent;
    }

    if (position) {
      particle.position = position;
    }
  },
};

const idMap = new Map()
const children = mesh.getChildren(undefined, false)
let parentId = sps.vars.particleParents?.length ?? 0;
let count =
  children.length +
  ((mesh.getTotalVertices && mesh.getTotalVertices()) ? 1 : 0);
let current = 0;
let start = parentId;

if (!sps.vars.particleParents) {
  sps.vars.positions = new Array(count);
  sps.vars.particleParents = new Array(count);
} else {
  sps.vars.particleParents.length += count;
  sps.vars.positions.length += count;
}

sps.vars.positions[parentId] = new Vector3(
  x - worldOriginOffset[0],
  y - worldOriginOffset[1],
  z - worldOriginOffset[2]
);

let parentNode = mesh;
mesh.position = sps.vars.positions[parentId];

sps.vars.particleParents[parentId] = 0;

for (let i = 0; i < children.length; i++) {
  if (children[i] === parentNode || !children[i].getVerticesData) {
    continue;
  }

  idMap.set(children[i], current);
  sps.vars.positions[current + 1] = mesh.position;
  sps.vars.particleParents[current + 1] = idMap.get(children[i].parent);
  sps.addShape(children[i], 1, sps.vars.opts);
  current++;
}

if (plane) {
  sps.vars.particleParents[current] = parentId;
  current = sps.addShape(plane, 1, sps.vars.opts);
}
1 Like

This is a good question for our SPS master: @jerome

I tried using the preview release for Babylon instead of the stable one, and noticed that when isPickable is set to true on the SolidParticleSystem, it throws an exception on sps.buildMesh().

I think this might be a bug? Here’s a repro: https://playground.babylonjs.com/#KY74WM#1

You can see that if you change the version from latest to 4.1, it works correctly without throwing an exception.

Should I create an issue for it?

main.js:1 TypeError: Cannot read property '_model' of undefined
    at e.buildMesh (babylon.js:16)
    at createScene (<anonymous>:39:9)
    at <anonymous>:54:9
    at fastEval (main.js:1)
    at main.js:1

Thanks for the repro. Please bear with us @jerome should answer soon(ish)

I’ll have a look at this

isPickable = true seems to still work with an updatable SPS for now https://playground.babylonjs.com/#KY74WM#2

OK, I understand by reading back the doc Solid Particle System - Babylon.js Documentation
It’s done by design.
When building a non updatable SPS, there’s actually no particles at all, it is to say no array holding particle objects. In this case, the SPS is simply a mesh and has no feature related to the particles… so no pickable particle at all.

You can still make the mesh pickable with sps.mesh.isPickable = true
https://playground.babylonjs.com/#KY74WM#3

However, if you want to manage pickable particles afterwards, you need that SPS creates them, so it can’t be immutable (non updatable) in this case.

This is a not a bug but this should be better documented : no particle implies no pickable particle. I’ll do it.

1 Like

Thanks for looking into it. That makes sense.

Maybe the particles just shouldn’t be pickable but .isPickable would be set to the true on the Mesh returned by buildMesh()? If not, maybe we could throw an error with a message saying how to fix it (“Immutable SolidParticleSystem cannot have pickable particles. Either set updatable to true or isPickable to false. To make the mesh pickable, call mesh.isPickable = true;”)

Regarding earlier question, do you have any ideas for how to use SolidParticleSystem for this case where these are glTF models that potentially have TransformNode or meshes with no vertices?

If I use SolidParticleSystem.digest it seems to be an empty mesh and if I manually loop through child meshes with vertices & addShape or digest, the size/scale/rotations are off and the mesh takes up the entire scene