Tutorial #10: Advanced FreeCamera
In this tutorial we’ll take a look at more advanced uses of a FreeCamera. We’ve already seen how to add a camera to a scene, how to move it (to a specific place), how to rotate it and how to switch to another camera. Now we’ll be expanding on by taking rotations into account so we can move based on where the camera is pointing, not just the global axes.
First we’ll need another html page, very similar to all the others:
The HTML document
Inside the mydemo folder, start by creating a simple html page.
<html>
<head>
<title>Canvas3D tutorial #10: Advanced FreeCamera</title>
<script type="application/javascript" src="../canvas3dapi/c3dapi.js" ></script>
<script type="application/javascript" src="tutorial10.js"></script>
</head>
<body>
<!-- Add a canvas element to the page. It is scripted by using its id -->
<canvas id="tutorial" style="border: 2px solid blue" width="500" height="500"></canvas>
</body>
</html>
The JavaScript Code
The first thing we’re going to do is move the camera according to it’s own axes, not those of the scene. This change is surprisingly simple. When calling cam.setPosition, instead of manipulating the coordinates directly like we did in tutorial #9, we’ll take a unit vector (a vector with a length of one) that represents one of the camera’s axes (left, up or forward) and use c3dl.multiplyVector to multiply that vector by the amount we want to move, giving us a vector representing movement in the desired direction by the amount we want. If we want to move in one of the other directions (i.e. down instead of up). just pass a negative value as the second argument to multiplyVector. The result of that multiplication is a vector representing movement along the axis you chose, translated into global coordinates. Once you have that you can just add it to the current position of the camera (very similar to what we did in tutorial #9).
// Tutorial 10
c3dl.addMainCallBack(canvasMain, "tutorial");
c3dl.addModel("duck.dae");
//This function is going to be called when the user releases a key.
// If shift is currently pressed, the camera will rotate a bit in the chosen direction,
// if shift is not being held, the camera will move.
function up(event){//a key is released
var cam = scn.getCamera();
if(event.shiftKey) {
switch(event.keyCode) {//determine the key released
case 65://a key
cam.roll(-Math.PI * 0.025);//tilt to the left
break;
case 37://left arrow
cam.yaw(Math.PI * 0.025);//yaw to the left
break;
case 68://d key
cam.roll(Math.PI * 0.025);//tilt to the right
break;
case 39://right arrow
cam.yaw(-Math.PI * 0.025);//yaw to the right
break;
case 83://s key
case 40://down arrow
cam.pitch(Math.PI * 0.025);//look down
break;
case 87://w key
case 38://up arrow
cam.pitch(-Math.PI * 0.025);//look up
break;
}
}
else {
var pos = cam.getPosition();
var mov = [0,0,0];
switch(event.keyCode) {//determine the key released
case 65://a key
case 37://left arrow
mov = c3dl.multiplyVector(cam.getLeft(),10,mov);//move the camera left
break;
case 68://d key
case 39://right arrow
mov = c3dl.multiplyVector(cam.getLeft(),-10,mov);//move the camera right
break;
case 83://s key
mov = c3dl.multiplyVector(cam.getUp(),-10,mov);//move the camera down
break;
case 40://down arrow
mov = c3dl.multiplyVector(cam.getDir(),-10,mov); //move the camera 'back' (towards the user)
break;
case 87://w key
mov = c3dl.multiplyVector(cam.getUp(),10,mov); //move the camera up
break;
case 38://up arrow
mov = c3dl.multiplyVector(cam.getDir(),10,mov);//move the camera 'forward' (into the scene)
break;
}
cam.setPosition([pos[0]+mov[0],pos[1]+mov[1],pos[2]+mov[2]]);
}
}
function canvasMain(canvasName){
scn = new c3dl.Scene();
scn.setCanvasTag(canvasName);
renderer = new c3dl.WebGL();
renderer.createRenderer(this);
scn.setRenderer(renderer);
scn.init(canvasName);
if(renderer.isReady() )
{
var duck = new c3dl.Collada();
duck.init("duck.dae");
duck.setTexture("duck.png");
duck.yaw(-Math.PI * 0.5); //rotate the duck to look up the Z axis
scn.addObjectToScene(duck);
var cam = new c3dl.FreeCamera();
cam.setPosition(new Array(0, 0, 600));
cam.setLookAtPoint(new Array(0.0, 0.0, 0.0));
scn.setCamera(cam);
//let the scene know which function to call when a key is pressed (down)
//and released (up).
scn.setKeyboardCallback(up);
scn.startScene();
}
}
Now you have a camera that the user can move about the scene, provided they are willing to repeatedly press a key for a tiny increment of movement. An alternative to this is to use setLinearVel and setAngularVel so that the camera is automatically moved or rotated by a small amount each time the scene is updated, allowing the user to hold the key(s) for as long as they want that movement (or rotation) to continue. For this example we’ll make use of keyboard callbacks for when the key is pressed down and when it is released. Movement (or rotation) will start when the user presses a key and will stop when that key is released. Place the following code in tutorial10b.js and update the script tag in tutorial10.html accordingly:
// Tutorial 10b
c3dl.addMainCallBack(canvasMain, "tutorial");
c3dl.addModel("duck.dae");
//This function is going to be called when the user releases a key.
// If shift is currently pressed, the camera will stop rotating in that direction,
// if shift is not being held, the camera will stop moving.
function up(event){//a key is released
var cam = scn.getCamera();
var vel;
if(event.shiftKey) {
switch(event.keyCode) {//determine the key released, if it is any of the angular velocity keys, set angular velocity to 0
case 65://a key
case 68://d key
case 37://left arrow
case 39://right arrow
case 83://s key
case 40://down arrow
case 87://w key
case 38://up arrow
cam.setAngularVel([0,0,0]);//stop rolling
break;
}
}
else {
var pos = cam.getPosition();
switch(event.keyCode) {//determine the key released, if it is one of the linear velocity keys, set linear velocity to 0
case 65://a key
case 37://left arrow
case 68://d key
case 39://right arrow
case 83://s key
case 40://down arrow
case 87://w key
case 38://up arrow
cam.setLinearVel([0,0,0]);
break;
case 16://shift key
cam.setAngularVel([0,0,0]);
break;
}
}
}
//This function is going to be called when the user presses a key.
// If shift is currently pressed, the camera will start rotating in the chosen direction,
// if shift is not being held, the camera will move.
function down(event){//a key is released
var cam = scn.getCamera();
if(event.shiftKey) {
switch(event.keyCode) {//determine the key pressed
case 65://a key
cam.setAngularVel([0,0,-0.001]);//roll the camera left
break;
case 37://left arrow
cam.setAngularVel([0,0.001,0]);//yaw left
break;
case 68://d key
cam.setAngularVel([0,0,0.001]);//roll the camera right
break;
case 39://right arrow
cam.setAngularVel([0,-0.001,0]);//yaw right
break;
case 83://s key
case 40://down arrow
cam.setAngularVel([0.001,0,0]);//pitch down
break;
case 87://w key
case 38://up arrow
cam.setAngularVel([-0.001,0,0]);//pitch up
break;
}
}
else {
var mov = [0,0,0];
switch(event.keyCode) {//deterime the key pressed
case 65://a key
case 37://left arrow
mov = c3dl.multiplyVector(cam.getLeft(),0.1,mov);
break;
case 68://d key
case 39://right arrow
mov = c3dl.multiplyVector(cam.getLeft(),-0.1,mov);
break;
case 83://s key
mov = c3dl.multiplyVector(cam.getUp(),-0.1,mov);//move the camera down
break;
case 40://down arrow
mov = c3dl.multiplyVector(cam.getDir(),-0.1,mov); //move the camera 'back' (towards the user)
break;
case 87://w key
mov = c3dl.multiplyVector(cam.getUp(),0.1,mov); //move the camera up
break;
case 38://up arrow
mov = c3dl.multiplyVector(cam.getDir(),0.1,mov);//move the camera 'forward' (into the scene)
break;
case 16://shift key, stop linear movement
cam.setLinearVel([0,0,0]);
break;
}
cam.setLinearVel(mov);
}
}
function canvasMain(canvasName){
scn = new c3dl.Scene();
scn.setCanvasTag(canvasName);
renderer = new c3dl.WebGL();
renderer.createRenderer(this);
scn.setRenderer(renderer);
scn.init(canvasName);
if(renderer.isReady() )
{
var duck = new c3dl.Collada();
duck.init("duck.dae");
duck.setTexture("duck.png");
duck.yaw(-Math.PI * 0.5); //rotate the duck to look up the Z axis
scn.addObjectToScene(duck);
var cam = new c3dl.FreeCamera();
cam.setPosition(new Array(0, 0, 600));
cam.setLookAtPoint(new Array(0.0, 0.0, 0.0));
scn.setCamera(cam);
//let the scene know which function to call when a key is pressed (down)
//and released (up).
scn.setKeyboardCallback(up,down);
scn.startScene();
}
}
Now you have a camera that the user can use to navigate the scene. You’ll notice that the values used in setting the linear and angular velocities are quite small. They will vary depending on the scale of your objects/scene and how fast/fine you want movement to be. There are some weaknesses to this code, notably that when you press a new button before releasing the old one it forgets the old movement and only uses the new linear/angular velocity. This could be overcome by using getLinearVel and getAngularVel to find out what the camera is currently doing, and adding the new movement to that.
