Hidden line removal for wireframe obj as PShape

Starting to play with importing an obj file using loadShape() and rendering it as a wireframe.
Now comes the tricky part… how to do ‘hidden line removal’ (similar to back-face detection, culling… still sorting out exact term here). Essentially, can someone point me in the right direction for doing this filtering or know of a solution from a previous forum discussion? I’ve found a handful of PDFs and presentations presenting algorithms that go over my head in terms of applying within this context… a few such links are:


http://www.cs.brandeis.edu/~cs155/Lecture_14.pdf
http://csis.pace.edu/~marchese/CG_Rev/Lect9New/cg_l9new.htm
http://underpop.free.fr/j/java/developing-games-in-java/1592730051_ch09lev1sec1.html

Here’s the base code for viewing the obj/PShape as a wireframe.
Wondering if/how screenX/Y/Z might play a role to solve this?

 // obj file instance
PShape obj;

public void setup() {
  size(512, 512, P3D);

  // load object
  obj = loadShape("teapot.obj");
}

public void draw() {
  background(0);

  translate(width/2, height/2);
  scale(2);
  rotateZ(PI);
  rotateY(radians(frameCount));

  noFill();
  stroke(255);
  int children = obj.getChildCount();

  beginShape();
  for (int i = 0; i < children; i++) {
    PShape child = obj.getChild(i);

    for (int j = 0; j < child.getVertexCount(); j++) {
      PVector v = child.getVertex(j);
      vertex(v.x, v.y, v.z);
    }
  }
  endShape();
}
1 Like

Hi ffd8,

Have you tried using fill(0, 0, 0, 0)? I think it should work.

Ha interesting – but unfortunately it only placed some black triangles within the shape (could still see past/around them and see plenty of lines in the back).

The ultimate goal is combining with my XYscope library for an efficient 3D object w/ hidden lines removed, so searching for a solution to filter out those PVectors.

You are getting that weird effect because you are not building the face properly I think.

I had to make a custom shape builder to load a shape and display it properly. The fill(0, 0, 0, 0) is working nice with it.

The code:

import peasy.*;

// obj file instance

myObject obj;
PeasyCam cam;

public void setup() {
  size(512, 512, P3D);
  cam = new PeasyCam(this, 400);

  obj = new myObject("Suzanne.obj");
}

public void draw() {
  background(200, 20, 20);
  
  fill(0, 0, 0, 0);
  stroke(255);
  strokeWeight(1);

  ArrayList<Quad> quads = obj.getQuads();  
  for (int i = 0; i < quads.size(); i++) {
    beginShape(QUADS);
    quads.get(i).createVertex();
    endShape();
  }
  
  ArrayList<Tri> tris = obj.getTris();  
  for (int i = 0; i < tris.size(); i++) {
    beginShape(TRIANGLES);
    tris.get(i).createVertex();
    endShape();
  }
  
}

class myObject {
  private ArrayList<PVector> vertices;
  private ArrayList<Tri> tris;
  private ArrayList<Quad> quads;

  public myObject(String objFile) {
    String[] lines;
    vertices = new ArrayList<PVector>();
    tris = new ArrayList<Tri>();
    quads = new ArrayList<Quad>();

    lines = loadStrings(objFile);
    for (int idx = 0; idx < lines.length; idx++) {
      
      String[] data = split(lines[idx], " ");

      if (data[0].equals("v")) {
        vertices.add(new PVector(float(data[1]), float(data[2]), float(data[3])));         
      } else if (data[0].equals("f")) {
        PVector v0, v1, v2;
        int idx0, idx1, idx2;

        idx0 = int(split(data[1], "/")[0]) - 1;
        idx1 = int(split(data[2], "/")[0]) - 1;
        idx2 = int(split(data[3], "/")[0]) - 1;
        
        printArray(data);
        println(idx0, idx1, idx2);

        v0 = vertices.get(idx0).copy();
        v1 = vertices.get(idx1).copy();
        v2 = vertices.get(idx2).copy();

        if (data.length == 5) {
          PVector v3;
          int idx3;
          idx3 = int(split(data[4], "/")[0]) - 1;
          v3 = vertices.get(idx3).copy();
          quads.add(new Quad(v0, v1, v2, v3));
        } else {
          tris.add(new Tri(v0, v1, v2));
        }
        
      }
      
    }
  }

  public ArrayList<Tri> getTris() {
    return tris;
  }
  
  public ArrayList<Quad> getQuads() {
    return quads;
  }
}

class Tri {
  private PVector[] vertices;

  public Tri(PVector v0, PVector v1, PVector v2) {
    vertices = new PVector[3];
    vertices[0] = v0;
    vertices[1] = v1;
    vertices[2] = v2;
  }

  public void createVertex() {
    vertex(vertices[0].x, vertices[0].y, vertices[0].z);
    vertex(vertices[1].x, vertices[1].y, vertices[1].z);
    vertex(vertices[2].x, vertices[2].y, vertices[2].z);
  }
}

class Quad {
  private PVector[] vertices;

  public Quad(PVector v0, PVector v1, PVector v2, PVector v3) {
    vertices = new PVector[4];
    vertices[0] = v0;
    vertices[1] = v1;
    vertices[2] = v2;
    vertices[3] = v3;
  }

  public void createVertex() {
    vertex(vertices[0].x, vertices[0].y, vertices[0].z);
    vertex(vertices[1].x, vertices[1].y, vertices[1].z);
    vertex(vertices[2].x, vertices[2].y, vertices[2].z);
    vertex(vertices[3].x, vertices[3].y, vertices[3].z);
  }
}

You can download a Suzanne wavefront example here

1 Like

On screen this looks really promising towards only showing the visible lines… however the hidden lines are still there – being covered up by this fill(0, 0, 0, 0) but not actually filtered from the arrayList. I’m hoping there’s a way (perhaps it requires a shader?) so that at any given rotation of the object, I can ask are you visible or being “covered” by another line/surface and only have the visible ones be part of the beginShape/endShape vertices.

It is, indeed just a trick to hide them.

To perform what you want to do you would also need to split your lines if they are partially covered. Sounds quite tricky ^^

May I ask what is the goal at the end?

Yeah it’s been quite tricky to wrap my head around… so i’m still hopeful someone in this community has already solved it in Processing style w/ PVectors.

Sure sure – the end goal is passing the filtered ArrayList of PVectors through my XYscope library (not shamelessly promoting… just want to extend the fun examples it comes with) – it translates the lines to L/R stereo sound, getting them back as an image on an analog oscilloscope/laser. It works fine as I have it above, but it would look cleaner and possibly sound nicer w/ fewer points once figuring out how to hide the back/hidden lines. In the end, once hopefully finding a solution, I’ll learn it was best not hiding those lines… but I won’t know until seeing/hearing it.

Yes, nice trick indeed! :+1: btw, your Suzanne link doesn’t work.

Thanks for the link: it should be working now!

If you were trying to resolve this at the graphics level and not
I believe (untested) that you can set a clipping plane in P3D by defining the frustum.

Thus, any form inside the clipping planes is rendered and visible; anything outside those planes is not visible. https://processing.org/reference/frustum_.html

Also in the graphics buffer direction are libraries like Picking – so you could paint every triangle / face, then check which ones are in the view to get your face list, then use your face list to get your point list or line list.

If you wanted to directly test a bunch of points for visibility from the perspective point, perhaps Box2D raycasting?

A related approach might be to calculate the hemesh HEC_ConvexHull of your shape to get the exterior (assuming your mesh is convex!) then throw everything away that isn’t on that exterior, then raycast the remaining points to ID what is front-facing, then throw away anything line with a point that didn’t hit.

1 Like

Thanks for those additional links! (sorry for lag, got sidetracked by other coding). The raycasting w/ box2d sounds the most promising. Indeed, wanting to do this at a pre-rendering level and not just painting over the hidden lines – since the goal is a smaller array of visible points to draw.