Rotate an ArcRotateCamera around so that the selected mesh is visible and centered?

Here is my playground: https://playground.babylonjs.com/#TZTHV1#5

Right now you can place pins by double clicking on imported STL. Once you have multiple pins, you can ‘single click’ on a red pin to highlight it green.

I want to rotate the camera so that when you highlight a pin, the camera animates to be centered on that pin. For example, if you have a pin on the top of the imported mesh and you click on it, I want the camera to rotate up and be centered over the pin you clicked on. What is the best way to do this?

It seems that if I could just get a new alpha and beta value then I can rotate the camera with that. But I’m not sure how to get the new alpha/beta so that the camera points at the highlighted pin. Maybe this isn’t even the right approach.

Should I be rotating the mesh itself instead of the camera?

It seems complicated to figure out if the selected pin is visible in the camera…

Hi tjshipe,

(Typing this a little preemptively, before I have a complete answer together, because I don’t want to keep you waiting for an answer forever. :slight_smile:)

If I understand correctly, what you’re trying to do is a little tricky in the general case, but I think there might be a reasonably simple way to go about it by, as you mentioned, directly calculating the alpha and beta from the position of the thing you clicked on. Alpha and beta (as I understand it) proscribe angles from different primary axes, meaning you might could do something along the lines of the following:

var clickPosition = pickResult.pickedMesh.position;
var centerToClick = clickPosition.subtract(scene.activeCamera.target).normalize();
var alpha = BABYLON.Vector3.GetAngleBetweenVectors(centerToClick, BABYLON.Vector3.Up(), BABYLON.Vector3.Cross(centerToClick, BABYLON.Vector3.Up()));
var beta = BABYLON.Vector3.GetAngleBetweenVectors(centerToClick, BABYLON.Vector3.Right(), BABYLON.Vector3.Cross(centerToClick, BABYLON.Vector3.Right()));
scene.activeCamera.alpha = alpha;
scene.activeCamera.beta = beta;

All this code does is compute a unit vector pointing from the camera’s target toward the point you clicked on, then attempt to calculate the alpha and beta of a camera looking down that vector. (Note that the calculations above are wrong and produce the incorrect alpha and beta, but I think I’ll just need to play around with the parameters and calculations to get it right.) This approach should work reasonably well – if your object is convex and the camera’s target lies at its center, as in the case of the sphere in the other Playground you referenced.

The problem gets a bit harder with a concave object because the direction from the camera’s target to the clicked point may not be the direction you want to view from. Originally, I thought this was going to necessitate a different approach entirely; however, as I’m writing this, I’m thinking it might be sufficient to negate the object-to-target vector if you’ve clicked on something on the other side of the camera’s target. I’ll fiddle with this a little more and see if I can get something running; but wanted to let you know that this is what I’m currently thinking about this, in case it helps.

Thanks @syntheticmagus I appreciate the response. I look forward to seeing what you come up with. I’ll try what you have above in my playground.

I found an old post by @Deltakosh Arc Rotate Camera Animation (Solved) - Questions & Answers - HTML5 Game Devs Forum that talks about how to do rotation animation. But like you said, the hard part is making sure that the camera is animated to the correct alpha/beta so so that the pin is visible.

I also found this post about checking if a mesh is “in frustum” which seems useful. I’ve never heard of frustum until now though.

The question is then how to rotate the camera until the selected mesh (pin) is “in frustum”…

This post from @Wingnut might also be something I can try:

Okay, I’ve put in an example of something that does the calculation: https://playground.babylonjs.com/#TZTHV1#7. (I didn’t do an animation, it just snaps to the calculated value.) The code I inserted starts on line 43.

The code essentially just calculates out the alpha and beta to match the expectations of the ArcRotateCamera (Cameras - Babylon.js Documentation). It’s a slightly wonky interaction, though, because in several cases a viewpoint calculated just based on clicks doesn’t necessarily provide an actually good view. Again, this is really only a problem with concave meshes, but this mesh has a lot of concavity. I did try the “invert the object-to-target vector” thing (line 47), but I’m undecided on whether that makes the behavior better or worse in the general case.

If you really need something more high-fidelity that will work on arbitrary models (concavity and all), I can’t really think of a way to do it without going quite complicated – something like a raycast burst centered on the head of your pin, then move the camera toward the area where the fewest rays hit something. You could also try to do a screen space logic thing akin to spherical panning logic (Spherical Panning - Babylon.js Documentation), but that will still allow the camera to move such that something in the foreground moves to occlude your pin. For example, if you click on the top of the model’s base, it might move such that it faces down over the pin, but the top of the model actually obscures your pin. Any way you slice it, it’s tricky.

If you don’t mind, try the Playground I linked to (it might be worth trying it both with and without line 47) and see if that’s along the lines of what you’re looking for. Like I said, there’s some funkiness to it, but maybe it’s enough to be a starting point.

Wow, this is really good! I’m going to attempt to get the rotation animation working with it now.

I see a little bit of the wonkiness you’re talking about… if I put a pin on the base of 3d model pointing up, clicking on it moves the camera so it’s looking at the bottom of the model.

I think this is a really good starting point though!! Thanks again. I’ll post updates once I have the animation working.

@syntheticmagus I updated the playground with some animation: https://playground.babylonjs.com/#TZTHV1#9

It moves to the alpha and beta from your calculations. Sometimes the animation takes a really weird path. Sometimes instead of just moving a few degrees around it will take the longest way possible to get there. Not sure how to fix that yet. But it’s a start. Let me know if you or anyone else has any ideas! Thanks again.

Nice! That rotation bug is funny. Looks like, under the hood, camera.alpha is allowed to be arbitrarily large or small numbers; larger than PI and smaller than -PI, for example. The math will never produce such values, which allows you to “wind up” the model for the next animation. In your Playground, try spinning all the way around the model three or four times, then click on a pin; it will spin back the other way to “unwind” the changes you did to alpha.

https://playground.babylonjs.com/#TZTHV1#10 isn’t really a good solution, but it’s a quick hack to get it to behave a little better. To properly do what you’re trying to do, you’ll need to handle the problem of “looping” in alpha: an animation from 3* PI/4 to -3* PI/4 should go through PI, not through 0. You could hack to get around this by abusing the fact that alpha can be larger or smaller than PI; given a target value a for targetAlpha, consider candidate values a, a + 2* PI, and a - 2* PI, and reassign targetAlpha to be whichever of those candidate values is closest to your original alpha (after the modulation hack I’ve put in the Playground above). Haven’t tried that, but I’m pretty sure it would work. :wink: Again, though, it’s a hack. A more long-term solution would be to do your animation by slerping on quaternions, but you’d probably need to change a lot of other things to make that approach fit in your program.