GENERATIVE 3D

simple tools for complex form

INTRODUCTION

Generative 3D in visual communication is a field that is emerging trough the availability of affordable computers with ever increasing computing capacity and open-source programming languages aimed for ease of use to produce visual material.

This workshop is an introduction to create complex generative and parametric 3D form through programming in processing, to be used as building blocks in 3D software, where it is possible to create impressive lightning and materials for final output.

The possibilities of finding novel form through open-source programming are tremendous. This makes it a considerable tool for designer and artists to generate complex designs and bring them to life in a compelling way. A great part of the exploration of form through code comes from unexpcted results of modifying the code.

Examples









Composition No.5 by Stas Chepurnov gives great impact from different viewing angles with its carefully composed planes with a natural texture and rotation limited to steps of 90 degrees.

Victor Martins' complex compositions for The Viewer No.5 explores the photographic nature of creation and deformation of limited space.

Silica-Esc by Vladimir Todorovic is a generative movie that portrays possible computing platform for the future. The movie is created by writing code and designing a software environment, which can generate high number of variations and visual material in short periods of time. All the visual material is generated by working with processing.

Recently this kind of approach to identity work has been used even for large institutions as the UN Copenhagen Climate Summit, to which OKDeluxe made COP15 - Generative Identity Software, based on the static design by nr2154.

Another good example is the participative approach on the identity for V&A Decode exhibition, to which people were encouraged to make modifications to the original piece by Karsten Schmidt.

Setting up processing

Processing can be downloaded freely to all systems from http://processing.org/download/. Just unzip the package and it's ready to run.

For exporting 3D-models, you need to add the superCAD library. Download the file and extract it to a folder named "libraries" in your sketchbook folder. To find the Processing sketchbook location on your computer, open the Preferences window from the Processing application and look for the "Sketchbook location" item at the top. Copy the superCAD library's folder into the "libraries" folder at this location. You will need to create the "libraries" folder if this is your first contributed library. You need to restart processing in order to use the library.

VARIABLES AND DATA

Creation of this kind of material doesn't necessarily require advanced programming skills, rather just an general understanding of the basic concepts and curiosity to explore the possibilities of modifying ready-made code.

Variables

Variables are the fundamental pieces of a program that makes it possible for things to happen. Basically they are names to which different values and data can be assigned and changed dynamically in the program. You can think of variables as boxes that are passed around in the program. Variables must be declared to make them exist. In the declaration you must also state what type it should be. More on that later. An online example can be found here.

one-line comments and simple variable declaration and assignment

// this is a one-line comment, this will not run in the program

int beer;  // declaration of variable beer of type integer

beer = 6;  // assign the number 6 to beer

int anotherBeer = 6;  // a value can be assigned at the same time it is declared

Arrays

Arrays are lists of variables under one common name. They are used to store similar kind of data in one place. E.g. if you have four boxes of beer, the amount of beer in each box can be stored in an sigle array called beer. Each element in the array has an index number to identify it and an array can hold only variables of one type. As with variables, arrays must be declared to make them exist. In addition arrays must be initialized with a certain length, how many elements (boxes) it can hold.

multi-line comments and simple array declaration, initalizing and assignment

/* this is a multi-line comment, 
this will not run in the program either */

int[] beer;  // arrays are declared with brackets behind the type

beer = new int[4];  // initialize the array with the length 4

beer[0] = 6;  // assign 6 to the first element. array indexes start from 0
beer[1] = 8;  // assign 8 to the next element
beer[2] = 10; // i like where this is going
beer[3] = 12; // this is as much as our array can take

beer[1] = 12; // different values can be assigned when ever needed

Datatypes

As stated earlier all variables must be declared with a specific datatype, and no other type can be assigned to that variable*. Below are presented the most used basic datatypes. In addition images and many other things can be datatypes.

examples on declaring variables with different data types

int beer = 6;  // integers are whole numbers

float pint = 0.47317647;  // floating point numbres are with decimals

char letter = 'A';  // characters are single characters

color c1 = color(204, 153, 0);  // colors are defined by red, green and blue value from 0-255 
color c2 = #FFCC00;  // or by web-style hexadecimal values

boolean boring = true;  // booleans are either true or false

A comprehensive list of all the commands in the processing language can be found here. This reference is a key resource for learning processing and debugging errors you might encounter.

* processing offers datatype conversion, more on that here.

NAVIGATING IN 3D

Dealing with navigation in 3D requires understanding over the coordinate system that is in use, how to move within it, rotate and scale it. It is also possible to create own coordinate spaces for objects for better control of multiple objects.

Coordinates

one point in 3D-space


size(740, 416, P3D); // set the size of the window and that we work in 3D

strokeWeight(5); // set stroke weight to 5 so we can see our point

point(200, 100, 50);  // draw point at x=200, y=100, z=50

The coordinate system in processing is a left-handed coordinate system, with x-axis running from left to right, y-axis from top down and z-axis increasing towards the viewer. The origin of the coordinate system is at top left corner of the window. The default camera (the 3D-view you see) position is at half of the width, half of the height, and positive z as a function the height of the window and it is directed at the center of the view.

Translation

one point in 3D-space after translation


size(740, 416, P3D); // set the size of the window and that we work in 3D

strokeWeight(5); // set stroke weight to 5 so we can see our point easier

translate(width/2, height/2, 0); // translate to half of window width and height

point(200, 100, 50);  // draw point at x=200, y=100, z=50

Translating means shifting the origin of the coordinate system with a desired amount. The three parameters for the function are the amount to be translated on x-, y- and z-axises. It is usually desireable to shift the origin to the center of the view when dealing with 3D objects. Transformations apply to everything that happens after it and subsequent calls to the function accumulates the effect. For example, calling translate(50, 0, 0) and then translate(20, 0, 0) is the same as translate(70, 0, 0).

Scaling

two rectangles in 3D-space before and after scaling


size(740, 416, P3D); // set the size of the window and that we work in 3D

noStroke(); // turn off stroke to draw solid shape

fill(#FF0000); // set the fill color to red 
rect(100, 100, 200, 200); // draw a rectangle from 100, 100 to 200, 200
scale(0.5, 0.6, 0); // scale 0.5 on x-axis 0.6 on y-axis and 0 on z-axis

fill(#00FF00); // set the fill color to red 
rect(100, 100, 200, 200); // draw a rectangle from 100, 100 to 200, 200

Rotation

the axises after rotation around z-axis


size(740, 416, P3D); // set the size of the window and that we work in 3D

smooth(); // set antialiasing on for smooth edges

background(#000000); // set the background to black

translate(width/2, height/2, 0); // translate to half of window width and height

rotateZ(PI/6); // rotate PI/6 radians (30 degrees) clockwise around z-axis

stroke(#FF0000); // set stroke color to red
line(-200, 0, 0, 200, 0, 0);  // draw x-axis as line from -200, 0, 0 to 200, 0, 0

stroke(#00FF00); // set stroke color to green
line(0, -200, 0, 0, 200, 0);  // draw y-axis as line from 0, -200, 0 to 0, 200, 0

stroke(#0000FF); // set stroke color to blue
line(0, 0, -200, 0, 0, 200);  // draw z-axis as line from 0, 0, -200 to 0, 0, 200

There are three rotation functions rotateX(), rotateY() and rotateZ() for rotating around each of the axises. They take an angle defined in radians as their parameters, positive values for clockwise and negative for counter-clockwise rotation. It is important to remember that the whole coordinate system has rotated after each rotation.

(Rotation is also possible around an arbitrary axis by calling rotate(angle, x, y, z); where x, y and z are components of a vector defining the axis.)

Matrices

translating two points from the center of the view


size(740, 416, P3D); // set the size of the window and that we work in 3D
smooth(); // set antialiasing on for smooth edges
strokeWeight(5); // set stroke weight to 5 so we can see our point easier
background(#000000); // set the background to black

translate(width/2, height/2, 0); // translate to half of window width and height

stroke(#FF0000); // set stroke color to red
point(0, 0, 0);  // draw point at the center of the view

pushMatrix();  // create a separate coordinate space

  translate(-100, -100, 0); // translate up and left 100

  stroke(#00FF00); // set stroke color to green
  point(0, 0, 0);  // draw point at x=0, y=0, z=0

popMatrix();  // return to previous coordinate space

pushMatrix();  // create a new separate coordinate space

  translate(100, -100, 0); // translate up and right 100

  stroke(#0000FF); // set stroke color to blue
  point(0, 0, 0);  // draw point at x=0, y=0, z=0

popMatrix();  // return to previous coordinate space

Creating a separate coordinate space where transformations does not affect anything outside of it can be done by calling pushMatrix() and popMatrix(). The translations and rotations done between these calls will only affect the shapes drawn inside them. This is useful for moving an object or groups of objects separately.

FORM IN 3D

Forms in 3D are generally drawn within a local coordinate space with their centrpoint at the origin and then translated to the desired location. This gives a realtively intuitive way for positioning and rotation.

Primitives

render with OpenGL and draw the basic primitives offered by processing

import processing.opengl.*; // import OpenGL library for better 3D performance

size(740, 416, OPENGL); // set the size of the window and that we work in OpenGL
smooth(); // set antialiasing on for smooth edges
noStroke(); // turn off the stroke as we are drawing solid shapes
background(#000000); // set the background to black

lights(); // turn on the lights to see simple shading

translate(width/2, height/2, 0); // translate to half of window width and height

fill(#FF0000); // set fill color to red
sphere(50);  // draw a sphere with the radius 50

pushMatrix();  // create a separate coordinate space

  translate(-100, -100, 0); // translate up and left 100
  rotate(PI/6, 1, 1, 0); // rotate PI/6 radians (30 degrees) around a x-y diagonal axis

  fill(#00FF00); // set fill color to green
  box(50);  // draw a cube with the side length 50

popMatrix();  // return to previous coordinate space
 
pushMatrix();  // create a new separate coordinate space

  translate(100, -100, 0); // translate up and right 100
  rotate(PI/6, 1, 1, 0); // rotate PI/6 radians (30 degrees) around a x-y diagonal axis

  fill(#0000FF); // set fill color to blue
  box(5, 30, 100);  // draw a box with the width 5, height 30 and depth 100

popMatrix();  // return to previous coordinate space

Processing offers two functions for drawing 3D primitives, sphere() for drawing spheres with a defined radius and box() to draw either cubes when passed one argument or boxes with width, height and depth if passed three arguments. Note that there is no way of defining their position directly and that they are drawn around the origin of the current coordinate space.

Vertex based

drawing a cube with the lengt of side 50 based on it's vertices

import processing.opengl.*; // import OpenGL library for better 3D performance

size(740, 416, OPENGL); // set the size of the window and that we work in OpenGL
smooth(); // set antialiasing on for smooth edges
noStroke(); // turn off the stroke as we are drawing solid shapes
background(#000000); // set the background to black

lights(); // turn on the lights to see simple shading

translate(width/2, height/2, 0); // translate to half of window width and height

pushMatrix();  // create a separate coordinate space

  rotate(PI/6, 1, 1, 0); // rotate PI/6 radians (30 degrees) around a x-y diagonal axis
  scale(50);
  
  fill(#00FF00); // set fill color to green
  beginShape(QUADS);  // start drawing a free form based on quads
  
  // top square
  vertex(-1, 1, 1);  // define first vertex of the quad
  vertex(-1, 1, -1);  // define second vertex of the quad
  vertex(1, 1, -1);  // define third vertex of the quad
  vertex(1, 1, 1);  // define fourth vertex of the quad
  
  // bottom square
  vertex(-1, -1, 1);  // define first vertex of the quad
  vertex(-1, -1, -1);  // define second vertex of the quad
  vertex(1, -1, -1);  // define third vertex of the quad
  vertex(1, -1, 1);  // define fourth vertex of the quad
  
  // right square
  vertex(-1, -1, 1);  // define first vertex of the quad
  vertex(-1, -1, -1);  // define second vertex of the quad
  vertex(-1, 1, -1);  // define third vertex of the quad
  vertex(-1, 1, 1);  // define fourth vertex of the quad
  
  // left square
  vertex(1, -1, 1);  // define first vertex of the quad
  vertex(1, -1, -1);  // define second vertex of the quad
  vertex(1, 1, -1);  // define third vertex of the quad
  vertex(1, 1, 1);  // define fourth vertex of the quad
  
  // back square
  vertex(-1, 1, -1);  // define first vertex of the quad
  vertex(1, 1, -1);  // define second vertex of the quad
  vertex(1, -1, -1);  // define third vertex of the quad
  vertex(-1, -1, -1);  // define fourth vertex of the quad
  
  // front square
  vertex(-1, 1, 1);  // define first vertex of the quad
  vertex(1, 1, 1);  // define second vertex of the quad
  vertex(1, -1, 1);  // define third vertex of the quad
  vertex(-1, -1, 1);  // define fourth vertex of the quad
  
  
  endShape();  // end the shape

popMatrix();  // return to previous coordinate space

All 3D shapes are ultimately drawn as triangles between vertices. Given the control of the vertices, any custom shape can be created and controlled point by point. You can see the different methods of drawing shapes based on it's vertices from the beginShape() reference page. The Learning 3D section on the processing page has good examples of how this can be used.

MATH RECAP

Refreshing the memory on the basics of some mathematical concepts will make it a lot easier to work with generative form.

Trigonometry

Trigonometry will come in handy when dealin with geometry. Drawing forms based on circles or making motion based on the sine function will make for a nice flow.

Vectors

COMPLEXITY

Loops

Random

Noise

ANIMATION

Movement

Transformation

EXPORTING

Images

OBJ-Files

create a single OBJ-file when a key is pressed

import processing.opengl.*; // import OpenGL library for better 3D performance
import superCAD.*; // import superCAD library for exporting 3D shapes

void setup(){
  size(740, 416, OPENGL); // set the size of the window and  that we work in OpenGL
  smooth(); // set antialiasing on for smooth edges
  noStroke(); // turn off the stroke as we are drawing solid shapes

  SuperCAD.init(this); // initialize superCAD to be ready
  SuperCAD.mode("OBJFile"); // set the mode to export OBJ-files
  SuperCAD.fileName("myOBJFile"); // define a filename
}

void draw(){
  background(#000000); // set the background to black

  lights(); // turn on the lights to see simple shading

  translate(width/2, height/2, 0); // translate to half of window width and height
  
  // do your drawing
  box(20); // draw a box with the side 20

}

void keyPressed(){     // listen for keystrokes
  SuperCAD.recordNextFrame(); // make an OBJ-File if a key is pressed
}

OBJ-Sequences

create an OBJ-sequence for a certain amount of frames

import processing.opengl.*; // import OpenGL library for better 3D performance
import superCAD.*; // import superCAD library for exporting 3D shapes


String sequenceName = "myOBJSequence"; // define a name for the sequence files
int animationLength = 120;  // define the length of the animation

float rot = 0; // create a variable for rotation

void setup(){
  size(740, 416, OPENGL); // set the size of the window and  that we work in OpenGL
  smooth(); // set antialiasing on for smooth edges
  noStroke(); // turn off the stroke as we are drawing solid shapes
}

void draw(){
  background(#000000); // set the background to black

  lights(); // turn on the lights to see simple shading

  translate(width/2, height/2, 0); // translate to half of window width and height
    
  String fc = nf(frameCount, 5); // make a 5 digit string of the framenumber
  
  // start recording an OBJ-file with the sequence name and frame number
  beginRaw("superCAD.ObjFile", sequenceName + "_" + fc + ".obj");      
 
  // do your drawing
  
  rotateY(rot);
  
  box(20); // draw a box with the side 20
  
  rot += 0.1;  // add 0.1 to the rotation for each frame
    
  endRaw(); // end recording of the single obj-file
  
  if (frameCount == animationLength + 1) exit();  // close the program when animation lengt is reached
  
}

IMPORTING TO 3D SOFTWARE

Importing sequences

for importing an OBJ-sequence to Modo, you will need MeshSequence import script from the Modo Resource.

RESOURCES




Inspiration

CreativeApplications.Net CreativeApplications.Net brings together applications that challenge the ways how we share and engage with information. By scouting the web, CAN brings you best in creative app development and thinking. CreativeApplications.Net is platform independent. We look at OSX, Windows, Linux, iPhone, Web Apps, Flash, Physical Interfaces, Max MSP development, Processing and many others.

Processing links

The Nature Of Code Daniell Shiffman's teaching material, a very good point to start exploring the basics of processing. Tutorials include Numbers and Vectors, Vectors and Forces, Oscillations (trigonometry), Collisions, Particle Systems, Steering Behaviors, Fractals and Recursion, L-Systems and Cellular Automata, Genetic Algorithms and Neural Networks.

Tutorials by Don Havey A series of good processing tutorials and classes. Spheres, Trees, Maps etc.

OpenProcessing OpenProcessing is an online community platform devoted to sharing and discussing Processing sketches in a collaborative, open-source environment. Hosts lots of nice sketches companied by their source codes.

Processing \ Hacks Processing Hacks is an effort to document some of the trickier and more advanced topics that Processing users are stumbling across as they gain experience and cross-fertilize their work with other platforms, languages, and libraries.

toxiclibs toxiclibs is an independent, open source library collection for computational design tasks with Java & Processing developed by Karsten "toxi" Schmidt (thus far). The classes are purposefully kept fairly generic in order to maximize re-use in different contexts ranging from generative design, animation, interaction/interface design, data visualization to architecture and digital fabrication, use as teaching tool and more.

Processing Blogs Processing Blogs aggregates posts about the Processing language and development environment. The blog is currently down due to hacking, but there reads that it will come up some day again.