Monday, November 22, 2010

So awesome!

There's nothing quite like the feeling of accomplishment getting something done gives. Best natural high EVER!

What is it that I accomplished? Nothing much, just got a true 6 degrees of freedom camera to work correctly and fly around a boring plane. It can roll, it can climb, it can move forward, it can do pretty much everything I want it to do. The control scheme I'm using is WASD to move around the plane, RF to climb or fall, and QE to roll clockwise and counter clockwise, plus what I had done before of the mouse to pith and yaw.

I'm very happy, but I also need to document how I do this.

Keyboard events are generated whenever someone presses or releases a key. The camera class has a number of boolean toggles that are flipped whenever the corresponding event happens. Then, during the camera update, it checks for each of the toggles and if it is true, the camera is moved along the corresponding axis. Here, the use of vectors to store the camera's position and view pays off, since with these already in place advancing is trivial.

To move forward, I multiply the view vector by a scalar that determines my speed. The resulting vector is added to my camera's position vector, and it's done. To move back, it's the same but the resulting vector is subtracted from the camera's position. The other movements follow the same logic but along the up and right vectors. Finally, roll is a rotation of the up vector around the view vector, and is done with quaternions the same as yaw and pitch.

I'm very excited to be able to get around the world like this. Now I just need to populate this world with something interesting to visit. The next few updates should prove a lot more interesting, now that the basic framework at least is done.

Sunday, November 21, 2010

A small step for man, a giant leap for the game

I have finally managed to get the camera to work! I control the vertical! I control the horizontal!

Ok, so the mouse now moves the camera around. After days and hours of experimentation, I have figured out how it all fits together. I am making a 6DOF camera, although right now only attitude and bearing are affected (pitch and yaw).

The way this works is that the camera stores three vectors: the position of the camera, the view, and the up vector. Position is fairly straight forward. View is the direction in which the camera is looking. Up vector serves to orient the camera, indicating which way is up. All pretty self explanatory.

I then use the gluLookAt function to set the correct perspective. One warning about the way I do this, my view vector stores the direction the camera is facing, but gluLookAt expects as one of its parameters the position of something you're looking at, which is then used as the center of the screen. The way I get this position is to add the camera's position and the view vector and passing this along to gluLookAt.

gluLookAt then subtracts the position from the target to get its own view vector which it then normalizes. So gluLookAt is undoing some of my work. I will do a function of my own to do what gluLookAt does, so I avoid the unnecessary step, but it isn't a priority at this time.

The trick is how to get the camera to rotate, with what I have. The solution I am using is using quaternions to describe the rotations. The way this works is by incrementally rotating the view and up vectors with the quaternions.

I use the SDL to detect when the mouse moves, which creates an event. This provides me with both absolute and relative movement. Absolute movement provides the screen coordinates of the mouse, with (0,0) being the top left corner. Relative movement is the accumulated movement since the last event. Because the program doesn't keep track of the mouse outside of the screen, I have to warp the mouse back to the center of the screen each time it moves away.

This was creating a problem where the warp movement created a mouse movement event that was the opposite of the movement itself,. To fix it I am now checking the absolute position of the mouse when I catch an event, and if it is at the center I ignore that particular event. Since it can only be at the center right after warping, and any movement I care about draws it away, I don't lose anything.

Once I have the relative movement, I multiply it by a sensitivity factor and store it as the number of degrees the camera moved in that frame. Horizontal movement of the mouse affects the bearing of the camera, while vertical movement affects the attitude.

So, on to the rotations themselves. Changes in bearing are described as a rotation around the up vector of the camera. This modifies the view vector, but leaves position and up unchanged. Quaternions are used here to describe this rotation. I use a convert_axis_angle function, using the up vector and the angle to build the quaternion and then normalize it. I then create a quaternion from my view vector, which for any given vector v = [ x , y , z ] , q = [ 0 , v ] .

The rotation of the view vector is the sandwich product qbearing x qview x conjugate(qbearing) . This result is stored in view.

For attitude changes, I need to determine the cross product of view and up, which gives me the right vector of the coordinate trio ( view , up , right ). This time, both view and up are modified by the rotation. So I create the attitude quaternion with convert_axis_angle, using the right vector this time, and make both rotations:

qattitude x qview x conjugate(qattitude)
qattitude x qup x conjugate(qattitude) 

The results are stored in view and up respectively.

After this is done, I use gluLookAt as described above, and voila, it is done.

There's room for growth, of course. Roll can be easily appended, once the input is mapped. In the case of roll, the rotation would be around the view vector, and only up would be modified. I also need to add translation, to move the camera around. This is easily done, however, now that I have a view vector. Lateral movement (strafing) is done by getting the right product of view and up, and moving along that vector, vertical movement is done along the up vector and forward movement is done along the view vector.

Ok, that's it for now. Next up, making the world a more interesting place.

Friday, November 19, 2010

Square One

Well, got the framework back up and running. That was fairly painless, though I noticed my previous posts were a bit light on details. So, just in case something like this ever happens again, the libraries I'm linking to are:

mingw32
SDLmain
SDL.dll
opengl32
glu32

I will try to focus on getting the camera working for now. The camera is after all at the heart of the game. You can't have a 3d space game without freedom in all three dimensions.

Thursday, November 18, 2010

Why backups are important

So it turns out the usb drive where I was keeping my project files died a couple days ago. Attempts to revive it have not been successful. As a result, I'm back at square one. What I learned up to now can help get up to speed quickly when I start up again, but it is fairly sad to see all that work gone. :(

Friday, November 12, 2010

Project 1 - Life getting in the way

Had a busy week. Between a short trip and work, haven't had the chance to fix the camera. I've managed to break it, and get the new computer's developing environment up and running though. I'll get some more stuff up here as soon as I can.

I might just get the camera up to some basic stuff and continue working on other things to keep everything going.

Wednesday, November 3, 2010

Project 1 - Moving the camera, part 5

I said I'd work on something else, but I couldn't leave the camera as it was. I knew there had to be a better way to do what I'd been doing, and it turns out there is. Its called quaternions, and its confusing. Thankfully I've found plenty of explanations online, most directed at modeling in 3d. So I'm taking a bit from a few different tutorials to make my camera more robust. The code from NeHe's Lesson: Quaternion Camera Class is coming particularly handy.

I'm not lifting the camera class completely, it doesn't do everything I need, but the quaternion class is handy and saves me the time it would take me to fully grasp how to multiply them and turn them into matrices. So I'm keeping that. I'll still need to read up on them, though. As it is things mostly worked, though I'm still stuck with the controls being reversed after making a half turn (inverting up and down).

After a couple of days of thinking about it, it's occurred to me that the way I'm going about it might be to blame. Particularly with the mouse. I'm trying to keep track of attitude and bearing, both on 360 degrees. I had to, because if I didn't let them move freely (by limitting attitude for instance to +/- 90°) then I wouldn't be able to make full revolutions. The problem is that when I go over 90° I start flying inverted, but moving the mouse still adjusts the absolute angle around the fixed y axis. Which inverts it. I could try telling the program to invert the rotation when I go over 90°, but then if I rolled while flying forward I'd have the same problem.

The idea I'm toying with now is to keep track of the camera's frame of reference. I am not yet sure how I'll go about doing this, though some vector algebra and the cross product seem to offer promising opportunities.

Sunday, October 31, 2010

Project 1 - Moving the camera, part 4

This is proving a little more complex than I had hoped. It appears my first attempt was on the right track. No way around mucking with trigonometry I guess. I'll start with rotation first this time around. The idea here is that while standing at the origin, I rotate the world around the origin so I'm looking in the right direction, and then translate to the correct spot.

I need to keep track of three angles, rotation about x, y and z axes. Let's see. Aha, here's one problem. I've been trying to make two rotations, one after the other, on each three axes. However, it appears that when using glRotate, the vector you give is relative to the absolute coordinate axes, not the relative coordinates after the previous rotation. This explains the odd behaviors I was finding, where in one direction mouse movement worked as it should and 90° to the right or left it made me roll about the axis.

So I need a way to calculate the direction of the new axis after the first rotation. This means calculating x and z components (y component remains 0). The components, for a given rotation will be the cosine of that angle on the x axis, and the sin of it on the z coordinate.

 There we go, that's much better. I'm going to need to work out all three coordinates when I add rolling to the camera. Now, we add camera movement. Moving forwards and backwards is easy, since we can ignore camera roll as well. I need to first figure out the vector on which I'm moving (camera's z axis), normalize the vector, and move my speed along that vector.

Yes! There we go. Got the forward and back working correctly at last. As I said a couple of times, the way the coordinates work here is kind of confusing. Vertex positions are relative to your last translation, but glTranslate and glRotate work on absolute coordinates. Need to streamline the camera functions, but I have a good start now.

Next up, I'm going to try to draw some of the variables on the screen, for debugging purposes. One of the problems my current implementation is having is that if you do half a turn going up, all the controls end up wonky. Probably a sin or cosine somewhere giving me a negative number where I'm expecting a positive or something like that. One of the reasons I didn't want to muck too much with trigonometry.

Saturday, October 30, 2010

Project 1 - Moving the camera, part 3

Last time I cobbled together the very basics for a camera. Today I mean to make it somewhat more robust, and add mouse input.

Right now the camera is a bunch of variables, and lacking some important functionality. My plan is to create a class to hold all of the camera information. To know what the camera is looking at, we need a position and a bearing. The position is easy enough to describe. Bearing is a little more complex. Finding a good way to describe it is important since I will need to know the current bearing in order to be able to move forwards, sideways or back.

The bearing can be described in terms of the angle on two axis. We take the natural camera bearing (looking towards the negative z axis) as centered on the y and x axis. Increasing the angle with respect to the y axis turns the camera to the left, decreasing it turns it towards the right. Increasing the angle with respect to the x axis, on the other hand, makes the camera look up, while decreasing it makes the camera look down.

I will eventually need to add a third angle, twisting the camera with respect to the z axis. This would roll the camera one way or the other, but due to the affect that can have on further mouse movement I'll leave it aside for now.

I will need to ensure that the camera angles are always between 0° and 360° on the y axis and between 0° and +/-180° on the x axis. Allowing the x axis to perform a full revolution would cause the world to end up bottom-up if you look 'up' too much. This could be confusing, at least until I get the roll movement down.

All this means that there will be two distinct camera modes I will need. One, the one I'm working on now, follows FPS conventions. It properly represents someone walking on a fixed surface. However, it is very poor for a flying or space sim, or people in freefall. The second should provide free movement on all axis, and would be well suited to the rest of the game. It would be simpler overall to create the first type out of the second one, by adding the limitations I described above, but for now I want to be able to move around and the first type of camera is quicker to build.

It also occurs to me that there may be a better way around determining the position and orientation of the camera. The translate and rotate functions I spoke about before, and which I use to move the camera about ultimately create a matrix, which ModelView uses to render the scene. Instead of trying to deal with the trigonometry in the camera class, wasting CPU, I can use some of the OpenGL functions and work on a matrix with these, then feed the matrix directly to ModelView. I'll need to think about how best to do this.

Another good idea is to try and decouple the camera from the keys themselves. I am already using boolean variables for determining if the camera has to be moving. By having a pointer to them in the camera class that can be changed to look at a different key, it should be easy to reconfigure the controls if required.

Matrices in OpenGL are stored as arrays of 16 GLfloats or GLdoubles. Unfortunately, all matrix operations are applied to the current matrix, so it's all the same. I'll still need to do all the work on my own. Pity. I may still be able to use this to be quicker, however. The translation matrix is trivial to make, leaving only the rotation matrix left to finish up. First, we make room for the coordinates.

Now, the bearing. I'll use structs for these, to keep things tidy. We initialize all these to 0. When I move, I need to adjust my position on all three axis... no, this will not work. Too messy. There has to be a more elegant solution. What I have works on a fixed bearing, but as soon as I look around it will break down.

So, back to the drawing board. Thinking about it, there is no reason I can't do translations and rotations during the loop fase, and have the camera already set up for the rendering portion. If I push at the beginning of rendering, and pop at the end, I can work on the camera's position without problem. So, let's see how that works.

Well, it's kind of embarrasing after a couple of day's work, but these few lines of code:

void Camera::update()
{
    if (*moveForward)
    {
        glTranslatef(0 , 0 , 1) ;
    }
    if (*moveBackward)
    {
        glTranslatef(0 , 0 , -1) ;
    }
    if (*strafeLeft)
    {
        glTranslatef(1 , 0 , 0) ;
    }
    if (*strafeRight)
    {
        glTranslatef(-1 , 0 , 0) ;
    }
} ;

are all I ended up needing. They should work even as the bearing changes. I will however need a way to work out my position and bearing later. But for now, this is good. Let's see about adding the mouse then.

Well, the mouse works, but it's not behavinf the way I'd like it to. Probably because of the way the coordinate system works. Still, I have some ideas about how to get around that. Will continue later.

Project 1 - Moving the camera, part 2

As I was saying in the last post, we will use the SDL to handle key presses and other events. There is a pretty complete tutorial for doing this in the SDL Tutorials page, and I'm going to lift the code from there since I could not possibly do it better. I'm also using the basic framework they offered in the previous tutorial, so it fits in nicely.

I said in the previous update that I would be using the WASD + mouse control scheme. There are a few things to keep in mind when building the camera controls though. For starters, SDL only detects an event when a key is pressed or released. If I want the camera to advance while the 'W' key is pressed, I have to keep track of whether it is currently pressed or not, making the camera move while it is pressed and having it stop when I detect that it has been released. Just something to keep in mind.

Once I have the keyboard commands working, I will get the mouse controls ironed out. To start off, I need to keep track of the camera's position. So I add the camx, camy and camz variables. These will store the current camera position. I also create the boolean variables keyWdown, keyAdown, keySdown, and keyDdown. When polling for events, if I detect one of these keys being pressed I will switch the proper boolean to true, and on a key up event I'll switch it to false again. This has a number of advantages, such as allowing me to detect multiple simultaneous key presses.

Then, on the loop portion of the program, I check the boolean variables. If W is being pressed I move forwards, if A is pressed I move to a side, etc. Doing it quick and dirty, I can get the camera moving on the z and x axis. However, before I add the mouse I will need to figure out what my current facing is, so I can move freely on all axis. I will leave that for tomorrow. For today, having the camera move within the world is a good place to stop.

Thursday, October 28, 2010

Project 1 - Moving the camera

So, I have a working terrain, but the camera is fixed. I want to change that. Being able to see it from different angles without having to change the code and recompile would be handy.

I've mentioned a few times that OpenGL is kind of strange about how moving around the world works. Since I'm going to be talking about the camera now, it seems a good moment to delve into that. The first thing that should be explained I guess is that OpenGL doesn't have a camera. It has a viewport. The viewport never moves, in the sense that the absolute origin of OpenGL's coordinate system is always the middle of the viewport.

Instead, in order to have different views, we have to move the rest of the world. Why do I talk about a camera then? Because it is easier for me to think that way. A camera moving around a world is easier to think about than a world moving around a fixed point in space. Moving the world 5 units to the left is functionally the same as moving the 'camera' 5 units to the right, but the latter is easier to picture.

So, how do we move everything around then? OpenGL has this thing called the ModelView matrix. While working with this matrix, we can change the origin of the coordinate system. There are a couple of functions that do this, glTranslatef and glRotatef. glTranslatef moves the origin by the number of units you tell it to. glRotate changes its orientation. After modifing the matrix with these functions, the points we pass to the program are modified by these operations and placed in the proper place.

So, as soon as the program starts, if you draw something at 0,0,0 it will appear at the center of the screen. But if you then call glTranslatef( 5 , 0 , 0 ), and draw something at 0 , 0 , 0, the resulting image appears at 5,0,0, or to the right of the screen. If you then call glRotatef( 180 , 0 , 0 , 1 ) and draw at 1 , 0 , 0 the drawing will appear at 4 , 0 , 0 . We basically draw on a relative coordinate system centered where we want with the translate and rotate functions, and then it is drawn on the absolute coordinate system centered on the screen.

How does this relate to the camera then? As I said, to move the 'camera' around, what we do instead is move the world around. If before we start drawing we translate 5 units to the right, when we then start drawing everything will end up 5 units to the right. Because everything maintains its position relative to everything except the viewport, it ends up looking as though the camera had moved 5 units to the left.

This covers the OpenGL side of things. If we want to move inside the world in a given direction, we have to move everything else in the opposite direction. How do we get our commands to the system, though? The SDL will handle recognizing when an 'event' happens. An event is any keystroke, mouse movement or whatever other input method we use to give commands to the computer.

I've decided to go with a fairly standard set of commands to start with. 'W' and 'S' move the camera forwards and backwards. 'A' and 'D' move it sideways. Horizontal mouse movements will rotate it around the vertical axis, letting us look around. Vertical mouse movement will rotate it around the horizontal axis, letting us look up and down. This should be enough to enable us to move everywhere in the world.

Next up, how to get the SDL to catch our commands.

Project 1 - Building a solar system, part 2

Last time we'd created a bunch of objects. It would be nice to be able to draw them, or something like them. OpenGL already has a coordinate system running. The sun dominates our solar system, meaning everything revolves around it, and so we'll put the sun at the origin.

For this part of the project, we won't give each planet the right proportions and distance from the sun. For one thing, if we did, we wouldn't be able to see most of the stuff with a quick glance. So we'll make the sun 10 units in radius, planets 5, and moons 1. The orbit of each planet will be 20 units from the previous one, while the moons orbit 2 units from each other.

First, we'll draw the planets on a line, make sure everything works. While OpenGL's coordinate system is a good thing to have, it is also somewhat strange to deal with. We won't be drawing everything where it goes. Rather, we move around the world and draw where we are standing. This requires a lot of pushing and popping matrices, and I am not sure yet how much resources that eats up. But I can look into optimizing in the future.

What I want, essentially, is a function that goes down the list in the celestial body and draws a circle, then moves down the list. Need also a way of differentiating between stars, planetary bodies and moons. For a first attempt, I go with

enum bodytype { star , planet , moon , asteroid field } // this would grow as needed
void drawSystem( CelestialBody * parent , bodytype body = star ) ;

The steps that I need to go through are draw the main body at the current location, then for each object in the list move to its position and call this function with the appropriate parameters. This should recursively build our entire system. Speaking of the position of each object, in OpenGL the three dimensions start off oriented so that the x axis increases to the right, the y axis increases toward the top of the screen, and the z axis increases towards the viewer. I'll be placing the plane of the system on the plane y = 0, so as things move away from the star they'll be towards the right, and we'll be looking at them from above. Of course, once I get some code to move the camera, the original orientation of the axis won't matter.

I am writing this as a stand alone function for now, but because of how intimately the function is tied to CelestialBody objects it should probably be a method of the class. Something to keep in mind going forwards.

After fixing a few silly mistakes, this is what we are left with:



Pretty sweet, huh? There's a little trouble with the moons running into the following planet, the orbits are too short. But it behaves mostly as expected.

That's a good place to leave for now. Next up, some camera controls.

Monday, October 25, 2010

Project 1 - Building a solar system

Now, at last, we start our first foray into the actual game elements. Part of the game is meant to be about exploration, and we need somewhere to go. For science fiction, that generally means new planets. But planets don't just exist by themselves, they orbit a star, and are orbited themselves by satellites.

What do we need to create a solar system? We need a star, first of all. Many stars actually have a companion star forming a binary system, and even larger sets exist, but for now I'll focus on single star systems.

This star is going to be orbited by all manner of bodies. Comets, asteroids, planets, gas giants, etc. We can make a list of the bodies orbiting a star. The different bodies orbiting the star, at the same time, may also have their own satellites, which we can list as well. So the bodies in a solar system can be organized as a list of lists.

Let's take for instance our own solar system. The objects would sort themselves as

Sun
|- Mercury
|- Venus
|- Earth
|   |- Moon
|- Mars
|   |- Deimos
|   |- Phobos
|- Asteroid Field
|   |- Ceres(dp)
|- Jupiter
|   |- Io
|   |- Europa
|   |- Ganymede
|   |- Callisto
|- Saturn
|   |- Mimas
|   |- Enceladus
|   |- Tethys
|   |- Dione
|   |- Rhea
|   |- Titan
|   |- Iapetus
|- Uranus
|   |- Miranda
|   |- Ariel
|   |- Umbriel
|   |- Titania
|   |- Oberon
|- Neptune
|   |- Triton
|   |- Nereid
|   |- Larissa
|- Pluto (dp)
    |- Charon
|- Eris (dp)
|   |- Dysnomia

The list isn't exhaustive. I'm focusing on those with the largest masses. There's also comets, which I didn't add, and the Kuiper Belt, but for now this list will do.

So, to start off, I need a way of storing this list. The STL gives us Vectors. But since Vector is a template, we need to determine what data type best describes the planets themselves. We can create for now a 'CelestialBody' class that will hold the information of each planet, and one vector. So our first celestial body is the sun, which will have a vector with pointers to the planets orbiting it, which will each have a vector of pointers to the moons orbiting them.

class CelestialBody
{
    public:
        CelestialBody();
        ~CelestialBody();
        vector<CelestialBody *> satellites_ ;
    protected:

    private:

};

The code for setting the system up is ugly and has a lot missing, but we'll sort that out later. We want to be able to draw it before we can create new systems elegantly.

    //----------------------------------------------------------------
    // System Setup
    //----------------------------------------------------------------

    Sol.satellites_.push_back( new CelestialBody ) ; // Mercury
    Sol.satellites_.push_back( new CelestialBody ) ; // Venus
    Sol.satellites_.push_back( new CelestialBody ) ; // Earth
        Sol.satellites_[2]->satellites_.push_back( new CelestialBody ) ; // Moon
    Sol.satellites_.push_back( new CelestialBody ) ; // Mars
        Sol.satellites_[3]->satellites_.push_back( new CelestialBody ) ; // Deimos
        Sol.satellites_[3]->satellites_.push_back( new CelestialBody ) ; // Phobos
    Sol.satellites_.push_back( new CelestialBody ) ; // Asteroid Belt
        Sol.satellites_[4]->satellites_.push_back( new CelestialBody ) ; // Ceres
    Sol.satellites_.push_back( new CelestialBody ) ; // Jupiter
        Sol.satellites_[5]->satellites_.push_back( new CelestialBody ) ; // Io
        Sol.satellites_[5]->satellites_.push_back( new CelestialBody ) ; // Europa
        Sol.satellites_[5]->satellites_.push_back( new CelestialBody ) ; // Ganymede
        Sol.satellites_[5]->satellites_.push_back( new CelestialBody ) ; // Callisto
    Sol.satellites_.push_back( new CelestialBody ) ; // Saturn
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Mimas
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Enceladus
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Tethys
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Dione
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Rhea
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Titan
        Sol.satellites_[6]->satellites_.push_back( new CelestialBody ) ; // Iapetus
    Sol.satellites_.push_back( new CelestialBody ) ; // Uranus
        Sol.satellites_[7]->satellites_.push_back( new CelestialBody ) ; // Miranda
        Sol.satellites_[7]->satellites_.push_back( new CelestialBody ) ; // Ariel
        Sol.satellites_[7]->satellites_.push_back( new CelestialBody ) ; // Umbriel
        Sol.satellites_[7]->satellites_.push_back( new CelestialBody ) ; // Titania
        Sol.satellites_[7]->satellites_.push_back( new CelestialBody ) ; // Oberon
    Sol.satellites_.push_back( new CelestialBody ) ; // Neptune
        Sol.satellites_[8]->satellites_.push_back( new CelestialBody ) ; // Triton
        Sol.satellites_[8]->satellites_.push_back( new CelestialBody ) ; // Nereid
        Sol.satellites_[8]->satellites_.push_back( new CelestialBody ) ; // Larissa
    Sol.satellites_.push_back( new CelestialBody ) ; // Pluto
        Sol.satellites_[9]->satellites_.push_back( new CelestialBody ) ; // Charon
    Sol.satellites_.push_back( new CelestialBody ) ; // Eris
        Sol.satellites_[10]->satellites_.push_back( new CelestialBody ) ; // Dysnomia

Well, this is as far as we go for now. We have a bunch of celestial bodies created now, but as far as the computer is concerned, they're all exactly alike. The only thing distinguishing them is their position in the vector. Next up, we'll work on a way to display this.

Saturday, October 23, 2010

Project 1 - Drawing a sphere, part 3

Last night I stopped at describing the algorithm for creating one strip of the circle. The code that does that is this:

glBegin(GL_TRIANGLE_STRIP);
        float r = 1 ;
        glVertex3f ( 0 , 0 , 1 ) ;
        glVertex3f( r*sin( 30 * M_PI / 180 )*cos( 0 * M_PI / 180 ) ,
                    r*sin( 30 * M_PI / 180 )*sin( 0 * M_PI / 180 ) ,
                    r*cos( 30 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 30 * M_PI / 180 )*cos( 60 * M_PI / 180 ) ,
                    r*sin( 30 * M_PI / 180 )*sin( 60 * M_PI / 180 ) ,
                    r*cos( 30 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 60 * M_PI / 180 )*cos( 0 * M_PI / 180 ) ,
                    r*sin( 60 * M_PI / 180 )*sin( 0 * M_PI / 180 ) ,
                    r*cos( 60 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 60 * M_PI / 180 )*cos( 60 * M_PI / 180 ) ,
                    r*sin( 60 * M_PI / 180 )*sin( 60 * M_PI / 180 ) ,
                    r*cos( 60 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 90 * M_PI / 180 )*cos( 0 * M_PI / 180 ) ,
                    r*sin( 90 * M_PI / 180 )*sin( 0 * M_PI / 180 ) ,
                    r*cos( 90 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 90 * M_PI / 180 )*cos( 60 * M_PI / 180 ) ,
                    r*sin( 90 * M_PI / 180 )*sin( 60 * M_PI / 180 ) ,
                    r*cos( 90 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 120 * M_PI / 180 )*cos( 0 * M_PI / 180 ) ,
                    r*sin( 120 * M_PI / 180 )*sin( 0 * M_PI / 180 ) ,
                    r*cos( 120 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 120 * M_PI / 180 )*cos( 60 * M_PI / 180 ) ,
                    r*sin( 120 * M_PI / 180 )*sin( 60 * M_PI / 180 ) ,
                    r*cos( 120 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 150 * M_PI / 180 )*cos( 0 * M_PI / 180 ) ,
                    r*sin( 150 * M_PI / 180 )*sin( 0 * M_PI / 180 ) ,
                    r*cos( 150 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 150 * M_PI / 180 )*cos( 60 * M_PI / 180 ) ,
                    r*sin( 150 * M_PI / 180 )*sin( 60 * M_PI / 180 ) ,
                    r*cos( 150 * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 180 * M_PI / 180 )*cos( 0 * M_PI / 180 ) ,
                    r*sin( 180 * M_PI / 180 )*sin( 0 * M_PI / 180 ) ,
                    r*cos( 180 * M_PI / 180 ) ) ;
    glEnd();

This is unwieldy. And I wouldn't look forwards to repeating it 5 more times to complete the sphere. However, we can get around that with a loop structure. We have two kind of loops, for and while. We have two values that vary in this example, the degrees for the first trigonometry call in each parameter, and the degrees on the second one. We will call these theta and phi.

Theta increases from 0 to 180, while phi oscillates between 0 and 60. When we try to build the whole circle, however, theta will grow to 180, then back to 0, and so on until it completes a strip with phi oscillating between 300 and 360. So we need one part of the loop where theta increases, and another where theta decreases. We also need a way to describe by how much.

So it would seem that a first way to approach it could be like this:

float r = 1 ;
for ( int phii = 0 ; phii <= 6 ; ++phii )
{
    glVertex3f( 0 , 0 , r ) ;
    for ( int thetai = 1 ; thetai <= 5 ; ++thetai )
    {
        glVertex3f( r*sin( 30 * thetai * M_PI / 180 )*cos( 60 * phii * M_PI / 180 ) ,
                          r*sin( 30 * thetai * M_PI / 180 )*sin( 60 * phii * M_PI / 180 ) ,
                          r*cos( 30 * thetai * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 30 * thetai * M_PI / 180 )*cos( 60 * (phii+1) * M_PI / 180 ) ,
                          r*sin( 30 * thetai * M_PI / 180 )*sin( 60 * (phii+1) * M_PI / 180 ) ,
                          r*cos( 30 * thetai * M_PI / 180 ) ) ;
    }
    glVertex3f( 0 , 0 , -1*r ) ;
    ++phii;

    for ( int thetai = 5 ; thetai >= 1 ; --thetai )
    {
        glVertex3f( r*sin( 30 * thetai * M_PI / 180 )*cos( 60 * phii * M_PI / 180 ) ,
                          r*sin( 30 * thetai * M_PI / 180 )*sin( 60 * phii * M_PI / 180 ) ,
                          r*cos( 30 * thetai * M_PI / 180 ) ) ;
        glVertex3f( r*sin( 30 * thetai * M_PI / 180 )*cos( 60 * (phii+1) * M_PI / 180 ) ,
                          r*sin( 30 * thetai * M_PI / 180 )*sin( 60 * (phii+1) * M_PI / 180 ) ,
                          r*cos( 30 * thetai * M_PI / 180 ) ) ;
    }

 Holy crap it works! Lookit:






There's still some modifications we can do to the code, though. For one, as it is, while the size is variable the detail is not. It only makes spheres with the same number of faces. To change this, we can add a few variables to take the place of some fixed numbers.

Let's say we want to slice the sphere in 10 instead of the 6 slices we are drawing now, north to south. Then phii would need to go up to 10, while phi would need to grow in increments of 36 instead of 60. In fact, for a given number of slices, nslices, phii has to go up to nslices and phi has to grow in 360/nslices steps. So, we add these changes to the code and....

Voila. The number of slices is now configurable. This is what the sphere looks like with 20 slices instead of 6:





You can see that while it retains the hard edges from the few number of parallels, the parallels themselves look more round now. We're looking at it now such that the north is up and the south is down, but we can look at the north pole directly to have a better view of the change:


It actually looks pretty cool from here.

Anyway, we can now do the same for theta. For a given number of cuts, ncuts, thetai has to go up to ncuts-1, and advance by 180/ncuts degrees. The results, for 20 cuts and 20 slices:






So I now have a bit of code that, for a given level of detail (cuts and slices) and size, will provide a sphere. Pretty sweet. And a good bit of practice to get back in the swing of things. There's quite a few improvements to be made still, of course. All those calls to sin and cos could be simplified by storing the values. There are only as many z values as there are cuts, so I could compute those once, store them in an array, and then refer to them instead of calculating them each time. Similar optimizations can be done for x and y.

But for now this is good enough. All that's left is to put that inside a function and I'm golden. Next up, by next week, create a solar system! It's less ambitious than it sounds, really.

Friday, October 22, 2010

Project 1 - Drawing a sphere, part 2

So, last night I decided on using triangle strips for drawing the circle. The reason strips are better than the primitives on their own (something I should have mentioned in the last post) is that it cuts down on redundancy. In order to draw a triangle, you need to provide three points that the computer will then group into a triangle. If you want to draw a triangle adjacent to the one you just drew, however, you have to again give three points, two of which are the same ones as the ones in the first triangle. This is wasteful.

The strip primitives, on the other hand, will take three points for the first triangle, but then each following triangle takes only a single new point. The computer knows it is adjacent to the last triangle you drew, and uses the points it already knows instead of asking you to repeat them.

While this is great for strips, however, we still end up repeating points when we need to add triangles to the free edge. Still, this is better than having to draw each of them individually.

Now on to the code.

I want to draw the triangle strips going north to south, then back up, so I start at the north pole, and all the triangles fall within two meridians. Each couple of triangles should form squares that are delimited by the parallels. I promise to post images eventually.

However, I have to provide the positions of the points in a cartesian coordinate system. So I'll need to do some trigonometry. Before that, however, I need to define some parameters. Let's call the center of the sphere (0,0,0). The radius will be 1. And I'll use 6 meridians and 6 parallels to draw the sphere. So the meridians will be 0°, 60°, 120° , 180° , -120° , -60° (positive is east, negative is west). The parallels will be 0°, +/-30°, +/-60°, +/-90° (positive is north, negative is south).

The 0° meridian will cross the x positive axis, while the 0° parallel will be at z = 0.

The reason I like the parallels and meridians description is because it maps directly to spherical coordinates, from which we can get the cartesian coordinates simply with these equations:

x = r * sin θ * cos φ
y = r * sin θ * sin φ
z = r * cos θ

θ maps to the parallels, almost. Parallels go from -90° to +90°, θ goes from 0° to 180°. φ maps to the meridians. r is the radius (1 for now). Will need a basic math library for the sine and cosine functions, but the cmath header from the STL should have everything we need. And we need to use radians instead of degrees.

So, we need a list of the points that will make up the triangles that will make up the sphere. We start with the north pole, (1,0,0) in spherical coordinates. Then advance θ one step, mark a vertex, then increase φ one step, mark a vertex, then advance θ another step and decrease φ, mark a vertex, and so on.

Well, that's all for today. I managed to do some progress, though I need to work out the commands better. However, I'm all out of time. Until next time.

Project 1 - Drawing a sphere

Let's get started on this then.

Drawing a sphere should not be something too fundamental to the process of creating this game, but it should help me get some experience working with OpenGL. Spheres are an interesting challenge to work on as well, since they are not exactly easy to do.

The reason why I want to draw spheres, is because planets and stars should be prominent in a science fiction game. Now, we know the basic description of a sphere, the surface described by all the points a certain distance from the center. We can't draw a perfect sphere because that would require drawing infinite points. But we can draw a good approximation with a body of sufficient faces. The more faces the body has, the closer the resemblance.

If we look at a map of the earth, we see that it is crisscrossed by parallels (crossing east to west) and meridians (north to south). The regions trapped within them are square (on mercator projections, anyway), since the lines cross each other at square angles. So we can draw the sphere by drawing these lines. OpenGL has a primitive type that is a strip of squares. Using it, we can place these strips side by side, each border describing a meridian, and go around filling the whole sphere. This has the added benefit that we can increase the count of strips for better detail, or lower it when we don't need to (and save some performance).

The only remaining problem I have to figure out is the caps that the north and south. Since a sphere is wider at the equator than at the poles, the square strips need to widen as they reach the middle, and thin towards the poles. The problem arises in that at the poles themselves, all the top 'squares' meet in a point, making them in fact triangles.

I'll have to make some drawings in the future to illustrate my points. I'll add that as a weekend project. Together with customizing the look.

Two possibilities occur to me. One is to use a different kind of primitive near the caps, a triangle flower thing that would serve my needs well enough. The other is to see if I can convince OpenGL to use a single point to describe two vertexes of a square. And the third option is to use the triangle strip primitive. It works mostly the same way as the square one, but should fit my purposes perfectly.

I don't want to have to switch primitives while drawing, and I don't want to force squares into triangles since that is a sure recipe for bugs. Plus, the triangle strip has an added benefit in that squares have to have all their points within a plane (not sure how the rendering behaves if this is not true. The points of a triangle, however, are guaranteed to always lie on the same plane since three points define a plane. This might come in handy in the future if I'm still using them when I start adding detail to the worlds.

 So, I have an idea of what I want to do, an idea of how to get about doing it, and a place where to do it. However, I'm all out of time for today. I should be able to get working on it tomorrow, have some results by Saturday. Well within schedule.

As a sidenote, now that I have a place to vent my ideas, I find I have a lot I want to talk about and not enough time to say it. I'll try to make some posts regarding my plans for the game in the future.

Wednesday, October 20, 2010

Project 1 - Setting up OpenGL

Like in the previous update, this isn't much more than the steps required to set everything up for the upcoming coding work. The plan is to get the project, and the SDL, to be able to work with the OpenGL calls.

To get started, I'm getting some help from tutorials. One in particular that proved helpful is this one. Lots of great tutorials in that page. I haven't used the SDL before, so I'll be going back to it now and again. Since I have about as much experience with the Windows API, the decision to use SDL doesn't have much of a drawback.

First step is to tell the linker to use the opengl and glu libraries.

Next up, we include the header files in the project,
#include <gl/gl.h>
#include <gl/glu.h>

In order to tell SDL to use OpenGL, we add a couple of flags to the call to create a window,

SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL)

The parameters are almost the same, the changes help set up the details that OpenGL needs to operate, and keeps us from having to do it. Handy.

Well, OpenGL is a little involved, and it's been a while since I toyed with it, so the going is a little slow. Nevertheless, I have it up and running now. And, we're ahead of schedule!

So, coming up, drawing a sphere. Due on Sunday.

See ya.

 

Project 1 - Building the window

We're just starting out this project. While seemingly simple, going so far as creating a window actually requires some setting up. Essentially I have to instruct my developing environment to be aware of the tools I want to use.

I code in c++, basically because it is the first language I learned and the one I've got the most experience with. I am toying around with CLISP, I'll probably write something up about how that goes eventually, but I wouldn't feel comfortable coding with it just yet.

I am writing my code in Code::Blocks, paired with mingw for compiling. They're pretty cool open source projects. I'm kind of a fan of open, accessible software, so I'm doing my best to give back with this project. I'll be using OpenGL for rendering (when I get to it), coupled with the SDL libraries for interfacing with the OS. This should help make the program compatible with most platforms eventually.

Not that I expect to have such a wide audience that this is required, but I think it's a healthy habit to nurture.

So, on to the project itself. Installed the SDL headers and libraries, and my compiler is aware of where they are. Next up, telling the project itself to link with the required libraries while compiling.

And already ran into the first problem. Just #including the SDL.h header is causing issues. Yay. The error I'm getting back is

c:\mingw\bin\..\lib\gcc\mingw32\4.4.0\..\..\..\libmingw32.a(main.o):main.c|| undefined reference to `WinMain@16'|


So, time to do a quick google search.... Well, that wsn't too helpful. Thankfully, I had a tutorial example that helped me along. The problem? I linked the libraries in the wrong order.


When including SDL.h, in mingw, I have to tell the linker to use mingw32, SDLmain, and SDL. In that order. Live and learn. And hopefully, with the blog, remember.

Behold:

#include <SDL/SDL.h>

int main( int argc , char * argv[] )
{
    return 0 ;
}

Well, not very exciting. And a little far from being a window just yet. But it compiles!

Now we can get started. I'm using SDL to interact with the OS, which includes creating a window. If I didn't, I'd be working in a console. And we don't want that. So first we have to start SDL up. For now, I only want to start the video subsystem, so I start off with

SDL_Init( SDL_INIT_VIDEO )

Now we need to actually create the window. This is done with a call to

SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)

which returns a pointer to a SDL surface with the qualities established in the parameters. SDL surfaces are the 'canvas' we will be drawing on, and the call creates a working window. the first flag tells it to use hardware memory, the second to be double buffered (meaning the image is first drawn in memory, then sent to the screen, instead of drawing directly to the screen). It should help with flickering.

And done. Got a window! The 'program' still does nothing, but I have a starting point. Next up: get OpenGL running by the weekend.

See ya!

Project 1 - Sci-Fi RPG

So, the first project I want to work on. A Computer Role Playing Game, or CRPG. Set in space.

My goals here are somewhat grand, which might explain why the going is so slow. I've been curious for a while about procedural content, an interest fed in no small part by Shamus Young (over at www.shamusyoung.com/twentysidedtale). The goal of this little project? To create an CRPG spanning an entire galaxy. The player should be able to explore different star systems, realistically (or close to it) depicted, visit planets and moons, etc. No two playthroughs should be alike, given the number of choices the players would be given.

I had originally wished to start by modeling a simple physics model, and add to it as I went, but that approach turned out not to be too practical. Instead, I've realized I need to approach this in a way that allows me to see some real progress on a relatively consistent fashion.

The plan now is to set achievable goals, and get them done in a timely manner. I know what the end product should look like, I just need to get from here to there. I don't know what the road will be like, and I guess I'll be doing a lot of backtracking this way, but three steps froward, two steps back is faster than no steps at all.

A more experienced programmer or designer would of course know how to start and handle a project better, but I'm lacking in experience. Hopefully I'll gain some of that with this.

So, first goal: Get a window running. I should be done with it by tonight, and post the screenshot.

Let's get cracking.

Tuesday, October 19, 2010

Hello, World!

Hello, everyone. Or no one, more likely.

The idea behind this blog is to track the development of different projects I want to work on. Primarily in programing. Hopefully the blog will provide the drive I need during slumps, as well as help document the decisions I make and the way I get around problems I'm sure to come upon.

I may occasionally post regarding other stuff that comes to mind, I suppose. It shouldn't be too often, and hopefully they will remain somehow, if tangentially, related to the projects I'm working on.

Of course, I already have a number of ideas that are slow in getting off the ground. I'll be
writing about them in my next post. Get this show on the road.

So welcome, I hope you find this blog interesting at least.