Best method for moving camera to target object

Hi,

I’m currently working in a project in babylonjs, where we are trying to make an environment where each element in the scene is interactable. At the moment we are trying to make a camera feature that “zoom” in on a specific element (which has been interacted with) in the scene. Me and my friend has slightly different views on how to approach this problem - he is a skilled programmer, I have a background in film production (and have occasionally worked on projects in Cinema 4D, Blender, After Effects etc.)

In essence, the scene as it stands is using a slightly limited arc camera, where the user can rotate in a a half circle around the origo of the scene and click on objects. Later down the road, I would want to make a dolly camera, much like the one we have now, but instead of dragging around the scene, uses simply the pointer for movement, where the focus point of the camera is always at the center of the screen, but the camera itself moves on an arced rail to create more “feel” to the scene. In any case, requirements like this created the need for the starting position to be very flexible.

I have two suggested solutions.

  1. Making a spline with a fixed amount of positions, animating the camera as on a dolly, with rotation set to target the focus object (the object that the user has interacted with).

This is less ideal as this approach makes travel to an object which is pretty far down the spline quite long. And also, it is unclear how this “rail” would correspond to the starting position, which will likely be outside the scope of the spline. So the other potential solution I am thinking of would be something like:

  1. A given start position, and some fixed end position + target based rotation, where you animate the camera down an arc/spline to this fixed position.

My friend, who has been reading more around the internet on this topic than me at the moment suggests:

  1. Creating three camera instances. One camera on the start position, and one camera on each end position (which is variable and due to what object the user interact with), and then another camera that moves between the starting point and the end point, where the arc is generated relative to the positions of each of the other camera instances.

It is very similar to my latter solution, but to me it seems like a slightly atypical way of doing things, and I think more control can be achieved by simply using splines. Especially if you can import them from Blender and use them as rails for the camera.

So my questions are basically:

How would you solve this?

Is it possible to solve it in any of these ways?

Is it possible to import splines and use them as a dolly?

What is the most optimal way from a BabylonJS point of view to the problem?

Thanks in advance,
Henrik

1 Like

Hi HD, welcome to the BabylonJS forum. This is a tough issue. One thing you have not told us about… is the positions of the models. Can I assume you want complete freedom to move the models (the target points) anytime and anywhere?

Some models may be smaller and/or nearer to scene origin. This means… when camera is “focused-upon” a small mesh near scene origin, and if it is a close-up shot, user won’t be able to SEE all the other mesh, and therefore cannot click on them.

Related… if you want camera animations to NOT “pass thru” not-focused models on the way to its new target, the first step in the animation process is to dolly-out to a wider shot, then start animated left-right trucking the camera (and animating its invisible mesh .target). You want to avoid making the camera go THROUGH another mesh… on the way to its new target. This is rough.

Fixed splines for both cam-path and invisi-mesh cam.target… will prevent easy-moving the models in BJS… because after moving a model/mesh, the spines needs re-calculation. If your mesh never change positions from their Blender-set position, then i’ts possible. But stlll, you might want a NEXT and PREV buttons, and not allow users to select ANY mesh/model. Then you never need to animate “across the group” (where cam could accidentally fly THRU another model… while traveling to new target).

SO… positions of models/mesh… frozen? Are some closer to scene origin than others, and/or smaller/larger than others? (arc cam radius different for each “featured” model?)

WHAT IF… you didn’t fly/animate the camera/target to the clicked mesh. What if you fade to black, fast-move camera and target, and then fade-up from black? After each re-position, standard arc cam operations, except camera.target is set to new mesh, and radius is forced to be perfect for framing the new mesh, too.

You’ll probably want the user to have full model-examination/exploring at each “model station”. This means… at the beginning of an “animate to new mesh”… you won’t know the camera alpha/beta orbit position, nor its current .radius. (user has been moving/zooming cam to examne mesh). Fixed pre-made splines won’t handle this well.

There are some ways to “fake” on-the-fly path-finding for the camera, but it might look odd. First step would be to animate the camera.radius WAY OUT (dolly out far)… then truck-animate the target to the new mesh (which COULD be done WHILE the far dolly-out is happening, so user might not notice.

Then, once new arcCam target is set, start cam-position anim-dollying-IN, WHILE animating the .alpha and .beta to some viewing angle. The models/mesh MIGHT carry with them… info about “where to put the camera to best view me”… so each mesh might have a .bestCamPosition/.bestRadius, .bestAlpha, and .bestBeta properties. These would be used be camera animations… after user clicks on new mesh and invisble mesh .target has been animated/quick-moved to newTarget.position.

In short, back-out cam to real wide shot WHILE moving camera.target to new .position. Then start down-animating cam.radius toward newTarget.bestRadius (dolly in), WHILE truck-animating the cam to newTarget.bestAlpha and newTarget.bestBeta. (radius, alpha, beta set cam position… no need to animate a cam.position IF one exists on arcCams, which i doubt.)

Is essence, this is similar to a crop-dusting plane. At the end of each pass… plane goes wide and high to make a turn and begin targeting a new target on the field. You would sort-of do the same, but camera would always keep looking-down towards “the group”. You would back-out the camera far from the old target, and then fly it to new target. During that “backing-out” or flying-in, the “group” will likely appear to be spinning. (depending upon WHEN… you start animating to newTarget .bestAlpha and .bestBeta). You could start animating those… during the far-distance dolly-out, or wait for the dolly-in animation and do the spins then.

SO much… depends upon getting camera.target… changed from oldTarget, to newTarget. IF you do that move/anim WHILE camera is near to group… it will be obvious to user. If you back-out the camera to a half mile, then do a cam.target = newTarget.position, user won’t notice… even if NOT animated (jump-cut setting, instead).

You will want to wait until AFTER the cam.target is set to newTarget.position… before you start animating cam .alpha, and .beta, or dolly-in animating .radius.

Backing the camera WAY OUT, making adjustments, then animating back-in (.radius animations)… is somewhat similar to fade-to-black/fade-up-from-black. The only problems eliminated by this back-out/fly-in system… is to prevent camera from ever flying THRU a model on-the-way to another, and not knowing where user has put the camera prior to selecting a different target. In this system, you will ALWAYS begin your animations at cam .radius, .alpha, and .beta (the big 3). :slight_smile: This is because the user has FULL arc-cam powers no matter which mesh they are “featured-upon”. The cam’s .target will be set to the chosen target, and that’s the only thing you “force”. The rest… you don’t know until you ask the camera for its curren t.radius, .alpha, and .beta. (where-ever the user placed the camera at time of newTarget click).

The sequence would be…

  • user clicks on newTarget
  • Quickly truck-out cam to 1/4 mile with cam.beta of maybe .75 (altitude), perhaps.
  • Maybe WHILE doing that fairly-swift anim, swiftly animate cam.target to clicked newTarget.position
  • Then begin animating cam alpha, beta, and radius… to NEW positions/orientations. Because cam.target is already set to newTarget.position, camera will remain facing the new target during the in-bound flying.

Just some ideas. Again, it would be nice to know if SOME of your models/mesh are smaller/larger than others (special .radius depending upon which model is selected)… and IF/NOT the mesh will ever be re-positioned. Having each model/mesh carry with it… information about WHERE/HOW to position an arcCam for best viewing starting position (after its .target is set to ME)… would seem handy, in my opinion. Each mesh would be carrying “I’m best-viewed when cam is blah blah blah” info. After a mesh is clicked by user, you can get that info… and use it in your animate-to settings.

Stay tuned for more/wiser ideas, and please tell us about mesh re-positioning possibilities, and IF/NOT some mesh will be lots smaller/larger than others. Fade-to-black-and-back system… and the crop-dusting-like fly-way-out-and-back-in system… are both fairly fool-proof… with little chance of cam accidentally animating THROUGH other mesh to travel-to newTarget. Both systems will take approx 5 seconds to complete “the move”, I suspect.

There MAY BE advanced rayCasting and pathFinding ways… all of which are too advanced for ME to talk about. They would, essentially, establish no-fly-zones, and camera would likely use no fixed animations, and instead… use AI/fuzzy-logic to fly itself to new orientations/locations. Erf. A type of scene mesh analyzer might be employed… with a function that “maps” all the mesh in the scene and gets their boundingBox.extendsWorld values… to establish the no-fly-zones. Ouch. :slight_smile:

Yuh, camera flying has always been a HUGE challenge for 3D scenes… multiple decades now, with little progress toward a magical solution. When mesh carry their own “best viewed-from” info… that removes SOME of the hassles. Sort of like a “best used-by” date on a jar of pickles, eh? :smiley:

I’m pretty sure that “best viewed-from” data was used on the Grand Canyon object… to help park planners nicely position their “scenic overlook” locations. :slight_smile:

Again, there is likely no camera.position to SET… on an arcRotate cam. Not sure. You’ll be animating .alpha, .beta, .radius and .target… to establish cam position/rotation. On free/universal cams… it’s all different again. :slight_smile: I like arcCams best, because of their orbital mesh-examining power/ease. And their .target is the pivot-point of the orbit… which sort-of indicates to the user… that a certain mesh is being featured/focused-upon (selected). You might find that a NEXT TARGET and PREVIOUS TARGET buttons are all you really need… and no anims. The buttons just move camera.target to clickedMesh.position, and then its user’s job to mousewheel-out their radius and pan-around camera until new target is nicely viewable. They animate themselves to the new button-set cam.target. shrug

Semi-decent example: https://www.babylonjs-playground.com/#STX192#16 Ease-animating .target to clicked mesh (of 74)… user handles arc-cam normally after the .target animating.

Less than 30 mesh in scene? Might want to activate a GUI stackPanel of buttons with mesh names on them. Click a button… takes cam to preset place… instantly or flown. Not really on-topic, but it lets users select ANY mesh, whether it is currently in-view or not. shrug

Caution: Wingnut often confuses “truck” and “dolly”. I think dolly is left/right, truck is forward/backward, and ped is up/down. Or maybe reverse that. :confused: All in all, it is LIKELY that I have reversed dolly/truck terms throughout my yammering. :slight_smile:

Stay tuned for more ideas.

4 Likes

@HenkusDigitalis Have a look at this post.

Babylon~cinematics Demos and projects

IMPORTANT Found a great animation syntax. Had to dig for it. Very obscure. Hidden. Not explained on web. It was necessary to “instrument” a web cinematic. It uses “Inbetweening” syntax underneath, (CPU ok) and works well! EXAMPLE: interpolated 4 things simultaneously: x, y, z, and 1 rotation. FROM TO: x: 0 - 1 y: 10 - 0 z: 100 - 1 anything: 3.14 - 0 $({x:0,y:10,z:100, anything: 3.14}) .animate({x:1,y:0,z:1,anything:0}, {queue:false,duration:8000,easing:‘swing’, step: functio…

It was used to great effect to solve camera movements and character movements simultaneously.

I mention it many times, with 0 responses. :crying_cat_face:

Why crying cat, why???

If this works for you, I am happy to walk you through the more advanced version which involves blending of ANMZ with CAMZ.

:eagle:

1 Like

Hi i use RecastJSPlugin for compute path points

let startPoint = YourCamPosition;
let endPoint = YourTargetMeshPosition;
let navigationPlugin = new BABYLON.RecastJSPlugin();
let pathPoints = this.navigationPlugin.computePath(
startPoint, endPoint
);
for (let i = 0; i < pathPoints.length - 1; i++) {
followPathPoints.push(pathPoints[i]);
}
let path3dPoints = path3dCamera.getCurve();
let catmullRom = BABYLON.Curve3.CreateCatmullRomSpline(followPathPoints, 30);
let path3dCamera = new BABYLON.Path3D(catmullRom.getPoints());

let animationCameraPosition = new BABYLON.Animation(
‘animateCameraPositon’,
‘position’,
path3dPoints.length / 3,
BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);

for (var j = 0; j < path3dPoints.length; j++) {
keysCameraPosition.push({
frame: j,
value: path3dPoints[j]
});
}

animationCameraPosition.setKeys(keysCameraPosition);
animateCameraPosition = this.scene.beginDirectAnimation(camHelper, [
animationCameraPosition
], 0, path3dPoints.length, false);

real code is more complicated and move and rotate camera and camera target so you must do many homework but the meaning is as follows:
1)get start point from camera position and end point from target mesh position and compute follow patches for camera and camera target helpers with recastJS
2)after compute follow patches generate catmullRom splines for smooth interpolation of recastJS calculation results and push catmullRom spline points in array
3)animate camera helpers position by splines points positions which you previously saved in the array

PS dont try animate camra directly becaise you lost control/ animate helpers e.g. meshes and attach camera as child to mesh when animate and detach camera from mesh helper after animation is complete
work example results here Showroom but i dont know if we can use recast e.g. on terrains and other curved surfaces

2 Likes

These are all great answers, I feel bad for not replying sooner, but we have been fully into the project and soaking in your answers - especially @Wingnut’s as he was first on the ball :slight_smile: As it stands we are actually trying out the solution with an object that moves to the targeted mesh, and then we’re targeting the camera to it. For our scene this works pretty well, however it is a bit jerky - so if we don’t manage to polish it better we may have to go with another solution.

I think I’ll get back with more details on the project and what we have implemented so far when we have either finished or failed polishing. When we are done we’ll likely post our work somewhere, and “give back” whatever solution we come up with (which will likely be a hybrid of some sort).

Thanks for all great help so far! :blush:

1 Like

Hi guys. HD, I found another playground that does some “eased” arcCam animating.

https://www.babylonjs-playground.com/#3U658N#32

If I remember correctly off the top of my head, this one uses animateCameraTargetToPosition() to move cam target to a location “on the far side” of the selected mesh. Apparently, we found this to be important, and NOT just let cam target stay at selected mesh position. “Far-siding” the cam target might not be necessary for YOUR applications. Moving/animating cam target to selected mesh.position… might be all you need to do. Either way, you have funcs there… to animate both cam.position and cam.target… easily and smoothly. These animators are also rigged with onAnimationEnded funcs, too… lines 173-181.

So, two animations run with each button press… animateCameraTargetToPosition() and animateCameraToPosition()… and a function called forceRebuild is called on the camera… after anims end. That forceRebuild is needed… because we are force-animating camera.position to a new position… which does NOT automatically update cam .alpha, .beta, and .radius properly for the new forced-position. forceRebuild sets those values correctly… PER the new camera position/orientation.

Earlier versions of this PG (#17) put the forceRebuild() function call… inside the renderLoop. This was before I added the onAnimationEnd functions. I THINK the rebuild is only needed after the anims end… but that depends upon IF/NOT user interaction is allowed to cancel an animation before it ended.

Generally speaking, the rebuild is needed BEFORE normal user-driven arcCam operations can be re-started.

shrug. Just another example, and this does not use any invisible mesh, as far as I know. It might be a place to “borrow” some code/ideas. And obviously, camera.position IS a set-able/animatable thing. (I was mistaken on that point, in my previous post.) ArcCam rotations are based-upon target position, of course. Free/Universal cams are likely different. A property named .lockedTarget might be worth investigating. Party on!

2 Likes