Too many calls to pushMatrix()

Hey,

Is there a way to increase the matrix stack depth for pushMatrix()?
I am working with 3D objects that have to be displayed with a relative position from one and other. The 3D objects are all created with box() from a list of positions saved in a tree structure. I use a recursive function to display the tree.

void displayModelRelative(Node node) {
  pushMatrix();
  translate(node.data[0], node.data[1], node.data[2]);
  
  rotateX(radians(node.data[3]));
  rotateY(radians(node.data[4]));
  rotateZ(radians(node.data[5]));
 
  box(node.data[6], node.data[7], node.data[8]);

  for (Node child : node.children) {
    displayModelRelative(child);
  }
  popMatrix();
}

Thanks for the fast reply, I already came across this post and tried it.
I’ve tested it again and I’m getting this error:

ClassCastException: processing.opengl.PGraphics3D cannot be cast to processing.awt.PGraphicsJava2D

In the above code use popMatrix before the for loop instead of after it

Unfortunately that is not possible. If I place the popMatrix before the loop the whole relative position is gone.

This is an example of the display model:
adsf

Each object has a relative position to the parent in the tree.

That reflection hack was intended for renderer JAVA2D only, sorry.

For the OPENGL renderers P2D & P3D a hacking workaround is substantially more complicated.

B/c their method pushMatrix() checks against constant MATRIX_STACK_DEPTH rather than the length of 1 of the 4 2D arrays:

So we have to go beyond than just expand() the length of the 4 2D arrays:

Besides that we’d have to keep in check the counter modelviewStackDepth is always less than MATRIX_STACK_DEPTH after a call to pushMatrix() and always more than 0 after a call to popMatrix().

EDIT:

B/c modelviewStackDepth acts not only as a push/pop counter but also as the current index for the 4 float[][] arrays, we cannot simply cheat on its current value!

Both methods pushMatrix() & popMatrix() would need to be re-written so they would check against the length of 1 of the 4 float[][] arrays instead of constant MATRIX_STACK_DEPTH.

An even easier fix would be to make field MATRIX_STACK_DEPTH public & non-final, so we could customize its value on our own if we needed to:
static protected final int MATRIX_STACK_DEPTH = 32;
static public int MATRIX_STACK_DEPTH = 32;

But for that, you’re gonna need to be very patient and lucky in order to convince the devs to make such piece-of-cake fix:

And if you work without pushMatrix and popMatrix all together?

Then all values be real relative

OR use pushMatrix and popMatrix for each box individually and use global positions (not relative)

I’ve come up w/ a workaround for the pushMatrix()'s 32x limit! :partying_face:

The current transform is stored in 4 PMatrix3D fields on the PGraphicsOpenGL object:

So I’ve defined my own wrapper class named MatrixFrameGL to store those 4 fields:

MatrixFrameGL.pde:

/**
 * Push/Pop Limit Workaround (v1.0.1)
 * GoToLoop (2020/Aug/22)
 *
 * Discourse.Processing.org/t/too-many-calls-to-pushmatrix/23411/8
 * Gist.GitHub.com/GoToLoop/e35ac69e9bd4dbbcce557218c3b20588
 */

// Comment out the package statement below if it's inside a ".java" file:
//package processing.core;

import processing.opengl.PGraphicsOpenGL;

static // Comment the keyword static if it's inside a ".java" file!
  public class MatrixFrameGL implements Cloneable
{
  public PMatrix modelview, modelviewInv, camera, cameraInv;

  public MatrixFrameGL() {
  }

  public MatrixFrameGL(final PGraphics pg) {
    pushMatrix(pg);
  }

  public MatrixFrameGL pushMatrix(final PGraphics pg) {
    return pushMatrix((PGraphicsOpenGL) pg);
  }

  public MatrixFrameGL pushMatrix(final PGraphicsOpenGL pg) {
    modelview    = pg.modelview.get();
    modelviewInv = pg.modelviewInv.get();
    camera       = pg.camera.get();
    cameraInv    = pg.cameraInv.get();
    return this;
  }

  public MatrixFrameGL popMatrix(final PGraphics pg) {
    return popMatrix((PGraphicsOpenGL) pg);
  }

  public MatrixFrameGL popMatrix(final PGraphicsOpenGL pg) {
    pg.modelview.set(modelview);
    pg.modelviewInv.set(modelviewInv);
    pg.camera.set(camera);
    pg.cameraInv.set(cameraInv);
    return this;
  }

  @Override public MatrixFrameGL clone() {
    try {
      final MatrixFrameGL frame = (MatrixFrameGL) super.clone();
      frame.modelview           = modelview.get();
      frame.modelviewInv        = modelviewInv.get();
      frame.camera              = camera.get();
      frame.cameraInv           = cameraInv.get();
      return frame;
    }
    catch (final CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }
}

We instantiate the class MatrixFrameGL by passing a P2D or P3D PGraphics object.

Alternatively we can simply instantiate it w/o passing anything and only later on invoke its method pushMatrix() passing the P2D or P3D PGraphics object.

For the sketch’s main canvas we can pass its field g or pass getGraphics():

  • MatrixFrameGL frame = new MatrixFrameGL(g);
  • MatrixFrameGL frame = new MatrixFrameGL(getGraphics());
  • MatrixFrameGL frame = new MatrixFrameGL().pushMatrix(g);
  • MatrixFrameGL frame = new MatrixFrameGL().pushMatrix(getGraphics());

The opposite operation is by calling method popMatrix() which restores the transform back into the passed PGraphics object argument:

  • frame.popMatrix(g);
  • frame.popMatrix(getGraphics());

As a bonus, class MatrixFrameGL also provides a clone() method: :wink:

  • MatrixFrameGL clonedFrame = frame.clone();

To test-drive the class if it’s indeed working I’ve coded the sketch below: :racing_car:

Push_Pop_Limit_Workaround.pde:

/**
 * Push/Pop Limit Workaround (v1.0.1)
 * GoToLoop (2020/Aug/22)
 *
 * Discourse.Processing.org/t/too-many-calls-to-pushmatrix/23411/8
 * Gist.GitHub.com/GoToLoop/e35ac69e9bd4dbbcce557218c3b20588
 */

import java.util.Queue;
import java.util.ArrayDeque;

final Queue<MatrixFrameGL> frames = new ArrayDeque<MatrixFrameGL>();

static final int BOXES = 32, SIZE = 6, MOV = SIZE << 2;
static final float ROT = TAU / BOXES;

void setup() {
  size(1200, 200, P3D);
  noLoop();
  stroke(#FFFF00); // yellow
}

void draw() {
  clear();

  translate(0, height >> 2);
  fill(#FF0000); // red
  recursiveBoxes(BOXES, SIZE, MOV, ROT, g, null); // 32 boxes

  translate(0, height >> 1);
  fill(#0000FF); // blue
  recursiveBoxes(BOXES * 3 >> 1, SIZE, MOV, ROT, g, frames); // 48 boxes
}

static final void recursiveBoxes(
  final int qty, final int size, final int moveX, final float rot, 
  final PGraphics pg, 
  final Queue<MatrixFrameGL> frames // pass null to use regular push/pop
  )
{
  if (qty <= 0)  return;

  if (frames != null)  frames.add(new MatrixFrameGL(pg));
  else                 pg.pushMatrix();

  pg.translate(moveX, 0);
  pg.rotateX(rot);
  pg.box(size);

  recursiveBoxes(qty - 1, size, moveX, rot, pg, frames);

  if (frames != null)  frames.remove().popMatrix(pg);
  else                 pg.popMatrix();
}

There’s also a Java “library” version of the class MatrixFrameGL on the link below: :octopus:

1 Like

Given this is a tricky workaround, having a Python Mode version for it can be helpful: :snake: