Wednesday, August 31, 2011

Rotation

With translation out of the way, I moved on to something more complicated. Rotation.

The last time I went about it, I tried it with quaternions. It was time consuming and I'm not entirely sure how it even ended up working. Euler angles are even worse. Vectors and matrices, however, seem more suited to solve the problem and play nice with OpenGL. And since the GLTools library I'm using already handles that, I really only need to wrap a few calls in my entities to get it working.

As I mentioned in my previous post, I have a reference frame determined by three vectors and a position. This makes local rotations simple. If I want to roll along the length of the model, I rotate around the forward vector. If I want to change the pitch of the model, I rotate around the right vector. And if I want to turn to either side, I rotate around the up vector.

I do the same basic set up as I did for translation, except my keys now affect rotation. I tie each pair of keys to a fixed rotation around each of my local axis, so that as long as I hold a key the model rotates and on release it stops rotating.

This gets the model turning about, and to spice things up I decide to give it the ability to accelerate. I first create a velocity vector, initially set to 0 so the body is at rest. Then, when I accelerate, I add to the vector a speed increment in the forward direction. When it comes time to update my entity's position, I add the velocity vector to it's position and done. This models newtonian movement well enough; velocity is constant unless I accelerate in a given direction. If I want to stop, I have to accelerate in the opposite direction to my movement.

I can rotate around independently of my direction of movement because I store the velocity vector in global coordinates. I could easily work out the vector in local coordinates as well, and it would provide a vector that moves around as I rotate the ship.

The result is Framework-0.0.2r.

What is missing, however, is that rotation should also be continuous unless I stop it. I'm modeling conservation of momentum, but not of angular momentum. I would need to store another vector, much like the velocity vector, which is affected by what kind of rotation I attempt. Unfortunately I'm not 100% sure on the physics involved here. Ideally I could just add vectors, same as with speed. The angle of rotation would be determined by the magnitude of the vector, while the axis of rotation would be the direction of the vector. That feels about right, and I should have it set up for Framework-0.0.4r.

Next up, I reworked my sphere building algorithms from the very beginnings of this blog. I used to use triangle strips, but together with the fixed pipeline that's obsolete. Vertex lists and indices is where it's at now.

Progress!

Lots to talk about. I haven't been posting here as often as I'd like, but I have managed to get quite a bit of work done.

Let's see, last time, I had a triangle on the screen. And I wanted to make it interactive. The first thing I did was create a few methods in my entity object that would modify the entity's frame of reference.The frame of reference describes the position and orientation of something in space, and helps translate between local and global coordinates.

Local coordinates are coordinates from the object's point of view, while global coordinates are the coordinate system of the world the object is in. For ease of reading, I'll denote local coordinates as (x,y,z)l, and global coordinates as (x,y,z)g.

From the object's perspective (meaning in local coordinates), all points are static. It's center is always at (0,0,0)l, and the axis are always (1,0,0)l,(0,1,0)l and (0,0,1)l. A point at (1,1,1)l is always at (1,1,1)l.

The same point in global coordinates, however, depends on the position and orientation of the object in the world. When the object sits still at the global origin, both coordinate systems match up. If the object moves 1 unit forward, so the local origin now sits at (1,0,0)g, then the point that is (1,1,1)l is actually (2,1,1)g.  

We can define a frame of reference as an origin (the position in global coordinates), a forward vector (in global coordinates) and an up vector (in global coordinates). From the cross product of these two vectors I can get the right vector, and these three determine the orientation of the local coordinate axis.

That's the difficult part out of the way, then.

I started with translation, which was easy enough. In order to advance 'forwards', I need to advance along the object's forward vector. To do this, I simply add the forward vector times a speed factor to the object's origin, and tie this to a key input. I can do the same with the up and right vectors, and the result is that the object will move around the screen when you press a key.

Quite surprisingly, I did it, and it worked! I was on a roll, so I even modified the shader so it would accept the color of a given point and shade the resulting triangle with the value of each point.

I'll put up a few screenshots later. I'm also thinking I'll put the demo here just in case. If I can work out how to do it.

And I did, thanks to google. Now you too can get Framework 0.0.1r! You only need 7zip to uncompress it, and it comes ready to run.

Monday, August 8, 2011

Back to square one again!

Well, I spent a lot of time scratching my head over a problem that turned out to be a simple dumb issue.

As I detailed in my last post, my models consists of pointers to video memory where the actual information is stored. In a fit of genius, I realized that if I deleted the model but forgot to clean up the information, I'd be leaking video memory. That would be bad. So I made sure that, before a model is destroyed, the information is cleaned up.

This seemed like a clever solution, and the subtle problem didn't become obvious to me until I spent two weeks wondering what I was doing wrong.

The problem happened with copying the model. I created the model in one part of the program, and copied it to the cache that would store it. This preserved the pointers, but when the original model was cleaned up it cleaned up the video memory. Just as I told it to. The copy was then left with pointers that were no longer valid. So when I then asked the program to render the model, it couldn't.

Thankfully the problem wasn't hard to solve. I instead created the model in a space in memory where I have more control of when it gets destroyed, and copy instead a pointer to it. This way the model object itself is never copied or destroyed and I can store it directly. In the process I learned a bit, so I call it a success.

I also had to add a shader to the program, which I should have probably worked into the framework before I did the work on the models. Thankfully I had a ready made function that handles all the aspects of reading the shader from file, loading it to video memory, compiling, linking and binding attributes.

So now, after a couple of months, I'm able to render a red triangle to the screen. Which is about what I was at before the rewrite. So, yay?

Well, to be honest I have a lot more than before. I can read input, and configure it (just need to add more input to be read). My storage is a lot more organized and extensible. I need to do some adjustments to my design to account for lessons learned, but I'm very happy with what I have.

Next up, I'll be adding some actual interactivity. Well, first deal with matrices, then interactivity.