From 0f29b49287f7d19d56d89a00044f489ee73dabd1 Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Tue, 21 Jul 2020 17:25:04 +0200 Subject: [PATCH] gba 3d rendering post; --- .../2020/07/3d-software-rendering-on-gba.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 content/post/2020/07/3d-software-rendering-on-gba.md diff --git a/content/post/2020/07/3d-software-rendering-on-gba.md b/content/post/2020/07/3d-software-rendering-on-gba.md new file mode 100644 index 00000000..f8a56422 --- /dev/null +++ b/content/post/2020/07/3d-software-rendering-on-gba.md @@ -0,0 +1,57 @@ +--- +title: "3D Software Rendering on the GBA" +date: '2020-07-21' +subtitle: "Game Boy Advance Fixed-point math, here we come..." +tags: + - gba +categories: + - programming +--- + +When I started programming the [gba-sprite-engine](https://github.com/wgroeneveld/gba-sprite-engine/) two years ago, I knew I would be getting myself into trouble. The Game Boy Advance only has 16Mhz and it's whole software library is written in low-level C using DMA (Direct Memory Access) and memory-mapped IO. Translation: pointers! `**` - Yay! + +In the end, switching to `C++11` while trying to unit test and stub out BIOS code as much as possible did help soften the pain. I'm glad I got my hands dirty again, and it writing "closer to the metal" was a welcome change from the usual high-level stuff I produce. + +But GBA MODE 0-1-2 is not the only possibility to write a GBA game. There's also _bitmap mode_, 3-4-5, that lets you write pixel colors (or palette indices) yourself. This opens up possibilities of software rendering things yourself. 90% of the GBA library did **not** do that. But a few games did: + +- Doom, Doom II, Duke Nukem Advance (Ray casting engines and/or ports) +- 007 Nightfire (A more modern 3D engine) +- Asterix & Obelix XXL +- A few terrible race games + +How do you render things in 3D without hardware acceleration, and without an FPU on the circuit board that handles `float` digits, taken into account the (mostly) 16-BIT bus rate and 16Mhz CPU? Well... It does not exactly produce 30+ FPS: + +{{< row >}} +{{% col %}} +![](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/monkey.gif?raw=true "Wireframing 507 vertices and 968 faces") +{{% /col %}} +{{% col %}} +![octahedron](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/raster.gif?raw=true "Trying to rasterize the same thing") +{{% /col %}} +{{< wor >}} + +Drawing a lot of lines is not exactly something the GBA loves to do. And I did use [tonclib's optimized routines](https://www.coranac.com/tonc/text/toc.htm) after a failed attempt to implement Bresenham myself. MODE4 has weird byte-write requirements and you can optimize DMA writing of horizontal lines. + +But the worst part was fixed-point math, sine lookup tables, and calling the BIOS just to get a square root of something. `Math.sin()` takes input in radians, in any common programming language. The above imported [Babylon JS](https://sandbox.babylonjs.com) mesh expects the same, but my sine table is filled in `[1-512]` slices and expects it's input 16-BIT. More needless bit-shifting. + +I intended to design the engine again as high-level as possible taking advantage of C++'s objects and operator overloading. How about `worldMatrix * viewMatrix;`? Everything is unit-tested (thank god for that, it took out a lot of bugs). But passing objects around in limited RAM sounds ridiculous - and it probably is, even if it's a `const MatrixFx&` reference or a `std::shared_ptr`. + +Reverting to a simple box sped up the FPS: + +{{< row >}} +{{% col %}} +![](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/wired2.gif?raw=true "A BabylonJS-exported Box. (including a bug)") +{{% /col %}} +{{% col %}} +![](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/octa.gif?raw=true "A rasterized octahedron, with back-face culling.") +{{% /col %}} +{{< wor >}} + +Even calculating the frames per second is a pain. What's a "second"? Okay, so we need a hardware timer interrupt. When does this thing overflow? How many cycles does the CPU take before that happens? Are you seriously using the divide operator instead of `fxdiv()`? + +Also, I could not remember most of the math needed to project 3D vertices into a 2D view, so I let myself be guided by David's excellent [3D soft engine tutorial](https://www.davrous.com/2013/06/13/tutorial-series-learning-how-to-write-a-3d-soft-engine-from-scratch-in-c-typescript-or-javascript/) in JavaScript. Of course I had to port in all Matrix/Vector operations myself. + +Future work: texturizing - I'm curious to see at what rate we could get a simple box textured with a mario "?" block. I won't even try to attempt portal rendering like the 007 Nightfire devs. + +Check out the source code here: https://github.com/wgroeneveld/gba-bitmap-engine/ +