Question of SceneMode change and Camera position

1211 views
Skip to first unread message

sask

unread,
Mar 26, 2015, 4:41:39 AM3/26/15
to cesiu...@googlegroups.com
Hi all,

How can I keep the same position and attitude of camera (maybe just position in Columbus view) when I change the SceneMode? Does it have any examples? Thanks a lot~

Hyper Sonic

unread,
Mar 26, 2015, 9:29:15 AM3/26/15
to cesiu...@googlegroups.com
There should be an option to retain camera settings, at the very least longitude,latitude,altitude, and heading. Altitude would be viewing rectangle size in 2D I suppose. Pitch and roll would be lost in 2D mode, but those should transfer between 3D and Columbus. However what you're looking at with the same camera settings between 3D and Columbus can be quite different as the shape of the Earth is quite different. Maybe morphing has pre and post execute function calls?

sask

unread,
Mar 26, 2015, 11:18:14 PM3/26/15
to cesiu...@googlegroups.com
Yes, I got it wrong before. Position and attitude should be transferred between 3D and Columbus mode, and just position in 2D mode. I have just tried morphStart and morphComplete function:

scene.morphStart.addEventListener(function (){
    console.log('Morph start...');
   
    cameraPosition = viewer.camera.position;
    cameraHeading = viewer.camera.heading;
    cameraPitch = viewer.camera.pitch;
    cameraRoll = viewer.camera.roll;

    console.log('Camera data stored.');
});

scene.morphComplete.addEventListener(function (){
    console.log('Morph completed...');
   
    viewer.camera.flyTo({
        destination : cameraPosition,
        orientation : {
            heading : cameraHeading,
            pitch : cameraPitch,
            roll : cameraRoll
        }
    });
   
    console.log('Camera data setting finish.');
});

I can get the camera position and attitude successfully. But when I change the scene from 3D to Columbus mode, the camera is not moved and still in the default position. I have tried flyTo and setView function and the result is failed.

Another problem is the camera position is quite different between 3D to Columbus mode (as your describe). Can these problems be solved? thanks a lot~

Hyper Sonic於 2015年3月26日星期四 UTC+8下午9時29分15秒寫道:

Hyper Sonic

unread,
Mar 27, 2015, 1:00:01 AM3/27/15
to cesiu...@googlegroups.com
Well I got 3D to Columbus working, it can be modified to work both ways but I didn't have time to finish it, I'll finish it up later. I didn't even know morphing had event listeners, thanks for pointing that out! Also there may be cases where you're looking off into Space, in those cases the heading,pitch,roll should transfer instead.
var viewer = new Cesium.Viewer('cesiumContainer');
var scene=viewer.scene;var camera=viewer.camera;
var CC3=Cesium.Cartesian3;

var cameraRoll;
var myray = new Cesium.Ray();
var camCarto = new Cesium.Cartographic();
var desCarte = new CC3();
var desCarto = new Cesium.Cartographic();
var from3D;

scene.morphStart.addEventListener(function (){
    
    if (camera._mode === Cesium.SceneMode.SCENE3D)
    {
        camCarto=Cesium.Ellipsoid.WGS84.cartesianToCartographic(camera.position);
        myray.origin=viewer.camera.position;
        myray.direction=viewer.camera.direction;
        desCarte=scene.globe.pick(myray, scene);
        desCarto=Cesium.Ellipsoid.WGS84.cartesianToCartographic(desCarte);
        cameraRoll = camera.roll;
        from3D=true;
    }
    else{from3D=false;}
});

scene.morphComplete.addEventListener(function (){

    if(from3D==true)
    {
        camera.position.x = camCarto.longitude * 6378137;
        var sinLatitude = Math.sin(camCarto.latitude);
        camera.position.y = 0.5 * Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)) * 6378137;
        camera.position.z = camCarto.height; 

        desCarte.x = desCarto.longitude * 6378137;
        sinLatitude = Math.sin(desCarto.latitude);
        desCarte.y = 0.5 * Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)) * 6378137;
        desCarte.z = desCarto.height;        

        var temp=new CC3();
        CC3.subtract(desCarte,camera.position,temp);
        CC3.normalize(temp,camera.direction);
        CC3.cross(camera.direction,{x:0,y:0,z:1},camera.right);
        CC3.cross(camera.right,camera.direction,camera.up);
        camera.look(camera.direction,cameraRoll);
    }
});


Hyper Sonic

unread,
Mar 27, 2015, 1:13:57 AM3/27/15
to cesiu...@googlegroups.com
While it works well near the Equator, near the poles it gets a bit messed up. I got the conversion equations from WebMercatorProjection.prototype.project so I'm not quite sure what's the problem.

Hyper Sonic

unread,
Mar 27, 2015, 4:05:51 PM3/27/15
to cesiu...@googlegroups.com
Before an accurate conversion can take place (near poles also, not just near the equator) I need to figure out the exact geodetic latitude to rectangle y conversion. I made a sandcastle with various methods, method C seems to yield the closest to observed values. Method A is from WebMercatorProjection.prototype.project which uses Math.log, which I believe is base2. Is it supposed to be base2?

var viewer = new Cesium.Viewer('cesiumContainer');
function methodA(lat)
{
var sinLatitude = Math.sin(lat);
return (0.5 * Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)) * 6378137);
}
function methodB(lat)
{
var neg=1;if(lat<0){neg=-1;}lat=Math.abs(lat);
var c = new Cesium.Cartographic(0,lat,0);
var p = Cesium.Ellipsoid.WGS84.cartographicToCartesian(c);
var ratio = p.z/6356752.3142451793;
var halfheight = Math.PI * 6378137 / 2; //width/2/2;
//var halfheight = 10000000; //by definition 1 meter = 1/10000000 equator to pole along surface
return ratio * halfheight * neg;
}
function methodC(lat)
{
var neg=1;if(lat<0){neg=-1;}lat=Math.abs(lat);
var ratio = lat/(Math.PI/2);
var halfheight = Math.PI * 6378137 / 2; //width/2/2;
//var halfheight = 10000000; //by definition 1 meter = 1/10000000 equator to pole along surface
return ratio * halfheight * neg;
}

//the eye in london
//CV.y = 5733315 meters
//3D.lat = 0.8988992804 radians
console.log("eye observed 5733315 meters");
console.log("eye A ",methodA(0.8988992804));
console.log("eye B ",methodB(0.8988992804));
console.log("eye C ",methodC(0.8988992804));

//tip of south america
//CV.y = -6084331 meters
//3D.lat = -0.953949305 radians
console.log("tip SA observed -6084331 meters");
console.log("tip SA A ",methodA(-0.953949305));
console.log("tip SA B ",methodB(-0.953949305));
console.log("tip SA C ",methodC(-0.953949305));

//tip of antartica
//CV.y = -7049011 meters
//3D.lat = -1.104682618 radians
console.log("tip AN observed -7049011 meters");
console.log("tip AN A ",methodA(-1.104682618));
console.log("tip AN B ",methodB(-1.104682618));
console.log("tip AN C ",methodC(-1.104682618));

//little diomede (bering straight)
//CV.y = 7319301 meters
//3D.lat = 1.1475459941 radians
console.log("dio observed 7319301 meters");
console.log("dio A ",methodA(1.1475459941));
console.log("dio B ",methodB(1.1475459941));
console.log("dio C ",methodC(1.1475459941));


Hyper Sonic

unread,
Mar 28, 2015, 10:59:10 AM3/28/15
to cesiu...@googlegroups.com
I made a Sandcastle app that retains the vantage point when switching from any and to any scene mode. For latitude conversion I settled with method C as it seems to match the imagery the best. I have to use setTimeout in scene.morphComplete.addEventListener because camera._mode is still 0 (morphing.) There may be another way to know what it's morphing to, but I haven't looked into it yet. Sometimes the vantage point is turned upside down in the southern hemisphere, I'm not sure yet what's causing that.

Showstopper: sometimes a big wave comes in and crashes Sandcastle after you attempt to switch modes (the icon on the top stops shifting partway.) You can't do anything except close that tab and start again. I have no idea what causes this, it seems to occur randomly, but perhaps someone can figure out what set of circumstances causes this to occur.

var viewer = new Cesium.Viewer('cesiumContainer');

//abreviations
var scene=viewer.scene;var camera=viewer.camera;
var CC3=Cesium.Cartesian3;

//scratch vars
var desCarte = new CC3();

//vars passed from start to end of morph
var camCarto = new Cesium.Cartographic();
var desCarto = new Cesium.Cartographic();
var range;

//latitude conversion functions
function latToY(lat)
{
var neg=1;if(lat<0){neg=-1;}lat=Math.abs(lat);
var halfheight = Math.PI * 6378137 / 2; //width/2/2;
var ratio = lat/(Math.PI/2);
return ratio * halfheight * neg;
}
function yToLat(y)
{
    var neg=1;if(y<0){neg=-1;}y=Math.abs(y);
    var halfheight = Math.PI * 6378137 / 2; //width/2/2;
    var ratio = y / halfheight;
    return ratio * (Math.PI/2) * neg;
}
scene.morphStart.addEventListener(function (){
    
    if (camera._mode === Cesium.SceneMode.SCENE3D)//3
    {
        //get camCarto
        camCarto=Cesium.Ellipsoid.WGS84.cartesianToCartographic(camera.position);
        
        //get desCarto
        var myray = new Cesium.Ray();
        myray.origin=viewer.camera.position;
        myray.direction=viewer.camera.direction;
        desCarte=scene.globe.pick(myray, scene);
        desCarto=Cesium.Ellipsoid.WGS84.cartesianToCartographic(desCarte);
    }
    if ((camera._mode == 1)||(camera._mode == 2))//CV or 2D
    {
        //get camCarto
        camCarto.longitude=camera.position.x / 6378137;
        camCarto.latitude=yToLat(camera.position.y);
        camCarto.height=camera.position.z;
                
        //get desCarto
        if((camera._mode == 1)||(camera._mode == 2));//CV or 2D
        {
            var steps=Math.abs(camera.position.z/camera.direction.z);
            var vec=new CC3();
            CC3.multiplyByScalar(camera.direction,steps,vec);
            CC3.add(camera.position,vec,desCarte);
        }
        desCarto.longitude=desCarte.x / 6378137;
        desCarto.latitude=yToLat(desCarte.y);
        desCarto.height=desCarte.z; 
    }
    if((camera._mode == 1)||(camera._mode == 3))//CV or 3D
    {
        //get range
        var rangeVec=new CC3();
        CC3.subtract(desCarte,camera.position,rangeVec);
        range=CC3.magnitude(rangeVec);
    }
    if(camera._mode == 2)//2D
    {
        //get range (assumes 3D fov 60)
        var cf = viewer.scene.camera.frustum;
        var halfscreen = cf.right * 2;
        range=halfscreen/Math.tan(60/180*Math.PI);    
    }  
});

scene.morphComplete.addEventListener(function (){

setTimeout(function() //timeout because camera mode is 0 (still morphing) when function is called
{  
if (camera._mode === Cesium.SceneMode.SCENE3D)//3
{
//get camera position and destination position
camera.position=Cesium.Ellipsoid.WGS84.cartographicToCartesian(camCarto);
desCarte=Cesium.Ellipsoid.WGS84.cartographicToCartesian(desCarto);

//face destination
var temp=new CC3();
CC3.subtract(desCarte,camera.position,temp);
CC3.normalize(temp,camera.direction);
CC3.normalize(camera.position,temp);
CC3.cross(camera.direction,temp,camera.right);
CC3.cross(camera.right,camera.direction,camera.up);
}
    if ((camera._mode == 1)||(camera._mode == 2))//CV or 2D
    {
//get camera position and destination position
        camera.position.x = camCarto.longitude * 6378137;
        camera.position.y = latToY(camCarto.latitude);
        camera.position.z = camCarto.height; 
        desCarte.x = desCarto.longitude * 6378137;
        desCarte.y = latToY(desCarto.latitude);
        desCarte.z = desCarto.height;        

//face destination
        var temp=new CC3();
        CC3.subtract(desCarte,camera.position,temp);
        CC3.normalize(temp,camera.direction);
        CC3.cross(camera.direction,{x:0,y:0,z:1},camera.right);
        CC3.cross(camera.right,camera.direction,camera.up);
        
        if(camera._mode == 2)//2D only
        {
            //assumes 3D was fov 60
            var cf = viewer.scene.camera.frustum;
            var halfscreen=range*Math.tan(60/180*Math.PI);
            var ratio = cf.top / cf.right;
            cf.right = halfscreen/2;
            cf.left = -(halfscreen/2);
            cf.top = cf.right * ratio;
            cf.bottom = -cf.top;
        }
    }
}, 2000); //end setTimeout function
});




Hyper Sonic

unread,
Mar 28, 2015, 11:29:41 AM3/28/15
to cesiu...@googlegroups.com
I believe the crash has to do with the fact that in 2D mode camera.position.z is auto set at some constant. 2D mode doesn't actually give the user z control, rather frustum wall control which yields an illusion of range control for the orthographic mode. I'll check this out later.

sask

unread,
Mar 30, 2015, 11:04:09 PM3/30/15
to cesiu...@googlegroups.com
Sorry for not getting back to you earlier and very thanks for your help. I'll try it right away and feedback the result even if I afraid I can't provide some constructive suggestions. Thanks again~

Hyper Sonic於 2015年3月28日星期六 UTC+8下午11時29分41秒寫道:

sask

unread,
Mar 30, 2015, 11:57:58 PM3/30/15
to cesiu...@googlegroups.com
I found an issue. When I change from 3D or Columbus to 2D mode. The part of world map would be cut (like the attached figure). For your reference and thanks a lot.

sask於 2015年3月31日星期二 UTC+8上午11時04分09秒寫道:
2D mode.png

Hyper Sonic

unread,
Mar 31, 2015, 9:52:29 AM3/31/15
to cesiu...@googlegroups.com
I believe this is due to the orthographic camera frustum, which is a cuboid rather than a rectangle based pyramid as with perspective. The near clipping plane is very large. With a combination of being close to the flat Earth and having a tilted view it can clip out sections of the flat Earth. When moving to the clipped out section you'll notice that your z position is negative. The thing is Cesium has 2D setup so z remains constant and the view isn't tilted, and if you don't tilt the view you won't notice z changes anyways. Well I'm treating 2D just like Columbus view (other than the frustum wall adjustment) so at certain tilts and ranges you can have the near clipping plane clip the Earth. 

It's not really an error, the thing is with orthographic you basically have a very large flat eye you're moving around. So lets say an entire continent is in your view, well then that's the size of your very large flat eye! Unfortunately Cesium doesn't give tilt control nor zoom control in 2D mode, only roll control. 'Zoom' in 2D is actually controlling the size of the flat eye yielding the illusion of movement. I think I'll add in full controls in 2D mode in my plugin to demonstrate what's going on in orthographic.

Hyper Sonic

unread,
Apr 1, 2015, 11:00:37 AM4/1/15
to cesiu...@googlegroups.com
This could be solved if orthographic mode had an offset option. Basically with orthographic the view does not change when you move along the camera.direction vector. So instead of having the flat eye positioned right on the camera position, just for rendering purposes temporarily move it in the opposite direction of camera.direction so that the flat eye plane doesn't hit the Earth. 45 Mega Meters backward (sqrt(40^2 + 20^2)=~45) should be enough to clear the 40x20 Mega Meter flat Earth rectangle.

Controls should be the same in 2D as they are in Columbus. Currently in 2D:

-non-zoom horizontal movements are perpendicular to camera.direction rather than to the world z vector, causing horizontal movements to cut into the Earth when tilted.
-Similarly 'yaw' control spins around the camera.direction vector rather than the world z vector when tilted.

Of course none of this is noticed until you tilt the view in 2D, which you are not supposed to be able to do.

Hyper Sonic

unread,
Apr 11, 2015, 5:02:20 PM4/11/15
to cesiu...@googlegroups.com
Replacing all of the camera._mode statements with viewer.scene.mode in the above code will remove the need for the setTimeout as viewer.scene.mode changes immediately. I still need to fix the view width transitions and account for cases where the camera isn't facing the surface.
Message has been deleted

Hyper Sonic

unread,
Apr 15, 2015, 9:59:27 PM4/15/15
to cesiu...@googlegroups.com
You can make it instantly change with viewer.sceneModePicker.viewModel.duration=0;
Default is 2 seconds. You can change it to a high value like 16 to get a good look at the morphing animation.
Reply all
Reply to author
Forward
0 new messages