/**
*
This demo allows you to interactively explore different mesh subdivision strategies.
* Starting with a simple cube mesh, you can choose one of 5 strategies and subdivide the
* mesh iteratively. At each point you can also choose to randomly deform the mesh or smooth
* it using a laplacian geometry filter. In wireframe mode the mesh vertices are colored
* based on the distance from their original position.
*
* Usage:
* - move mouse to rotate camera
* - 1-5: choose subdivision strategy
*
- s: apply current subdivision strategy once
* - d: deform mesh
* - l: smooth mesh
* - w: wireframe on/off
* - -/=: zoom in/out
*
*/
/*
* Copyright (c) 2010 Karsten Schmidt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* http://creativecommons.org/licenses/LGPL/2.1/
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
import toxi.color.*;
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.geom.mesh.subdiv.*;
import toxi.math.*;
import toxi.util.*;
import toxi.processing.*;
import processing.opengl.*;
ToxiclibsSupport gfx;
WETriangleMesh mesh;
SubdivisionStrategy subdiv=new MidpointSubdivision();
List vertBackup = new ArrayList();
float currZoom = 1;
boolean isWireframe=true;
void setup() {
size(1280, 720, OPENGL);
gfx = new ToxiclibsSupport(this);
initMesh();
}
void draw() {
background(0);
fill(255);
text("subdiv: "+subdiv.getClass().getSimpleName(),20,20);
translate(width / 2, height / 2, 0);
rotateX(mouseY * 0.01f);
rotateY(mouseX * 0.01f);
scale(currZoom);
if (isWireframe) {
noFill();
drawWireMeshDelta();
}
else {
fill(255);
noStroke();
lights();
gfx.mesh(mesh, true, 0);
}
}
void drawWireMeshDelta() {
beginShape(LINES);
TColor col = TColor.newHSV(0, 1, 1);
for (WingedEdge e : mesh.edges.values()) {
int idA=((WEVertex) e.a).id;
int idB=((WEVertex) e.b).id;
float da = e.a.distanceTo(vertBackup.get(idA));
float db = e.b.distanceTo(vertBackup.get(idB));
col.setHue(da * 0.05);
stroke(col.toARGB());
vertex(e.a.x, e.a.y, e.a.z);
col.setHue(db * 0.05);
stroke(col.toARGB());
vertex(e.b.x, e.b.y, e.b.z);
}
endShape();
}
void keyPressed() {
if (key == '-') {
currZoom -= 0.1f;
}
if (key == '=') {
currZoom += 0.1f;
}
if (key == 'w') {
isWireframe = !isWireframe;
}
if (key == 'l') {
new LaplacianSmooth().filter(mesh, 1);
}
if (key == 's') {
// subdivide all mesh edges if their length > 10
mesh.subdivide(subdiv,10);
backupMesh();
}
if (key == 'x') {
mesh.saveAsSTL(sketchPath("subdiv-" + DateUtils.timeStamp() + ".stl"));
}
if (key=='d') {
deformMesh();
}
if (key=='r') {
initMesh();
}
if (key>='1' && key<='5') {
switch(key) {
case '1':
// midpoint subdiv splits an edge in half
subdiv=new MidpointSubdivision();
break;
case '2':
// splits an edge in half and displaces midpoint along dir from mesh centroid
// in this case the point moves 22% of original edge length towards mesh centroid
subdiv=new MidpointDisplacementSubdivision(mesh.computeCentroid(),-0.22);
break;
case '3':
// splits edges at 33% and 66%, resulting in 3 shorter ones
subdiv=new DualSubdivision();
break;
case '4':
// splits edges at 25%, 50% and 75%, resulting in 4 shorter ones
subdiv=new TriSubdivision();
break;
case '5':
// similar to MidpointDisplacementSubdivision, only displacement direction is
// not based on relation to a fixed reference point, but uses average normal vector
// of faces attached to each edge
subdiv=new NormalDisplacementSubdivision(0.25f);
break;
}
}
}
void initMesh() {
mesh = new WETriangleMesh();
//mesh.addMesh(new Plane(new Vec3D(), new Vec3D(0, 1, 0)).toMesh(400));
mesh.addMesh(new AABB(new Vec3D(0, 0, 0), 200).toMesh());
backupMesh();
}
// keep a backup of all vertex positions for wireframe rendering
void backupMesh() {
vertBackup.clear();
for (Vec3D v : mesh.getVertices()) {
vertBackup.add(v.copy());
}
}
// randomly displace some mesh vertices
void deformMesh() {
for (Vec3D v : mesh.getVertices()) {
if (random(1)<0.2) {
v.scaleSelf(random(0.8,1.2));
}
}
mesh.rebuildIndex();
backupMesh();
}