An attempt on merging instances in a tree structured model

Introduction

So I have this really large model (cant share) which has a tree structure and lots of instance but also multi-materials and since I import from GLTF I the multi-materials end up as separate meshes and I don’t like that.

Idea

  1. I take all meshes
  2. Find the sources that are primitive meshes
  3. For each source I merge the all meshes of its parent (don’t dispose)
  4. For each instance related to the source create a new Instance based on the merged version
  5. I position it accordingly to the last old instance’s position
  6. And finally I dispose of the old primitive meshes

The problem

1 through 4 EASY …6 EASSY
5…
At this point I have no clue how to approach it differently so I will just list all the problems:

  • Instances usually don’t have a parent, except mine do.
  • This parent for some reason has a transform position of (e.g. (1948498432, 91269192, -1519829376))
  • I am using a primitive mesh as a position reference so what I really want is to position the new instance on it’s parent’s place (the new instance is pretty is the primitive’s parent but all of it’s meshes merged)
  • I want the new instance to have the parent of the parent as its parent
  • No matter what I do the new instances always seem to:
    Have the exact same position as the source mesh
    Dissapear (a bounding box of 0)
    Go millions in every direction
    Be at least slightly off
    At least some of them are slightly off
    All of them stacking in a seemingly unrelated place

Here is the code that works

node.getChildMeshes().forEach(mesh => {
      if(mesh instanceof Mesh && (mesh.id.includes("primitive0") || mesh.id.includes("primitive 0"))){
        //merge
        let newMesh = Mesh.MergeMeshes(
          mesh.parent.getChildMeshes(),
          false,     //dispose source
          true,      //32 bits?
          undefined,
          false,
          true
        )
        if(mesh.instances.length > 0){
          //create instances
          mesh.instances.forEach(instance => {
            let newParent = instance.parent.parent;
            let newInstance = newMesh.createInstance(instance.parent.id + "instance");
            //position newInstance where instance is while newParent is the parent of newInstance
          })
        }
        positionMergedMesh(newMesh,mesh.parent)
        mesh.parent.getChildMeshes().forEach(primitive => {
          primitive.dispose();
        })
      }
    })

For the merged mesh positioning I used GPT generated code which I am afraid to touch

function positionMergedMesh(newMesh,source){
      if (newMesh.isWorldMatrixFrozen) {
        newMesh.unfreezeWorldMatrix();
      }
      newMesh.parent=null;
      newMesh.computeWorldMatrix(true);
      newMesh.refreshBoundingInfo();
      //
      newMesh.rotationQuaternion = source.rotationQuaternion ? source.rotationQuaternion.clone() : null;
      newMesh.rotation.copyFrom(source.rotation);
      newMesh.scaling.copyFrom(source.scaling);
      //Step 1: Save world transform
      const worldMatrix = newMesh.getWorldMatrix().clone();

      // Step 2: Set new parent
      newMesh.setParent(source.parent);

      // 3. Decompose world matrix into global transform
      const worldPosition = new Vector3();
      const worldRotation = new Quaternion();
      const worldScaling = new Vector3();
      worldMatrix.decompose(worldScaling, worldRotation, worldPosition);

      // 4. Compute the inverse of the new parent's world matrix
      const parentWorldMatrix = source.parent.getWorldMatrix().clone();
      const invParentWorldMatrix = Matrix.Invert(parentWorldMatrix);

      // 5. Apply the inverse transform to get local transform
      const localMatrix = worldMatrix.multiply(invParentWorldMatrix);
      const localPosition = new Vector3();
      const localRotation = new Quaternion();
      const localScaling = new Vector3();
      localMatrix.decompose(localScaling, localRotation, localPosition);

      // 6. Assign local transform
      newMesh.position = localPosition;
      newMesh.rotationQuaternion = localRotation;
      newMesh.scaling = localScaling;
    }

Why bother?

  • Still uses instances so no performance lost
  • Less meshes less draw calls performance up
  • Less clutter when I want to show the tree structure
  • Less process time when I operate with the meshes