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/
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.
Now below is an example which uses another physics library to create funky generative graphics.
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.
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.