# STL export for 3D printing

**URL:** <https://discourse.processing.org/t/stl-export-for-3d-printing/43581>\
**Category:** Libraries\
**Created:** [December 29, 2023, 10:03pm UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581 "2023-12-29T22:03:56Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![asymmetric](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/asymmetric/32/16577_2.png) [@asymmetric](https://discourse.processing.org/u/asymmetric)\
**Post date:** [December 29, 2023, 10:03pm UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/1 "2023-12-29T22:03:56Z")

</div>

Happy Holidays, Processing Community!

I have been making bump maps from images with processing.js for the past year and I was wondering if there is a way to translate that code so that it can export to an .STL file to 3d print?

 ![Screen Shot 2023-12-01 at 4.02.50 PM](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/e/a/ea746206bd802fa6c758dca79603bffabccb8371.jpeg)

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [December 29, 2023, 10:41pm UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/2 "2023-12-29T22:41:50Z")

</div>

Hello @asymmetric,

I had success with this at the time:

> [@Exporting to OBJ](https://discourse.processing.org/t/exporting-to-obj/11051/5):
>
> OBJExport is available in the Processing 3 libraries with examples and it is working for me: I created a Mobius strip rotating and translating boxes. Exported it as an OBJ file. Imported it into Prusa Splicer. 3D printed it with my new Prusa MK3S. This is just a draft print.slight_smile

There are OBJ to STL convertors out there. A Google search will find these.

> [@asymmetric](#):
>
> I have been making bump maps from images with processing.js

_[Processing.js - Happy Coding](https://happycoding.io/tutorials/processing/processing-js)_ states:

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/0/0/008ccb227fe509b02cb3118ebe0cad116b2e4c93.png)

`:)`

---

<div class="post-metadata">

**Author:** ![asymmetric](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/asymmetric/32/16577_2.png) [@asymmetric](https://discourse.processing.org/u/asymmetric)\
**Post date:** [December 30, 2023, 1:53am UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/3 "2023-12-30T01:53:42Z")

</div>

How would I edit the following code in order to OBJ Export?

I tried the following but it is not working…

```auto
import peasy.PeasyCam; // Import the PeasyCam library for easy camera navigation
import toxi.geom.Vec3D; // Import the Vec3D class from toxiclibs for vector operations
import wblut.hemesh.HEC_FromImage;
import wblut.hemesh.HE_Mesh;
import wblut.processing.WB_Render;

boolean record;
boolean mode = false;
float halfWidth, halfHeight;
int rows, cols;
GridPoint[][] grid;
PImage img;
HE_Mesh terrainMesh;
WB_Render render;
PeasyCam cam; // PeasyCam instance for easy camera navigation

void setup() {
  size(980, 980, P3D);
  noLoop();
  frameRate(1);
  smooth(8);
  halfWidth = width / 2.0;
  halfHeight = height / 2.0;
  initGrid(7.0);
  textureMode(IMAGE);

  cam = new PeasyCam(this, width / 2, height / 2, 0, 500);
  cam.setMinimumDistance(50);
  cam.setMaximumDistance(1000);

  // Create a terrain mesh using the HEC_FromImage class from toxiclibs
  HEC_FromImage hemeshBuilder = new HEC_FromImage(this);
  hemeshBuilder.setWidth(cols)
    .setDepth(rows)
    .setMeshResolutionX(1)
    .setMeshResolutionY(1)
    .setHeightMap(img)
    .setHeightScale(150.0);
  terrainMesh = new HE_Mesh(hemeshBuilder);
  render = new WB_Render(this);
}

void initGrid(float scl) {
  img = loadImage("gradient888_2.jpg");
  img.loadPixels();
  img.resize(980, 980);
  rows = floor(height / scl);
  cols = floor(width / scl);
  grid = new GridPoint[rows][cols];
  for (int y = 0; y < rows; y++) {
    for (int x = 0; x < cols; x++) {
      int sx = floor(x * scl);
      int sy = floor(y * scl);
      grid[y][x] = new GridPoint(-halfWidth + sx + scl / 2, -halfHeight + sy + scl / 2, 150.0 * (brightness(img.get(sx, sy)) / 255 - 0.6));
    }
  }
}

void draw() {
  background(0);
  if (record) {
    beginRecord(OBJExport.class, "output_December19_2023.obj");
  }

  translate(halfWidth, halfHeight, 0.05);

  if (mode) {
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    noFill();
    stroke(128);
    strokeWeight(1.5);
    smooth(8);
  }

  cam.beginHUD(); // Switch to HUD (Heads-Up Display) mode for text
  fill(255);
  text("Press 'p' to record a single frame", 10, 20);
  cam.endHUD(); // Switch back to 3D mode

  render.drawFaces(terrainMesh); // Draw the terrain mesh

  if (record) {
    endRecord();
    record = false;
  }
}

void keyPressed() {
  if (key == 'p') {
    record = true;
  }
  redraw();
}

class GridPoint {
  float x, y, v;

  public GridPoint(float px, float py, float pv) {
    x = px;
    y = py;
    v = pv;
  }
}

```

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [December 30, 2023, 4:23pm UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/4 "2023-12-30T16:23:41Z")

</div>

> [@asymmetric](#):
>
> How would I edit the following code in order to OBJ Export?

I would start by installing the library and exploring the examples.

Using the code provided (with some modifications) I was able to export this OBJ file and view directly (no conversion) with 3D builder in Windows:

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/c/ac230e8609e4c35366a79c441948f6efa7661ac6.png)

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/9/f907ecd3f7f258546c6016d129071165e98b56bf.jpeg)

You need to understand the code that you are using.  
I suggest removing everything related to texturing and only retain the mesh.

You may not be able to 3D print a thin film like this and may have to add some depth to it. I had this issue with Möbius strip I printed and had to modify.

Consider making columns with some depth (from the base plane is one option) to the height of the image (brightness) to provide a solid model to print.

As a courtesy and out of respect to the author please link to the original source code you are using. Add a few lines to the top of the code with a link to the source of the code.

Image used:  
_[http://learningprocessing.com/code/assets/sunflower.jpg](http://learningprocessing.com/code/assets/sunflower.jpg)_

`:)`

---

<div class="post-metadata">

**Author:** ![asymmetric](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/asymmetric/32/16577_2.png) [@asymmetric](https://discourse.processing.org/u/asymmetric)\
**Post date:** [January 1, 2024, 11:07pm UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/5 "2024-01-01T23:07:42Z")

</div>

Hi @glv! Thank you for your help, but I am still struggling to export an OBJ or STL file for 3D printing. I downloaded Processing 3 but I am still not having any luck. When I remove the “texturing” I am left with a flat plain. I would like to 3d print the following code with a fill so it is easier to print.

Here is the following code that I have tried in Processing 3

```auto
// Add the necessary import for Toxiclibs OBJ writer
import toxi.geom.mesh.WETriangleMesh;
import toxi.processing.ToxiclibsSupport;
import toxi.obj.ToxiclibsOBJWriter;

// a class to represent a point on our grid.
class GridPoint {
  float x, y, v;
  public GridPoint(float px, float py, float pv) {
    x = px;  
    y = py;  
    v = pv;  
  }
}

float halfWidth, halfHeight;
boolean mode = false;
int rows, cols;
GridPoint[][] grid;
PImage img;
WETriangleMesh exportMesh; // Toxiclibs mesh for exporting
ToxiclibsSupport gfx; // Toxiclibs support for mesh conversion

void setup() {
  size(980, 980, P3D);
  noLoop();
  frameRate(1);
  smooth(8);
  halfWidth = width / 2.0;
  halfHeight = height / 2.0;
  initGrid(7.0);
  textureMode(IMAGE);

  // Initialize Toxiclibs mesh and support
  exportMesh = new WETriangleMesh();
  gfx = new ToxiclibsSupport(this);
}

void initGrid(float scl) {
  img = loadImage("gradient888_2.jpg");
  img.loadPixels();
  img.resize(980, 980);
  rows = floor(height / scl);
  cols = floor(width / scl);
  grid = new GridPoint[rows][cols];
  for (int y = 0; y < rows; y++) {
    for (int x = 0; x < cols; x++) {
      int sx = floor(x * scl);
      int sy = floor(y * scl);
      grid[y][x] = new GridPoint(-halfWidth + sx + scl / 2, -halfHeight + sy + scl / 2, 150. * (brightness(img.get(sx, sy)) / 255 - 0.6));
    }
  }
}

void draw() {
  translate(halfWidth, halfHeight, 0.05);
  exportMesh.clear(); // Clear existing vertices in the export mesh

  for (int y = 0; y < rows - 1; y++) {
    beginShape(QUAD_STRIP);
    for (int x = 0; x < cols; x++) {
      GridPoint ca = grid[y][x];
      GridPoint cb = grid[y + 1][x];
      exportMesh.addFace(gfx.createMeshFace(ca.x, ca.y, ca.v, cb.x, cb.y, cb.v, ca.x + halfWidth, ca.y + halfHeight),
                         gfx.createMeshFace(cb.x, cb.y, cb.v, ca.x + halfWidth, ca.y + halfHeight, cb.x + halfWidth, cb.y + halfHeight));
    }
    endShape();
  }

  // Export the mesh to OBJ file
  ToxiclibsOBJWriter.save(exportMesh, sketchPath("output.obj"));

  // Continue with your existing drawing code (commented out for brevity)
  /*
  if (mode) {
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    fill(150);
    stroke(255);
    strokeWeight(1.5);
    smooth(8);
  }

  for (int y = 0; y < rows - 1; y++) {
    beginShape(QUAD_STRIP);
    if (mode) {
      texture(img);
    }
    for (int x = 0; x < cols; x++) {
      GridPoint ca = grid[y][x];
      GridPoint cb = grid[y + 1][x];
      vertex(ca.x, ca.y, ca.v, ca.x + halfWidth, ca.y + halfHeight);
      if (mode) {
        PVector na = new PVector(ca.x - cb.x, ca.y - cb.y, 0).sub(new PVector(ca.x - cb.x, ca.y - cb.y, 1)).normalize();
        normal(na.x, na.y, na.z);
      }
      vertex(cb.x, cb.y, cb.v, cb.x + halfWidth, cb.y + halfHeight);
      if (mode) {
        PVector nb = new PVector(cb.x - ca.x, cb.y - ca.y, 0).sub(new PVector(cb.x - ca.x, cb.y - ca.y, 1)).normalize();
        normal(nb.x, nb.y, nb.z);
      }
    }
    endShape();
  }
  */
}

```

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [January 2, 2024, 12:43am UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/6 "2024-01-02T00:43:52Z")

</div>

Hello,

I can’t offer any assistance with the library you are using.

This is the library I am using that comes with Processing 4:

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/b/7/b7089aade0e10e1515552bc603c7b5fc796f582d.png)

[https://funprogramming.org/152-Exporting-3D-shapes-as-obj-files-in-Processing.html](https://funprogramming.org/152-Exporting-3D-shapes-as-obj-files-in-Processing.html)

`:)`

---

<div class="post-metadata">

**Author:** ![asymmetric](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/asymmetric/32/16577_2.png) [@asymmetric](https://discourse.processing.org/u/asymmetric)\
**Post date:** [January 2, 2024, 12:47am UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/7 "2024-01-02T00:47:39Z")

</div>

Hi @glv! Thank you again for your help. I have gotten somewhere with the nervous system library in Processing 3, but the output is a TIFF image with .obj in the name… The following code outputs a file type .obj.tif? What am I doing wrong? When I try the same code in Processing 4.3 there is no output.

```auto
import nervoussystem.obj.*;

// a class to representing a point on our grid.
// This should/would/could be done better by defining it as faces (of a triangle mesh).
// but would it keep as simple as possible 
class GridPoint {
  float x, y, v;
  public GridPoint(float px, float py, float pv) {
    x = px; // x-pos of the grid point
    y = py; // y-pos of the grid point
    v = pv; // height of the grid point
  }
}

//helper variables
float halfWidth, halfHeight;
boolean mode = false;
// storage of out grid
int rows, cols;
GridPoint[][] grid;
// image used for texture and heightmap
PImage img;

boolean record = false;

void setup() {
  size(980, 980, P3D); // OPENGL?
  noLoop();
  frameRate(1);
  smooth(8);
  halfWidth = width/2.;
  halfHeight = height/2.;
  initGrid(7.);
  // use textureMode IMAGE as the grid is not normalized
  textureMode(IMAGE);
  
}

void initGrid(float scl) {
  // load the image, used for texture and heightmap.
  // usually more than one picture will be used. One for heightmap, one for normalmap and one with the texture.
  // for simplicity it has the same size than our grid to not make the code more math intensive.
  // to get this work our image is somehow a bit blurred (like noise is), otherwise the mesh isn't smooth enough
  img = loadImage("gradient888_2.jpg");
  img.loadPixels();
  img.resize(980, 980);
  // initialize our gridpoints
  rows = floor(height/scl);
  cols = floor(width/scl);
  grid= new GridPoint[rows][cols];
  for (int y = 0; y < rows; y++) {
    for (int x = 0; x < cols; x++) {
      int sx = floor(x*scl);
      int sy = floor(y*scl);
      // set x and y coordinates (center origin) and the height of the current point.
      // height is the normalized brightness value [0..1] of the pixel color from the image.
      // Subtract 0.5 to align it to center [-0.5..0.5]
      // scaling it by 50, so the range is [50.0*(-0.5)..50.0*(0.5)]
      grid[y][x] = new GridPoint(-halfWidth+sx+scl/2, -halfHeight+sy+scl/2, 150.*(brightness(img.get(sx, sy))/255-0.6));
    }
  }
}

void draw() {
  if (record){
    beginRecord("nervoussystem.obj.OBJExport", "output.obj");
  }
  
  // set the origin to the center of the screen, and move it 400px far away on z-axis
  translate(halfWidth, halfHeight, 0.05); // zoom in (lower numbers) zoom out (higher numbers around 200)
  
  // switch between mesh and textured mode
  if (frameCount % int(TAU*100.) == 0) {
    mode = !mode;
  }

  if (mode) {
    // if textured mode no strokes and a light from viewpoint to object
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    // if mesh mode only show strokes
    fill(150);
    stroke(255);
    strokeWeight(1.5);
    smooth(8);
  }

  // apply the rotation after lighting, otherwise, the light gets also rotated, which we do not want.
  // tipping 30 degrees back on X-axis
  //rotateX(radians(30));
  // tipping left/right a bit per frame from -PI/4 - +PI/4 on Y-axis
  //rotateY(sin(frameCount/100.)*QUARTER_PI);

  // build the surface/terrain by triangle strips to display it
  for (int y = 0; y < rows-1; y ++) {
    beginShape (QUAD_STRIP); // or (TRIANGLE_STRIP),(QUAD_STRIP), 
    if (mode) {
      // on textured mode set the texture
      texture(img);
    }
    // common triangle strips
    for (int x = 0; x < cols; x++) {
      GridPoint ca = grid[y][x];
      GridPoint cb = grid[y+1][x];
      vertex(ca.x, ca.y, ca.v, ca.x+halfWidth, ca.y+halfHeight);
      if (mode) {
        // on textured mode, we need to set the normal vector so the light knows how to behave on hitting the surface
        // usually, there are better calculations than this cheap hack
        // comment it out to see the difference if normals are messed up
        PVector na = new PVector(ca.x-cb.x, ca.y-cb.y, 0).sub(new PVector(ca.x-cb.x, ca.y-cb.y, 1)).normalize();
        normal(na.x, na.y, na.z);
      }
      vertex(cb.x, cb.y, cb.v, cb.x+halfWidth, cb.y+halfHeight);
      if (mode) {
        // on textured mode, we need to set the normal vector so the light knows how to behave on hitting the surface
        // usually, there are better calculations than this cheap hack
        // comment it out to see the difference if normals are messed up
        PVector nb = new PVector(cb.x-ca.x, cb.y-ca.y, 0).sub(new PVector(cb.x-ca.x, cb.y-ca.y, 1)).normalize();
        normal(nb.x, nb.y, nb.z);
      }
    }
    endShape();
  }

  if(record){
      endRecord();
      record = false;
    }
}
void keyPressed(){
  if(key == 'p') {
    record = true;
  }
}

```

![Screen Shot 2024-01-01 at 4.54.10 PM](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/9/a/9ae7147136708698ce867d1b1b6ec4bcbb4a72a9.png)

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [January 2, 2024, 1:14am UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/8 "2024-01-02T01:14:23Z")

</div>

Comment this:

```auto
// noLoop();

```

Use this image:

```auto
img = loadImage("http://learningprocessing.com/code/assets/sunflower.jpg");

```

You will then get an error! But that is a step toward success.

Remove the u and v parameters in your vertices.  
You will have to take a look at the reference for vertex for this.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/2/e/2eebf6311240c5df60fcc765ff9a78b0d7dcdbb6.jpeg)

`:)`

---

<div class="post-metadata">

**Author:** ![asymmetric](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/asymmetric/32/16577_2.png) [@asymmetric](https://discourse.processing.org/u/asymmetric)\
**Post date:** [January 2, 2024, 1:30am UTC](https://discourse.processing.org/t/stl-export-for-3d-printing/43581/10 "2024-01-02T01:30:51Z")

</div>

It seems to have worked, @glv!! Do you have any recommendations for making the surface thicker within Processing?

 ![Screen Shot 2024-01-01 at 5.29.57 PM](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/9/3/93f55a2a35daffdd6c2e94a1dec1bc70e1d3b076.jpeg)  
 ![Screen Shot 2024-01-01 at 5.29.45 PM](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/f/ffee88e452f9a7b2fc492a92bf25a7e2e4221919.jpeg)

Happy New Year @glv!!! ⭐ 🌟 ⭐ 🌟 ⭐ 🌟 ✴ 🌠 💫 ✨
