Faster Rendering Techniques in P3D?

I’ve made a very large 3D game with Processing, and for the past few months, I’ve dealt with pretty shotty performance, considering only 2,000 or so quads are being drawn at any given time. With enough workarounds, I’ve been able to maintain 60 FPS on my GTX 1070, but the game is getting so large that I keep having to sacrifice more and more to maintain a smooth framerate.

The bulk of the game’s drawing is done by a drawWall() function that looks something like this: (I’ve shortened it to get to the point)

void drawWall(float x, float y, float z, float thickness, float width, float height, PImage texture) {
beginShape();
vertex(x,y,z,0,0);
vertex(x+width,y,z,1,0);
vertex(x+width,y+height,z,1,1);
vertex(x,y+height,z,0,1);
endShape();
beginShape();
vertex(x+width,y,z,0,0);
vertex(x+width,y,z+thickness,1,0);
vertex(x+width,y+height,z+thickness,1,1);
vertex(x+width,y+height,z,0,1);
endShape();
beginShape();
vertex(x,y,z+thickness,0,0);
vertex(x+width,y,z+thickness,1,0);
vertex(x+width,y+height,z+thickness,1,1);
vertex(x,y+height,z+thickness,0,1);
endShape();
beginShape();
vertex(x,y,z,0,0);
vertex(x,y,z+thickness,1,0);
vertex(x,y+height,z+thickness,1,1);
vertex(x,y+height,z,0,1);
endShape();
}

This basic example is a 3D wall being drawn with width, thickness, and height. There aren’t any quads drawn on the top or bottom to make it a full cuboid because those parts are invisible when the walls go from the floor to the ceiling.

So is there any way to do that more efficiently? I read this article about P3D alternatives, and while I was able to get raw OpenGL 2 to work (and it definitely had much better performance), I don’t know how to incorporate Processing’s operations such as pushMatrix(), popMatrix(), translate(), rotate(), and scale().

What I’m really looking for are alternative methods of drawing shapes that save on performance but don’t force me to remove all of Processing’s 3D operations listed before. Any ideas?

I’ll also mention that I found some articles about glPushMatrix, glPopMatrix, glTranslate, etc, which would still require a lot of code-rewriting, but it’d be worth it in the end. No idea how to implement them because I don’t know the necessary libraries I need to import! :sweat_smile: Here’s a good example of what I’m talking about.

Alright, now I’m slapping myself in the face. Starting with beginShape(QUAD); has SIGNIFICANTLY improved performance. Other tips for performance improvements would still be appreciated, but this alone has made performance 4 times better.

2 Likes

You might also try using noStroke(). I have found OpenGL slower when drawing lines than filling shapes.

1 Like

What exactly do you mean? Replacing each beginShape() with beginShape(QUADS) ? Because you should be able to have one begin / end shape wrapping all the vertex calls I think.

Hello,

I get a similar improvement:

beginShape(); //frameRate around 25

beginShape(QUADS) // frameRate around 60

Tested with 15*15*15 = 3375 textured cubes

I used your shapes, made a cube and added some textures:

I also cleaned up the code a bit but did not see any change:

  float xw = x + width;
  float yw = y + width; 
  float xt = z + thickness;
 
  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(xw, y, z, v, 0);
  vertex(xw, yw, z, v, v);
  vertex(x, yw, z, 0, v);
  endShape();

// And so on...

:)

2 Likes

Is that with a single beginShape()? Could you share the full code for this? Am interested in doing some performance comparisons.

1 Like

Here you go:

Code
// Texture Cube 
// v1.0.0
// GLV 2021-07-29

//drawWall() courtesy of CosmicCrowMC:
//https://discourse.processing.org/t/faster-rendering-techniques-in-p3d/31465

PImage img;

final int DEFAULT = 20; // default beginShape() parameter

int w = 25;   // width of cube
int w2;
int n = 15;   // number of cubes along each axis
int p;     // beginShape() parameter
boolean t;    // toggles parameter 

void setup() 
	{
  size(800,  800, P3D);
  noFill();
  noStroke();
  img = loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Processing_3_logo.png/240px-Processing_3_logo.png");
  img.resize(w, w);
  
  textureMode(NORMAL);
  
  String s = "Press t to toggle between beginShape() and beginShape(QUAD)";
  println(s);
  
  //hint(ENABLE_DEPTH_SORT); // frameRates < 15 with this!
  hint(DISABLE_DEPTH_TEST); // cool effect! Watch a few rotations.
  }

void draw() 
	{
  background(255, 255, 0);
  lights();
  translate(width/2, height/2, -25);
  rotateY(frameCount*TAU/500);
  w2 = w+5;                           // Add space between cubes
  float offset = -n*w2/2;
  translate(offset, offset, offset);
  
  for(int z=0; z<n; z++)
    {
    for(int y=0; y<n; y++)
      {
      for(int x=0; x<n; x++)
        {
        drawWall(x*w2,  y*w2,  z*w2, w,  w,  w);
        }  
      }
    }    
   if (frameCount%15 == 0) println(frameRate); 
  
  if (t)
    p = DEFAULT;
  else
    p = QUAD;

  }

void drawWall(int x,  float y,  float z,  float thickness,  float width,  float height) 
  {
  float xw = x + width;
  float yw = y + width; 
  float xt = z + thickness;
 
  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(xw, y, z, 1, 0);
  vertex(xw, yw, z, 1, 1);
  vertex(x, yw, z, 0, 1);
  endShape();
  
  beginShape(p);
  texture(img);
  vertex(xw, y, z, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(xw, yw, z, 0, 1);
  endShape();
  
  beginShape(p);
  texture(img);
  vertex(x, y, xt, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(x, yw, xt, 0, 1);
  endShape();
  
  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(x, y, xt, 1, 0);
  vertex(x, yw, xt, 1, 1);
  vertex(x, yw, z, 0, 1);
  endShape();
  }
  
void keyPressed()
  {
  if (key == 't') 
    t = !t;
  if (t) 
    println("beginShape()");
  else
    println("beginShape(QUAD)");
  }
  

I am sharing this as an example of beginShape() vs beginShape(QUAD) in the context of this topic to explore this:

And @CosmicCrowMC is ok with me using his drawWall() function for this example.

:)

1 Like

@glv thanks! What I meant in comment above is that you can do this instead -

void draw() {
  ...
  beginShape(QUADS);
  texture(img);
  for(int z=0; z<n; z++) {
    for(int y=0; y<n; y++) {
      for(int x=0; x<n; x++) {
        drawWall(x*w2,  y*w2,  z*w2, w,  w,  w);
      }  
    }
  }
  endShape();
  ...
  }

void drawWall(int x,  float y,  float z,  float thickness,  float width,  float height) {
  float xw = x + width;
  float yw = y + width; 
  float xt = z + thickness;
 
  vertex(x, y, z, 0, 0);
  vertex(xw, y, z, 1, 0);
  vertex(xw, yw, z, 1, 1);
  vertex(x, yw, z, 0, 1);
  
  vertex(xw, y, z, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(xw, yw, z, 0, 1);
  
  vertex(x, y, xt, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(x, yw, xt, 0, 1);
  
  vertex(x, y, z, 0, 0);
  vertex(x, y, xt, 1, 0);
  vertex(x, yw, xt, 1, 1);
  vertex(x, yw, z, 0, 1);
}

With your default sketch I don’t see much difference - I had to increase the number of cubes to see quite a difference (eg. w = 15, n = 25). Then it’s 40 fps vs 60 fps. Can’t use this with default (POLYGON) and beginShape() - renders differently … and crashes.

Incidentally, QUAD is theoretically not a valid value for beginShape(..) - should be QUADS - which is a good clue you can draw more than one at a time. It just so happens, the OpenGL backend treats QUAD the same.

2 Likes

Hello,

Thanks for your insight.

Can you provide a reference to this?
I do like to delve into the technical details.

That may be a good thing!
I have seen QUAD used in place of QUADS in some other examples including a published book.

I am curious if this applies to:
POINT, LINE, TRIANGLE and QUAD

If they are used in beginShape() in place of:
POINTS, LINES, TRIANGLES and QUADS

I have explored here so far:

Code to print some constants
//https://processing.org/reference/beginShape_.html lists 7
 
println(POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, QUAD_STRIP); // 7
println();

//http://www.songho.ca/opengl/gl_overview.html lists 10

//println(GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON);
println(POINTS, LINES, LINE_STRIP, LINE_LOOP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, QUADS, QUAD_STRIP, POLYGON); // 10
println();

println(POINTS, LINES, TRIANGLES, QUADS);
println(POINT, LINE, TRIANGLE, QUAD);
println();

// This lists much more:
//https://github.com/processing/processing/blob/master/core/src/processing/core/PConstants.java#L292

:)

The docs specifically mention the allowed values being “Either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, or QUAD_STRIP” - also at

QUAD is 16 and QUADS is 17. However in PGraphicsOpenGL you can see -

By the look of it, TRIANGLE yes, the others no.

2 Likes

Interesting.

Saw this thread a while ago, and wanted to test if a fixed/static(?) shape improves the speed. Like done from the reference page for createShape.

I finally got around to it, and It did not! Maybe it’s all the translations that really slows it down more than an eventual gain from not having to re-make all the shapes in every frame? Idk. Or maybe I do it in a very inefficient way.

Posting it here in case others find it interesting, even if the thread is marked solved.

Click to (un) expand
// Static shape test
// 2021-08-07 - Mod by raron
// Built on:

// Texture Cube
// v1.0.0
// GLV 2021-07-29

// drawWall() courtesy of CosmicCrowMC:
//https://discourse.processing.org/t/faster-rendering-techniques-in-p3d/31465

PImage img;

final int DEFAULT = 20; // default beginShape() parameter

int w = 25;     // width of cube
int w2 = w+5;   // Add space between cubes
int n = 15;     // number of cubes along each axis
int p;          // beginShape() parameter
boolean t;      // toggles parameter

// For the static shape test:
int sp = QUADS; // shape's createShape() parameter
boolean s;      // toggles shape method
PShape testcube, wall1, wall2, wall3, wall4;


void setup()
{
  size(800,  800, P3D);
  noFill();
  noStroke();
  img = loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Processing_3_logo.png/240px-Processing_3_logo.png");
  img.resize(w, w);

  textureMode(NORMAL);

  String s1 = "Press t to toggle between beginShape() and beginShape(QUAD)";
  String s2 = "Press s to toggle between createShape() and shape()";
  println(s1);
  println(s2);
  println();

  //hint(ENABLE_DEPTH_SORT); // frameRates < 15 with this!
  //hint(DISABLE_DEPTH_TEST); // cool effect! Watch a few rotations.

  makeCube(0, 0, 0, w, w, w);
}

void draw()
{
  background(255, 255, 0);
  lights();
  translate(width/2, height/2, -25);
  rotateY(frameCount*TAU/500);
  float offset = -n*w2/2;
  translate(offset, offset, offset);

  for(int z=0; z<n; z++)
  {
    for(int y=0; y<n; y++)
    {
      for(int x=0; x<n; x++)
      {
        if (!s) {
          drawWall(x*w2,  y*w2,  z*w2, w,  w,  w);
        } else {
          pushMatrix();
          translate(x*w2,  y*w2,  z*w2);
          shape(testcube);
          popMatrix();
        }
      }
    }
  }

  if (frameCount%15 == 0) println(frameRate);

  if (t)
  p = DEFAULT;
  else
  p = QUAD;

}


void drawWall(int x,  float y,  float z,  float thickness,  float width,  float height)
{
  float xw = x + width;
  float yw = y + height;
  float xt = z + thickness;

  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(xw, y, z, 1, 0);
  vertex(xw, yw, z, 1, 1);
  vertex(x, yw, z, 0, 1);
  endShape();

  beginShape(p);
  texture(img);
  vertex(xw, y, z, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(xw, yw, z, 0, 1);
  endShape();

  beginShape(p);
  texture(img);
  vertex(x, y, xt, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(x, yw, xt, 0, 1);
  endShape();

  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(x, y, xt, 1, 0);
  vertex(x, yw, xt, 1, 1);
  vertex(x, yw, z, 0, 1);
  endShape();
}

void makeCube(int x,  float y,  float z,  float thickness,  float width,  float height)
{
  wall1 = createShape();
  wall2 = createShape();
  wall3 = createShape();
  wall4 = createShape();

  float xw = x + width;
  float yw = y + height;
  float xt = z + thickness;

  wall1.beginShape(sp);
  wall1.texture(img);
  wall1.vertex(x, y, z, 0, 0);
  wall1.vertex(xw, y, z, 1, 0);
  wall1.vertex(xw, yw, z, 1, 1);
  wall1.vertex(x, yw, z, 0, 1);
  wall1.endShape(CLOSE);

  wall2.beginShape(sp);
  wall2.texture(img);
  wall2.vertex(xw, y, z, 0, 0);
  wall2.vertex(xw, y, xt, 1, 0);
  wall2.vertex(xw, yw, xt, 1, 1);
  wall2.vertex(xw, yw, z, 0, 1);
  wall2.endShape(CLOSE);

  wall3.beginShape(sp);
  wall3.texture(img);
  wall3.vertex(x, y, xt, 0, 0);
  wall3.vertex(xw, y, xt, 1, 0);
  wall3.vertex(xw, yw, xt, 1, 1);
  wall3.vertex(x, yw, xt, 0, 1);
  wall3.endShape(CLOSE);

  wall4.beginShape(sp);
  wall4.texture(img);
  wall4.vertex(x, y, z, 0, 0);
  wall4.vertex(x, y, xt, 1, 0);
  wall4.vertex(x, yw, xt, 1, 1);
  wall4.vertex(x, yw, z, 0, 1);
  wall4.endShape(CLOSE);

  testcube = createShape(GROUP);
  testcube.addChild(wall1);
  testcube.addChild(wall2);
  testcube.addChild(wall3);
  testcube.addChild(wall4);
}

void keyPressed()
{
  if (key == 't') t = !t;
  if (key == 's') s = !s;
  println();
  println();
  if (t) println("beginShape()");
  else println("beginShape(QUAD)");
  if (s) println("shape()");
  else println("createshape()");
  println();
}
1 Like

The translation methods on a retained PShape are definitely slow - eg. P3D performance with 62500 boxes - #8 by neilcsmith

Although if you do transforms globally I can’t remember it being so much of a problem.

1 Like

Hello,

I was doing the same! This aligns with another project I am working on as well.
When this happens I call it “electromagnetic waves propagating through space”.

Here is what I came up with:

Click here to see code
// Texture Cube 
// v1.0.0
// GLV 2021-08-06

// Inspired by:
//https://discourse.processing.org/t/faster-rendering-techniques-in-p3d/31465

// Cube faces rebuilt from scratch.
// Entire shape built in setup()

PImage img1, img2, img3, img4;

int w = 30;   // width of cube
int n = 10;   // number of cubes along each axis
int sp = 5;   // space between cubes

int p = QUADS;

PShape front, right, left, back;
PShape cube, borg, borg2;

void setup() 
	{
  size(800, 800, P3D);
  
  noFill();
  noStroke();
  noSmooth();
  
  img1 = loadImage("https://media.digikey.com/Renders/Riedon%20Renders/TO-220-2.jpg");
  img1.resize(w, w);
  
  img2 = loadImage("https://media.digikey.com/Renders/Stackpole%20Renders/CF%2027k.jpg");
  img2.resize(w, w);
  
  img3 = loadImage("https://media.digikey.com/Renders/TE%20Connectivity/220m-Ohm-5%25-ROX,-Neohm.jpg");
  img3.resize(w, w);
  
  img4 = loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Processing_3_logo.png/240px-Processing_3_logo.png");
  //img4 = loadImage("https://media.digikey.com/Photos/Illinois%20Capacitor/CKH-Series_12-17%28HT%29.jpg");
  img4.resize(w, w);
  
  textureMode(NORMAL);
  borg2 =  createShape(GROUP); 
  
  int w2 = w + sp; // Add a fixed space between cubes
  int o = n*w2/2;  // Offset to center cube
  
  for(int z=0; z<n; z++)
    {
    for(int y=0; y<n; y++)
      {
      for(int x=0; x<n; x++)
        {      
        cubeShape(x*w2-o,  y*w2-o,  z*w2-o, w,  w,  w);
        }  
      }
    } 
 
  println("Cube count: ", n*n*n);
  }

void draw() 
	{
  background(255);
  
  translate(width/2, height/2, 0);

  float angX = map(mouseY, 0, width, -TAU/2, TAU/2);
  rotateX(angX);
  
  float angY = map(mouseX, 0, width, -TAU/2, TAU/2);
  rotateY(angY);
  
  shape(borg2, 0, 0);
  
  if (frameCount%15 == 0) println(frameRate);   
  }
  
  void cubeShape(int x, int y, int z, int wx, int wy, int wz)
  {
  int sx = x + wx;
  int sy = y + wy;
  int sz = z + wz;
  
  borg = createShape();
  
  //front
  borg.beginShape(p);
  borg.texture(img1);
  borg.vertex( x,  y,  sz,   0, 0); 
  borg.vertex(sx,  y,  sz,   1, 0);
  borg.vertex(sx, sy,  sz,   1, 1);
  borg.vertex( x, sy,  sz,   0, 1);  
  borg.endShape(p); 
  borg2.addChild(borg);
  
  borg = createShape();
  //right
  borg.beginShape(p);
  borg.texture(img2);
  borg.vertex( sx,  y, sz,   0, 0);
  borg.vertex( sx,  y,  z,   1, 0);
  borg.vertex( sx, sy,  z,   1, 1);
  borg.vertex( sx, sy, sz,   0, 1);  
  borg.endShape(p);  
  borg2.addChild(borg);
  
  borg = createShape();
  //back
  borg.beginShape(p);
  borg.texture(img3);
  borg.vertex( sx,  y,  z,   0, 0);
  borg.vertex( x,   y,  z,   1, 0);
  borg.vertex( x,  sy,  z,   1, 1);
  borg.vertex( sx, sy,  z,   0, 1);  
  borg.endShape(p); 
  borg2.addChild(borg);
  
  borg = createShape();
  //left
  borg.beginShape(p);
  borg.texture(img4);
  borg.vertex( x,   y,   z,  0, 0);
  borg.vertex( x,   y,  sz,  1, 0);
  borg.vertex( x,  sy,  sz,  1, 1);
  borg.vertex( x,  sy,   z,  0, 1);  
  borg.endShape(p);
  borg2.addChild(borg);
  }  

I built the entire shape in setup().

On my PC I get 60 fps with 10x10x10 (1000) cubes… anything over that the fps go down.

:)

1 Like

Better!

Ah, I did it in a rather impractical way after all, just making one cube static. Not sure what I was thinking.

Btw, I get multiple NPE errors when running your code in Linux Mint, but it works great in Windows 10.

EDIT: Messed up the posting… reposting.

1 Like

Finally!

Coding late (it’s late for me :P) makes for the most obvious but hidden logical errors :slight_smile: No wonder I got bad FPS, as I drew the whole cube multiple times from the inner loop lol. (plus my previous not-optimal brainfart method…).

Thanks glv, wouldn’t have figured this out without your code! I was really puzzled.
(plus for the Borg name :vulcan_salute: )

Now I get the expected correct result in that the static shape performs much better! At least for more complex / larger objects. 15625 cubes (25 * 25 * 25) gave me 37ish FPS after some initial stutter, with createshape. But with the one fixed shape just below 60 FPS.

Ofc, the drawback is that you can’t do dynamic deformation like when collisiding objects, when using pre-made shapes.

Modified code:

Click to (un) expand
// Static shape test 2
// 2021-08-07 - Mod by raron (fixed the static cube shape now)
// Built on:

// Texture Cube
// v1.0.0
// GLV 2021-07-29

// drawWall() courtesy of CosmicCrowMC:
//https://discourse.processing.org/t/faster-rendering-techniques-in-p3d/31465

PImage img;

final int DEFAULT = 20; // default beginShape() parameter

int w = 15;     // width of cube
int n = 25;     // number of cubes along each axis
int w2 = w+5;   // Add space between cubes
int p;          // beginShape() parameter
boolean t;      // toggles parameter

// For the static shape test:
int sp = QUADS; // shape parameter
boolean s;      // toggles shape method
PShape testcube, wall;


void setup()
{
  size(800,  800, P3D);
  noFill();
  noStroke();
  noSmooth();

  img = loadImage("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Processing_3_logo.png/240px-Processing_3_logo.png");
  img.resize(w, w);

  println("Press t to toggle between beginShape() and beginShape(QUAD)");
  println("Press s to toggle between createShape() and shape()");
  println();
  println("  Cubicles: " + n*n*n);
  println();

  textureMode(NORMAL);
  //hint(ENABLE_DEPTH_SORT); // frameRates < 15 with this!
  //hint(DISABLE_DEPTH_TEST); // cool effect! Watch a few rotations.

  testcube = createShape(GROUP);

  for(int z=0; z<n; z++)
  {
    for(int y=0; y<n; y++)
    {
      for(int x=0; x<n; x++)
      {
        makeCubicle(x*w2, y*w2, z*w2, w, w, w);
      }
    }
  }
}

void draw()
{
  background(255, 255, 0);
  lights();
  translate(width/2, height/2, -25);
  rotateY(frameCount*TAU/500);
  float offset = -n*w2/2;
  translate(offset, offset, offset);

  if (!s) {
    for(int z=0; z<n; z++)
    {
      for(int y=0; y<n; y++)
      {
        for(int x=0; x<n; x++)
        {
          drawWall(x*w2,  y*w2,  z*w2, w,  w,  w);
        }
      }
    }
  } else {
    shape(testcube);
  }

  if (frameCount%15 == 0) println(frameRate);

  if (t)
  p = DEFAULT;
  else
  p = QUAD;

}


void drawWall(int x,  float y,  float z,  float thickness,  float width,  float height)
{
  float xw = x + width;
  float yw = y + height;
  float xt = z + thickness;

  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(xw, y, z, 1, 0);
  vertex(xw, yw, z, 1, 1);
  vertex(x, yw, z, 0, 1);
  endShape();

  beginShape(p);
  texture(img);
  vertex(xw, y, z, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(xw, yw, z, 0, 1);
  endShape();

  beginShape(p);
  texture(img);
  vertex(x, y, xt, 0, 0);
  vertex(xw, y, xt, 1, 0);
  vertex(xw, yw, xt, 1, 1);
  vertex(x, yw, xt, 0, 1);
  endShape();

  beginShape(p);
  texture(img);
  vertex(x, y, z, 0, 0);
  vertex(x, y, xt, 1, 0);
  vertex(x, yw, xt, 1, 1);
  vertex(x, yw, z, 0, 1);
  endShape();
}

void makeCubicle(int x,  float y,  float z,  float thickness,  float width,  float height)
{
  float xw = x + width;
  float yw = y + height;
  float xt = z + thickness;

  wall = createShape();
  wall.beginShape(sp);
  wall.texture(img);
  wall.vertex(x, y, z, 0, 0);
  wall.vertex(xw, y, z, 1, 0);
  wall.vertex(xw, yw, z, 1, 1);
  wall.vertex(x, yw, z, 0, 1);
  wall.endShape(sp);
  testcube.addChild(wall);

  wall = createShape();
  wall.beginShape(sp);
  wall.texture(img);
  wall.vertex(xw, y, z, 0, 0);
  wall.vertex(xw, y, xt, 1, 0);
  wall.vertex(xw, yw, xt, 1, 1);
  wall.vertex(xw, yw, z, 0, 1);
  wall.endShape(sp);
  testcube.addChild(wall);

  wall = createShape();
  wall.beginShape(sp);
  wall.texture(img);
  wall.vertex(x, y, xt, 0, 0);
  wall.vertex(xw, y, xt, 1, 0);
  wall.vertex(xw, yw, xt, 1, 1);
  wall.vertex(x, yw, xt, 0, 1);
  wall.endShape(sp);
  testcube.addChild(wall);

  wall = createShape();
  wall.beginShape(sp);
  wall.texture(img);
  wall.vertex(x, y, z, 0, 0);
  wall.vertex(x, y, xt, 1, 0);
  wall.vertex(x, yw, xt, 1, 1);
  wall.vertex(x, yw, z, 0, 1);
  wall.endShape(sp);
  testcube.addChild(wall);
}

void keyPressed()
{
  if (key == 't') t = !t;
  if (key == 's') s = !s;
  println();
  println();
  if (t) println("beginShape()");
  else println("beginShape(QUAD)");
  if (s) println("static shape()");
  else println("dynamic createshape()");
  println();
}

Btw, replacing the images in your code above fixed it for my linux box as well, and I get the same results on my computer (maybe a tad less at 58-59 FPS).

1 Like

You would have!

It took some persistence on my part and I did this after a good nap.

I have many versions of this (one very similar to yours) and learned quite a bit in the process.

I have a dynamic version that updates cube faces (images) as well.
It is a much smaller cube count and larger images and works for the that purpose.

That was fun!

:)

1 Like