Align models using normals

You can see for example that when I call the function it get align but only the half of the base (sphere) and my rocks are random positioned. So far I got

  alignToSurfaceNormal() {
        this.group.children.forEach((child) => {
            const position = child.position.clone();

            // Reset the quaternion to avoid cumulative transformations
            child.quaternion.identity();

            // Dynamically adjust the ray's origin to ensure it starts far enough above the geometry
            const boundingBox = new THREE.Box3().setFromObject(new THREE.Mesh(this.base));
            const rayOrigin = position.clone().add(new THREE.Vector3(0, boundingBox.max.y - boundingBox.min.y + 1, 0));

            // Cast a single downward ray
            const raycaster = new THREE.Raycaster();
            raycaster.set(rayOrigin, new THREE.Vector3(0, -1, 0));

            const intersects = raycaster.intersectObject(new THREE.Mesh(this.base), true);

            if (intersects.length > 0) {
                const closestIntersection = intersects[0];
                let normal = closestIntersection.face?.normal.clone();

                if (normal) {
                    // Transform the normal to world space
                    const matrix = closestIntersection.object.matrixWorld;
                    normal.applyMatrix3(new THREE.Matrix3().getNormalMatrix(matrix)).normalize();

                    // Skip alignment if normal.y > 0.5
                    if (normal.y > 0.5) {
                        //Check why normal act like this
                        return;
                    }
                    // Align the object's up direction to the normal
                    const quaternion = new THREE.Quaternion();
                    quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), normal);

                    child.quaternion.premultiply(quaternion);
                }
            }
        });
    }

I personally use a virtual triangle after obtaining the face/points I want.
it’s a (very) primitive aproach. Shouldn’t require world conversion, very consistent. Then using lookAt instead of quaternion operation.

	//init a virtual triangle
	const tri = new THREE.Triangle(),
	//init points
	const a = new THREE.Vector3(), 
    const b = new THREE.Vector3(), 
    const c = new THREE.Vector3(),
	//vector to store normal
	const nRot = new THREE.Vector3();

	//copy position of vertices 
    a.fromBufferAttribute( positionAttribute, indexA );
    b.fromBufferAttribute( positionAttribute, indexB );
	c.fromBufferAttribute( positionAttribute, indexC );

	tri.set( a, b, c ); //create the triangle
    tri.getNormal( nRot );//get the normal

    myMesh.lookAt(nRot);

This is a multipurpose method. Doesn’t require raycast at all, if you know the vertices index.

4 Likes