simple tools for complex form
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.
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.
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.
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 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.
// 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 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.
/* 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
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.
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.
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.
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.
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).
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
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.)
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.
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.
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.
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.
Refreshing the memory on the basics of some mathematical concepts will make it a lot easier to work with generative form.
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.
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 }
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 }
for importing an OBJ-sequence to Modo, you will need MeshSequence import script from the Modo Resource.
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.
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.