Voxel Game Memory Problems

Hi! I recoded my voxel game, and now it gets 700 Fps with as many chunks as i want, the only problem is memory. 32 chunks that are meshed take up almost 2 gb (a chunk is 32x32x32) without the mesh data load it takes up around 700 mb. Is there a way or different ways to store mesh data more efficiently?

This is my chunk generation code, which is executed for 32 chunks in an PShape array. (image textures can be replaced with empty images, they are not relevant!)
PShape createChunk() {
  PShape chunk;
  chunk = createShape(GROUP);
  triangles = 0;

  //generate chunk data
  int size = 32;
  noiseSeed(round(random(0, 255)));

  data = new int[size][size][size];
  for (int x = 0; x < size; x++) {
    for (int y = 0; y < size; y++) {
      for (int z = 0; z < size; z++) {
        if (y > noise(x*0.02, z*0.02)*16) data[x][y][z] = 1;
      }
    }
  }

  //mesh cunk
  for (int x = 0; x < size; x++) {
    for (int y = 0; y < size; y++) {
      for (int z = 0; z < size; z++) {
        tint(#FFFFFF, 555);
        if (data[x][y][z] != 0 && shouldShowCube(x, y, z, size)) {
          //boolean showTopFace = false, showBottomFace = false, showFrontFace = false, showRightFace = false, showBackFace = false, showLeftFace = false;
          boolean showTopFace = true, showBottomFace = true, showFrontFace = true, showRightFace = true, showBackFace = true, showLeftFace = true;

          if (y > 0) showTopFace = data[x][y-1][z] == 0;
          if (y < size-1) showBottomFace = data[x][y+1][z] == 0;
          if (z < size-1) showBackFace = data[x][y][z+1] == 0;
          if (z > 0) showFrontFace = data[x][y][z-1] == 0;
          if (x < size-1) showRightFace = data[x+1][y][z] == 0;
          if (x > 0) showLeftFace = data[x-1][y][z] == 0;

          chunk.addChild(voxel(x, y, z, imgs[0], imgs[0], imgs[0], imgs[0], imgs[0], imgs[0], showTopFace, showBottomFace, showFrontFace, showRightFace, showBackFace, showLeftFace));
        }
      }
    }
  }
  println("TRI_COUNT: "+triangles);
  return chunk;
}

boolean shouldShowCube(int x, int y, int z, int size) {
  if (x > 0 && y > 0 && z > 0 && x < size-1 && y < size-1 && z < size-1) {
    if (data[x-1][y][z] == 0 || data[x+1][y][z] == 0 ||
      data[x][y-1][z] == 0 || data[x][y+1][z] == 0 ||
      data[x][y][z-1] == 0 || data[x][y][z+1] == 0) {
      return true;
    }
  } else {
    return true;
  }
  return false;
}

PShape voxel(int x, int y, int z, PImage front, PImage back, PImage right, PImage left, PImage top, PImage bottom, boolean showTopFace, boolean showBottomFace, boolean showFrontFace, boolean showRightFace, boolean showBackFace, boolean showLeftFace) {
  PShape voxel = createShape(GROUP);
  voxel.translate(x, y, z);

  if (showFrontFace || showBackFace || showTopFace || showBottomFace || showLeftFace || showRightFace) {
    PShape quad;

    // Front face
    if (showFrontFace) {
      quad = createShape();
      quad.beginShape(QUADS);
      quad.texture(front);
      quad.tint(enviromentLight);
      quad.vertex(0, 0, 0, 0, 0);
      quad.vertex(1, 0, 0, 1, 0);
      quad.vertex(1, 1, 0, 1, 1);
      quad.vertex(0, 1, 0, 0, 1);
      quad.endShape();

      voxel.addChild(quad);

      triangles += 2;
    }

    // Back face
    if (showBackFace) {
      quad = createShape();
      quad.beginShape(QUADS);
      quad.texture(back);
      quad.tint(enviromentLight);
      quad.vertex(0, 0, 1, 0, 0);
      quad.vertex(1, 0, 1, 1, 0);
      quad.vertex(1, 1, 1, 1, 1);
      quad.vertex(0, 1, 1, 0, 1);
      quad.endShape();

      voxel.addChild(quad);

      triangles += 2;
    }

    // Right face
    if (showRightFace) {
      quad = createShape();
      quad.beginShape(QUADS);
      quad.texture(right);
      quad.tint(enviromentLight);
      quad.vertex(1, 0, 0, 0, 0);
      quad.vertex(1, 0, 1, 1, 0);
      quad.vertex(1, 1, 1, 1, 1);
      quad.vertex(1, 1, 0, 0, 1);
      quad.endShape();

      voxel.addChild(quad);

      triangles += 2;
    }

    // Left face
    if (showLeftFace) {
      quad = createShape();
      quad.beginShape(QUADS);
      quad.texture(left);
      quad.tint(enviromentLight);
      quad.vertex(0, 0, 0, 0, 0);
      quad.vertex(0, 0, 1, 1, 0);
      quad.vertex(0, 1, 1, 1, 1);
      quad.vertex(0, 1, 0, 0, 1);
      quad.endShape();

      voxel.addChild(quad);

      triangles += 2;
    }

    // Top face
    if (showTopFace) {
      quad = createShape();
      quad.beginShape(QUADS);
      quad.texture(top);
      quad.tint(enviromentLight);
      quad.vertex(0, 0, 0, 0, 0);
      quad.vertex(1, 0, 0, 1, 0);
      quad.vertex(1, 0, 1, 1, 1);
      quad.vertex(0, 0, 1, 0, 1);
      quad.endShape();

      voxel.addChild(quad);

      triangles += 2;
    }

    // Bottom face
    if (showBottomFace) {
      quad = createShape();
      quad.beginShape(QUADS);
      quad.texture(bottom);
      quad.tint(enviromentLight);
      quad.vertex(0, 1, 0, 0, 0);
      quad.vertex(1, 1, 0, 1, 0);
      quad.vertex(1, 1, 1, 1, 1);
      quad.vertex(0, 1, 1, 0, 1);
      quad.endShape();

      voxel.addChild(quad);

      triangles += 2;
    }
  }

  return voxel;
}


-Libby

1 Like

Your chunk PShape would be much simpler as a single list of QUADS rather than as a GROUP of separately transformed PShapes. Instead of using voxel.translate, do the additions yourself to position the xyz of each vertex for each quad. Create just one PShape of QUADS for the visible faces of the entire chunk.

Put all of your textures together into one single texture (called an “atlas”) and use the texture coordinates to read out the piece you want for each face of each block type. For instance, if your textures are 16x16, then make the texture 16 * 6 pixels wide, for each side of the voxel, and 16 * number of block types high. Then use the side number and the block type to compute the texture coordinates for each quad face. All of your chunks then only need a single texture call for drawing the whole world.

Thank you!
I optimised the code from 2gb of memory down to 800mb which is 200mb for 32 meshes.
Someone probably needs the code so here is it :slight_smile:

The fixed code
PShape createChunk() {
  PShape chunk;
  chunk = createShape();

  //generate chunk data
  int size = 32;
  noiseSeed(round(random(0, 255)));

  data = new int[size][size][size];
  for (int x = 0; x < size; x++) {
    for (int y = 0; y < size; y++) {
      for (int z = 0; z < size; z++) {
        if (y > noise(x*0.02, z*0.02)*16) data[x][y][z] = 1;
      }
    }
  }

  //mesh cunk
  chunk.beginShape(QUADS);
  chunk.texture(imgs[0]);
  for (int x = 0; x < size; x++) {
    for (int y = 0; y < size; y++) {
      for (int z = 0; z < size; z++) {
        tint(#FFFFFF, 555);
        if (data[x][y][z] != 0 && shouldShowCube(x, y, z, size)) {
          //boolean showTopFace = false, showBottomFace = false, showFrontFace = false, showRightFace = false, showBackFace = false, showLeftFace = false;
          boolean showTopFace = true, showBottomFace = true, showFrontFace = true, showRightFace = true, showBackFace = true, showLeftFace = true;

          if (y > 0) showTopFace = data[x][y-1][z] == 0;
          if (y < size-1) showBottomFace = data[x][y+1][z] == 0;
          if (z < size-1) showBackFace = data[x][y][z+1] == 0;
          if (z > 0) showFrontFace = data[x][y][z-1] == 0;
          if (x < size-1) showRightFace = data[x+1][y][z] == 0;
          if (x > 0) showLeftFace = data[x-1][y][z] == 0;

          //chunk.addChild(voxel(x, y, z, imgs[0], imgs[0], imgs[0], imgs[0], imgs[0], imgs[0], showTopFace, showBottomFace, showFrontFace, showRightFace, showBackFace, showLeftFace));
          if (showFrontFace) {
            chunk.vertex(x, y, z, 0, 0);
            chunk.vertex(x+1, y, z, 1, 0);
            chunk.vertex(x+1, y+1, z, 1, 1);
            chunk.vertex(x, y+1, z, 0, 1);
          }

          if (showBackFace) {
            chunk.vertex(x, y, z+1, 0, 0);
            chunk.vertex(x+1, y, z+1, 1, 0);
            chunk.vertex(x+1, y+1, z+1, 1, 1);
            chunk.vertex(x, y+1, z+1, 0, 1);
          }

          if (showRightFace) {
            chunk.vertex(x+1, y, z, 0, 0);
            chunk.vertex(x+1, y, z+1, 1, 0);
            chunk.vertex(x+1, y+1, z+1, 1, 1);
            chunk.vertex(x+1, y+1, z, 0, 1);
          }

          if (showLeftFace) {
            chunk.vertex(x, y, z, 0, 0);
            chunk.vertex(x, y, z+1, 1, 0);
            chunk.vertex(x, y+1, z+1, 1, 1);
            chunk.vertex(x, y+1, z, 0, 1);
          }

          if (showTopFace) {
            chunk.vertex(x, y, z, 0, 0);
            chunk.vertex(x+1, y, z, 1, 0);
            chunk.vertex(x+1, y, z+1, 1, 1);
            chunk.vertex(x, y, z+1, 0, 1);
          }

          if (showBottomFace) {
            chunk.vertex(x, y+1, z, 0, 0);
            chunk.vertex(x+1, y+1, z, 1, 0);
            chunk.vertex(x+1, y+1, z+1, 1, 1);
            chunk.vertex(x, y+1, z+1, 0, 1);
          }
        }
      }
    }
  }
  chunk.endShape();
  return chunk;
}

boolean shouldShowCube(int x, int y, int z, int size) {
  if (x > 0 && y > 0 && z > 0 && x < size-1 && y < size-1 && z < size-1) {
    if (data[x-1][y][z] == 0 || data[x+1][y][z] == 0 ||
      data[x][y-1][z] == 0 || data[x][y+1][z] == 0 ||
      data[x][y][z-1] == 0 || data[x][y][z+1] == 0) {
      return true;
    }
  } else {
    return true;
  }
  return false;
}

-Libby