Feb 22, 2020

Real-Time 3D Vector Graphics with Python

I used to program real-time graphics demos on the Commodore Amiga at the end of the 1980's and the beginning of the 1990's. The programming language of choice at the time was assembly, a.k.a. machine language, to be able to squeeze everything possible (and then some) out of the hardware.

Now, some 30 years later, I wanted to learn Python, perhaps to read data from files and perform some math operations on it. In need of a fun project to help me get a decent start, I decided to try and replicate what I did back then with assembly. Using a high-level, run-time interpreted language like Python adds a lot of overhead compared to executing assembly, and so does Windows in the background. On the other hand, my ~4 year old laptop has a 2.6Ghz 64-bit Intel i7 with four cores, 16GB RAM and a graphics card, while the Amiga 1000 had a 7.09Mhz 16/32-bit Motorola 68000, 512kB RAM and some co-processors. The i7 has more L2 cache memory than the Amiga (no cache whatsoever) has total RAM. According to the famous Moore's law (loosely interpreted) I might have to the tune of 30 000 times more computing power!

Sample of  (my) MC68000 assembly source code (1991).

So, this is where I took my inspiration: The 3D vector world I programmed for the demo Sound Vision, released by our group Reflect at the very first Assembly Demo Party in Kauniainen, Finland, in July 1992. (We finished 1st in the demo competition. Assembly is still very active, but nowadays more about gaming.) Some vector world routines had of course been presented earlier, but this was the first one with a moving light source and real time calculated shadows.

This blog is partly about how to do this with Python, and partly about how this was done 30 years ago ("old school" a.k.a. "oldskool") with assembly on the Amiga.

The whole demo, as a video, can be found at e.g. Assembly Archive. The part above I actually captured running an Amiga emulator on my laptop (WinUAE) with an original disk image of the demo, which is less than 880 kB including all the code, music, and graphics!  The Amiga (original chip set) was capable of 352 x 288 pixel resolution in 32 colors and basically there was so much happening in this part (considering the resources) that the frame rate (pictures per second) is at times as low as 12-15 - hence, it does look somewhat jerky.  

So, how to go about it with Python? First, I installed Anaconda on my laptop, and started Spyder. Quickly I found out that I needed a real-time graphics package, so I installed Pygame. Pygame can be used to set up a screen or a window, and to draw on it - something that was handled by the Copper and the Blitter co-processors in the Amiga, respectively. Also, a number of other - more standard - packages like math and numpy came in handy.

Setting up an Amiga demo

The Amiga had 512kB of "chip" RAM, ie. memory available for program code and usable to the graphics and sound co-processors. The separate ROM (read-only memory) contained the BIOS; in my Amiga 1000 even this had to be loaded from a "kickstart" disk at startup. A track loaded demo ("trackmo") would boot from a disk and then run the first 1kB stored on that disk (the "bootblock"). Basically, you could have your own code there then using the disk drive hardware directly to proceed - asking it to load "tracks" from the disk directly to memory. The Amiga OS was not needed for anything. No libraries, drivers etc., but accessing the hardware registers directly.

So once you had loaded some music, perhaps graphics like a font for a "scroller", and your actual program code, then what? The first thing to do with the demo code was to take hold of system interruptions (these "interrupt" the currently running code and give the CPU temporarily for some other code to use) and create a "copper list". The copper list would tell the Copper co-processor where the bitplanes to be shown on screen are located, how the colours (up to 32) are defined, and some other graphics related settings. Then, kick off with the music and start drawing on the screen! For the drawing operations the Blitter would be used. According to the manual, the Blitter is "the high speed line drawing and block movement component of the system" and able to clear, copy and fill areas, and to draw lines. So, to create a simple text scroller, first find the location of the first letter of your scroller text, and copy the picture of that letter to the rightmost edge of your screen. Then, in the next frame, move the screen image some bits to the left. Once the whole letter has been moved enough times, there's room for the next letter, etc. See this tutorial for an example!

More on demo programming:
    Crash Course to Amiga Assembly Programming
    Coder Aid
    Amiga Hardware Programming (video)