The Creative Computing Framework

This article wants to introduce you to the convenient Java Framework “Creative Computing” by Texone.

It is a java framework for interactive graphic applications that makes everyday things like very easy. So it is conceptually the same like processing but more focussed on directly using Java and OpenGL. So if you want to dive deeper into graphics programming this is a very helpful tool.

This article supposes you are able to link your Eclipse or Netbeans Project to a Java Library.

The source can be found here: http://code.google.com/p/creativecomputing/

The Basic Class

A raw Creative Computing Class looks like this:

package cc.creativecomputing;
 
import cc.creativecomputing.CCApp;
import cc.creativecomputing.CCApplicationManager;
import cc.creativecomputing.CCApplicationSettings;
import cc.creativecomputing.graphics.CCGraphics;
 
public class CCCreativeFirstSketch extends CCApp {
 
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
 
	public CCCreativeFirstSketch(CCApplicationSettings theSettings) {
		super(theSettings);
	}
 
	@Override
	public void setup() {
		g.clearColor(125);
	}
 
	@Override
	public void draw() {
		g.clear();
 
		g.polygonMode(CCGraphics.FILL);
		g.color(255,0,0);
		g.rect(0,0,100,100);
 
		g.polygonMode(CCGraphics.LINE);
		g.color(255);
		g.rect(0,0,100,100);
	}
 
	public static void main(String[] args) {
		final CCApplicationManager<CCCreativeFirstSketch> _myManager = new CCApplicationManager<CCCreativeFirstSketch>(CCCreativeFirstSketch.class);
		_myManager.settings().size(400, 400);
		_myManager.start();
	}
}

Notice how the Class name has to be provided in the main funtion. Furthermore it provides the setup and draw function you know from processing.

When downloading the source you will find a lot of examples that show certain aspects of graphics programming.

Putting it to life

Now below is an example which uses another physics library to create funky generative graphics.

A Video

Some of ou know the Traer Physics Library and its cloth example. The Example: http://www.cs.princeton.edu/~traer/cloth/

Its needed for the upcoming example. Download it from here.

When you linked it to your project you can run this code:

package com.briansteen.sketch;
 
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
 
import javax.media.opengl.GL;
 
import traer.physics.Particle;
import traer.physics.ParticleSystem;
import traer.physics.Vector3D;
import cc.creativecomputing.CCApp;
import cc.creativecomputing.CCApplicationManager;
import cc.creativecomputing.CCApplicationSettings;
import cc.creativecomputing.graphics.CCColor;
import cc.creativecomputing.graphics.CCGraphics;
import cc.creativecomputing.graphics.CCMaterial;
import cc.creativecomputing.graphics.CCMesh;
import cc.creativecomputing.graphics.CCPointLight;
import cc.creativecomputing.math.CCVector3f;
 
public class FabricSketch extends CCApp {
 
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
 
	private ParticleSystem physics;
	private Particle[][] particles;
	private int _gridSize = 15;
	private int _historySize = 5;
	private int _historyZ = 50;
	private float _gravity = -.01f;
	private float _drag = 0.005f;
	private float _mass = 0.75f;
	private float _ks = 3.0f;
	private float _d = .5f;
	private float offsetX;
	private float offsetY;
	private CCMaterial _myMaterial;
	private int _currentFrame = 0;
	private int _saveFrameAmount = 60;
	private CCMesh mesh = new CCMesh();
	private ArrayList<CCVector3f> _vertices = new ArrayList<CCVector3f>();
	private ArrayList<ArrayList> _verticeHistory = new ArrayList<ArrayList>();
	private int camX = -500;
	private int camY = 300;
	private int camZ = 1500;
	private int targetX = 50;
	private int targetY = 250;
	private int targetZ = -660;
 
	public FabricSketch(CCApplicationSettings theSettings) {
		super(theSettings);
	}
 
	@Override
	public void setup() {
 
		// physics
 
		offsetX = -width * .5f;
		offsetY = height * .5f;
 
		physics = new ParticleSystem(_gravity, _drag);
		particles = new Particle[_gridSize][_gridSize];
 
		float gridStepX = (float) ((width / 2) / _gridSize);
		float gridStepY = (float) ((height / 2) / _gridSize);
 
		for (int i = 0; i < _gridSize; i++) {
			for (int j = 0; j < _gridSize; j++) {
				particles[i][j] = physics.makeParticle(_mass, j * gridStepX
						+ (width / 4), -i * gridStepY + 20, 0.0f);
				if (j > 0) {
					physics.makeSpring(particles[i][j - 1], particles[i][j],
							_ks, _d, gridStepX);
				}
			}
		}
 
		for (int j = 0; j < _gridSize; j++) {
			for (int i = 1; i < _gridSize; i++) {
				physics.makeSpring(particles[i - 1][j], particles[i][j], _ks,
						_d, gridStepY);
			}
		}
 
		particles[0][0].makeFixed();
		particles[0][_gridSize - 1].makeFixed();
 
 
		// fill mesh
		for (int i = 0; i < _gridSize-1; i++) {
			for (int j = 0; j < _gridSize-1; j++) {
				CCVector3f p0 = new CCVector3f(particles[i][j].position().x() + offsetX,
						particles[i][j].position().y() + offsetY,
						particles[i][j].position().z());
				CCVector3f p1 = new CCVector3f(particles[i][j+1].position().x() + offsetX,
						particles[i][j+1].position().y() + offsetY,
						particles[i][j+1].position().z());
				CCVector3f p2 = new CCVector3f(particles[i+1][j].position().x() + offsetX,
						particles[i+1][j].position().y() + offsetY,
						particles[i+1][j].position().z());
				CCVector3f p3 = new CCVector3f(particles[i+1][j+1].position().x() + offsetX,
						particles[i+1][j+1].position().y() + offsetY,
						particles[i+1][j+1].position().z());
				_vertices.add(p0);
				_vertices.add(p1);
				_vertices.add(p3);
				_vertices.add(p0);
				_vertices.add(p3);
				_vertices.add(p2);
			}
		}
 
		// create history array
		for(int i=0;i<_historySize;i++) {
			_verticeHistory.add((ArrayList)_vertices.clone());
		}
 
		// graphics
		g.clearColor(255);
 
		// lights
		g.lights();
		g.light(new CCPointLight(1,1,1,1000,0,1000));
 
		g.gl.glEnable(GL.GL_SHADE_MODEL);
		g.gl.glShadeModel(GL.GL_SMOOTH);
 
		// material
		g.colorMaterial(CCGraphics.OFF);
		_myMaterial = new CCMaterial();
		_myMaterial.ambient(.33f, .33f, .33f);
		_myMaterial.diffuse(.55f, .55f, .55f);
		_myMaterial.specular(.95f, .95f, .95f);
		_myMaterial.shininess(.03f);
 
		g.camera().position().set(camX, camY, camZ);
		g.camera().target().set(targetX, targetY, targetZ);
	}
 
	@Override
	public void update(float theDeltaTime) {
		physics.advanceTime(0.25f);
 
		float moveX = (float)Math.cos(frameCount * .015f) * 5;
		float moveY = (float)Math.sin(frameCount * .011f) * 4.5f;
		float moveZ = (float)Math.sin(frameCount * .015f) * 2;
		particles[0][_gridSize - 1].moveBy(moveX, moveY, moveZ);
		particles[0][_gridSize - 1].velocity().clear();
 
		particles[0][0].moveBy(-moveX*.25f, moveY*.25f, -moveZ*.25f);
		particles[0][0].velocity().clear();
 
		// update mesh
		// fill mesh
		_vertices.clear();
		for (int i = 0; i < _gridSize-1; i++) {
			for (int j = 0; j < _gridSize-1; j++) {
				CCVector3f p0 = new CCVector3f(particles[i][j].position().x() + offsetX,
						particles[i][j].position().y() + offsetY,
						particles[i][j].position().z());
				CCVector3f p1 = new CCVector3f(particles[i][j+1].position().x() + offsetX,
						particles[i][j+1].position().y() + offsetY,
						particles[i][j+1].position().z());
				CCVector3f p2 = new CCVector3f(particles[i+1][j].position().x() + offsetX,
						particles[i+1][j].position().y() + offsetY,
						particles[i+1][j].position().z());
				CCVector3f p3 = new CCVector3f(particles[i+1][j+1].position().x() + offsetX,
						particles[i+1][j+1].position().y() + offsetY,
						particles[i+1][j+1].position().z());
 
				_vertices.add(p0);
				_vertices.add(p1);
				_vertices.add(p3);
				_vertices.add(p0);
				_vertices.add(p3);
				_vertices.add(p2);
			}
		}
 
		if(this.frameCount - _currentFrame > _saveFrameAmount) {
			_currentFrame = this.frameCount;
 
			_verticeHistory.set(0, (ArrayList)_vertices.clone());
 
			// add current mesh to history and shift others
			for(int i=_historySize-1;i>0;i--)
				_verticeHistory.set(i, (ArrayList)_verticeHistory.get(i-1).clone());
		}
 
		// add saved positions to mesh array
		int historyID = 1;
		for(ArrayList<CCVector3f> historyVertices:_verticeHistory) {
			for(CCVector3f position:historyVertices) {
				CCVector3f historyPosition = position.clone();
				historyPosition.add(new CCVector3f(0,0,-_historyZ * historyID));
				_vertices.add(historyPosition);
			}
			historyID++;
		}
 
	}
 
	@Override
	public void draw() {
		// set Camera Position
		float camX = (float)Math.cos(Math.sin(frameCount * .001f)*10) * 1400;
		float camZ = (float)Math.sin(frameCount * .001f) * 1500;
 
		Vector3D target = particles[(int)((float)_gridSize*.25)][(int)((float)_gridSize*.25)].position();
		g.camera().target(target.x() - width*.5f, target.y() + height*.5f, target.z());
 
		g.camera().position().x(camX);
		g.camera().position().z(camZ);
 
		// set color
		CCColor currentColor = new CCColor((float)Math.abs(Math.sin(frameCount*.01)),
				(float)Math.abs(Math.cos(frameCount*.001)),
				(float)Math.abs(Math.sin(frameCount*.001)));
		g.clearColor(currentColor.red()*.75f, currentColor.blue()*.75f, currentColor.blue()*.75f);
		g.lightModelAmbient(currentColor);
 
		g.clear();
		g.material(_myMaterial);
 
		// draw mesh
		mesh.vertices(_vertices,true);
		mesh.draw(g);
	}
 
	public static void main(String[] args) {
		final CCApplicationManager<FabricSketch> _myManager = new CCApplicationManager<FabricSketch>(
				FabricSketch.class);
		_myManager.settings().size(640, 480);
		_myManager.start();
	}
}

The fabric is described in the setup. Namely the particles and springs are set. Notice how all drawing and calculation is divided. The computing is done in the update function wich is called before drawing automatically.

Immediate drawing vs. drawing arrays

Then in the drawing function OpenGL calls are sent and the mesh is drawn. The CCMesh object is a good tool for performant drawing. In processing you are used to send a drawing call and it is done immediately. But you can save a lot of time and processing power when collecting positions for example and draw them with one call. So you don't have to set the color all the time etc. This is where the CCmesh comes in and handles everything for you. It does the OpenGL setting and organization and you just have to send the vertices/positions to it.

I hope you find this little article useful and I wish a some Creative Coding.