I found a solution. Not sure it’s the best but it works. In blender, I reverted to the previous pattern, with a top level object with an empty mesh and several child objects with their mesh and materials e.g.:
Then for the import I use:
const parent_mesh_names = new Set([
mesh_name_low_poly_tree_1,
mesh_name_low_poly_house_2, // === "low_poly_house_2" same as in blender
])
function load_assets (assets_manager: BABYLON.AssetsManager)
{
assets_manager.addMeshTask("load low_poly_tree_1", null, "public/models/low_poly_tree/", "low_poly_tree_1.obj")
assets_manager.addMeshTask("load low_poly_house_2", null, "public/models/low_poly_house/", "low_poly_house_2.obj")
assets_manager.onTaskSuccess = task =>
{
if (is_MeshAssetTask(task))
{
const parent_mesh = task.loadedMeshes.find(mesh => parent_mesh_names.has(mesh.name))
if (!parent_mesh)
{
console.error("No parent mesh found whilst loading ", task.name)
return
}
task.loadedMeshes.forEach(mesh =>
{
if (mesh.name !== parent_mesh.name) parent_mesh.addChild(mesh)
mesh.visibility = 0
})
}
}
}
function is_MeshAssetTask (task: BABYLON.AbstractAssetTask): task is BABYLON.MeshAssetTask
{
return (task as BABYLON.MeshAssetTask).loadedMeshes !== undefined
}
And then when I come to use them I use the following utility functions:
interface GetMeshOptions
{
parent_node?: Node | null
position?: Vector3
receive_shadows?: boolean
shadow_generator?: ShadowGenerator
visibility?: number
}
export function get_mesh (scene: Scene, mesh_name: string, new_mesh_name: string, options: GetMeshOptions = {})
{
const mesh = scene.getMeshByName(mesh_name)?.clone(new_mesh_name, options.parent_node || null)!
const { position, receive_shadows, shadow_generator, visibility } = options
if (position) mesh.position = position
mesh.getChildMeshes().forEach(mesh =>
{
if (receive_shadows) mesh.receiveShadows = true
if (shadow_generator) shadow_generator.addShadowCaster(mesh)
})
if (visibility !== undefined) set_mesh_visiblilty(mesh, visibility)
return mesh
}
function set_mesh_visiblilty (mesh: AbstractMesh | Node, visibility: number)
{
if (is_mesh(mesh)) mesh.visibility = visibility
mesh.getChildMeshes().forEach(mesh => mesh.visibility = visibility)
}
Which I am using like:
// create_house.ts
export const mesh_name_low_poly_house_2 = "low_poly_house_2"
export function create_house (scene: Scene, shadow_generator: ShadowGenerator, position: Vector3, name: string)
{
const house = get_mesh(scene, mesh_name_low_poly_house_2, "house_" + name, {
position,
receive_shadows: true,
shadow_generator,
visibility: 1,
})
}
Full code here: Fix importing nested models · AJamesPhillips/3d_sandbox@6266318 · GitHub