Camera - so we can move
around
by
Ben Kenwright
Yes its cool to create amazing 3D demo's, rotating cubes, 3d teapots - but
you want to be able to create more objects and place them in your virtual
world...then move around and go closer and rotate around them! What am I
talking about? Well a camera setup - so in essence we can create a 3D
world and move around it.
Believe me, its not that hard to do a basic camera setup...a lot of the times
its complicated with matrix's and lots and lots of code for calcuating all sorts
of things, like clipping and frustrum which make the camera code look even more
complex.
For example, lets say we have a simple cube or sphere for example - no if we
add a value to all the z points...e.g...zinc - then our object will seem to move
away...and if we subtract zinc from all the points, the z value will move closer
to us.....this is a simple forward backward motion. Sometimes refereed to
as 'translation'. The difference with camera translation though, is
everything is in reverse. As if we want our camera...which is move things
closer to the screen, so they get bigger, we subtract a z value from all our
vertices. Assuming a left handed system, where positive z is into the
screen. So just dealing with x, y and z...and not mixing in the rotation
things yet...we can straff forward, backward or up and down, by simply adding or
subtracting a value from our vertices values.
In most games and books, it tells us not to harm or modify our original data
- so you usually have two sets - a master copy and a translated version.
You could keep as many different copies of the data for the different stages of
the 3D pipeline that where building, but it takes up lots of space...and we want
to keep it simple :).
Simple Camera Movement (with No
Rotation) |
float x, y, z;
// These would be our original
values
float tx,ty,tz;
// Our translated - new values
float movx,movy,movz
// These are our precious camera movement info, telling us
// the current position of our camera.
// First copy our values over
tx = x;
ty = y;
tz = z;
// Now our movement! Simple
forward, back or left right (with no rotation involved)
tx -= movx;
ty -= movy;
tz -= movz;
|
Hey, now thats simple...but we can only see forward! We are moving
around our 3D world, but we're always looking into the positive z axis.
Now we can do better than that can't we :) So now comes our rotation
equations...which at first are a bit scary, especially if your trig and maths
are a bit rusty...but hey, you usually lock these baby's away in a function and
use them when you need them. And I found, that after a while, they sort of
start to make sense in a way..hehe....well I did the trig and prooved them...but
sometimes it takes time for you to truly get a feel for 3D trig....but if you
stick with it, it comes to you in time :)
Reminder of the Euler Rotation
Equations: |
// ax, ay, and az our the angles
of rotation :)
// Rotation about the x-axis
rx = x;
ry = y*cos(ax) - z*sin(ax)
rz = y*sin(ax) + z*cos(ax)
// Rotation about the y-axis
rx = x*cos(ay) + z*sin(ay);
ry = y;
rz = -x*sin(ay) + z*cos(ay)
// Rotation about the z-axis
rx = x*cos(az) - y*sin(az);
ry = x*sin(az) + y*sin(az);
rz = z;
|
So how is this all going to connect...you can see the rotation
equations...and of course basic how-to of moving around. We now have to sort out
an order of what to do first, and how to do it. As we first move our
camera to the position its suppose to be - which means translating...then we
rotate our world, in a set order, X then Y followed by Z. Of course you
can use ZYX if you prefer, but you must stick to the same method.
Note: I've not mentioned local to world coordinates here. First
you might want to place all your objects in a different place in your world,
compared to there values when you loaded them. So you would translate and
rotate them to there final world position, then do your camera conversion.
I've taken the main code from the applet render loop - which shows the
conversion of all our triangles from world space to camera space. We have
a 'm_EyePos' and 'm_EyeAngle' for our camera location and direction where
facing. And of course a 'm_BoxPos' for the conversion of our box from
local space to world space :)
Main Applet Loop:
(Download Applet Source Code) |
....etc..other code
Vector3 m_EyeAngle =
new
Vector3( 0, 0, 0 );
Vector3 m_EyePos =
new
Vector3( 0, 0, -10 );
Vector3 m_BoxPos =
new
Vector3( 0, 0, 0 );
public
void
paint(Graphics g)
{
// Clear screen
offScreen.setColor(Color.white);
offScreen.fillRect(0,0,this.getSize().width,this.getSize().height);
Dimension appletSize =
this.getSize();
int
width =
appletSize.width;
int
height =
appletSize.height;
ResetAllCoords( m_tri,
m_NumTris );
// Lets rotate all of the
triangles and translate them back into the horizon
for(int
i=0; i<m_NumTris; i++)
{
Vector3 vBoxTempPos =
Vector3.subtract( m_BoxPos, m_EyePos);
m_tri[i].Translate(
vBoxTempPos.m_x,
vBoxTempPos.m_y,
vBoxTempPos.m_z);
m_tri[i].RotateY(-m_EyeAngle.m_y);
m_tri[i].RotateZ(-m_EyeAngle.m_z);
m_tri[i].RotateX(-m_EyeAngle.m_x);
}
RenderWireFloor(myImage, m_EyeAngle, m_EyePos);
CalculateNormals( m_tri, m_NumTris );
CullTriangles( m_tri, m_NumTris );
SortRenderOrder( m_tri, m_NumTris );
RenderAllTriangles( m_tri, m_NumTris );
//offScreen.drawLine(
mouse_x_left+10, mouse_y_left+10,
// mouse_x_right+10, mouse_y_right+10);
g.drawImage(myImage,0,0,this);
}// End of paint(..)
....etc..other code |
The code looks a bit complicated...but you can comment lots of it out.
I added a wire floor to the demo so you could move around and get a better feel
for movement. The movement is achieved, by taking a reference direction,
which I chose (0,0,1) - the positive z. And then we rotate this, so that
its facing the direction that where looking...and we can add this new direction
value to our current camera position. So we get a first person view walk
about effect :)
Here is in essence the movement code for the applet, it allows you to move,
either by dragging the mouse while holding the mouse button down...or using the
cursor keys on the keyboard:
Applet Movement Code:
(Download Applet Source Code) |
..etc..etc..other
applet code
boolean
bMouseDown =
false;
public
boolean
mouseDown(Event evt,
int
x,
int
y)
{
mouse_x = x;
mouse_y = y;
bMouseDown =
true;
repaint();
return
true;
}// End of mouseDown(..)
public
boolean
mouseUp(Event evt,
int
x,
int
y)
{
mouse_y = y;
mouse_x = x;
bMouseDown =
false;
return
false;
}// End of mouseUp(..)
public
boolean
mouseDrag(Event evt,
int
x,
int
y)
{
if(
bMouseDown)
{
float
speedrotation = 0.01f;
float
speed = 0.05f;
float
dx = mouse_x - x;
float
dy = mouse_y - y;
mouse_y = y;
mouse_x = x;
Vector3 dir =
new
Vector3( 0, 0, 1 );
dir = Vector3.rotatey(dir, m_EyeAngle.m_y);
dir = Vector3.scale(dir, dy*speed);
m_EyePos = Vector3.add(m_EyePos, dir );
m_EyeAngle.m_y += speedrotation * dx;
repaint();
}//
End if(..)
return
true;
}// End of mouseDrag(..)
public
boolean
keyDown(Event e,
int
key)
{
double
inc = 0.1;
float
speed = 2.0f;
if(key==Event.LEFT)
m_EyeAngle.m_y += Math.PI/32;
if(key==Event.RIGHT)
m_EyeAngle.m_y -= Math.PI/32;
if(key==Event.UP)
{
Vector3 dir =
new
Vector3( 0, 0, 1 );
dir = Vector3.rotatey(dir, m_EyeAngle.m_y);
dir = Vector3.scale(dir, speed);
m_EyePos = Vector3.add(m_EyePos, dir );
}
if(key==Event.DOWN)
{
Vector3 dir =
new
Vector3( 0, 0, 1 );
dir = Vector3.rotatey(dir, m_EyeAngle.m_y);
dir = Vector3.scale(dir, -speed);
m_EyePos = Vector3.add(m_EyePos, dir );
}
repaint();
return
true;
}// End of keyDown(..)
..etc..etc..other
applet code |
Well its only a simple camera model - but it works fine, and demonstrates how
we are in fact moving the world around us...and not us really moving in the
world :)
|