-
Working on a new blog design
This blog is based on a standard Tumblr theme. While I really like it .. it is not very suited for sections with wide blocks of code - as is evident below.
Therefore, I have started work on a new blog design that will also be the future visual representation for twoFLY.
-
Creating a chase camera with GLKit
For the game I am working on I needed to create a camera that will follow an object from a specific distance and angle. This kind of camera - also known as a chase camera - is very popular in racing games and other third person games.
Before moving to iOS and Apple devices I did most of my games programming in XNA Game Studio. I was therefore lucky enough to own a copy of the excellent book “3D Graphics with XNA Game Studio 4.0” by Sean James and this book describes how to create a chase camera. Obviously, the book uses C# and XNA for it’s code so it is not usable on the iOS platform directly - but the concept is the same.
The concept of the chase camera is visualized in the next figure: (source: 3D Graphics with XNA Game Studio 4.0 by Sean James (PACKT Publishing))

An offset from the camera position is used to determine the view direction and chase distance. The target position is calculated by using an offset from the object’s position. Based on these values the view matrix can then be calculated.
The chase camera created by Sean James has the added bonus of having a relative rotation value that allows the camera to rotate independently of the object as well as a “springiness” value that allows the camera to “bend” from side-to-side instead of rigidly following the object. This makes the camera behave more like real-life and gives a much better feeling of movement.
As the code created by Jean James is targeted for XNA I have re-created to code in Objective-C using GLKit math helpers. As some of the math methods used in XNA does not exist in GLKit I have had to re-create these methods in Objective-C using code created for the MonoXNA project.
ChaseCamera.h
// // ChaseCamera.h // #import "AbstractCamera.h" @interface ChaseCamera : AbstractCamera @property (nonatomic, readwrite) GLKVector3 position; @property (nonatomic, readwrite) GLKVector3 target; @property (nonatomic, readwrite) GLKVector3 followTargetPosition; @property (nonatomic, readwrite) GLKVector3 followTargetRotation; @property (nonatomic, readwrite) GLKVector3 positionOffset; @property (nonatomic, readwrite) GLKVector3 targetOffset; @property (nonatomic, readwrite) GLKVector3 relativeCameraRotation; @property (nonatomic, readwrite) float springiness; - (id) initChaseCameraWithPositionOffset:(GLKVector3)positionOffset targetOffset:(GLKVector3)targetOffset relativeCameraRotation:(GLKVector3)relativeCameraRotation; - (void) moveToTargetFollowPosition:(GLKVector3)targetFollowPosition targetFollowRotation:(GLKVector3)targetFollowRotation; - (void) rotate:(GLKVector3)rotationChange; @endChaseCamera.m
// // ChaseCamera.m // #import "ChaseCamera.h" #import "MathHelper.h" @implementation ChaseCamera - (id) initChaseCameraWithPositionOffset:(GLKVector3)positionOffset targetOffset:(GLKVector3)targetOffset relativeCameraRotation:(GLKVector3)relativeCameraRotation { if (( self = [super init] )) { _positionOffset = positionOffset; _targetOffset = targetOffset; _relativeCameraRotation = relativeCameraRotation; } return self; } - (void) setSpringiness:(float)springiness { // Clamp the value between 0 and 1 _springiness = clamp(springiness, 0, 1); } - (void) moveToTargetFollowPosition:(GLKVector3)targetFollowPosition targetFollowRotation:(GLKVector3)targetFollowRotation { _followTargetPosition = targetFollowPosition; _followTargetRotation = targetFollowRotation; } - (void) rotate:(GLKVector3)rotationChange { _relativeCameraRotation = GLKVector3Add(_relativeCameraRotation, rotationChange); } - (void) update { // Sum the rotations of the model and the camera to ensure it is // rotated to the corret position relative to the model's rotation GLKVector3 combinedRotation = GLKVector3Add(_followTargetRotation, _relativeCameraRotation); // Calculate the rotation matrix for the camera GLKMatrix4 rotation = Matrix4MakeFromYawPitchRoll(combinedRotation.y, combinedRotation.x, combinedRotation.z); // Calculate the position the camera would be without the spring // value, using the rotation matrix and the target position GLKVector3 desiredPosition = GLKVector3Add(_followTargetPosition, Vector3Transform(_positionOffset, rotation)); // Interpolate between the current position and the desired position _position = GLKVector3Lerp(_position, desiredPosition, _springiness); // Calculate the new target using the rotation matrix _target = GLKVector3Add(_followTargetPosition, Vector3Transform(_targetOffset, rotation)); // Obtain the up vector from the matrix GLKVector3 up = Vector3Transform(GLKVector3Make(0.0f, 1.0f, 0.0f), rotation); // Recalculate the view matrix self.view = GLKMatrix4MakeLookAt(_position.x, _position.y, _position.z, _target.x, _target.y, _target.z, up.x, up.y, up.z); } @endAs you might have noticed the ChaseCamera is inheriting from the AbstractCamera class. The AbstractCamera class is very basic and I created it because I will have a few different camera’s in my game. Using the abstract camera class allows be to switch cameras seamlessly as for any camera all you ever care about is the projection and view matrices . What else the camera is doing to calculate these are mostly irrelevant to the rest of the game. I have posted the code for the AbstractCamera class below.
AbstractCamera.h
// // AbstractCamera.h // @interface AbstractCamera : NSObject // The view matrix for the camera @property (nonatomic, readwrite) GLKMatrix4 view; // The projection matrix for the camera @property (nonatomic, readwrite) GLKMatrix4 projection; // Update the camera - (void) update; @endAbstractCamera.m
// // AbstractCamera.m // #import "AbstractCamera.h" @implementation AbstractCamera - (id) init { if (( self = [super init] )) { [self generatePerspectiveProjectionMatrixWithFieldOfView:45.0f]; } return self; } - (void) generatePerspectiveProjectionMatrixWithFieldOfView:(float)fieldOfView { // Get the size of the screen CGRect winRect = [[UIScreen mainScreen] bounds]; // Calculate aspect ratio float aspectRatio = winRect.size.width / winRect.size.height; // Create perspective projection matrix self.projection = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(fieldOfView), aspectRatio, 0.1f, 2000.0f); } - (void) update {} @endAs already mentioned above some of the math methods available in XNA do not exist in GLKit. Therefore, I created a MathHelper class that has methods for these missing XNA methods. I have cut-out those methods and made a focused MathHelper class just for the ChaseCamera class
MathHelper.h
// // MathHelpers.h // #pragma mark - #pragma mark Prototypes #pragma mark - static __inline__ GLKVector3 Vector3DDirectionToAngle(GLKVector3 direction); // Transforms a GLKVector3 by the given matrix static __inline__ GLKVector3 Vector3Transform(GLKVector3 vector, GLKMatrix4 transform); // Creates a new rotation matrix from a specified yaw, pitch and roll angles static __inline__ GLKMatrix4 Matrix4MakeFromYawPitchRoll(float yaw, float pitch, float roll); // Creates a new quaternion from specified yaw, pitch and roll angles static __inline__ GLKQuaternion QuaternionMakeFromYawPitchRoll(float yaw, float pitch, float roll); #pragma mark - #pragma mark Implementations #pragma mark - static __inline__ GLKVector3 Vector3DDirectionToAngle(GLKVector3 direction) { // Convert direction vector to angle float rotation = (float) acos(direction.y > 0 ? -direction.x : direction.x); if (direction.y > 0) rotation = M_PI; rotation = M_PI_2; return GLKVector3Make(0, rotation, 0); } static __inline__ GLKVector3 Vector3Transform(GLKVector3 vector, GLKMatrix4 transform) { return GLKVector3Make(((vector.x * transform.m00) (vector.y * transform.m10) (vector.z * transform.m20) transform.m30), ((vector.x * transform.m01) (vector.y * transform.m11) (vector.z * transform.m21) transform.m31), ((vector.x * transform.m02) (vector.y * transform.m12) (vector.z * transform.m22) transform.m32)); } static __inline__ GLKMatrix4 Matrix4MakeFromYawPitchRoll(float yaw, float pitch, float roll) { GLKMatrix4 matrix; GLKQuaternion quaternion; quaternion = QuaternionMakeFromYawPitchRoll(yaw, pitch, roll); return GLKMatrix4MakeWithQuaternion(quaternion); } static __inline__ GLKQuaternion QuaternionMakeFromYawPitchRoll(float yaw, float pitch, float roll) { GLKQuaternion quaternion; quaternion.x = (((float)cos((double)(yaw * 0.5f)) * (float)sin((double)(pitch * 0.5f))) * (float)cos((double)(roll * 0.5f))) (((float)sin((double)(yaw * 0.5f)) * (float)cos((double)(pitch * 0.5f))) * (float)sin((double)(roll * 0.5f))); quaternion.y = (((float)sin((double)(yaw * 0.5f)) * (float)cos((double)(pitch * 0.5f))) * (float)cos((double)(roll * 0.5f))) - (((float)cos((double)(yaw * 0.5f)) * (float)sin((double)(pitch * 0.5f))) * (float)sin((double)(roll * 0.5f))); quaternion.z = (((float)cos((double)(yaw * 0.5f)) * (float)cos((double)(pitch * 0.5f))) * (float)sin((double)(roll * 0.5f))) - (((float)sin((double)(yaw * 0.5f)) * (float)sin((double)(pitch * 0.5f))) * (float)cos((double)(roll * 0.5f))); quaternion.w = (((float)cos((double)(yaw * 0.5f)) * (float)cos((double)(pitch * 0.5f))) * (float)cos((double)(roll * 0.5f))) (((float)sin((double)(yaw * 0.5f)) * (float)sin((double)(pitch * 0.5f))) * (float)sin((double)(roll * 0.5f))); return quaternion; }That is it .. I will try an package this code in an example later on. For now I just wanted to post it so that I could close a question I made on the Ray Wenderlich forum some time ago.
Hope you find the code useful. If you have questions feel free to contact me via Twitter.
-
OpenGL - You turn my world upside down
Yesterday I was working on a small side-project for the One Game a Month challenge. Naturally, I was going for an OpenGL game and I realized something that I was not seeing in my current project: My world was upside down.
The horror!!! “Obviously, this is a bug in my code” I thought. So, I naturally started looking at the math behind the auto-generated geometry I was rendering. I am no math wiz anymore, I must admit. I was pretty good at once - but after 12 years of not using vector algebra and matrices most of my knowledge has been replaced by stuff that I use in my daily work at a large pharmaceutical company. So, I started to debug ….
After debugging my code - calculating coordinates on paper - re-checking that my understanding of cross products and dot products were correct, dumping arrays of coordinates etc I found that everything was correct in my code .. so WTF!??!?!??!?!?!?!
Turned out that I had forgotten one simple small fact: The camera moves the world around it and therefore all transformations on the camera must be inverted. So what I thought was “upside down” was actually just me viewing the geometry from below as I was translating the camera from a Y-coordinate of 0 to +10 thereby translating the geometry in the opposite direction of what I was intending.

I recommend reading this excellent post about cameras in OpenGL by Diney Bomfim.
-
It went horribly wrong
Programming can be just as frustrating as it is fun. While creating the latest game I ran into a few bugs that left my hex planet in a horrible state.
During my attempt to create the planet I first ran into this - not so desirable - effect:

Turned out the winding of some of the triangles in the planet was not in counter-clockwise order so when GL_CULL_FACE was enabled these triangles were culled by GL. So, had to make some corrections to the code to ensure the same winding as it makes it simpler to do collision detection and lighting.
Then when I ported the planet into my production code I got this very “Dark side of the moon” effect.

While it was not the effect I wanted it looks pretty cool. Turned out that I forgot to attach the proper shader to the model - so it was an easy fix. In the end it looked like this (without proper texturing):

By the way - the code to generate the HexPlanet was inspired by this whitepaper and accompanying source code (which I converted from C++ to Objective-C) created by Joel Davis. I will release the source code for the HexPlanet converted to Objective-C on GitHub soon.
-
Welcome to twoFly
Welcome to twoFly. twoFly is a one man hobby project based Denmark trying to bring small but exciting games to the Apple AppStore.
Currently I am working on my first 3D game written in Objective-C using GLKit and OpenGL ES 2.0. The game is a small strategy war game focusing on fast 1-4 player war mongering among growing nations.
As this game will also be my first venture into the new world that is OpenGL ES I will be posting small tutorials on this page in the coming 3-4 months while creating the game. It will primarily be a way for me to document what I learn but hopefully this will also be helpful to others.
In case you want to get in contact with me you can follow me on Twitter (@twofly3).