Tracked Entity + camera heading

Hello,

I create an entity which position is a SampledPositionProperty from polyline.

What I want to achieve is to follow this entity from behind so the camera’s heading need to be updated dynamically during clock tick.

I try 2 methods :
1/ use viewer.trackedEntity but as far as I know viewFrom cannot be changed during tracking
2/ use viewer.zoomTo(trackedEntity, COMPUTED_HEADINGPITCHRANGE) on clock.onTick, but it seems to not working as zoomTo is async and even if my computed heading is correct, the camera doesnt follow the desired heading (onTick is too fast and if I slow tick step, my animation will not be very nice :slight_smile: )

Thank u

Perhaps you can put an example up on Cesium sandcastle to try? Here’s a recent related thread
Orientation problem at the end path VelocityOrientationProperty

1 Like

Thank U for your quick answer @Hyper_Sonic
Here is the sandcastle :slight_smile:https://sandcastle.cesium.com/#c=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

As you can see,I try to use viewer.zoomTo with a computed heading inside clock.onTick; the computed heading is correct, but the camera is not behind the pointEntity as desired.

Thanks again,

I’ve tested my getHeading function using positions of known headings, and it works fine.

I’ve attempted another approach just for testing purposes. I’ve commented out the addeventlistener, and added a button. I’ve also made positionProperty a global variable so the button could access it (removed it’s declaration in the function as well to make sure the global version is populated.)

Sandcastle.addToolbarButton("Position/Orient camera", function () {
var secs = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);
var slightlyBefore = Cesium.JulianDate.addSeconds(animationStart, secs-0.2, new Cesium.JulianDate());
var now = Cesium.JulianDate.addSeconds(animationStart, secs, new Cesium.JulianDate());

var prevPos=new Cesium.Cartesian3();var currPos=new Cesium.Cartesian3();
prevPos=positionProperty.getValue(slightlyBefore);
currPos=positionProperty.getValue(now);

if(currPos==undefined||prevPos==undefined){console.log("a position is missing");return;}
  
//vectors have to have magnitude, so these can't be equal  
if(currPos.x===prevPos.x && currPos.y===prevPos.y && currPos.z===prevPos.z)
{console.log("equal, can't get heading");return;}

//make a rot mat
var CC3=Cesium.Cartesian3;
var mydir = new CC3();
mydir = CC3.subtract(currPos,prevPos,new CC3());
CC3.normalize(mydir,mydir);
var GC_UP = new CC3();
CC3.normalize(viewer.scene.camera.position,GC_UP); //GC_UP
var myrig = new CC3();
var myup = new CC3();
myrig = CC3.cross(mydir,GC_UP,new CC3());
myup = CC3.cross(myrig,mydir,new CC3()); 
  
//raise camera up 333 meters, put back 333 meters
CC3.multiplyByScalar(GC_UP, 333, GC_UP);
CC3.add(GC_UP,currPos,currPos);
CC3.subtract(currPos,CC3.multiplyByScalar(mydir, 333,new CC3()),currPos);
viewer.scene.camera.position=currPos;
  
mydir=rotateVector(mydir,myrig,-45*Math.PI/180);
myup=rotateVector(myup,myrig,-45*Math.PI/180);

//orient camera using rot mat
viewer.scene.camera.direction=mydir;
viewer.scene.camera.right=myrig;
viewer.scene.camera.up=myup;
});

Each time you press the button it’ll put the camera above and behind the dot, and face in the direction the dot is traveling in.

Thank U @Hyper_Sonic.
Your code works indeed.
But when I try to put it inside onTick, the camera seems to be upside down, I dont know how to change mydir to fix this.

sorry, it is working @Hyper_Sonic ! :smiley:
I just have to increase the up value .

Thanks again

Good to hear! I’ve edited the code slightly, adding in a check for undefined values.

You could also have the camera pitch down as well, rotating mydir and myup before setting the rot mat to the camera.

Might want to check out some smoothing options (Hermite Polynomial seems to work well)
sandcastle.cesium.com/?src=Interpolation.html&label=All
It’ll handle alot of what you’ve coded for. Can get orientation real time as you’re traveling along the track.

There’s a caveat with interpolation: If there’s a large gap between 2 points and you want to travel between the 2 points in a straight line, but you enter and exit this line at a sharp degree, it’ll travel along a far out curve. I’ve witnessed this trying out the non-linear interpolations. A solution would be providing some more points between large gaps.

I’ve edited the code again (check above post) to add a pitch down. It makes use of a rotate function:

function rotateVector(rotatee,rotater,angleRad)
{
var CC3 = Cesium.Cartesian3;var rotated=new CC3();
var dotS = CC3.dot(rotatee,rotater);
var base = CC3.multiplyByScalar(rotater,dotS,new CC3());
var vpa = CC3.subtract(rotatee,base,new CC3());
var cx = CC3.multiplyByScalar(vpa,Math.cos(angleRad),new CC3());
var vppa = CC3.cross(rotater,vpa,new CC3());
var cy = CC3.multiplyByScalar(vppa,Math.sin(angleRad),new CC3());
var temp = CC3.add(base,cx,new CC3());
var rotated = CC3.add(temp,cy,new CC3());
return rotated;
}

Oh ya, if you want to see what I mean by curving around large gap, put this at the end of the addpointentity function:

        pointEntity.position.setInterpolationOptions({
          interpolationDegree: 2,
          interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
        });
1 Like

Thank U @Hyper_Sonic, pitching down was my next question, you read in my mind :slight_smile:

For rotateVector, I guess angleRad should set accordingly to camera position (the “333” value in your example : if I up to x meters and also back to x meters, angleRad sould be -45 degree to point camera to the tracked entity).

Also thank U with your explanation about interpolation, I knew it was about rounding trajectory, it is more clear now.
My path example is a pretty classic path, but some paths are very hard : some angles are very sharp so the camera make a quick 180° turn some times and it is not very nice, I guess this cannot be done by setting some cesium constant but I have to code it (by analyzing the entire path and tell the camera to not change the heading when angles are too sharp)

Thanks
Andry

You’re welcome! I thought it might be helpful to anticipate a next question, though you did have this way in the original code so I thought I’d try to duplicate it. Ya with both the up & back legs being equal it’s clear that 45deg would be the angle to pitch, but not so clear for unequal leg values. You can replace the camera offset code with this so you don’t even have to calculate the angle for the 2 legs:

//look down angleDeg, then move back by moveBackMeters
var mydir2 = CC3.clone(mydir);
mydir2 = rotateVector(mydir2 , myrig, angleDeg*Math.PI/180);
CC3.negate(mydir2,mydir2);
CC3.multiplyByScalar(mydir2, moveBackMeters, mydir2);
CC3.add(mydir2,currPos,viewer.scene.camera.position);

Perhaps on tight turns the speed could be slowed by increasing the time between points. I noticed in your original code you have something called virgule to smooth out the turns. Something like that could still be implemented. Something like:

_maintain a mydirPrev variable
_compare heading difference between mydirPrev and mydir
_rotate mydir&myrig around myup so that mydir turns toward mydirPrev
_then offset&pitch, then apply myrig,mydir,myup to camera

How much to rotate, I’m not sure. Perhaps start with 50% the heading difference each frame and work from there, then when the difference is say less than 5 degrees don’t rotate at all.

Another thing you might try is just change the tic rate of the simulation clock on tight turns. Turn tightness being the ratio of heading change / sim time.

Hello @Hyper_Sonic,

I finally make it works with a smooth camera heading variation by changing “slightlyBefore” value : instead of 0.2 before, I put 1 sec before and the camera changing direction is smoother.
And I set prevPos with arrayOfPositions[0] when it is still undefined.

Thank U so much,
Andry

1 Like

That seems like a good idea. The camera doesn’t have to follow every little turn, just have the point be roughly in the center of the screen. It’s far enough back to see all the turns though the point makes anyways. The built-in interpolation of the sample function is convenient, allowing one to get the time for any point along the path. I suppose you could start the path +1 secs so slightlyBefore is still defined. Glad to see that it’s working well!