Minecraft-Like Clone Lagging too much

Hi! I am programming a Minecraft Like Clone in processing to learn more about the language. I have an RTX3050 but rendering 6800 triangles is only running at 20FPS. It would be helpfull if someone could explain me why it’s lagging and how i could fix that! -Libby

PVector camPos;
PVector camLookAt;
float camYaw;
float camPitch;
float camSpeed = 15;
float sensitivity = 0.005;

PImage[] imgs = new PImage[32];

byte[][][] data = new byte[128][128][128];

PVector raycastDirection; // Direction of the ray
float raycastLength = 500; // Length of the ray

import java.awt.Robot;
import java.awt.AWTException;
import java.awt.event.InputEvent;

Robot robot;

void setup() {
  size(1080, 720, P3D);
  surface.setLocation(100, 100);
  frameRate(500);
  textureMode(NORMAL);
  windowTitle("Camera Test");
  camPos = new PVector(0, 0, 0);
  camLookAt = new PVector(0, 0, -1);
  camYaw = 0;
  camPitch = 0;

  try {
    robot = new Robot();
  }
  catch (AWTException e) {
    e.printStackTrace();
  }

  //try {
  //  imgs[0] = loadImage("graeess.png");
  //  imgs[1] = loadImage("gerass-side.png");
  //  imgs[2] = loadImage("diert.png");
  //}
  //catch (Exception e) {
    PGraphics img1 = createGraphics(16, 16);
    PGraphics img2 = createGraphics(16, 16);
    PGraphics img3 = createGraphics(16, 16);

    img1.beginDraw();
    img1.background(#3BCE34);
    img1.endDraw();
    img2.beginDraw();
    img2.background(#287125);
    img2.endDraw();
    img3.beginDraw();
    img3.background(#6F341D);
    img3.endDraw();
    
    imgs[0] = img1;
    imgs[1] = img2;
    imgs[2] = img3;
  //}

  ((PGraphicsOpenGL)g).textureSampling(3);

  noiseDetail(2);

  int size = 128;
  float detail = 0.092;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      for (int k = 0; k < size; k++) {
        push();
        translate(100*i, k*100, 100*j);
        if (noise(i*detail, 0, j*detail)*100 < k*15) {
          scale(50);
          data[i][j][k] = 1;
        }
        pop();
      }
    }
  }
}

void draw() {
  //PVector p = getPosition(null);
  //camYaw += (mouseX - width/2)*sensitivity;
  //camPitch += (mouseY - height/2)*sensitivity;
  //robot.mouseMove(round(p.x+width/2), round(p.y+height/2));
  if (mousePressed && mouseButton == CENTER) {
    camYaw -= (pmouseX-mouseX)*sensitivity;
    camPitch -= (pmouseY-mouseY)*sensitivity;
  }

  windowTitle("("+round(frameRate)+") Camera Test "+this);
  background(25);
  //directionalLight(153, 153, 153, .5, 0, -1);
  //ambientLight(255, 255, 255);
  //ambient(255, 255, 128);

  camera(camPos.x, camPos.y, camPos.z, camLookAt.x, camLookAt.y, camLookAt.z, 0, 1, 0);

  int polyCount = 0;
  int size = 32;
  float detail = 0.092;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      for (int k = 0; k < size; k++) {
        push();
        translate(100*i, k*100, 100*j);
        scale(50);
        if (data[i][j][k] == 1 && isNotSurrounded(i, j, k)) {
          boolean showTopFace = true;
          boolean showBottomFace = true;
          boolean showXpositiveFace = true;
          boolean showXnegativeFace = true;
          boolean showYpositiveFace = true;
          boolean showYnegativeFace = true;
          if (k < size-1) showTopFace = data[i][j][k+1] == 0;
          if (k > 1) showBottomFace = data[i][j][k-1] == 0;
          if (i < size-1) showYpositiveFace = data[i+1][j][k] == 0;
          if (i > 1) showYnegativeFace = data[i-1][j][k] == 0;
          if (j < size-1) showXpositiveFace = data[i][j+1][k] == 0;
          if (j > 1) showXnegativeFace = data[i][j-1][k] == 0;
          if (showXpositiveFace) polyCount+=2;
          if (showYpositiveFace) polyCount+=2;
          if (showXnegativeFace) polyCount+=2;
          if (showYnegativeFace) polyCount+=2;
          if (showTopFace) polyCount+=2;
          if (showBottomFace) polyCount+=2;

          texturedCube(imgs[0], imgs[1], imgs[2], showXpositiveFace, showXnegativeFace, showYpositiveFace, showYnegativeFace, showTopFace, showBottomFace);
        } else {
          //box(10);
        }
        pop();
      }
    }
  }

  //Block Breaking

  //try {

  //  PVector raycastDirection = camLookAt.copy().sub(camPos); // Calculate ray direction
  //  PVector rayOrigin = camPos.copy();

  //  float stepSize = 0.025; // Adjust this step size as needed
  //  float maxDistance = 700; // Set your desired raycast length

  //  for (float d = 0; d < maxDistance; d += stepSize) {
  //    int i = round(rayOrigin.x / 100);
  //    int j = round(rayOrigin.z / 100);
  //    int k = round(rayOrigin.y / 100);

  //    if (i >= 0 && i < data.length && j >= 0 && j < data[0].length && k >= 0 && k < data[0][0].length) {
  //      if (data[i][j][k] == 1) {
  //        if (mousePressed && mouseButton == LEFT) data[i][j][k] = 0;
  //        translate(100*i, 100*k, 100*j);
  //        noFill();
  //        stroke(0);
  //        box(101);
  //        scale(25);
  //        break;
  //      }
  //    }

  //    rayOrigin.add(raycastDirection.normalize().mult(stepSize));
  //  }
  //}
  //catch (Exception e) {
  //}

  // Calculate the camera's direction
  float camX = cos(camYaw) * cos(camPitch);
  float camY = sin(camPitch);
  float camZ = sin(camYaw) * cos(camPitch);
  camLookAt = new PVector(camX, camY, camZ);

  // Update the camera position
  if (keyPressed) {
    if (key == 'w') {
      camPos.add(camLookAt.mult(camSpeed));
    }
    if (key == 's') {
      camPos.sub(camLookAt.mult(camSpeed));
    }
    if (key == 'a') {
      PVector right = camLookAt.cross(new PVector(0, 1, 0));
      right.normalize();
      camPos.sub(right.mult(camSpeed));
    }
    if (key == 'd') {
      PVector right = camLookAt.cross(new PVector(0, 1, 0));
      right.normalize();
      camPos.add(right.mult(camSpeed));
    }
  }

  // Rotate the camera using the mouse
  //if (mousePressed) {
  //camHeading += (mouseX - pmouseX) * sensitivity;
  //camPitch += (mouseY - pmouseY) * sensitivity;
  // }


  // Constrain the pitch to prevent camera flipping
  camPitch = constrain(camPitch, -HALF_PI + 0.1, HALF_PI - 0.1);

  //camYaw+=0.1;


  // Move the camera's "lookAt" point
  camLookAt.add(camPos);

  camera();
  noLights();

  noStroke();
  fill(255);
  rect(width/2-15, height/2-1, 30, 2);
  rect(width/2-1, height/2-15, 2, 30);

  text(frameRate+"\n"+polyCount, 100, 100);
}

boolean isNotSurrounded(int i, int j, int k) {
  try {
    if (!(data[i][j][k-1] == 1 && data[i][j][k+1] == 1 && data[i][j-1][k] == 1 && data[i][j+1][k] == 1 && data[i-1][j][k] == 1 && data[i+1][j][k] == 1)) {
      return true;
    } else {
      return false;
    }
  }
  catch (Exception e) {
    return true;
  }
}

void keyPressed() {
  // Use key presses to control camera movement
  if (key == ' ') {
    int playerBlockX = int(camPos.x / 100);
    int playerBlockY = int(camPos.y / 100);
    int playerBlockZ = int(camPos.z / 100);

    if (playerBlockX >= 0 && playerBlockX < 32 &&
      playerBlockY >= 0 && playerBlockY < 32 &&
      playerBlockZ >= 0 && playerBlockZ < 32) {
      data[playerBlockX][playerBlockY][playerBlockZ] = 0; // Set the block at player's position to 0
    }
  }
}

void texturedCube(PImage texture1, PImage texture2, PImage texture3, boolean a, boolean b, boolean c, boolean d, boolean e, boolean f) {
  //noStroke();
  if (a) {
    beginShape(QUADS);
    texture(texture2);
    vertex(-1, -1, 1, 0, 0);
    vertex( 1, -1, 1, 1, 0);
    vertex( 1, 1, 1, 1, 1);
    vertex(-1, 1, 1, 0, 1);
    endShape();
  }
  if (b) {
    beginShape(QUADS);
    texture(texture2);
    vertex( 1, -1, -1, 0, 0);
    vertex(-1, -1, -1, 1, 0);
    vertex(-1, 1, -1, 1, 1);
    vertex( 1, 1, -1, 0, 1);
    endShape();
  }
  if (c) {
    beginShape(QUADS);
    texture(texture2);
    vertex( 1, -1, 1, 0, 0);
    vertex( 1, -1, -1, 1, 0);
    vertex( 1, 1, -1, 1, 1);
    vertex( 1, 1, 1, 0, 1);
    endShape();
  }
  if (d) {
    beginShape(QUADS);
    texture(texture2);
    vertex(-1, -1, -1, 0, 0);
    vertex(-1, -1, 1, 1, 0);
    vertex(-1, 1, 1, 1, 1);
    vertex(-1, 1, -1, 0, 1);
    endShape();
  }
  if (e) {
    beginShape(QUADS);
    texture(texture3);
    vertex(-1, 1, 1, 0, 0);
    vertex( 1, 1, 1, 1, 0);
    vertex( 1, 1, -1, 1, 1);
    vertex(-1, 1, -1, 0, 1);
    endShape();
  }
  if (f) {
    beginShape(QUADS);
    texture(texture1);
    vertex(-1, -1, -1, 0, 0);
    vertex( 1, -1, -1, 1, 0);
    vertex( 1, -1, 1, 1, 1);
    vertex(-1, -1, 1, 0, 1);
    endShape();
  }
}


The problem may be that you are using so-called immediate mode for rendering - caculating the geometry in CPU and send it to GPU every frame even though it is not changing. In game engines (for example, Unity), usually they use retained mode to store the geometry in GPU and call it every frame so it runs much faster. You can use createShape to achieve it in Processing (with a bit more code), and there is a tutorial here. As you are already using begin/endShape, it won’t be too hard to adapt it.

4 Likes

Hello @Noodlybanan,

The first thing I did was start comment out lines to see what slowed things down.

Commenting this changed the frame rate from 16 to 18 fps:

texturedCube(imgs[0], imgs[1], imgs[2], showXpositiveFace, showXnegativeFace, showYpositiveFace, showYnegativeFace, showTopFace, showBottomFace);

I explored further and it was your isNotSurrounded(i, j, k) that slowed things down.

if (!(data[i][j][k-1] == 1 && data[i][j][k+1] == 1 && data[i][j-1][k] == 1 && data[i][j+1][k] == 1 && data[i-1][j][k] == 1 && data[i+1][j][k] == 1)) 

was giving this exception:

ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 128

I replaced the try\catch with this and was getting a solid 60 fps:

boolean isNotSurrounded(int i, int j, int k) 
  {
  boolean test = false;
  if(i>0 && j>0 && k>0) 
    {
    if (!(data[i][j][k-1] == 1 && data[i][j][k+1] == 1 && data[i][j-1][k] == 1 && data[i][j+1][k] == 1 && data[i-1][j][k] == 1 && data[i+1][j][k] == 1)) 
      test = true;
    else
      test = false;
    }
  return test;  
  } 

I did not scrutinize code further than this and there may be more room for improvement.

:)

2 Likes

Thanks! This was an improvment in performance :slight_smile:

Hi! I tried this option, and this is my code, it definitly is a performance improvment! Thank you <3

Mesh Builder + Cube Generator
void buildMeshChunk() {
  chunktest = createShape(GROUP);
  polyCount = 0;
  int size = 16;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      for (int k = 0; k < size; k++) {
        if (data[i][j][k] != 0 && isNotSurrounded(i, j, k)) {
          boolean showTopFace = true;
          boolean showBottomFace = true;
          boolean showXpositiveFace = false;
          boolean showXnegativeFace = false;
          boolean showYpositiveFace = false;
          boolean showYnegativeFace = false;

          if (k < size - 1) showTopFace = data[i][j][k + 1] == 0;
          if (k > 1) showBottomFace = data[i][j][k - 1] == 0;
          if (i < size - 1) showYpositiveFace = data[i + 1][j][k] == 0;
          if (i > 1) showYnegativeFace = data[i - 1][j][k] == 0;
          if (j < size - 1) showXpositiveFace = data[i][j + 1][k] == 0;
          if (j > 1) showXnegativeFace = data[i][j - 1][k] == 0;
          
          if (showXpositiveFace) polyCount += 2;
          if (showYpositiveFace) polyCount += 2;
          if (showXnegativeFace) polyCount += 2;
          if (showYnegativeFace) polyCount += 2;
          if (showTopFace) polyCount += 2;
          if (showBottomFace) polyCount += 2;

          if (data[i][j][k] == 1) {
            chunktest.addChild(texturedCube(imgs[0], imgs[1], imgs[2], i*2, k*2,j*2, showXpositiveFace, showXnegativeFace, showYpositiveFace, showYnegativeFace, showTopFace, showBottomFace));
          }
          if (data[i][j][k] == 2) {
            chunktest.addChild(texturedCube(imgs[2], imgs[2], imgs[2], i*2, k*2, j*2, showXpositiveFace, showXnegativeFace, showYpositiveFace, showYnegativeFace, showTopFace, showBottomFace));
          }
        }
      }
    }
  }
}

&

PShape texturedCube(PImage texture1, PImage texture2, PImage texture3, int x, int y, int z, boolean a, boolean b, boolean c, boolean d, boolean e, boolean f) {
  PShape cube = createShape(GROUP);
  if (a) {
    PShape face = createShape();
    face.beginShape(QUADS);
    face.translate(x, y, z);
    face.texture(texture2);
    face.vertex(-1, -1, 1, 0, 0);
    face.vertex(1, -1, 1, 1, 0);
    face.vertex(1, 1, 1, 1, 1);
    face.vertex(-1, 1, 1, 0, 1);
    face.endShape();
    cube.addChild(face);
  }
  if (b) {
    PShape face = createShape();
    face.beginShape(QUADS);
    face.translate(x, y, z);
    face.texture(texture2);
    face.vertex(1, -1, -1, 0, 0);
    face.vertex(-1, -1, -1, 1, 0);
    face.vertex(-1, 1, -1, 1, 1);
    face.vertex(1, 1, -1, 0, 1);
    face.endShape();
    cube.addChild(face);
  }
  if (c) {
    PShape face = createShape();
    face.beginShape(QUADS);
    face.translate(x, y, z);
    face.texture(texture2);
    face.vertex(1, -1, 1, 0, 0);
    face.vertex(1, -1, -1, 1, 0);
    face.vertex(1, 1, -1, 1, 1);
    face.vertex(1, 1, 1, 0, 1);
    face.endShape();
    cube.addChild(face);
  }
  if (d) {
    PShape face = createShape();
    face.beginShape(QUADS);
    face.translate(x, y, z);
    face.texture(texture2);
    face.vertex(-1, -1, -1, 0, 0);
    face.vertex(-1, -1, 1, 1, 0);
    face.vertex(-1, 1, 1, 1, 1);
    face.vertex(-1, 1, -1, 0, 1);
    face.endShape();
    cube.addChild(face);
  }
  if (e) {
    PShape face = createShape();
    face.beginShape(QUADS);
    face.translate(x, y, z);
    face.texture(texture3);
    face.vertex(-1, 1, 1, 0, 0);
    face.vertex(1, 1, 1, 1, 0);
    face.vertex(1, 1, -1, 1, 1);
    face.vertex(-1, 1, -1, 0, 1);
    face.endShape();
    cube.addChild(face);
  }
  if (f) {
    PShape face = createShape();
    face.beginShape(QUADS);
    face.translate(x, y, z);
    face.texture(texture1);
    face.vertex(-1, -1, -1, 0, 0);
    face.vertex(1, -1, -1, 1, 0);
    face.vertex(1, -1, 1, 1, 1);
    face.vertex(-1, -1, 1, 0, 1);
    face.endShape();
    cube.addChild(face);
  }
  return cube;
}

However, i still have performance problems. Is there a way to fix this, because i saw this post and i wonder how they made their game so efficient!
-Libby

2 Likes