Skip to content

Instantly share code, notes, and snippets.

@atduskgreg
Last active January 4, 2019 09:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atduskgreg/4638679 to your computer and use it in GitHub Desktop.
Save atduskgreg/4638679 to your computer and use it in GitHub Desktop.
Extrude 3D geometry from an image based on its brightness. Plus has the ability to save out an STL and OBJ using toxilibs. Also generates a texture with correct uv coordinates. Appends uv coords to obj file, saves texture file along with material file and obj in a folder together.
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.processing.*;
PImage img;
ArrayList<ImagePoint> points;
float xmag, ymag = 0;
float newXmag, newYmag = 0;
int minX = 10000;
int minY = 10000;
float minZ = 10000;
float s = 1;
PImage tex;
WETriangleMesh mesh = new WETriangleMesh("wave");
DefaultSTLColorModel colModel;
STLWriter stl;
ToxiclibsSupport gfx;
int maxX, maxY;
float maxZ = -1000;
boolean showImage = true;
ArrayList<Vec2D> uvs;
void setup() {
img = loadImage("mask_frame235.png");
colModel = new DefaultSTLColorModel();
stl = new STLWriter(colModel, 64);
tex = createImage(1024, 1024, RGB);
uvs = new ArrayList<Vec2D>();
println("format color: " + colModel.formatRGB(color(255, 0, 0)));
Integer c = color(255, 0, 0);
println("format color: " + colModel.formatRGB(c));
size(800, 800, P3D);
points = new ArrayList<ImagePoint>();
processImage();
gfx = new ToxiclibsSupport(this);
}
void keyPressed() {
if (key == ' ') {
showImage = !showImage;
}
if (key == '=') {
s += 0.1;
}
if (key == '-') {
s -= 0.1;
}
if (key == 's') {
buildAndSaveMesh();
}
}
class ImagePoint {
PVector pos;
color c;
boolean valid;
ImagePoint(PVector pos, color c, boolean valid) {
this.pos = pos;
this.c = c;
this.valid = valid;
if (pos.x == 0 && pos.y == 0 && pos.z == 0) {
valid = false;
}
}
}
void draw() {
background(125);
noStroke();
pushMatrix();
translate(width/2, height/2);
newXmag = mouseX/float(width) * TWO_PI;
newYmag = mouseY/float(height) * TWO_PI;
float diff = xmag-newXmag;
if (abs(diff) > 0.01) {
xmag -= diff/4.0;
}
diff = ymag-newYmag;
if (abs(diff) > 0.01) {
ymag -= diff/4.0;
}
rotateX(-ymag);
rotateY(-xmag);
scale(s);
translate(-(maxX - minX)/2, -(img.height - (maxY - minY)/2), -(maxZ - minZ)/2);
if (showImage) {
textureMode(NORMAL);
gfx.texturedMesh(mesh, tex, false);
}
else {
gfx.mesh(mesh);
}
translate((maxX - minX)/2, (img.height - (maxY - minY)/2), (maxZ - minZ)/2);
popMatrix();
if (showImage) {
image(img, 0, 0);
image(tex, width-tex.width/4, height-tex.height/4, tex.width/4, tex.height/4);
}
}
void addFaceToMesh(ImagePoint p1, ImagePoint p2, ImagePoint p3, Vec2D uv1, Vec2D uv2, Vec2D uv3) {
mesh.addFace(PToV(p1), PToV(p2), PToV(p3), uv1, uv2, uv3);
uvs.add(uv1);
uvs.add(uv2);
uvs.add(uv3);
}
Vec3D PToV(ImagePoint p) {
return new Vec3D(p.pos.x, p.pos.y, p.pos.z);
}
int texIndex(ImagePoint p) {
return (int)(p.pos.x + p.pos.y * tex.width);
}
void buildAndSaveMesh() {
mesh.clear();
uvs.clear();
ArrayList<Integer> meshColors = new ArrayList<Integer>();
println("num points: " + points.size());
Vec2D scaleUV=new Vec2D(tex.width, tex.height).reciprocal();
println("scaleUV: " + scaleUV.x + " "+ scaleUV.y);
tex.loadPixels();
for (int i = 0; i < points.size() - img.width - 1; i+=1) {
int nw = i;
int ne = i + 1;
int sw = i + img.width;
int se = i + img.width + 1;
if (points.get(nw).valid && points.get(ne).valid && points.get(sw).valid && points.get(se).valid) {
// meshColors.add(points.get(nw).c);
Vec2D uva = new Vec2D(points.get(nw).pos.x, points.get(nw).pos.y).scaleSelf(scaleUV);
Vec2D uvb = new Vec2D(points.get(se).pos.x, points.get(se).pos.y).scaleSelf(scaleUV);
Vec2D uvc = new Vec2D(points.get(ne).pos.x, points.get(ne).pos.y).scaleSelf(scaleUV);
Vec2D uvd = new Vec2D(points.get(sw).pos.x, points.get(sw).pos.y).scaleSelf(scaleUV);
tex.pixels[texIndex(points.get(nw))] = points.get(nw).c;
tex.pixels[texIndex(points.get(se))] = points.get(se).c;
tex.pixels[texIndex(points.get(ne))] = points.get(ne).c;
tex.pixels[texIndex(points.get(sw))] = points.get(sw).c;
addFaceToMesh(points.get(nw), points.get(se), points.get(ne), uva, uvb, uvc);
// fill(points.get(nw).c);
//meshColors.add(points.get(sw).c);
addFaceToMesh(points.get(nw), points.get(sw), points.get(se), uva, uvd, uvb);
}
}
tex.updatePixels();
//tex.resize(512, 512);
tex.save(sketchPath("obj/tex01.png"));
new LaplacianSmooth().filter(mesh, 2);
createMaterialFile(); // have to do this before saving obj, etc. to create folder
saveAsObj(mesh, sketchPath("obj/wave_with_uvs.obj"));
}
void saveAsObj(WETriangleMesh m, String path) {
println("here");
boolean saveNormals = true;
boolean saveUVs = true;
OBJWriter obj = new OBJWriter();
obj.beginSave(path);
int vOffset = obj.getCurrVertexOffset() + 1;
int nOffset = obj.getCurrNormalOffset() + 1;
int uvOffset = obj.getCurrUVOffset() + 1;
println("writing OBJMesh: " + this.toString());
obj.newObject(m.name);
obj.addMaterialFile("tex.mtl");
// vertices
for (Vertex v : m.vertices.values()) {
obj.vertex(v);
}
if(saveUVs){
for (Face f : m.faces) {
obj.uv(f.uvA.x, f.uvA.y);
obj.uv(f.uvB.x, f.uvB.y);
obj.uv(f.uvC.x, f.uvC.y);
}
}
// faces
if (saveNormals) {
// normals
for (Vertex v : m.vertices.values()) {
obj.normal(v.normal);
}
for (Face f : m.faces) {
obj.faceWithNormalsAndUVs(f.b.id + vOffset, f.a.id + vOffset, f.c.id
+ vOffset, f.b.id + nOffset, f.a.id + nOffset, f.c.id
+ nOffset, f.b.id + uvOffset, f.a.id + uvOffset, f.c.id
+ uvOffset);
}
}
else {
for (Face f : m.faces) {
obj.face(f.b.id + vOffset, f.a.id + vOffset, f.c.id + vOffset);
}
}
obj.endSave();
}
void createMaterialFile() {
PrintWriter out = createWriter(sketchPath("obj/tex.mtl"));
out.println("newmtl Texture_0");
out.println("map_Kd tex01.png");
out.close();
}
void processImage() {
img.loadPixels();
points.clear();
for (int row = 0; row < img.height; row++) {
for (int col = 0; col < img.width; col++) {
int i = row*img.width + col;
if (alpha(img.pixels[i]) < 255 || brightness(img.pixels[i]) < 50) {
img.pixels[i] = color(0, 0, 0, 0);
points.add(new ImagePoint(new PVector(), color(0), false));
}
else {
float z = map(brightness(img.pixels[i]), 0, 255, -30, 30);
if (row < minY) {
minY = row;
}
if (row > maxY) {
maxY = row;
}
if (col < minX) {
minX = col;
}
if (col > maxX) {
maxX = col;
}
if (z < minZ) {
minZ = z;
}
if (z > maxZ) {
maxZ = z;
}
points.add(new ImagePoint(new PVector(col, row, z), color(img.pixels[i]), true));
}
}
}
println("minX : " + minX + " maxX: " + maxX + "\nminY: " + minY + " maxY: " + maxY + "\nminZ: " + minZ + " maxZ: " + maxZ);
img.updatePixels();
}
public class OBJWriter {
public final String VERSION = "0.3";
protected OutputStream objStream;
protected PrintWriter objWriter;
protected int numVerticesWritten = 0;
protected int numNormalsWritten = 0;
protected int numUVsWritten = 0;
public void beginSave(OutputStream stream) {
try {
objStream = stream;
handleBeginSave();
} catch (Exception e) {
e.printStackTrace();
}
}
public void beginSave(String fn) {
try {
objStream = new FileOutputStream(fn);
handleBeginSave();
} catch (Exception e) {
e.printStackTrace();
}
}
public void endSave() {
try {
objWriter.flush();
objWriter.close();
objStream.flush();
objStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addMaterialFile(String filename){
objWriter.println("mtllib " + filename);
}
public void face(int a, int b, int c) {
objWriter.println("f " + a + " " + b + " " + c);
}
public void uv(float uvx, float uvy){
objWriter.println("vt " + uvx + " " + uvy);
}
public void faceList() {
objWriter.println("s off");
}
public void faceWithNormalsAndUVs(int a, int b, int c, int na, int nb, int nc, int uv1, int uv2, int uv3) {
objWriter.println("f " + a + "/" + uv1 + "/" + na + " " + b + "/" + uv2 + "/" + nb + " " + c
+ "/" + uv3 + "/" + nc);
}
public int getCurrNormalOffset() {
return numNormalsWritten;
}
public int getCurrVertexOffset() {
return numVerticesWritten;
}
public int getCurrUVOffset() {
return numUVsWritten;
}
protected void handleBeginSave() {
objWriter = new PrintWriter(objStream);
objWriter.println("# generated by OBJExport v" + VERSION);
numVerticesWritten = 0;
numNormalsWritten = 0;
numUVsWritten = 0;
}
public void newObject(String name) {
objWriter.println("o " + name);
}
public void normal(Vec3D n) {
objWriter.println("vn " + n.x + " " + n.y + " " + n.z);
numNormalsWritten++;
}
public void vertex(Vec3D v) {
objWriter.println("v " + v.x + " " + v.y + " " + v.z);
numVerticesWritten++;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment