I’ve come up w/ a workaround for the pushMatrix()'s 32x limit!
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:
MatrixFrameGL clonedFrame = frame.clone();
To test-drive the class if it’s indeed working I’ve coded the sketch below:
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: