It’s been a while…

It’s been a while since I’ve posted here, and ironically this will likely be my last post on this blog. Aside from being busy with university, I’ve also been working on setting up a small software company with some like-minded individuals. We’re starting off with some smaller scale app development to get the ball rolling, and will hopefully be moving on to larger projects in a few months time. Ion Engine may be involved. 😉

The main site for our company, Orange Bytes, can be found at orangebytes.net. My new blog can be found at taylorpetrick.com

Taylor


C-A-Day

One of the suggestions my C programming professor gave us was to try to use a command line editor to do as many assignments as possible if we weren’t already familiar with one. Given that I fall in the “never used one” category, I decided to take it a bit further and do a sort of “c program a day” using Vim via the OS X terminal. The purpose is actually two-fold; I’m also using it to learn new algorithms as each program deals with an algorithm I’ve never tried writing before.

I started on the 16th by implementing Newton’s Method for finding the roots of the function x^2 = 916; in other words, the square root of 916. Yesterday I wrote a prime number sieve, and today I wrote a MergeSort implementation. I’m getting to know Vim pretty well too, and although I still prefer using an IDE I could probably survive with command line if I had to.

With regards to Ion Engine, I’ve done a bit of work on some small items over the last few days. I haven’t had time to sit down and work for a solid block of time so progress has been rather limited. Hopefully during this week I’ll be able to work on some other larger features on my list (terrain engine in particular).

Taylor


Mesh loader

I’ve started to work on fleshing out a proper set of mesh classes for handling mesh instancing, animation and whatnot. I scrapped my old .obj loader and wrote a new one that’s more compliant with current codebase. Here are a few shots of the space scene running with different meshes:

Given that I do my modeling in Blender 3D, I felt obligated to include Susanne, a.k.a the “Blender monkey” as one of my test objects. I haven’t setup trimesh collisions for Bullet yet, so Susanne is stuck with (rather poorly sized) bounding box. The tori use a cylinder. I’m really happy with how the lighting is working on smoother objects though, and with the scene’s performance in general.

T


Math and Campus Maps

It’s been a few days since I’ve posted as I’ve been pretty busy with frosh week events here at Waterloo. Since I’m going into software engineering, I’m technically part of both the engineering faculty and math faculty and therefore participate in events from both faculties. At this point, all “softies” have earned their engineering hard hats and pink math ties:

The other night, one of the Comp Sci guys on my floor came to my room with a pretty cool math problem that would have fit well on Project Euler. It basically wanted to know how many downward-pointing triangles were present in a pyramid made of triangles. Of course, these downward-pointing triangles could be built from multiple triangles; for 4 layers of pyramid the answer is 7 because of one bigger multi-triangle:

Arriving at a computer-friendly answer is very easy, and requires a simple nested for-loop. I also found a slightly more elegant solution with only a single for-loop, but again this solution was fairly easy to come by. The hard part began when a few more CS/Eng students arrived and asked if we could find a single equation that would work for all cases that was “calculator-friendly”, i.e. without any looping. This was tougher.

We worked for several hours that night and didn’t really produce any solid results. I decided to try again earlier this morning and I’ve finally come up with an extremely ugly solution for cases were the number of layers (n) is greater than 4:

I’m still not satisfied, but I’m putting it away because I’ve already put way too much time into it.

 The second item I mentioned in my title is an idea for an app I’ve started working on. I used my iPhone + Google Maps to get around campus for the first few days, but this required some looking back/forth between the phone and the actual campus map. I think it would be handy to have a campus map super imposed onto Google Maps. In addition this map could take building names as start/end locations and plot the optimal route. I’m hoping to have this done by the time class starts on Monday. 😛

T


Move-In!

Well, it’s official, I’m a University of Waterloo student! Well not quite, classes don’t start until next week. The move was straightforward enough and I’m already starting to feel at home on campus.

Believe it or not, I still need to go buy several more textbooks. Many of the ones on my shelf are from previous courses I’ve taken that I thought might be handy for reference at some point.

Unfortunately today also marks a shift in gears as far as my own personal projects go. I’m still going to be working on my game engine, iPhone programming, RakNet, etc but will be unable to devote the same amount of time to them as I had over this past summer. It’s tough for me to judge how much free time I’ll have at this point in time, but I do know the program I’m in is supposed to require a significant weekly hour commitment. I guess I’ll just have to see how things go.

T


Tutorial : RakNet Replica3 on the iPhone

Sorry this took a while getting out, it took a bit longer than expected to finish.

One of the more useful features of RakNet, particularly for games, is the ReplicaManager plugin. This piece of code handles “replication” of objects, ensuring that all clients have the same state as the server. For example, in some of my previous posts I demonstrated the feature by running a physics simulation on a server and viewing the scene on several connecting clients. Replica is very scaleable and allows for fine-grained control over what aspects of an object are transmitted over the network, how reliable the arrival order is, etc. This tutorial will only cover a basic client-side iPhone integration based on the Replica sample provided with RakNet; more details on the specifics of using the system can be found in the RakNet forums and documentation section.

I’m going to assume that you’ve looked at the previous tutorials using RakNet, and have at least compiled an iOS RakNet library. We’ll need to use that to compile the application in this tutorial.

The first step is to create a new “View-based Application” project for the iPhone. You can name it whatever you want, but I’m using RaknetReplicaSample and this name may appear in some screenshots.

The original RakNet sample was contained in a single .cpp file that would run both  a client server, making things a bit confusing to work with. I’ve split the example into several files and added a logger class that will print to a UITextView, which makes things a bit easier on the iOS platform. These files, along with a compiled version of the RakNet sample for OS X, can be downloaded here.

Import these files into your newly created Xcode project and add them to the Classes folder. There are still some hooks to add to the ViewController and AppDelegate files. These are similar to the ones made for the previous tutorial on setting up a RakNet chat client on the iPhone, but there are a few key differences in the ViewController.

We’ll start off with the AppDelegate though. Like last time, we need to setup an NSTimer to run our network loop. Add a new NSTimer to the AppDelegate.h file at the end of the @interface declaration:

NSTimer * mTimer;

And a new method to actually call the network update function:

-(void) networkUpdate: (NSTimer *) pTimer;

The interface should look something like this:

In the corresponding implementation file, <ProjectName>AppDelegate.m, add the following line at the end of the application:didFinishLaunching: method:

mTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:@selector(networkUpdate:) userInfo:nil repeats: YES];

Then, add an implementation for the networkUpdate method:

-(void) networkUpdate: (NSTimer *) pTimer
{
	[self.viewController tickClient];
}

That’s all that needs to be done in AppDelegate. In theViewController.h, add the following includes to the top of the file, directly after UIKit.h. Please keep them in this order as well:

#include "SampleReplica.h"
#include "SampleConnection.h"
#include "SampleReplicaManager.h"
#include "Logger.h"

Then add the following items to the @interface declaration:

	NetworkIDManager networkIdManager;
	RakPeerInterface *rakPeer;
	ReplicaManager3Sample replicaManager;
	SocketDescriptor sd;
	Packet *packet;

	UITextView* mTextView;

This is similar to the chat tutorial, but now we had a new “ReplicaManager” instance. This class and its super class handle much of the work behind the object replication process.

We’ll also need to add the following lines after the closing brace on the @interface declaration:

-(void)tickClient;

@property (nonatomic,retain) IBOutlet UITextView* mTextView;

In the implementation file for the ViewController, we first need to @synthesize  the UITextView by adding the following line after the @implementation declaration:

@synthesize mTextView;

In the viewDidLoad method, add the following block of code after the call to the super class:

	Logger::getLogger()->setTextView(mTextView);

	sd.socketFamily=AF_INET;
	sd.port=0;

	rakPeer = RakNet::RakPeerInterface::GetInstance();
	rakPeer->Startup(32,&sd,1);
	rakPeer->AttachPlugin(&replicaManager);
	replicaManager.SetNetworkIDManager(&networkIdManager);
	rakPeer->SetMaximumIncomingConnections(32);	

	rakPeer->Connect("192.168.2.17",12345,0,0,0);

Just like the chat client example, the IP address can and should be changed to suit the environment you’re using. The port needs to remain as it is, since the RakNetReplicaSample server is hardcoded to use this port. The port shouldn’t be a problem for anyone.

Next, add the tickClient method. This method is called by our NSTimer once every 10th of a second and updates the network:

-(void)tickClient
{
	for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive())
	{
		switch (packet->data[0])
		{
			case ID_CONNECTION_ATTEMPT_FAILED:
				Logger::getLogger()->appendLine(@"Connection failed\n");
				break;
			case ID_NO_FREE_INCOMING_CONNECTIONS:
				Logger::getLogger()->appendLine(@"Connection failed - No free incoming connections\n");
				break;
			case ID_CONNECTION_REQUEST_ACCEPTED:
				Logger::getLogger()->appendLine(@"Connection request accepeted\n");
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				Logger::getLogger()->appendLine(@"Disconnecting\n");
				break;
			case ID_CONNECTION_LOST:
				Logger::getLogger()->appendLine(@"Connection lost\n");
				break;
			case ID_SND_RECEIPT_LOSS:
			case ID_SND_RECEIPT_ACKED:
			{
				uint32_t msgNumber;
				memcpy(&msgNumber, packet->data+1, 4);

				DataStructures::Multilist replicaListOut;
				replicaManager.GetReplicasCreatedByMe(replicaListOut);
				DataStructures::DefaultIndexType idx;
				for (idx=0; idx < replicaListOut.GetSize(); idx++) 
				{ 					
                                      ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED);
				}
			}
				break;
		}
	}
}

Finally, add two lines of clean-up to the viewDidUnload method to disconnect from the network:

	rakPeer->Shutdown(100,0);
	RakNet::RakPeerInterface::DestroyInstance(rakPeer);

Like last time, we also need to tell the compiler that this project will contain some C++ code. This means that all the .m files need to be renamed to .mm, or set to compile as Objective-C++ by right clicking them, going to Get Info, and changing the File Type field to Objective-C++:

That should cover all of the code changes needed. You’ll also need to add a new UITextView to the interface builder and link it to the UITextView created in the ViewController class. This will be used to display output from the server and any connect/disconnect messages.

Like last time, you’ll need to include your RakNet library and add the RakNet source directory to your header search path. At this point, everything should be ready to compile.

To test the app, first start the provided OS X Replica application. Since this application is taken straight from RakNet, it can run as a client, server or peer-to-peer instance. Start it as a server by entering ‘s’ into the console. You can create new objects by pressing ‘c’, and assign them random values with ‘r’. Delete all created objects with the ‘d’ key. All three of these actions will be replicated on the client, and can be verified by checking the output UITextView on the iPhone app.

As always, I’m happy to help sort out any issues that come up with the tutorial. Let me know in the comments if you need anything, or if there are any other specific RakNet samples that you want to see ported to the iPhone.

T


New Light Manager and Deferred Rendering

I haven’t had as much time to work on Ion over the last few days, however I put in a few solid hours of coding today and finished the first version of my light manager. It’s only hooked into the deferred renderer at the moment, since I still need to finish the code that’ll allow the forward render to pull a limited number of lights based on some input factors. At the moment lights can have custom position, color, intensity and a variety of different fall-off equations. I’d like to add support for lens flares and cubemap masks later on, to add some extra visuals.

For the deferred renderer, the light values are stored in a pixel buffer which is passed into the final deferred shader. Unfortunately I’m doing something wrong with glTexSubImage2D and isn’t working properly, so I haven’t been able to add light animation to the system quite yet.

The scene setup I’m using to test things out has 7 lights in it; a white light at the center, four colored lights at the four sides of the cube stack, and then two small, bright lights (pink and cyan) directly next to the stack. I setup the scene to be in a 0-gravity space environment as well, so the effects of the lighting were more obvious. Here are a handful of screenshots of the test scene:

I also recorded a video, however QuickTime dropped the FPS down a bit. In the video each click adds another 6x6x6 group of physics cubes; at the end of the recording there are several thousand floating around. At one point there’s a bit of visual lag since I added several groups in quick succession, and the physics and render loop are tied together. Anyways, here’s the video: http://vimeo.com/28528048.

That’s it!

T


Rubik’s Cupe Map – Part 2

Earlier in the week I mentioned that I was working on “cube mapping” one of the may ways to solve a Rubik’s Cube. I’ve finished rendering out the final video of the process, starting with a scrambled cube and ending at a solved Rubik’s Cube. The movement in the cube is admittedly a bit harder to follow than I expected it to be, but the results were still quite interesting.

I’ve uploaded two versions of the video. One’s rendered at 24 fps and the other at 8 fps:

Rubik’s Cube Map Fast (24 fps)

Rubik’s Cube Map Slow (8 fps)

T


Python Support for Ion

I’ve been a bit quiet on the Ion Engine front for a few day now, mainly because I’ve been reacquainting myself with Python. As I mentioned before, I picked up a book on the language as I was considering using it as the scripting language for my engine. I’ve decided that it’s the route I want to take, so I took a look at it today to see what sort of work would have to be done.

There are two ways to integrate Python with a C++ application: compile the application as a Python module and load it into Python, or compile Python into the application and run it as an embedded interpreter. The Torque MMOKit, a package I used extensively a few years back, makes use of the first method. It caused a lot problems with packaging, since end users expect an .app or .exe file to run, not a .py file. The tool chain used a Python utility called Py2Exe to solve this, but it wasn’t without its problems and was often challenging to set up correctly. I eventually ended up moving over to the second method, which required significant work upfront but greatly simplified the overall distribution process. I plan to use the embedded method again for Ion Engine.

On the Windows platform, Python is extremely straightforward to integrate. There are pre-built .lib files included in the binary package, so no source building is required. The Mac platform is kinda in between. Apple provides Python support as part of the default libraries, but unfortunately it’s a little bit outdated at this point. I intend to use Python 3.2.x, while Snow Leopard shipped with version 2.7.x. It was fairly easy to upgrade though,and a Python.Framework is provided so I won’t need to compile source in this case either. The iPhone is a huge pain in the ass.The iPhone has very little support for Python at all; in fact until recently the App Store wouldn’t even allow embedded Python in any apps.

I haven’t been able to find compiled versions of Python 3.2 for the iPhone, so I decided to try compiling it myself. Python’s make compile settings worked just fine on my Mac, but I have no idea how to set up make to compile an iPhone library. I had to move everything over to Xcode to do this, which turned to be a lot more tedious than I expected. Eventually it all worked out though, and I was able to test a very basic “hello world” Python script in the iPhone simulator.

I intend to start integrating the Python stuff into Ion tomorrow. The end goal is to be able to control most of the game logic and world building with Python, leaving networking, rendering, scene management, etc to the core C++ engine. Since it’s a pretty big task and I’m not sure how long it will take, I plan to develop it in parallel with some of the other components I’m working on. That way I won’t get sucked into working on the one thing and burn out.

T


Tutorial : RakNet Chat on the iPhone

The purpose of this tutorial is to set up a basic chat client on the iPhone that is able to connect to a chat server running on your computer. Since this tutorial is for the iPhone, I’m going to assume that you’re using a Mac and therefore the example chat server is intended to be run on OS X. We’ll get to that in a minute though.

I’m going to assume you’ve completed the last RakNet tutorial and have compiled RakNet into an iPhone library. We’ll be using that library again here in this tutorial.

The first step is to create a fresh iPhone project. I chose a View-based Application since it’s basic and doesn’t include anything extra:

You can name the project whatever you want, but I’ll be using “RaknetTextClient.”

Once the project is created, the first thing you’ll need to do is add the library to the project as described in the last tutorial. I’ll paraphrase it again here though:

  • To add the library, right-click the Frameworks folder and go to Add>Existing items. Chose your RakNet library.
  • To add the headers, right-click the project and go to Get Info. Scroll down to the Search Paths and add the folder containing your RakNet source to the Header Search Paths variable.

With these items done you’re ready to start adding RakNet code. First, open up AppDelegate.h. We’re going to use this file to control the network updates for RakNet. To do this, we need to add a new NSTimer to the interface and a networkUpdate method. After these changes, I have this:

If you want to copy the code, here are the changes:

NSTimer * mTimer;
-(void) networkUpdate: (NSTimer *) theTimer ;

Since our project is using RakNet, it will need to be able to compile C++ code. To do this, we need to rename our AppDelegate.m file to AppDelegate.mm. This will tell Xcode that it can expect some C++ code in the project.

In the newly renamed file, we also need to make some changes to work with the changes to AppDelegate.h. The first item of business is to add the following line to the method -(BOOL)application: … didFinishLaunchingWithOptions: …

mTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:@selector(networkUpdate:) userInfo:nil repeats: YES];

This line can go anywhere, but I placed it right before the return YES; line. The next step is to add the networkUpdate method. This can go anywhere in the file, but I put it directly after the method mentioned above. The method looks like:

-(void) networkUpdate: (NSTimer *) theTimer
{
[self.viewController tickClient];
}

That’s all for AppDelegate changes, onto the ViewController modifications. These are a bit more extensive since we’re going to be adding some actual RakNet code. The first set of changes needed are in ViewController.h. Add the following #includes below the existing #includes in the file:

#include "MessageIdentifiers.h"
#include "RakPeerInterface.h"
#include "RakNetStatistics.h"
#include "RakNetTypes.h"
#include "BitStream.h"
#include "PacketLogger.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include "RakNetTypes.h"
#include "Kbhit.h"
#include <unistd.h>
#include "Gets.h"

After all of the #includes you’ll also need to add  using namespace RakNet; before the @interface declaration. Inside the declaration, add the following variables:

RakPeerInterface *mRakPeer;
SystemAddress mClientID;
Packet* mPacket;
unsigned char mPacketID;
UITextField *mSendText;
UITextView *mTextBox;

The first four are for RakNet, and the last two are for the simple UI we’re going to set up in a bit. We also need to add some methods and properties; these are added after the interface declaration. Add the following:

-(void)appendMessage:(NSString*)message;
-(IBAction)sendMessage;
-(void)tickClient;

@property (nonatomic, retain) IBOutlet UITextField *mSendText;
@property (nonatomic, retain) IBOutlet UITextView *mTextBox;

That’s all for the ViewController header file.

Like the AppDelegate.mm file, we need to rename the ViewController.m file so it has the .mm extension. Inside the file, we first need to add a @synthesize line for our two properties. This goes directly after the @implementation line.

@synthesize mTextBox, mSendText;

Directly after this, I added the following method that was taken straight from RakNet’s chat sample:

unsigned char GetPacketIdentifier(RakNet::Packet *p)
{
	if (p==0)
		return 255;

	if ((unsigned char)p->data[0] == ID_TIMESTAMP)
	{
		RakAssert(p->length > sizeof(RakNet::MessageID) + sizeof(RakNet::Time));
		return (unsigned char) p->data[sizeof(RakNet::MessageID) + sizeof(RakNet::Time)];
	}
	else
		return (unsigned char) p->data[0];
}

This method will be used by the network code to determine what sort of message is being sent. The next code change is in the viewDidLoad method. By default this method is commented out, so uncomment it and then add the following code after [super viewDidLoad]; 

mRakPeer  = RakPeerInterface::GetInstance();
mClientID = UNASSIGNED_SYSTEM_ADDRESS;

SocketDescriptor socketDescriptor(54001,0);
socketDescriptor.socketFamily=AF_INET;

mRakPeer->Startup(8,&socketDescriptor, 1);
mRakPeer->SetOccasionalPing(true);	

ConnectionAttemptResult car = mRakPeer->Connect("192.168.2.17", 54000, "Rumpelstiltskin", (int) strlen("Rumpelstiltskin"));

RakAssert(car==RakNet::CONNECTION_ATTEMPT_STARTED);

You’ll want to change a few things here based on your network configuration. In the Connect() method, the first number is the IP address of the machine that will be running the server and the second number is the port for the server. If you’re only going to run this application on the iPhone simulator, you can use “127.0.0.1” as the IP. If you plan on testing on the device itself you’ll need to use the internal IP of the computer that you plan to use as the server. There are a variety of ways to find this; I got mine by checking the config page for my router. If you’re using AirPort and a WiFi connection it’ll be list on the first page of the AirPort settings, something like “AirPort is connected toand has the IP address 192.168.2.17.” You should be able to keep the port number the same. The “Rumpelstiltskin” is the default password in RakNet and should be left as-is to work with the server application.

Anyways, there are a few more changes that need to be made. Add the following set of methods (it’s a long one):

//Sends a message to the server and appends it to the text view
- (IBAction)sendMessage
{
	NSString* message = [NSString stringWithFormat:@"iPhone: %@",mSendText.text];
	const char* cMessage = [message cStringUsingEncoding:NSASCIIStringEncoding];
	mRakPeer->Send(cMessage, (int) strlen(cMessage)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);

	[mSendText setText:@""];
	[self appendMessage:message];
}

//Appends a string to the text view
-(void)appendMessage:(NSString*)message
{
	mTextBox.text = [NSString stringWithFormat:@"%@\n%@", mTextBox.text,message];
}

//Update the network and checks for incoming packets
-(void)tickClient
{
	for (mPacket = mRakPeer->Receive(); mPacket; mRakPeer->DeallocatePacket(mPacket), mPacket=mRakPeer->Receive())
	{
		// We got a packet, get the identifier with our handy function
		mPacketID = GetPacketIdentifier(mPacket);

		// Check if this is a network message packet
		switch (mPacketID)
		{
			case ID_DISCONNECTION_NOTIFICATION:
				// Connection lost normally
				[self appendMessage:@"Disconnected from server."];
				break;
			case ID_ALREADY_CONNECTED:
				[self appendMessage:@"Already connected to server."];
				break;
			case ID_INCOMPATIBLE_PROTOCOL_VERSION:
				[self appendMessage:@"Protocol version error"];
				break;
			case ID_REMOTE_DISCONNECTION_NOTIFICATION:
				[self appendMessage:@"A client disconnected from the server."];
				break;
			case ID_REMOTE_CONNECTION_LOST: 
				[self appendMessage:@"A client lost connection with the server."];
				break;
			case ID_REMOTE_NEW_INCOMING_CONNECTION: 
				[self appendMessage:@"Another client is connecting!"];
				break;
			case ID_CONNECTION_BANNED:
				[self appendMessage:@"You are banned from this server."];
				break;
			case ID_CONNECTION_ATTEMPT_FAILED:
				[self appendMessage:@"Connection failed. "];
				break;
			case ID_NO_FREE_INCOMING_CONNECTIONS:
				[self appendMessage:@"Server is full."];
				break;

			case ID_INVALID_PASSWORD:
				[self appendMessage:@"Invalid password."];
				break;

			case ID_CONNECTION_LOST:
				[self appendMessage:@"Connection lost."];
				break;

			case ID_CONNECTION_REQUEST_ACCEPTED:
				[self appendMessage:@"Connection accepted."];
				break;
			default:
				[self appendMessage:[NSString stringWithCString:(const char*)mPacket->data encoding:NSASCIIStringEncoding]];
				break;
		}
	}
}

There’s only one more change needed to this file and then we’ll build the UI. In the viewDidUnload method, add the following code to cleanup the network:

// Be nice and let the server know we quit.
mRakPeer->Shutdown(300);

// We're done with the network
RakPeerInterface::DestroyInstance(mRakPeer);

That’s all the source code changes we’ll need! Next, open up theViewController.xib file. We need to place 3 items: a UITextView, a UITextField and a UIButton. The UITextView should be connected to the “mTextBox” variable, and then UITextField should connect to the “mSendText” variable. The “sendMessage” method should be connect to the UIButton’s Touch Up event. Here’s the full configuration I used:

Save the .xib, and then compile. At this point you’re ready to test the app, so you’ll need to run some sort of server. I’ve compiled the RakNet sample server as a Mac app, which will work with this iPhone app. You can download the server file here.

Download and launch the server app, and enter “54000” as the port to listen on. You can then run your iPhone app on the simulator or device, and it should connect to the server. A connection message will be displayed in the server window when a client successfully connects. Typing messages in the server window will send the messages to all connected clients, as well typing/sending messages on the iPhone app.

Let me know if there are any issues and I’ll try to help resolve them. I’m going to do put up an example of using RakNet’s ReplicaManager on the iPhone as well, possibly tonight but more likely tomorrow.

T