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);
}
```