Good practices for Blender mesh hierarchies

There must be a better pattern for this but I haven’t found it. I want to have one blend file, with 1 or more parent meshes. In this example I have a house which can have an optional chimney.

However I can not figure out how to organise the Blender objects that avoids the creation of tiny unused meshes which I hide inside the object (walls are hidden to reveal one of the two “tiny” meshes):

This works well as when I export to .glb I just import the .glb and then scene.getMeshByName("low_poly_house_2") and scene.getMeshByName("low_poly_house_2_chimney") to get the house’s component parts
What patterns do other people use? Thank you!

I’m not really used to Blender (or any other DCC actually) but I think @PatrickRyan can share some good practices here

There are no un-used meshes. The green triangle child is just always there for every mesh, as your shot shows. This has nothing to do with hierarchies.

Based on the color, you seem to be pointing to part of the chimney hierarchy. If you do not want it, then edit the mesh which has it, and remove those faces. To find which mesh, just click on that part.

I’ve found that if the “tiny” meshes do not contain anything, then Babylonjs seems to ignore all the child meshes. In other words if I was to:

  • go into that blender file, click the tiny mesh and delete all its vertices/edges/faces
  • then export to a .glb
  • then import the .glb into babylonjs

then the parent mesh “low_poly_house_2” would not be imported and would not be found when calling: scene.getMeshByName("low_poly_house_2"). I am pretty sure that’s the situation though I might have something else mixed up here so I am happy to produce a minimal repository to show the situation.

My question is, how do other people organise 1 or more meshes that have submeshes? Do they perhaps not use nested meshes at all and instead merge the sub meshes into one (the parent) mesh and then apply the different materials using vertexgroups or something? (I am a Blender and BabylonJS beginner… I’m not sure what the best approach is to using Blender models with BabylonJS)

@ajp, one of the techniques I use all of the time for componentizing meshes is to use null transforms as locators in the mesh. This is an empty in Blender, which is just a transform with no mesh attached. It does not render in scene, but does hold translation, rotation, and scale information. If you set another mesh as a child of the null with mesh.parent = null , it will take the position scale and rotation of the null so you can create all of your components at the world origin and they will snap into the right place on the model.

The other way you can do this is with a skeleton. If your mesh already uses a skeleton, you can create extra bones in the hierarchy that don’t have any skinning information just to use as attachment points. These work in the same way in that any child attached to a bone will take the translation, rotation, and scale of the bone. You may even find it easier to work with skeletons even on meshes that don’t deform since you are not managing a lot of extra nodes in your scene, especially if you have a lot of attachment points.

Hope this helps, but feel free to ping with more questions.

1 Like

Thanks! Is the “null transform” you mentioned one of these or is it something else?..

1 Like

That’s correct. Just the Plain Axes Empty is what I would use.

1 Like

Right this makes sense. I guess when the top empty mesh was being imported before it might have been being converted into a TransformNode and then my import script was incorrectly disposing of it and all the child meshes. I’ve now fixed my import script to be:

export function load_assets (assets_manager: AssetsManager)
{
    assets_manager.addMeshTask("load low_poly_tree_1", null, "public/models/low_poly_tree/", "low_poly_tree_1.glb")
    // assets_manager.addMeshTask(
    // ... etc ...

    assets_manager.onTaskSuccess = task =>
    {
        if (is_MeshAssetTask(task))
        {
            if (typeof task.sceneFilename === "string" && task.sceneFilename.endsWith(".glb"))
            {
                const roots = task.loadedMeshes.filter(mesh => mesh.name === "__root__")

                roots.forEach(mesh =>
                {
                    // Before I was incorrectly calling `mesh.getChildrenMeshes()`
                    // which was resulting in all of the meshes being disposed of
                    // as the `child.parent === mesh` was never true because
                    // there was an intermediate empty TransformNode.
                    mesh.getChildren().forEach(child =>
                    {
                        if (child.parent === mesh) child.parent = null
                    })
                    mesh.dispose()
                })
            }
            else
            {
                // ...
            }
        }
    }
}