Trouble exporting 3D animation to SVG

Hey, everyone.

I’m just starting with coding and Processing, in particular. I’ve watched this great tutorial on how to make this nice 3D animation and I tried it myself. Everything worked great but I wanted to export the end result as a SVG file so I can edit it on Figma or Illustrator.

But no matter what I tried, I always end up with nothing but a white canvas. This is the code and what I’ve tried to do to solve this.

First attempt:

PImage img;
import processing.svg.*;
boolean record;

void setup() {
  size(900, 900, P3D);
  img = loadImage("art.png");
  img.resize(900, 900);
}

void draw() {
  if (record) {
    beginRecord(SVG, "frame-####.svg");
  }
  
  background(#f1f1f1);
  fill(0);
  noStroke();
  sphereDetail(3);
  float tiles = 100;
  float tileSize = width/tiles;
  
  push();
  
  translate(width/2,height/2);
  rotateY(radians(frameCount));
  
  for (int x = 0; x < tiles; x++) {
    for (int y = 0; y < tiles; y++) {
      color c = img.get(int(x*tileSize),int(y*tileSize));
      float b = map(brightness(c),0,255,1,0);
      float z = map(b,0,1,-150,150);
      
      push();
      translate(x*tileSize - width/2, y*tileSize - height/2, z);
      sphere(tileSize*b*0.4);
      pop();
    }
  }
  
  pop();
  
  if (record) {
    endRecord();
    record = false;
  }
}

// Use a keypress so thousands of files aren't created
void mousePressed() {
  record = true;
}

What I’ve got from this is a small freeze in the animation when I click with my mouse and a gray (#f1f1f1) frame-####.svg (897 bytes).

Second attempt:

PImage img;
import processing.svg.*;
boolean record;

void setup() {
  size(900, 900, P3D);
  img = loadImage("art.png");
  img.resize(900, 900);
}

void draw() {
  if (record) {
    beginRaw(SVG, "output.svg");
  }
  
  background(#f1f1f1);
  fill(0);
  noStroke();
  sphereDetail(3);
  float tiles = 100;
  
  float tileSize = width/tiles;
  push();
  
  translate(width/2,height/2);
  rotateY(radians(frameCount));
  
  for (int x = 0; x < tiles; x++) {
    for (int y = 0; y < tiles; y++) {
      color c = img.get(int(x*tileSize),int(y*tileSize));
      float b = map(brightness(c),0,255,1,0);
      float z = map(b,0,1,-150,150);
    
      push();
      
      translate(x*tileSize - width/2, y*tileSize - height/2, z);
      sphere(tileSize*b*0.4);
      pop();
      
      if (record) {
        endRaw();
        record = false;
      }
    }
  }
   
  pop();
}

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

What I’ve got from this is a small freeze in the animation when I hit r and a full white output.svg file (2KB).

I’ve tried moving the background and the if(record) statement but nothing has changed.

Any ideas on how to export this?

Welcome @Lucianoinfanti

I don’t think Processing works like this. It won’t automatically convert 3D objects to 2D vectors (for SVG). I’d suggest exporting as an obj file. You can install the OBJExport library (using Sketch > Import Library > Add Library). Here’s an example sketch:

import nervoussystem.obj.*;

size(600, 400, P3D);
beginRecord("nervoussystem.obj.OBJExport", "ellipse.obj"); 
translate(width/2, height/2, -200);
sphere(100);
endRecord();

This will create a new ellipse.obj in your sketch folder. You can convert that to an SVG using scVector.

1 Like

Thanks for your help!

I’m quite confused now because I was actually following instructions from Processing.org on how to export SVG files from 3D scenes. The link is this one: SVG Export / Libraries / Processing.org

It says:

SVG Files from 3D Geometry (With Screen Display)

To create vectors from 3D data, use the beginRaw() and endRaw() commands. These commands will grab the shape data just before it is rendered to the screen. At this stage, your entire scene is nothing but a long list of lines and triangles. This means that a shape created with sphere() method will be made up of hundreds of triangles, rather than a single object.

When using beginRaw() and endRaw(), it’s possible to write to either a 2D or 3D renderer. For instance, beginRaw() with the SVG library will write the geometry as flattened triangles and lines.

I also tried the Single Frame from an Animation (With Screen Display) method, but it didn’t work either. Am I missing something?

1 Like

This is awesome! I didn’t realize that Processing can do this! Ignore what I said before.

I ran through your script, which I managed to get working. I’ve changed a few things:

  1. A tiles value of 100 is too demanding for the computer I’m on right now, so I reduced this to 10.

  2. I’ve shuffled the nesting of your statements a bit – the final if statement isn’t nested within a for loop anymore.

  3. On my (Linux) computer, I have to add the settings() function (at the top of the sketch) to get 3D Processing working properly. I don’t think you need to do this, but just maybe this is part of the solution.

void settings() { 
  System.setProperty("jogl.disable.openglcore", "false");
  size(900, 900, P3D);
}

PImage img;
import processing.svg.*;
boolean record;

void setup() {
  //size(900, 900, P3D);
  img = loadImage("art.png");
  img.resize(900, 900);
}

void draw() {
  
  if (record) {
    beginRaw(SVG, "output.svg");
  }
  
  background(#f1f1f1);
  fill(0);
  noStroke();
  sphereDetail(3);
  float tiles = 10;
  float tileSize = width/tiles;
  push();
  translate(width/2,height/2);
  rotateY(radians(frameCount));
  
  for (int x = 0; x < tiles; x++) {
    
    for (int y = 0; y < tiles; y++) {
      color c = img.get(int(x*tileSize),int(y*tileSize));
      float b = map(brightness(c),0,255,1,0);
      float z = map(b,0,1,-150,150);
      push();
      translate(x*tileSize - width/2, y*tileSize - height/2, z);
      sphere(tileSize*b*0.4);
      pop();
    }
  }

  pop();
      
  if (record) {
    endRaw();
    record = false;
  }
}

void keyPressed() {
  
  if (key == 'r') {
    record = true;
  }
}
1 Like

That worked just fine! I was intrigued by your last statement so I decided to remove the settings() function and it worked as well.

That leads me to believe the problem was the if statement incorrectly placed inside the draw() function.

Thanks again for your help.

1 Like