entry list | previous: Working on a graphics editor. Have some Synthwave. | next: strictNullChecks

subject: Procedural Shape Editor
thumbnail-image-url: http://picture-files.nuke24.net/uri-res/raw/urn:bitprint:2DK3UN74KGHEIBUGK7PG7PNFEXB4YWUY.AX6O5MF7BRTGBMLSTXCS56OVHQ5GX57YVWCBOYY/treef.png
date: 2016-07-14

Procedural Shape Editor - 2016-07-14 - Entry 11

I've made a Procedural Shape Editor! By 'editor' I mean a text area that you can type programs into and some canvases to show the result. It's rudimentary but it means that it's at least possible for people to come up with some interesting models while I go and work on other stuff.

Scroll down for instructions.

Shapes are defined using a Forth-like language. Defining them as a procedure allows us to re-render them at different rotations and scales and under different lighting conditions and at different points in their animation cycle (for animated shapes).

The end result of the Forth program is a 'shape sheet', which consists of a material index and corner depths for each pixel. This shape sheet can then be rendered as an RGB image by providing materials for each material index and a set of lights.

This means we could, for instance, swap out steel for copper without having to re-run the Forth script.

The text area on the left is where you write your script. Hit 'compile' to compile it and update the shape views on the right. If there are errors they'll be printed to your browser's console. You can drag around the shape views to change the rotation, use the scroll wheel to zoom in and out, and use the '⏸'/'▶' button to toggle auto-rotation.

The second preview does 4x supersampling, which is why it takes longer to render than the others.

Now I will try to describe the language.

Similar to traditional Forth, words are delimited by whitespace, literal numbers push themselves onto the data stack, : starts a word definition, dup, drop, swap, and pick have their traditional meanings. ; inserts an exit (return) and marks the end of word definitions; anything outside of a function definition is run immediately at the beginning of the program. Rational numbers, e.g. 1/5, can also be represented literally (though internally they are just floats).

! and @ store and fetch values to/from a variable, respectively. You can define dynamically-scoped variables using the context-variable: word. A few context variables have special meaning, but you still need to declare them to use them.

Drawing is done by transforming the 'cursor' by translating, rotating, and scaling, and plotting spheres or polygons. Cursor transformations are always relative to and alter the current transformation. e.g. if you move by 3,0,0, scale by 2, and then move by 0,1,0, the cursor will be at 3,2,0, and a sphere plotted with size=1 will actually have radius=2 relative to the original coordinate system.

The initial coordinate system is +x = right, +y = down, and +z = into the screen. One unit = one meter. I expect standard-sized blocks in the game to be 1m by 1m, with bounds from -0.5,-0.5,-0.5 to +0.5,+0.5,+0.5, so if you want to design a block-sized thing, keep it approximately within those bounds.


Listed with (parameters and -- return values)

Cursor transformations


Back sides of polygons will not be drawn. The 'front' side is the side that appears to be drawn clockwise.

Context loading/saving


There will also be jump:label and $label variants of any user-defined wors that jump to and push the location of any user-defined words.

You can play 2/3 Tower of Hanoi by shuffling data between the data and return stacks.




Stack operations

Definition words

These are interpreted at compile-time rather than run-time, and don't use the stack. Any arguments tend to be symbolic and come after the word.


There are only a few defined.

I usually : mati! $material-index ! ; so that I can just type material-number mati! to set the current material rather than the whole $material-index rigamarole.

Eventually one would be able to define custom materials and indicate your preferred palette in your script, but that's for another day.

I probably forgot some things. You can ask me questions if you want.

next: strictNullChecks