#How to rotate the camera paralel to a mesh face ?
53 messages · Page 1 of 1 (latest)
const handleButtonClick = async (targetRef) => {
const currentPos = await refs.cameraControlsRef.current.getPosition();
const target = await targetRef.current.position;
const targetRotation = await targetRef.current.rotation;
const geometry = targetRef.current.children[0].children[0].geometry;
const faceNormals = targetRef.current.children[0].children[0].geometry.attributes.normal.array;
const cameraControls = await refs.cameraControlsRef.current;
const camera = await refs.cameraRef.current;
const center = new THREE.Vector3();
geometry.computeBoundingBox();
geometry.boundingBox.getCenter(center);
targetRef.current.localToWorld(center);
const normal = new THREE.Vector3();
faceNormals.forEach((n, i) => {
if (i % 2 !== 1) {
normal.set(n, faceNormals[i + 1], faceNormals[i + 2]);
targetRef.current.children[0].children[0].matrixWorld.extractRotation(
targetRef.current.children[0].children[0].matrixWorld
);
normal.applyMatrix4(targetRef.current.children[0].children[0].matrixWorld);
normal.negate();
return;
}
});
const distance = 15;
const cameraPosition = center.clone().add(normal.clone().multiplyScalar(distance));
gsap.to(currentPos, {
duration: 1,
x: cameraPosition.x,
y: cameraPosition.y,
z: cameraPosition.z,
onUpdate: () => {
cameraControls.setPosition(currentPos.x, currentPos.y, currentPos.z);
cameraControls.setLookAt(
cameraPosition.x,
cameraPosition.y,
cameraPosition.z,
target.x,
target.y,
target.z,
true
);
},
onComplete: () => {
cameraControls.rotate(Math.PI/2, 0, true);
}
});
console.log(targetRotation);
console.log(cameraControls.camera.rotation);
};
when i rotate like this, the camera rotates around the center of the target cuboid mesh, but not paralel to it
gif of what it looks like:
what can i do ?
thx
There are two main changes that need to be made:
Calculating the face normal
Right now, you're iterating over faceNormals and using every second normal as the target normal. If you want to align the camera with a specific face of the mesh, you'll need to identify that face and calculate its normal vector. This might involve identifying the vertices of the face and calculating the cross product of two of its edges.
Aligning the camera with the normal
Once you have the correct normal vector, you need to orient the camera along that vector. Right now, the camera's position is being updated, but its orientation is not.
Note that this code still needs to define the calculateFaceNormal() function, which is responsible for calculating the normal of the face you're interested in. This would typically involve identifying the vertices of that face and calculating the cross product of two of its edges.
thx alot for you help !
but i thought i am already calculating the face normal here :
const normal = new THREE.Vector3();
faceNormals.forEach((n, i) => {
if (i % 2 !== 1) {
normal.set(n, faceNormals[i + 1], faceNormals[i + 2]);
targetRef.current.children[0].children[0].matrixWorld.extractRotation(
targetRef.current.children[0].children[0].matrixWorld
);
normal.applyMatrix4(targetRef.current.children[0].children[0].matrixWorld);
normal.negate();
return;
}
});
am i wrong ?
Yes, you're right that you're attempting to calculate the face normal in that piece of code. But, there are a few issues with your current method.
**Normal Selection **
The faceNormals array contains the normals for all faces in your mesh, not just one. With if (i % 2 !== 1), you're essentially picking the normal of every other face. If you're interested in a specific face, you need a more precise way of identifying its normal. The exact method will depend on how your mesh is structured.
**Matrix Transformation **
You're applying the matrixWorld of the mesh to the normal, but before that, you're doing extractRotation(targetRef.current.children[0].children[0].matrixWorld). extractRotation gets the rotational part of a matrix, but in this case, it's not being assigned to anything, so this line doesn't do anything.
**Return Statement **
You're using a return statement inside your forEach loop. This will exit the current iteration of the loop, but it won't prevent the other normals from being processed. If you want to exit the loop entirely once you've found the right normal, you might want to use a traditional for loop with a break statement.
I have provided a small code which you can try instead. You would have to replace faceIndex with the index of the face you're interested in.
so i guess that if (i % 3 === 0) will choose every face ?
i can see the other mistakes that ive made now. thx so much again !
Yes, your understanding is correct, but make sure you're aware that this is processing each triangle's normal, not each 'face' of a cube if you're thinking of faces as the six sides of a cube. If you're wanting to align the camera with a specific side of a cuboid, you would need to identify which triangles make up that side, and then calculate the average of their normals to get the face normal.
got it ! after then, how to make the camera rotate to the correct place, so it doesnt end up rotated in a wrong diretion ?
In Three.js, you can do this using the lookAt() function, which orients an object so that its forward direction points at a specific location. In the case of the camera, you would use this function to make it look at the center of the face you're interested in.
Here is a small piece of code which I drafted up for you.
i see, but if the camera is looking at a cuboids face so that its paralel to the camera face, how will it rotate to the correct position ? also, i cant rotate camera directly since its being overwritten by a camera controls.
You will need to set the target of the camera controls to the desired position rather than the camera itself.
If the cuboid's face is parallel to the camera's viewing direction, then the camera should already be oriented correctly. However, if you also need the camera's up vector (which defines the camera's rotation around its viewing axis) to align with a specific direction, you may need to do additional calculations.
i see, thank you so much for your help. i have been having so much trouble with this
i want to make it so that the camera aligns itself with a mesh thats rotated in a different way, so its not like in this example here, where the contact page is misrotated:
You need to match the camera's "up" direction with the "up" direction of the target mesh.
is it possible to do that with these controls ? https://www.npmjs.com/package/camera-controls
A camera control for three.js, similar to THREE.OrbitControls yet supports smooth transitions and more features.. Latest version: 2.3.4, last published: a month ago. Start using camera-controls in your project by running npm i camera-controls. There are 57 other projects in the npm registry using camera-controls.
i just tried to use applyCameraUp() method, but now the camera just seems to assume the up of the previous mesh
oh, but what is the up property in the geometry object ?
this
¨do i need to compute the up vector from the objects rotation or is it just the normal of the meshes top side ?
The 'up' property is a THREE.Vector3 that defines the up direction for an object in 3D space. I wasn't aware it was included in the geometry object so in this case you can just grab it from the Geometry object directly.
it must be something else than the up vector, that would help rotate the camera correctly, since the up vector and the camera vector are the same when i console.log them:
OH
when i do
refs.cameraControlsRef.current.camera.up = targetRef.current.up
refs.cameraControlsRef.current.updateCameraUp()
refs.cameraControlsRef.current.applyCameraUp()
the camera really does rotate according to the previous meshes up
and the up does change
ill just have to compute the normal of the top face because the up property of the geometry object is some kind of prank by three js developers
Ahhhh sad. if you need some guidance on where to go to.
const up = new THREE.Vector3(0, 1, 0); // y-axis is the 'up' direction in local space.
targetRef.current.localToWorld(up); // Transform the 'up' direction into world space.
up.sub(targetRef.current.position).normalize(); // Subtract the mesh position to get the correct vector, then normalize.
I am currently busy with an issue of my own, which I will probably have to file an issue to the github for, so I am sorry if I take a bit to respond sometimes.
ill see what i can do. thank you so much for helping me. i hope you find the solution for your issue soon
wow ! your solution actually works !!!
const up = new THREE.Vector3(0, 1, 0);
targetRef.current.localToWorld(up)
up.sub(targetRef.current.position).normalize();
refs.cameraControlsRef.current.camera.up.copy(up);
refs.cameraControlsRef.current.updateCameraUp()
refs.cameraControlsRef.current.applyCameraUp()
i have to click two times on the target for the camera to actually rotate to the desired place, but ill just use a promise somewhere !!!
thank you yet again so much for this. i have been struggling with this for 3 days in a row now. you really made someone happy rn. thank you ! thank you ! thank you !
No worries. Glad you fixed it ^^
i just found out that this solution only works for meshes that are rotated on two axis max
is there anything that might be causing that ?
const positionArray = positionAttribute.array;
const indices = indexAttribute.array;
const vertexNormals = normalAttribute.array;
const center = new THREE.Vector3();
geometry.computeBoundingBox();
geometry.boundingBox.getCenter(center);
targetRef.current.localToWorld(center);
const normal = new THREE.Vector3();
vertexNormals.forEach((n, i) => {
if (i % 2 !== 1) {
normal.set(n, vertexNormals[i+1], vertexNormals[i+2]);
targetRef.current.children[0].children[0].matrixWorld.extractRotation(targetRef.current.children[0].children[0].matrixWorld);
normal.applyMatrix4(targetRef.current.children[0].children[0].matrixWorld);
normal.negate();
return;
}
});
const distance = 15;
const cameraPosition = center.clone().add(normal.clone().multiplyScalar(distance));
gsap.to(currentPos, {
duration: 1.7,
x: cameraPosition.x,
y: cameraPosition.y,
z: cameraPosition.z,
onUpdate: () => {
const up = new THREE.Vector3(0, 1, 0);
targetRef.current.localToWorld(up)
up.sub(targetRef.current.position).normalize();
refs.cameraControlsRef.current.camera.up.copy(up);
refs.cameraControlsRef.current.updateCameraUp()
refs.cameraControlsRef.current.applyCameraUp()
also, the extractRotation() is actually doing something because when i delete it, the code sends the camera far away