Collider made of geometry does not match mesh data(position, scale, ..etc)

I wanted to load the spatial model using the gltfloader, and then create a collision with geometry data.

By the way, I’m trying to get help with a different result than I expected.


here is sample image

You can see that the collider is larger than the visible Mesh data.
Most of the spatial data has the above situation.
The example code used is as follows. (tsx on react)

function Space(props) {
  const url = props.data?.spaceFileName ? `https://space-test.vrin.co.kr/files/${props.data.spaceFileName}` : null;
  const model = useGLTF(url);
  const clone = model.scene;

  const getCollide = (child) => {
    console.log(child);
    
    const geometry = new Geometry().fromBufferGeometry(child.geometry);
    const vertices = geometry.vertices.map((v) => new THREE.Vector3().copy(v));
    const faces = geometry.faces.map((f) => [f.a, f.b, f.c]);
    const normals = geometry.faces.map((f) => new THREE.Vector3().copy(f.normal));
  
    return <Object key={child.uuid} clone={clone} args={[vertices, faces, normals]} model={model} />
  };

  const traverseObject = (child) => child.children.map((c) => {
    if (c.type === 'Object3D' || c.type === 'Group') {
      return traverseObject(c);
    } if (c.type === 'Mesh') {
      return getCollide(c);
    }
  });

  const objects = traverseObject(model.scene)
  return <group dispose={null}>{objects}</group>;
}

Some spatial data generate collisions properly, but some spatial data do not generate collisions properly.


here is sample what i made on blender

As a result of watching the Blender program, it is expected that it does not seem to be able to properly load the Geometry layer at the top.


If you look at the collation in the image above, there is a group called Sketchfeb at the top.
If i erase that group layer,

It can be seen that it is very similar to the shape of the collision body shown in the first picture.

I don’t know if I wrote the recursive function incorrectly or if there is an internal problem with the module.
Does anyone know the solution?

Do you also copy position, scale and rotation? They exist outside the geometry.

Where should I copy and apply the data be applied?

function Space(props) {
  const url = props.data?.spaceFileName ? `https://space-test.vrin.co.kr/files/${props.data.spaceFileName}` : null;

  const model = useGLTF(url);
  console.log(model);
  
  const clone = model.scene;

  const getCollide = (child) => {
    console.log(child);
    
    const geometry = new Geometry().fromBufferGeometry(child.geometry);
    geometry.rotateX(child.rotation._x)
    geometry.rotateY(child.rotation._y)
    geometry.rotateZ(child.rotation._z)
    geometry.translate(child.position.x, child.position.y, child.position.z)
    geometry.scale(child.scale.x, child.scale.y, child.scale.z)

    geometry.applyMatrix4(child.matrix)

    const vertices = geometry.vertices.map((v) => new THREE.Vector3().copy(v));
    const faces = geometry.faces.map((f) => [f.a, f.b, f.c]);
    const normals = geometry.faces.map((f) => new THREE.Vector3().copy(f.normal));
  
    return <Object key={child.uuid} clone={clone} args={[vertices, faces, normals]} />
  };

  const traverseObject = (child) => child.children.map((c) => {
    if (c.type === 'Object3D' || c.type === 'Group') {
      return traverseObject(c);
    } if (c.type === 'Mesh') {
      return getCollide(c);
    }
  });

  const objects = traverseObject(model.scene)
  return <group dispose={null}>{objects}</group>;
}

here is my new code.
Several changes have been made, but they are not the right result yet.

I’m not sure I understand why you need to convert buffer geometry into the old geometry structure. Maybe you use some old library that can operate only with the old structure?!?

If the clone of the geometry goes into a Mesh, then just set the mesh’s position, scale and rotation, instead of modifying the geometry.

If you have to modify the geometry itself, the operations should be in the correct order. First is scaling, then rotation and translation is at the end. Rotations should also be in the correct order (I’m not sure whether it is x → y → z or vice versa).

The problem has not been solved, but thank you for your advice.

By any chance, is there the best way to create a Collision Object by importing a GLTF file?(New geometry structure?)

Or how can I duplicate geometry information that you mentioned?

this is my full code!

import { useEffect } from 'react';
import {  useGLTF } from '@react-three/drei';
import { useConvexPolyhedron } from '@react-three/cannon';
import * as THREE from 'three';
import { Geometry } from 'three-stdlib';


function Space(props) {
  const url = props.data?.spaceFileName ? `https://space-test.vrin.co.kr/files/${props.data.spaceFileName}` : null;

  const model = useGLTF(url);
  console.log(model);
  
  const clone = model.scene;

  const getCollide = (child) => {
    console.log(child);
    
    const geometry = new Geometry().fromBufferGeometry(child.geometry);

    geometry.scale(child.scale.x, child.scale.y, child.scale.z)
    geometry.translate(child.position.x, child.position.y, child.position.z)

    geometry.rotateX(child.rotation._x)
    geometry.rotateY(child.rotation._y)
    geometry.rotateZ(child.rotation._z)

    geometry.applyMatrix4(child.matrix)
    geometry.computeFaceNormals()
    geometry.computeFlatVertexNormals()
    geometry.computeVertexNormals()

    const vertices = geometry.vertices.map((v) => new THREE.Vector3().copy(v));
    const faces = geometry.faces.map((f) => [f.a, f.b, f.c]);
    const normals = geometry.faces.map((f) => new THREE.Vector3().copy(f.normal));
    
  
    return <Object key={child.uuid} clone={clone} args={[vertices, faces, normals]} />
  };

  const traverseObject = (child) => child.children.map((c) => {
    if (c.type === 'Object3D' || c.type === 'Group') {
      return traverseObject(c);
    } if (c.type === 'Mesh') {
      return getCollide(c);
    }
  });

  const objects = traverseObject(model.scene)
  return <group dispose={null}>{objects}</group>;
}

function Object({ clone, args }) {
  const [ref] = useConvexPolyhedron(() => ({
    mass: 100,
    args,
    type: 'Static',
  }));
  
  useEffect(() => {
    clone.traverse((child) => {
      child.castShadow = true;
      child.receiveShadow = true;
    });
  }, []);

  return (
    <mesh ref={ref} castShadow receiveShadow>
      <primitive object={clone} />
    </mesh>
  );
}

export default Space;

useConvexPolyhedron() is cannon js method for react.
i use cannon for make Collision

Would it be possible to have it online and editable, so that it is easier to experiment with?

I still see problems with your code, but without experimenting it might take too long.

I’d suggest the following:

  1. remove scale, translate, rotate..., and applyMatrix.
  2. now the collide model is with wrong position, wrong orientation and wrong scale. As it was in the beginning, right?
  3. add only scale. Does the scale of the collide object change? Is it now the correct scale? (position and rotation should still be wrong).