Apologies for the delay, I took the plunge and upgraded to a Mac mini and 4K display so had to migrate my setup and then figure out why my workflow was broken (hint: retina!) compared to my old rMBP with non-retina display.
So, my workflow uses the following apps:
- OpenSCAD "The Programmers Solid 3D CAD Modeller"
- Retrobatch "a unique application for automating actions on multiple images at the same time"
- post-processing "greyscale and dithering tool" (I use my own realtime tool, but any image editor would do it to a degree, see this other thread)
This is so I can re-run a workflow at any point (maybe in a make file) which I often do during development. These become executable assets, of sorts, in my project.
OpenSCAD
- define 3D models (I get a feeling like coding CSS in a strange way)
- animate the model spinning through one 360-degree rotation
- dump frames out as PNG files
Here's the model definition to try out: car.scad · GitHub
I love building 3D this way, it's kind of like LEGO. I use basic geometric building blocks (cube, sphere, cylinder, polygon, etc) and some boolean operations (difference, intersection, union). There are some other cool things (hull, minkowski). I have the commands in Dash.app alongside the Playdate SDK docs.
This is what the model looks like with all the blocks I have used to make it visible at once.
My particular approach is subtractive — kind of like sculpting — I start with large blocks and cut away at them using other shapes and the difference function. When finding the exact placement for a block I use the # precedent which makes the blocks show up as semi-opaque red blocks.
I colour each block in a one or two shades of grey, black and white. This is to help with the conversion to 1-bit later on. It's not so obvious here as the lighting makes the colours look many different shades - for example the wheels are black with white centre but look grey here.
Using some simple programming constructs and variables I can add booleans to trigger different states, I use this for angled front wheels and tilted car body.
And also to set the distance and rotation of the camera relative to the model when in animation mode. In this mode I enter a speed (doesn't matter but higher the better) number of frames = 32, and the tick the box to dump the images. The tick disappears when the images are all done.
I also rendered the skid marks, car shadow, and some other elements.
There is a lot that is annoying about this app
- not retina-optimised (so I run it in Low Resolution, set using Get Into on the app) [no longer true in 2022]
- runs maxed out on a single core [run it from the command line for parallelism/multi-core]
- doesn't have configurable lighting (I'd prefer uniform or no lighting)
- Qt framework app so not really macOS-native
- etc
...but I still use it! I am not aware of anything else quite like it.
Retrobatch
- process dumped frames x 32
- once each for left, right and straight directions
- stitch processed frames together as spritesheets x 3
- we end up with three long images
- stitch 3x spritesheets together
- we end up with our final image ready for post-processing
Edit: since writing this post I've been able to condense this process into 1 single workflow that executes much faster. See further down this thread.
For each batch all I need to do (now the they are set up!) is set source and destination folders and press the go button. As time goes on I refine these, so at some point I'll make it so that it automatically gets the filename of my current model. Not a priority right now though.
batch 1
batch 2
batch 3
Post-processing (details vary per car sprite)
- greyscale using one of many algorithms
- reduce colours to 1-bit with dithering or by threshold level
The grey shades that I applied to my model in OpenSCAD give an element of control during this conversion process. Greys can be pushed either way, towards black or white, depending on my need with the specific model I am working on. In this instance I desaturated the greys which blows them out to nearer white. And then I chose to threshold to reduce to b/w. I also have the wheels as a separate finished image so I don't have to worry if their detail is lost during this phase, I can just paste over the accurate/finished wheels.
Final result, unedited:
In-game:

I would later touch up the sprite by hand to reinforce any details I think have been lost. I use Piskel for edited sprites because it has really nice sprite sheet support, drag and drop loading, and quick and versatile exporting.