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;
}
}