Hii,
I have created a box with 4 walls, a bottom and a ceiling.
The code for this box
<Physics gravity={[0, -9.81, 0]}>
<RigidBody type="fixed">
<mesh position={[0, -1, 0]}>
<boxGeometry args={[4, 0.1, 2]} />
<meshPhysicalMaterial visible={false} />
</mesh>
<mesh position={[-2, 0, 0]}>
<boxGeometry args={[0.1, 2, 2]} />
<meshPhysicalMaterial visible={false} />
</mesh>
<mesh position={[2, 0, 0]}>
<boxGeometry args={[0.1, 2, 2]} />
<meshPhysicalMaterial visible={false} />
</mesh>
<mesh position={[0, 0, 1]}>
<boxGeometry args={[4, 2, 0.1]} />
<meshPhysicalMaterial visible={false} />
</mesh>
<mesh position={[0, 0, -1]}>
<boxGeometry args={[4, 2, 0.1]} />
<meshPhysicalMaterial visible={false} />
</mesh>
</RigidBody>
<mesh position={[0, 0, 0]} renderOrder={0}>
<boxGeometry args={[length, width, height]} />
<meshPhysicalMaterial
transparent
opacity={0.2}
roughness={0}
transmission={1}
thickness={0.5}
depthWrite={false}
/>
</mesh>
{items.map((item) =>
<Item key={item.id} path={item.glbRef} item={item} sizes={sizes} onItemClick={handleClick} />
)}
<mesh position={[0, -1, 0]} rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[length, height]} />
<meshStandardMaterial color={color} />
</mesh>
</Physics>
This creates a box with physical walls. Now I have a react component which can load any glb based on the path which is given as the parameter for the Item react component
The purpose of the Item component is that it can be dropped as many times within the walls as needed, the physical aspect is working for the initial dropping of the rocks within the box
Im using transform controls for moving the mesh around but this should within the boundaries of the box. but the boundaries of the box are not respected as it should be. The x axes is the center of the glb the border, so it sticks about 50% out. The Y axes sticks about 10% off but then inside the box
return (
<TransformControls
ref={transform}
onObjectChange={handleObjectChange}
mode="translate"
size={controlSize}>
<RigidBody
type={itemType}
colliders="hull"
restitution={0.2}
friction={1}
ref={rigidBodyRef}>
<primitive object={meshClone}
onClick={handleClick}
onDoubleClick={handleDoubleClick}/>
</RigidBody>
</TransformControls>
);
const handleObjectChange = useCallback(() => {
const object = transform.current?.object;
if (!object) return;
const boundingBox = new THREE.Box3();
boundingBox.setFromObject(object);
const boxSize = boundingBox.getSize(new THREE.Vector3()).length();
const boxCenter = boundingBox.getCenter(new THREE.Vector3());
const aquariumBox = new THREE.Box3(
new THREE.Vector3(sizes.xMin, sizes.yMin, sizes.zMin),
new THREE.Vector3(sizes.xMax, sizes.yMax, sizes.zMax)
);
if (!aquariumBox.containsBox(boundingBox)) {
object.position.x = Math.max(sizes.xMin, Math.min(sizes.xMax, boxCenter.x));
object.position.y = Math.max(sizes.yMin, Math.min(sizes.yMax, boxCenter.y));
object.position.z = Math.max(sizes.zMin, Math.min(sizes.zMax, object.position.z));
// object.userData.lastValidPosition = object.position.clone();
}
// Store last valid position
}, []);
My boundaries are calculated as follows
export function calculateBoundaries(box: Box) {
const halfX = box.length / 2;
const halfY = box.height / 2;
const halfZ = box.width / 2;
return {
xMin: -halfX + 0.05,
xMax: halfX - 0.05,
yMin: 0,
yMax: box.height,
zMin: -halfZ + 0.05,
zMax: halfZ - 0.05,
};
}
My initial thought was the calculation from containsBox is done from the center of the bounding but im not sure how I should calculate from the border of the glb.
2 questions:
- How do I set my boundaries on a item correct so the item stops exactly at the border of the walls (or bottom or ceiling) and not falling through partially
- If I want to detect collisions, is it just as easy as setting the positions in for example a registry for keeping track and for every movement, you calculate the new positions via vector3 and uses intersects() for the collision?
static checkCollision(
currentId: string,
currentObject: THREE.Object3D,
targetPosition: THREE.Vector3,
scale: number,
allItems: AquariumItem[]
): boolean {
const currentSize = this.getSize(currentObject, currentId).clone().multiplyScalar(scale);
const halfSize = currentSize.clone().multiplyScalar(0.5);
const movingMin = targetPosition.clone().sub(halfSize);
const movingMax = targetPosition.clone().add(halfSize);
const movingBox = new THREE.Box3(movingMin, movingMax);
return allItems.some(item => {
if (item.id === currentId) return false;
const otherObject = PositionRegistry.get(item.id);
if (!otherObject) return false; // Skip if not yet registered
const otherPosition = otherObject.position.clone();
const otherScale = item.scale ?? 1;
// Fix here: get size from otherObject, not currentObject
const otherSize = this.getSize(otherObject, item.glbRef).clone().multiplyScalar(otherScale);
const otherHalfSize = otherSize.clone().multiplyScalar(0.5);
const otherMin = otherPosition.clone().sub(otherHalfSize);
const otherMax = otherPosition.clone().add(otherHalfSize);
const otherBox = new THREE.Box3(otherMin, otherMax);
return movingBox.intersectsBox(otherBox);
});
}
static getBoundingBox(object: THREE.Object3D): THREE.Box3 {
object.updateWorldMatrix(true, true);
return new THREE.Box3().setFromObject(object, true);
}
static getSize(object: THREE.Object3D, id: string): THREE.Vector3 {
const size = new THREE.Vector3();
this.getBoundingBox(object).getSize(size);
this.sizeCache.set(id, size.clone());
return size;
}