Dicecards are produced using the open source Blender 3D software. This page gives a bit more detail about the way that works, towards the end I'll give some technical details, so feel free to bail at that point if you only want the overview.
I've used most of the major 3D modelling and animation packages on the market. In my day job as a consultant to the games industry, I've had to build software that uses and extends both Max and Maya. Alongside them, I've been using Blender since 2000, a couple of years before it was Open Source.
When I'm building software to automatically generate or layout content, I tend to reach for Blender as my standard tool. It has always had a superb scripting interface, but the most recent versions of Blender have extended and normalized that interface, to make it the best around.
Dicecards are a built by a whole series of scripts that drive Blender from the command line. As mentioned on the previous page, the elements are first distributed among the cards in their correct probabilities, then the geometry of each element is packed onto the card (while making it look like the elements were randomly scattered), and this packing and distribution plan is passed to a rendering script.
The rendering script is all command-line based. Though the image above shows Blender running with a card-layout loaded, this screenshot was actually a pain to take. In the normal course of making the cards, Blender is never visible. Instead the 'blender' command is run on the big 3D environment (which holds all elements), and a script. The script loads the plan, moves the elements to their correct locations (or puts them on a hidden layer if they shouldn't be displayed), and renders that card. The command line reports its rendering progress and the card is saved to the correct location.
The script that drives blender does not make use of Blender's ability to render frames from the command line. Instead the script itself manually calls the renderer. The relevant workflow is:
maker.load_or_build_configuration() blender = BlenderDisplayWrapper() for card_number in card_numbers: print("Rendering card: %d" % card_number) maker.configure(card_number, blender) blender.render(card_number, outdir)
To insulate the project against possible changes of renderer
(initially I thought I might want to render with an external
renderer), all configuration and rendering is done through
BlenderDisplayWrapper is the only code in
the entire system that is Blender specific. Its methods are called
maker.configure to set up the card, and the card
is rendered by calling its
render method, which is
implemented like this:
def render(self, card_number, output_dir): scene = bpy.data.scenes['Scene'] scene.render.filepath = os.path.join(output_dir, "%04d.png" % card_number) scene.layers = VISIBLE_LAYERS scene.frame_set(card_number) bpy.ops.render.render(animation=False, write_still=True)
frame_set call above has no effect on any
elements (nothing in the file uses keyframes, all setup is done
from scripts). The frame number is only set because it is
displayed in the command line output while the renderer is
running, and this helps me at a glance to see how far through the
deck the renderer has got.
I start the renderer with a command line that looks a bit like
default_layout allows me to have several
different sets of elements for different decks of cards - though I
only have one at the moment, and the
1:54 lets me
render a subset of the cards):
$ /path/to/blender -b src/layouts/default-layout/default-layout.blend \ -P src/code/layout-and-render/blender_render.py \ -- default_layout 1:54
I can run the render in about 6 hours on my PC at home, or I can very simply send the code and the source file to a bunch of high-CPU Amazon EC2 instances to act as a rendering cluster. That way I get a whole deck rendered for a few dollars in about 15 minutes.