skip to Main Content

I am currently developing an online browser 3d Game.
The whole collision detection should be calculated both on the server and the client to obviate hackers.

As the physics engine I decided to use rapier as it seems to be the most up to date one.
Now I need to load my game scene which I exported from blender as a .gltf file.
To display it on the client side I just use the threejs gltf loader.
However, on the server side I do not use threejs. Besides that I have to generate bounding boxes of all objects inside the scene to use it in rapier after loading the gltf file somehow.

How do I load the gltf file without threejs? How do I generate the bounding boxes? Should I maybe include the bounding boxes inside the gltf scene? Rapier does not seem to support it out of the box. I am not tied to rapier, but I would prefer it.

2

Answers


  1. Loading bounding boxes from glTF without the help of any preexisting library or loader will be a bit of a programming challenge, but it can be done. You’ll use the browser’s JSON parser of course, and a vector/matrix math helper library of some kind will come in quite handy.

    Here’s a general outline:

    1. Parse the JSON portion of the glTF into an object we’ll call glTF.
    2. Read glTF.scene to get the default scene index, typically 0.
    3. Get the root node list from glTF.scenes[sceneIndex].nodes, which is an array of indices into glTF.nodes[].
    4. At each node from glTF.nodes[n], consider the transformations either in matrix or in translation, rotation, and scale. These will transform the node’s bounding box and its children’s.
    5. Recurse down the node’s children if it exists, it will be an array of indices of child nodes with their own transforms.
    6. For all nodes found in steps 4 and 5, look for node.mesh which will be an index into the array of glTF.meshes.
    7. For each mesh in step 6, look for an array of objects mesh.primitives. Unlike other arrays in glTF, the primitives are embedded directly in the meshes, not indexed separately.
    8. For each primitive from step 7, look for primitive.attributes.POSITION which will be an index into an array of glTF.accessors[].
    9. Position accessors in glTF are required by spec to have two arrays accessor.min[] and accessor.max[] which both have 3 elements for X, Y, and Z. This is your bounding box for the raw, un-transformed data in that mesh.

    So now you’ve recursed down a whole structure, and found some bounding boxes out at the leaf nodes. These boxes enclose raw mesh data, and must be transformed by their parent nodes, grandparent nodes, all ancestor nodes’ transformations to understand where these boxes are in model space.

    Of course, if each mesh represents a separate physics object, then perhaps you don’t want all the ancestor transformations. You could take the box as-is for raw mesh data, and take it’s immediate parent node’s transformation as the starting location for physics. You’ll have to work out the details of what you want to happen here.

    Note that things like animation, skinning, and morphing will add extra complexity into the bounding box calculations, but that’s out of scope for this answer here. Good luck!

    Login or Signup to reply.
  2. To load a glTF file and extract bounding boxes from nodes without three.js, one option would be to use glTF Transform to parse the file. This would be an implementation of the steps emackey describes in his answer:

    import { NodeIO, getBounds } from '@gltf-transform/core';
    import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
    
    const io = new NodeIO().registerExtensions(ALL_EXTENSIONS);
    const document = await io.read('path/to/scene.glb');
    
    const scene = document.getRoot().listScenes()[0];
    
    scene.traverse((node) => {
      if (!node.getMesh()) return;
    
      const worldBounds = getBounds(node);
      const localBounds = getLocalBounds(node);
    });
    
    function getLocalBounds(node) {
      const min = [Infinity, Infinity, Infinity];
      const max = [-Infinity, -Infinity, -Infinity];
      for (const prim of node.getMesh().listPrimitives()) {
        const position = prim.getAttribute('POSITION');
        const primMin = position.getMinNormalized([]);
        const primMax = position.getMaxNormalized([]);
        for (let i = 0; i < 3; i++) {
          min[i] = Math.min(primMin[i], min[i]);
          max[i] = Math.max(primMax[i], max[i]);
        }
      }
      return {min, max};
    }
    

    To do the same thing in the browser, replace NodeIO with WebIO above, or you could use three.js own methods to compute each bounding box. The results should be the same.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search