Search code examples
javascriptthree.jscollision-detectionbounding-boxgltf

Three.js : Box3 from an object not in scene


I have a problem adding a Bounding Box from an object in a different module. Right now, it is working fine as long as I write everything in my main function, but as soon as I create my function in a different file, and import in in the main file, it's not working anymore.

The error code I get :

Uncaught TypeError: Cannot read properties of undefined (reading 'updateWorldMatrix')
    at Box3.expandByObject (three.module.js:4934:10)
    at Box3.setFromObject (three.module.js:4852:15)
    at camCollision (camColliders.js:68:37)
    at HTMLDocument.<anonymous> (World.js:355:7)

camColliders.js being the file I'm trying to put the function in, and World.js my main file.

Here is the function :

function camCollision() {
      const camBB = new Box3().setFromObject(camSphereDetector);

      const boule1BB = new Box3().setFromObject(boule1Obj);
      boule1BB.name = 'first';
      const boule2BB = new Box3().setFromObject(boule2Obj);
      boule2BB.name = 'second';
      const boule3BB = new Box3().setFromObject(boule3Obj);
      boule3BB.name = 'third';

      const boulesBB = [boule1BB, boule2BB, boule3BB];

      boulesBB.forEach((bbs) => {
        if (bbs.intersectsBox(camBB)) {
          console.log('got it');
        }
      });
    }

    document.addEventListener('mouseup', () => {
      camCollision();
    });

When I'm doing this in a separate file, i'm first importing the objects from another file and they are all Mesh.

I believe the problem is that I can't create the Bounding Boxes in a separate file, because it needs to be added to the scene first, and I'm only adding them in the scene in World.js. Yet, the error is leading me to line 68, the variable for 'boule1BB', which is weird because 'camBB' should have the problem first ?

Here is how I'm creating the Box3 (these are just copying some GLTF objects position and size, cause I can't manage to get a Box3 from it) :

const boule1Obj = new Mesh(
    new SphereGeometry(2, 32, 16),
    new MeshBasicMaterial({ color: 'red', transparent: true, opacity: 0 }),
  );
  boule1Obj.position.set(10, -3.5, 0);

Then, I would like to know, if I got the problem right : is there a way to create a Box3 in a different js file, from an object that is not added to the scene yet (even if it should when the function is being called) ? With a different way than 'setFromObject' maybe ? Or am I not understanding the real problem.

For a bit of context, the aim is to provide some informations about each model when the user clicks on it, and I'm planning on putting the informations as '.name' for each Mesh corresponding to a model. So I don't want to write all this stuff in the main file, but rather on a separate one.

I hope this is clear enough, and I've given enough content for a solution to be found. I couldn't find anyone else having this problem. If you need anything else, please tell me !

Thanks already for your help !


Solution

  • I believe the problem is that I can't create the Bounding Boxes in a separate file, because it needs to be added to the scene in World.js.

    Not so. Since a constructed THREE.Mesh has a shape with extents (from its geometry) and a transform (by default, translated to the origin, with no scaling or rotation), Three.js can and will determine a bounding box from that information as though the mesh were in the scene. I've posted a demo of this on CodePen.

    Nor should defining the object in one file and referencing it another make any difference, as long as the object is in scope and initialized at the time it's bound to.

    Here, I suspect that you're assigning boule1Obj, boule2Obj, and boule3Obj in World.js. In that case, the imported function is being hoisted before the variables are assigned, and the function is seeingbinding to them as unassignedundefined.

    Try changing camCollision() to accept the bouleXObjs as arguments.

    function camCollision(...objs) {
      const camBB = new Box3().setFromObject(camSphereDetector);
    
      for(let i = 0; i < objs.length; i++) {
        const objBB = new Box3().setFromGeometry(objs[i]);
        objBB.name = `Bounding Box ${i + 1}`;
        if(objBB.intersectsBox(camBB)) {
          console.log("Got it!");
        }
      }
    }
    

    And then call it as

    document.addEventListener("mouseup", () => {
      camCollision(boule1Obj, boule2Obj, boule3Obj);
    });