How to make clicked object face the camera?

Hi, I was trying to make the object from a model face the camera which is clicked. I’m changing the position of the camera taking the point where it is clicked but it’s showing in an angle not exactly facing the camera. I want to show the object exactly face the camera straight. Here is my code:

gsap.fromTo(
          camera.position,
          { x: camera.position.x, y: camera.position.y, z: camera.position.z },
          {
            duration: 1,
            x: e.point.x ,
            y: e.point.y+10 ,
            z: e.point.z,
            ease: "power2.inOut",
          }
        );

Try it

camera.setViewOffset(10, 10, -2, .5, 9, 9)

mesh.lookAt(camera.position);

None of them are working for me. Still have the same issue

Hi Tanjil,

I am having the same issue as well. Did you find a solution for it yet? I tried: mesh.lookAt(camera.position); but it is not working for me. Let me know if you found a solution.
Thanks
Siamak

Define „not working”

1 Like

Hi mjurczyk

I am trying to prevent some of the objects on the scene to not rotate with the rest of the objects when using trackballcontrol. So i want them to always face the camera. These are 6 squares drawn next to each other. Let me see if i can upload the screenshots for my problem as well.
thanks


Here is the correct initial rotation
incorrect_position
And here is when i have rotated the entire meesh. Note that the text on the screen is always facing the camera. But the 6 squares now have a different rotation and not facing the camera like the original screen before rotation. How can i make sure these 6 objects are always facing the camera regardless of trackball control rotations just like the first screen shot.?

Create a group, add your meshes to the group, add the group to the scene, and rotate the entire group in the animation loop.

group.quaternion.copy(camera.quaternion);

here is a working example

For more advance lookAt check this answer:

1 Like

Thanks so very much @Fennec. Your example solved 90 percent of my problem. Just to give you a bit more background this 6 boxes only show up when the user points the mouse to a circle on the screen. I am using raycasting and from there i display the circle name (chromosome in this case) and get the x,y,z, coordinates from intersects[ 0 ].point dynamically. My goal i s to display this group close to the circle i am hovering my mouse to. And from there i use it to set the position of the group as well. But that code does not work. Depending on the rotation those six boxes display in various places on the scene. So i am wondering is there any thing we can do there as well so the 6 boxes always show close tot he circle we hover on regardless of the trackballcontrol rotation?
Here is the two screenshots explaining that problem:


This screen shows pointing to the pink circle on the right side of the scene above it and it is the original default. This looks ok And six cubes show close enough to the pink circle.


This screen shows the same pink circle now rotated and moved to the left side of the scene. And as you can see the 6 cubes are now on the right side of the screen and very much away from the pink circle on the left. I tried with and without positioning the group but either way i am getting this result.

Alternatively, i tried to set the position to a fix position so it always shows up in the same spot for all the circles, but that also changes all the time. So, I wanted to see what your thoughts are on this issue.

thanks again

You need to get the point index, then set the group position accordingly.

if (intersects.length > 0) {
    const index = intersects[0].index;

    const positionArray = points.geometry.attributes.position.array;

    hoverGroup.visible = true;
    hoverGroup.position.set(
        positionArray[index * 3],
        positionArray[index * 3 + 1],
        positionArray[index * 3 + 2]
    );
} else hoverGroup.visible = false;

Here is a working example, I tried to make it as clear as possible, encapsulating the logic in separated methods, you’ll find the methods after // Logic -----

This is only working because, the points are at the root of the scene, this won’t work if you move the Points object position or if it is nested inside the scene graph, you’ll need to do some matrix multiplications to get the world position.

Thanks very much @Fennec for another great example. Unfortunately I am not using THREE.Points right now. Each circle in my code is a mesh consisting of its own geometry and material. So as a result the fallowing two statements will not work:

1- const index = intersects[0].index; // there is no index attribute in my intersects[0]. here is my intersects[0] object contents:

{
distance: 582.058624947502
face: {a: 537, b: 538, c: 539, normal: Vector3, materialIndex: 0}
faceIndex: 179
normal: Vector3 {x: 0.11245054170328869, y: -0.9186003401462188, z: 0.3469399613231969}
object: Mesh {isObject3D: true, uuid: ‘7d0f4b39-3b17-4fb9-823b-c92188cb285c’,
name: ‘cell1#chr3->D_target_385puncta=156|577|624|1350|1672|1849’,
type: ‘Mesh’,
parent: Group
point: Vector3 {x: 89.64933000396913, y: 65.41902857389003, z: -21.380425991357242}
uv: Vector2 {x: 0.8104542103133973, y: 0.12458234473216608}
}

2- const positionArray = points.geometry.attributes.position.array;

Also because i am not using points i cant use the points geometry.

The only coordinates i get are the XYZ coordinates from intersect[0].point here:
point: Vector3 {x: 89.64933000396913, y: 65.41902857389003, z: -21.380425991357242}

And using these points works fine for me with respect to positioning the tooltip text close to the hovered circle, but as you saw it does not position my six squares properly. I dont want to refactor my code using THREE.Points yet, since it could be a lot of changes, so i wanted to see if there is a non THREE.Points solution as well for this.

Thanks again for all your help @Fennec

This make things even simpler, you just need to get the object from the intersection, and copy its position.

if (intersects.length > 0) {
    const object = intersects[0].object;

    hoverGroup.visible = true;
    hoverGroup.position.copy(object.position);
} else hoverGroup.visible = false;

Here you can find the updated demo

Thanks @Fennec. Just tried your suggestion and i am still not getting the 6 boxes positioned close to the hovered circle. The odd thing is in your example this works fine:
hoverGroup.position.copy(object.position);

Not sure why it does not behave correctly in my code. I just changed your code to use intersect[0].point instead of intersects[0].object.position and that worked fine as well. So that tells me both solutions are correct…
Not sure why in my code neither solution produces the correct coordinates. Basically as i mentioned in my previous posts, the 6 squares dont stay close to the hovered circle. Specially if you rotate everything. Mind you i have integrated threejs in react environment. But that should not effect this issue.

@Fennec: One more thing i wanted to share is this: all my circles are part of a single Group. Would that effect this positioning as well? Do i have to account for that somehow?

Also, if i cant get this working another option I have is to choose a fixed area of the screen and always render the 6 squares in a fixe part of screen. What would be the best approach for that? Should i create a different scene?

Thanks again

p.s.: intersect[0].point works for me for the tooltips to make them close to the hovered circle, but not for ths group of six squares

The only other difference i see between my code and your code in terms of rotation is that i am using TrackballControls and you are using OrbitCotnrol, but i am not sure if that should be an issue in terms of positioning the object correctly. But i thought i put that out there just in case it does.

That’s what I was afraid of (I tried to warn you), the Meshes have two matrices, one local, relative to the parent, and a matrixWorld, absolute or relative to the world origin, if the hoverGroup and to meshes are not at the same level in the scene graph, you’ll need to do some wild matrix computation to get it right.

From my understanding, your points are nested, withing a rotated group. You can add your hover group to the same group with the meshes, it would work right, but if you rotate the group the lookAt won’t work.

I guess I’ve reached my limit, quaternions are my nemesis :sweat_smile:. I suggest you open a new thread, maybe someone could help you with that.

Hi @Fennec I wanted to sincerely thank you for all your great advise and educating me. Not to mention your great code samples. They were extremely helpful. You solved my biggest problem which was the six squares always facing the user regardless of rotation. That was my main issue. The position problem i can find a work around sooner or later. So very much appreciate you taking the time to help me and others on this issue. :pray:
Best Regards

1 Like

Hi @Fennec ,
I did some more debugging and found out this is the reason why the six squares dont display close to the circle we hover on:

            group.quaternion.copy(camera.quaternion);

The dynamic coordinates from the intersects[0].object.position work if we dont use the above statement. But as you know that will cause the 6 squares to not face the camera in different rotations. So do you think there is a way to account for camera.quaternion coordinates and account for the new position may be by applying some math to the intersects[0].object.position to get it to be exactly close to the hovered point?
I am just grasping for any straws here to see if there is some adjustment we can make to the dynamic position we get from intersects[0].object.position to make the squares be adjacent to the point we hover on?

Since you’re not willing to give up, I give it one last shot, and it seems to be working.

This is the relevant code to get the right position:

const vec3 = new THREE.Vector3();
const mat4 = new THREE.Matrix4();
function updateHoverGroupFromPoint(object) {
  hoverGroup.visible = true;

  const matrixWorld = (hoverGroup.parent || hoverGroup).matrixWorld;

  hoverGroup.position.copy(object.getWorldPosition(vec3)).applyMatrix4(
    mat4.copy(matrixWorld).invert()
  );

  updatePlaneText();
}

This should work no matter where you put the hoverGourp or the points group or rotate the latter. I just hope I’m not over complicating things, cause I have a feeling this could be mush simpler (some built-in method, I’m not aware of?).

the topic has a r3f tag, is this react?

because in that case drei has an easy helper GitHub - pmndrs/drei: 🥉 useful helpers for react-three-fiber

<Billboard>
  ...
</Billboard>

in vanilla, there has to be an outer group and an inner group, which holds the contents. matrixAutoUpdate and matrixWorldAutoUpdate should be off on the outer group.

const q = new THREE.Quaternion()
function ticker() {
  ...
  outer.updateMatrix()
  outer.updateWorldMatrix(false, false)
  outer.getWorldQuaternion(q)
  camera
    .getWorldQuaternion(inner.quaternion)
    .premultiply(q.invert())
}
...
const outer = new THREE.Group()
outer.matrixAutoUpdate = outer.matrixWorldAutoUpdate = false
const inner = new THREE.Group()
inner.add(modelThatWillFaceCamera)
scene.add(outer)
2 Likes

Thanks again @Fennec for giving me another try. So far it has not worked. But i will keep playing with it to see where we go and will update you guys.
Also thanks @drcmda for your help. Unfortunately i dont use react three fiber in my project. I use react and directly interface with threejs library. so not sure if you can integrate the two easily. But I iwll look at the example further to see if there is a way.
Regards