This is a bit silly – been playing with Processing, getting into the deep geometric capabilities of toxiclibs and GLGraphics and Android.
It started as a new VJ… er, “pixelist” app for live performance for a jam session celebrating our friend Olivier Ruellet’s life and untimely death last year from what we think was swine flu.
Now, I made an Android version… this technique will come to life a bit later as part of a”Build Your Own Superhero” workshop I’m co-developing with CoDesign.
Processing code (for Android only):
// Draw Particle Toads by pixelpusher // <info@pixelist.info> // Based on examples in the public domain by // Andres Colubri and toxi (Karsten Schmidt) // // Uses toxiclibs - http://toxiclibs.org import toxi.geom.*; import toxi.geom.mesh.*; import toxi.math.*; TriangleMesh triMesh; Vec3D prev=new Vec3D(); Vec3D p=new Vec3D(); Vec3D q=new Vec3D(); Vec2D rotation=new Vec2D(); boolean mouseWasDown = false; float MIN_DIST = 7.0f; float weight=0; LinkedList<PShape3D> models; PImage tex; void setup() { size(800, 400, A3D); orientation(LANDSCAPE); models = new LinkedList<PShape3D>(); triMesh =new TriangleMesh("mesh1"); // any particle texture... small is better tex = loadImage( "whitetoady.png"); } void draw() { background(0); // rotate around center of screen (accounted for in mouseDragged() function) translate(width/2, height/2, 0); rotateX(rotation.x); rotateY(rotation.y); // draw mesh as polygon (in white) drawMesh(); // draw mesh unique points only (in green) drawMeshUniqueVerts(); //hint(DISABLE_DEPTH_MASK); screenBlend(ADD); hint(DISABLE_DEPTH_TEST); // now models for (PShape3D model : models) { shape(model); model.loadVertices(); for (int n = 0; n < model.vertices.length; n+=3) { model.vertices[n] -= (0.0f- model.vertices[n])*0.03f; model.vertices[n+1] -= (0.0f - model.vertices[n+1])*0.03f; model.vertices[n+2] -= (0.0f - model.vertices[n+2])*0.03f; } model.updateVertices(); } //hint(ENABLE_DEPTH_MASK); // udpate rotation rotation.addSelf(0.014, 0.0237); } PShape3D makeModel(TriangleMesh mesh) { PShape3D model = null; // get unique x,y,z vertices, use with indices float[] triVerts = mesh.getUniqueVerticesAsArray(); if (triVerts.length > 0) { println("Got " + triVerts.length/3 + " verts"); int[] faces = mesh.getFacesAsArray(); model = (PShape3D)createShape(triVerts.length/3, PShape3D.newParameters(POINT_SPRITES, DYNAMIC)); model.setColor(color(255)); // TESTING - MAKE SURE WE HAVE CORRECT VERTS model.loadVertices(); for (int n = 0; n < triVerts.length; n++) { model.vertices[n] = triVerts[n]; } model.updateVertices(); //model.initIndices(faces.length); //model.updateIndices(mesh.getFacesAsArray()); // // for (int n=0; n<triVerts.length; n += 4) // { // println("TRIVERT["+n+"]="+ triVerts[n] +","+triVerts[n+1] +","+triVerts[n+2]); // } // // Handle colors // model.loadColors(); for (int i=0; i<triVerts.length/3; ++i) { float f = max( float(i) / (triVerts.length/3), 0.15 ); model.colors[4 * i + 0] = (1 - f) * 0.98 + f; model.colors[4 * i + 1] = (1 - f) * 0.75 + f; model.colors[4 * i + 2] = (1 - f) * 0.26 + f; model.colors[4 * i + 3] = 0.8f; } model.updateColors(); // float pmax = model.getMaxPointSize(); //println("Maximum sprite size supported by the video card: " + pmax + " pixels."); model.setTexture(tex); // Setting the maximum sprite to the 90% of the maximum point size. //model.setMaxSpriteSize(0.6 * pmax); // Setting the distance attenuation function so that the sprite size // is 20 when the distance to the camera is 400. model.autoBounds(false); model.setSpriteSize(10, 400, QUADRATIC); } return model; } void vertex(Vec3D v) { vertex(v.x, v.y, v.z); } void mouseReleased() { // MAKE A MODEL FROM CURRENT TRI MESH PShape3D model = makeModel (triMesh); if (model != null) models.add( model ); if (models.size() > 10) { PShape3D first = models.removeFirst(); first.delete(); } // clear tri mesh triMesh.clear(); } void mousePressed() { } void mouseDragged() { // get 3D rotated mouse position Vec3D pos=new Vec3D(mouseX-width/2, mouseY-height/2, 0); pos.rotateX(rotation.x); pos.rotateY(rotation.y); // use distance to previous point as target stroke weight weight+=(sqrt(pos.distanceTo(prev))*2-weight)*0.1; // define offset points for the triangle strip //println("weight " + weight); //if (weight < MIN_DIST && triMeshes.size() > 0) if (true) { Vec3D a=pos.add(0, 0, weight); Vec3D b=pos.add(0, 0, -weight); // add 2 faces to the mesh triMesh.addFace(p, b, q); triMesh.addFace(p, a, b); // store current points for next iteration prev=pos; p=a; q=b; } } void drawMesh() { noStroke(); fill(255,80); beginShape(TRIANGLES); // iterate over all faces/triangles of the mesh for (Iterator i=triMesh.faces.iterator(); i.hasNext();) { Face f=(Face)i.next(); // create vertices for each corner point vertex(f.a); vertex(f.b); vertex(f.c); } endShape(); } void drawMeshUniqueVerts() { // noStroke(); stroke(0,255,0); strokeWeight(4); beginShape(POINTS); // get unique vertices, use with indices float[] triVerts = triMesh.getUniqueVerticesAsArray(); for (int i=0; i < triVerts.length; i += 3) { vertex(triVerts[i], triVerts[i+1], triVerts[i+2]); } endShape(); } void keyPressed() { switch(key) { case 'x': //mesh.saveAsOBJ(sketchPath("doodle.obj")); //mesh.saveAsSTL(sketchPath("doodle.stl")); break; case ' ': // now models for (PShape3D model : models) { model.delete(); } models.clear(); break; } }