My Time as a Human Being

Computers, retro gaming, running, and baking. Maybe not interesting to others.

Stunt Car Racer: Starting From Zero

Before starting work on making graphics, sound, and input work in the browser-based version of Stunt Car Racer, I needed a baseline: A point where everything compiles, doesn’t crash, but otherwise doesn’t do much. Getting there required a surprising amount of grunt work.

This is the second post in a series about recreating the Stunt Car Racer game for modern platforms. In the first post we went through why I want to do this. I found the Stunt Car Racer Remake project on SourceForge which is a C++ code base very specifically targeting Windows and DirectX. I want to make this run in a browser using the amazing emscripten framework, which, as we will find out, involves implementing a whole lot of Windows and DirectX APIs using standard C/C++ and OpenGL.

Anyhow, before we get to the exciting stuff like drawing graphics on the screen, we need to have something compiling. This post is about getting to that point.

Reading time: 10 minutes.

Initial Status

Well, I started by installing emscripten with Homebrew. As with most things Homebrew, this was straightforward. Now I could run the emcc command, which will compile C++ code and output WebAssembly. It seems like the Common directory contains files that need to be on the include path, so let’s try this (on this git SHA):

$ emcc StuntCarRacer.cpp -ICommon/            
In file included from StuntCarRacer.cpp:10:
Common/dxstdafx.h:45:10: fatal error: 'windows.h' file not found
#include <windows.h>
         ^~~~~~~~~~~
1 error generated.

Good news: There’s only one error. But the error also indicates that I may need to implement a bit of Windows-specific APIs and types, in addition to the DirectX stuff I was already aware of.

Let’s remove the requirement of the dxstdafx.h file from the StuntCarRacer.cpp file, to get an initial feel for how much is required, and let’s try again (output pruned a bit):

emcc StuntCarRacer.cpp -ICommon/ 
In file included from StuntCarRacer.cpp:13:
./StuntCarRacer.h:40:5: error: unknown type name 'D3DXVECTOR3'
    D3DXVECTOR3 pos;    // The untransformed position for the vertex
    ^
./StuntCarRacer.h:41:5: error: unknown type name 'DWORD'
    DWORD color;                // The vertex diffuse color value
    ^
./StuntCarRacer.h:42:2: error: unknown type name 'FLOAT'
        FLOAT tu,tv;            // The texture co-ordinates
        ^
[...]
In file included from StuntCarRacer.cpp:14:
./3D Engine.h:141:8: error: unknown type name 'HRESULT'
extern HRESULT CreatePolygonVertexBuffer (IDirect3DDevice9 *pd3dDevice);
       ^
./3D Engine.h:141:43: error: unknown type name 'IDirect3DDevice9'
extern HRESULT CreatePolygonVertexBuffer (IDirect3DDevice9 *pd3dDevice);
                                          ^
./3D Engine.h:145:26: error: unknown type name 'POINT'
extern void DrawPolygon( POINT *pptr,
                         ^
[...]
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.

Bad news: There’s definitely more than one error. I have no experience with C++ programming on Windows, so all these upper-case types (FLOAT instead of just using the C++ float, same with DWORD and many others) was new to me. Apparently Windows C++ programmers are not content with the types provided by default by C++. I con’t know why, but probably it has something to do with being explicit about how many bits are in which types, which types are signed, etc.

There’s also a thing called HRESULT which seems to be used throughout the Windows APIs. This defines a set of standard error codes which are luckily well documented.

And of course DirectX is rearing its head a bit here, in the form of D3DXVECTOR3 IDirect3DDevice9, and POINT.

The Process

I had to implement all of these types. No turning back now - the job was to go through each and every error with missing types or missing methods, and implement what was missing. I am not going to write up everything I did in this phase, as it will not be interesting to anybody. Apart from the unexpected amount of Windows types, it gave me a good idea of the amount of functionality I would have to implement later to emulate the DirectX API.

It turned out that DirectX is amazingly well documented. I work for Microsoft, so this may seem like a plug, but really, whenever I had to look up a type, I just Googled Binged it, and in most cases the first search result pointed to the official documentation, which always turned out to be thorough and well-written.

While churning through these compile errors, one thing that got me a few times was that I thought I was done because I had just fixed the last compile error. But unfortunately, that just meant that emcc was able to compile that file, and then it would move on to the next file, and then a whole new set of compile errors would be shown to me. It seemed like a never-ending game of “whack-a-mole”.

Hello, emscripten Console

But eventually I got there: The glorious commit where everything compiles. You can check it out and run the ./build-and-serve.sh script. This will compile the code and start up a web server so you can go to http://localhost:8000/source.html. This will show you the default emscripten page that looks like this:

Screenshot of Emscripten console with an exception

The big black box with text shows some debug output. Basically that’s just a lot of puts lines from all of the stubbed functions I’ve created. This is not super interesting, except for the fact that, hey, something’s working!!

The black box in the middle of the screen is where, eventually, our OpenGL code will render beautiful 3D images. Since we only have stub functions, nothing is happening here for now.

But also, notice the text “Exception thrown, see JavaScript console”. Let’s look at the JavaScript console:

Screenshot of JavaScript console with an error

Something is out of bounds. But what?

Debugging or Not

I really don’t know how to properly debug WebAssembly. Chrome has some plug-ins for single-stepping WebAssembly bytecode, but I haven’t found anything that lets me set breakpoints and step through the original C++ source code. I may have been looking in the wrong places.

So… printf and puts debugging…

I’ll spare you most of the details. I had to implement resource loading, otherwise the track reading code would naturally crash. I had to implement some other APIs just enough to not make everything crash. Basically it’s “a lot of nothing”.

And finally, in this commit, we get this console:

Screenshot of Emscripten console with no exceptions

Yay!

One More Thing

The above version executes one iteration of the main loop, and then exits. But let’s end this post on a high note: We want the game loop to run over and over again, just like it’s going to do when the game is done. But of course with no graphics, no sound, and no input. And lots of other stuff missing.

The original code has this call to a DirectX function that will run an internal loop until the user quits the game. Then the function will exit, and the game will clean up all resources:

    // Start the render loop
    DXUTMainLoop();

This doesn’t work in a browser. Because of the execution model, we are not allowed to block the JavaScript thread - JavaScript is single-threaded, so if we block the one thread there is, nothing else using JavaScript can run in the browser.

Emscripten doesn’t magically fix this for us. We need to restructure the program to rely on callbacks so that we only do any processing when it is “our turn”.

This is no big deal. But it does violate my initial principle of not touching the original code at all. But there’s not really anything I can do about it. (Well, theoretically maybe I could create a C macro to turn the call to DXUTMainLoop() into something weird, but I don’t want to go there.)

Finally

With this commit, which also does a whole lot more stubbing, we have this main loop function:

// Our "main loop" function. This callback receives the current time as
// reported by the browser, and the user data we provide in the call to
// emscripten_request_animation_frame_loop().
EM_BOOL one_iter(double time, void* userData) {
	// Can render to the screen here, etc.
	puts("One iteration");

	if (frameMoveCallback) {
		frameMoveCallback(DXUTGetD3DDevice(), time, time, null);
	}

	if (frameRenderCallback) {
		frameRenderCallback(DXUTGetD3DDevice(), time, time, null);
	}

	// Return true to keep the loop running.
	//return EM_TRUE;
	// Well, return false right now to allow for some debugging
	puts("Iteration done");
	return EM_FALSE;
}

It emulates a bit of DirectX by calling into some existing hooks in the existing code, and then it returns EM_FALSE to signal that we don’t want to get another callback from emscripten. If we change that to EM_TRUE, run ./buld-and-serve.sh again, and go to http://localhost:8000/source.html, we get… an emscripten console running something that prints a lot of text to the debug area, and just keeps printing and printing and printing. This is not super useful for further development, which is why it is turned off in the commit.

But we’re there! We have “a walking skeleton” of a game. All of the original logic is running inside our scaffold of stub functions. It even reads the original Amiga tracks.

Nice! Now the fun can begin. Next time we’ll do some 3D graphics for real.