P3D slowing down outside of draw()

Hi,

I have a problem in my Processing program where the sketch lags outside of draw(). All my program does is draw around 300 textured cubes. After about 20 seconds of running time, the program slows down to one frame every few seconds, and then speeds up until I take another action in the program. On average, draw() takes 3 milliseconds, but the pause between frames takes up to ten seconds. Does anyone know what’s going on?

Thanks!

Could you post your code ?

There’s a lot of it, so I have put comments in.
Code:

// To control: drag your mouse to rotate, WASD/Shift/Space to navigate
// In output: time between frames, time during draw()

import java.io.*;
String scene = "test"; // unused
final static int WINDOW_WIDTH = 1440;
final static int WINDOW_HEIGHT = 810;
float rotX = 0;
float rotY = 0;
float rotZ = 0;
float camX = 0;
float camY = 0;
float camZ = 0;
int drawX = 0;
int drawY = 0;
int drawZ = 0;
int bpf = 300; // blocks per frame, laggy at high numbers
double realbpf = 300.0; // I used to have code that changes the bpf based on framerate
Boolean finishedDraw = false;
Boolean[] moveList = {false, false, false, false, false, false};
PImage grassBlock = new PImage();
HashMap<String, Integer> data = new HashMap<String, Integer>(); // stores the blocks
int tm = 0; // timer

void draw3dImage(PImage all, float size) {
  PImage top = all.get(0, 0, 16, 16);
  PImage s1 = all.get(16, 0, 16, 16);
  PImage s2 = all.get(32, 0, 16, 16);
  PImage s3 = all.get(48, 0, 16, 16);
  PImage s4 = all.get(64, 0, 16, 16);
  PImage bottom = all.get(80, 0, 16, 16);
  if (rotX <= 1*Math.PI/6 || rotX >= 5*Math.PI/6) { // these if statements prune the non-visible sides (glitchy on bottom)
    beginShape();
    texture(top);
    vertex(-size, -size, size, 0, 0);
    vertex(size, -size, size, 16, 0);
    vertex(size, -size, -size, 16, 16);
    vertex(-size, -size, -size, 0, 16);
    endShape(CLOSE);
  }
  if (rotY >= 3*Math.PI/2 || rotY <= Math.PI/2) {
    beginShape();
    texture(s1);
    vertex(-size, -size, size, 0, 0);
    vertex(size, -size, size, 16, 0);
    vertex(size, size, size, 16, 16);
    vertex(-size, size, size, 0, 16);
    endShape(CLOSE);
  }
  if (rotY >= Math.PI) {
    beginShape();
    texture(s2);
    vertex(size, -size, size, 0, 0);
    vertex(size, -size, -size, 16, 0);
    vertex(size, size, -size, 16, 16);
    vertex(size, size, size, 0, 16);
    endShape(CLOSE);
  }
  if (rotY <= 3*Math.PI/2 && rotY >= Math.PI/2) {
    beginShape();
    texture(s3);
    vertex(size, -size, -size, 0, 0);
    vertex(-size, -size, -size, 16, 0);
    vertex(-size, size, -size, 16, 16);
    vertex(size, size, -size, 0, 16);
    endShape(CLOSE);
  }
  if (rotY <= Math.PI) {
    beginShape();
    texture(s4);
    vertex(-size, -size, size, 0, 0);
    vertex(-size, -size, -size, 16, 0);
    vertex(-size, size, -size, 16, 16);
    vertex(-size, size, size, 0, 16);
    endShape(CLOSE);
  }
  if (rotX >= 1*Math.PI/3 && rotX <= 2*Math.PI/3) {
    beginShape();
    texture(bottom);
    vertex(-size, size, -size, 0, 0);
    vertex(size, size, -size, 16, 0);
    vertex(size, size, size, 16, 16);
    vertex(-size, size, size, 0, 16);
    endShape(CLOSE);
  }
}
void setup() {
  size(1440, 810, P3D);
  grassBlock = loadImage("grassBlock.png"); // This is a 96x16 texture. I will post it along with the code.
  for (int i = 0; i < 16; i++) {
    for (int j = 0; j < 1; j++) {
      for (int k = 0; k < 16; k++) {
        data.put(i+","+j+","+k, 1); //puts some data into the hashmap
      }
    }
  }
  frameRate(60);
  background(255, 255, 255);
  hint(DISABLE_TEXTURE_MIPMAPS);
  ((PGraphicsOpenGL)g).textureSampling(2); // turns off antialiasing
}
void draw() {
  String timeStr = (millis()-tm-16) + "";
  int m1 = millis();
  rotX = (rotX % TAU + TAU) % TAU;// keeps view within certain range
  rotX = rotX > Math.PI/2 && rotX <= Math.PI ? (float) Math.PI/2 : rotX;
  rotX = rotX < 3*((float)Math.PI)/2 && rotX >= Math.PI ? 3*((float)Math.PI)/2 : rotX;
  rotY = (rotY % TAU + TAU) % TAU; 
  fill(0, 0, 0);
  pushMatrix();
  translate(WINDOW_WIDTH/2+camX, WINDOW_HEIGHT/2, camZ); // rotation point
  rotateX(rotX);
  rotateY(rotY); // rotates camera
  rotateZ(rotZ);
  for (Boolean x : moveList) {
    if (x) {
      background(255, 255, 255);
      finishedDraw = false;
      drawX = 0;
      drawY = 0;
      drawZ = 0;
    } // detects arrow key movement
  }
  bpf = (int) Math.max(Math.round(realbpf), 1);
  if (moveList[0]) { // all of these move you around
    camZ+=5;
  }
  if (moveList[1]) {
    camZ-=5;
  }
  if (moveList[2]) {
    camX+=5;
  }
  if (moveList[3]) {
    camX-=5;
  }
  if (moveList[4]) {
    camY-=5;
  }
  if (moveList[5]) {
    camY+=5;
  }
  if (!finishedDraw) {
    for (int i = 0; i < bpf; i++) { // draws the blocks
      if (drawX < 16) {
        if (drawY < 1) {
          if (drawZ < 16) {
            drawZ++;
          } else {
            drawZ = 0;
            drawY++;
          }
        } else {
          drawY = 0;
          drawX++; // for loops without for looping
        }
      } else {
        finishedDraw = true;
      }
      if (data.get(drawX+","+drawY+","+drawZ) != null) {
        int blockId = data.get(drawX+","+drawY+","+drawZ);
        pushMatrix();
        translate(drawX*96, drawY*96+camY, drawZ*96); // draws block in specific spot
        switch(blockId) {
        case 0:
          break;
        case 1:
          draw3dImage(grassBlock, 48);
          break;
        }
        popMatrix();
      }
    }
  }
  popMatrix();
  strokeWeight(5);
  point(WINDOW_WIDTH/2, WINDOW_HEIGHT/2, -20); // rotation point
  strokeWeight(1);
  fill(255, 255, 255);
  rect(0, 0, 180, 60);
  fill(0, 0, 0);
  text(frameRate, 0, 17);
  text(bpf+"bpf", 0, 37);
  text(rotX + ", " + rotY + ", " + rotZ, 0, 57); // debug in top left
  timeStr += " " + (millis()-m1);
  println(timeStr);
  tm = millis();
}
void mouseDragged() {
  rotX-=(mouseY-pmouseY)*PI/180;
  rotY+=(mouseX-pmouseX)*PI/180;
  background(255, 255, 255);
  finishedDraw = false;
  drawX = 0;
  drawY = 0;
  drawZ = 0;
}
void keyPressed() {
  if (keyCode == 87) {
    moveList[0] = true;
  }
  if (keyCode == 83) {
    moveList[1] = true;
  }
  if (keyCode == 65) {
    moveList[2] = true;
  }
  if (keyCode == 68) {
    moveList[3] = true;
  }
  if (keyCode == 16) {
    moveList[4] = true;
  }
  if (keyCode == 32) {
    moveList[5] = true;
  }
}
void keyReleased() {
  if (keyCode == 87) {
    moveList[0] = false;
  }
  if (keyCode == 83) {
    moveList[1] = false;
  }
  if (keyCode == 65) {
    moveList[2] = false;
  }
  if (keyCode == 68) {
    moveList[3] = false;
  }
  if (keyCode == 16) {
    moveList[4] = false;
  }
  if (keyCode == 32) {
    moveList[5] = false;
  }
}

grassBlock.png:
grassBlock

Bump, I still need help.

A few suggestions:

You may be able to use static PShapes and place them where needed.
Take a look at Processing’s included examples “Demos/Graphics/Performance”. Specifically those marked “retained”.

Use an integer array rather than a HashMap.
You can create an int array with 3 dimensions directly. Same method as 2D arrays.

While a HashMap has a constant access time per get(), there is also overhead when converting numbers into strings followed by the hashing itself.
There is a little overhead with multi-dimensional arrays but has native read/write access.

HashMap is a good way to store data if you have a large, sparse world but for speed perhaps some buffering to an array can help.

Avoid using println() within the draw() function. This can flood the buffer to the console.

Thank you!

Is there a way to texture separate sides differently in PShape?

Someone likes Minecraft here. I do too. :slight_smile: Anyway, yes, you can make 6 squares and then a group PShape. Give all 6 sides their respective texture and the add all 6 sides as children to the group PShape.

//Init sides as squares (too lazy to do the math right now) :D
PShape side1 = createShape(RECTANGLE, new float[]{0, 0, 0, 0, 1, 1});
PShape side2 = //...

//Give textures to the squares
side1.setTexture(s1);
side2.setTexture(s2):
side3.setTexture(s3);
side4.setTexture(s4):
side5.setTexture(s5);
side6.setTexture(s6):

//Init group PShape
PShape cube = createShape(GROUP);

//Add children
cube.addChild(side1);
cube.addChild(side2);
cube.addChild(side3);
cube.addChild(side4);
cube.addChild(side5);
cube.addChild(side6);