Best way to store memory hogging objects

I’m creating a voxel game right now, and probably the biggest issue is the fact that if I use a three dimensional array to store my chunks, I can only store around 10 before the game crashes. If I use a one dimensional array, I can have up to 175 before the program crashes because of a heap space error, which really isn’t a problem. The problem is that with a one dimensional array, they’re very fast, but I can’t position the chunks around the player, just in one dimension. Here’s a picture to illustrate what I mean:

Obviously, this really isn’t acceptable. But, like I said if I use a three dimensional array, I can position the chunks around the player correctly, but I can’t actually have more than a few. My chunks hold quite a bit of data:

	private Tile[][][] tiles;
	private Tile[][][] transparent;
	private Tile[][][] temp;

The tiles array is for opaque tiles. The transparent is for transparent, and the temp is a combined transparent and tiles array for when I sort the chunks and decide which blocks need to be rendered. My chunks are 16^3, just in case that helps.

Is there a way to position chunks with a one dimensional array like with a three dimensional one?

As far as I know (and see in my own projects) JIT is trying to “split” x-dimensional arrays into 1-dimensional, so this shouldn’t be a problem.

Why do you have separate arrays for “normal” and transparent tiles? If tiles from two (or more) arrays never overlap then you should keep them in one array. Additionally, I would delete temp array from game data - keep one in your rendering classes if you really need it, though I would just use data from “normal” array.

Another tip - avoid “new” while creating tiles if these tiles don’t need any additional data - use references to the final static Tile instead.

Here is an even better Idea:
Don’t use Objects to store your Voxels, use Primitives.

Tile[][][] is a one-heck of a giant memory-hog.
int[][][] is better, but still bad.
byte[] is even better.
ByteBuffer is the most complicated, but also the fastest if used right.

Why are you using ‘Tile’ to store your voxel-data?

  • Longor1996

I have static tiles already, that’s the only reason the game can load as fast as it does right now. And I actually can’t have two separate arrays because its actually more efficient to only call upon each array once. Because I have transparent tiles, I need to draw the opaque objects first. Then I call the transparent tiles array. If I didn’t have two separate arrays, I’d have to call the tiles array twice, and I think sorting through a mostly full array and trying to find the few transparent is more expensive than sorting through a mostly empty transparent array of tiles.

I agree, the temp should be deleted, but my code actually doesn’t allow me to do that for reasons :confused:

How would I use bytes? Just store the id of a tile? Then when I want to render create a large switch statement to switch between the bytes and find the tile I need? If that’s what you mean, I think I could do that.

Well it’s more like:


(pseudocode)

// Object that contains the voxel-data
Chunk CHUNK = ...;

// Object that contains the singletons that describe the voxel types
BlockList BL = ...;

for_each Y in CHUNK_HEIGHT {
  for_each Z in CHUNK_LENGTH {
    for_each X in CHUNK_WIDTH {
      int blockData = CHUNK.getBlockData(X,Y,Z);
      int blockID = getIDfromData(blockData);
      
      BlockRenderer blockRenderer = BL.getBlockDescriptor(blockID).getRenderer();
      
      blockRenderer.render( ... All-Variables-Needed-For-Rendering ... );
    }
  }
}

Basically, a Chunk consists ONLY of the following:


- byte[] or int[] containing the voxel-data.
- int3 containing the total position of the chunk in the world.
- int3 containing the chunked position of the chunk in the world.
- Reference to the object that holds the chunk.
- State-Machine (Render-Data, Logic-Data, Special Voxel-Data)

That’s how I do it. That’s how Minecraft does it.
This is the fastest possible way, although it could be (therotically) optimized in some places.

Have fun implementing it!

  • Longor1996

Ok, that doesn’t seem so bad! So basically every block needs a byte id? And then I would need a texturestore class or something to store all my texture coordinates for my sprite sheets, then I can grab the texture coordinates using that blok id… Sounds very doable! Thanks!

Now, how do you actually hold all your chunks? I’d imagine you’d create another byte array and store all your chunks in it with their id’s. I could also adapt that so I could say if I want a chunk to the left of chunk with the id of 4, then it’ll be 5! Super easy chunk lookup! I guess it may be harder to do with infinite worlds because some chunks may not be loaded. In a finite world I could say I have a 4x4x4 chunk world, so if I want to find the chunk below chunk lets say 3, I could just say well I know that each row of chunks is 4 in length, so logically if I look down a row from 3, it would be 6! Now, I don’t think that would work in an infinite world as you don’t (entirely) know how long a row of chunks is.

Sorry if that made no sense, it makes complete sense in my mind, and it would be a really easy way to look up a chunk.

There are better ways to do it though…

  • TreeSet or HashMap: O(log(N)) if optimized. (Using chunk coordinates as key)
  • Cascaded Chunks: O(?n^0.3?)
  • ArrayList: O(n)

I actually use a simple ArrayList to hold my chunks.
It works perfectly well, with the exception of slower Chunk-lookup if there are more chunks.
:smiley:

Also, implement a limit on how many chunks are allowed to generate their mesh, per frame.
Or throw that away and use multithreaded rendering.

I know quite a bit about optimizing voxels…

Have a nice day!

  • Longor1996

Can you go into detail about cascaded chunks? I’ve never heard about that. As for the arraylist, how do you generate chunks in 3 dimensions if an arraylist is 1 dimensional. I just don’t see how that would work. Besides, shouldn’t you just use an array and store the chunks that are around the player? Then you could write the chunks that aren’t visible. It seems like an arraylist would be a waste of memory, although I don’t know if an arraylist is more expensive than a array. So maybe it wouldn’t make a difference.

ArrayList Technique:


interface ChunkCache
{
  Chunk getChunk(int3 coords);
}

class World implements ChunkCache
{
  ArrayList<Chunk> chunks = ...(2 * ViewAreaChunkVolume);
  
  public Chunk getChunk(int3 coord)
  {
    for_each chunk in chunks
    {
      if(chunk.x != coord.x)
          continue;
      if(chunk.y != coord.y)
          continue;
      if(chunk.z != coord.z)
          continue;
      
      return chunk;
    }
    
    // Chunk not found, return null!
    return null;
  }
}

Cascaded Chunks:


Sorry, but this technique can take up some pages to write down.
Have a (very) short explanation:
Threat Chunks as Voxel's too, but on a very large scale.

That should help a bit.

Have a nice day/night!

  • Longor1996

Right, I understand how to look up chunks in an arraylist, but how do you generate the chunks? Thats my issue, I just don’t see how to do it with a 1 dimensional array.

You basically need to do:


(pseudocode)
# Game.class :: ON EACH TICK
for_each (int3)PossibleChunkPosition inside (int3,int3)PlayerViewZone
{
  if(!world.hasChunk(PossibleChunkPosition))
    world.generateChunk(PossibleChunkPosition);
}

# WORLD.class
void generateChunk(int3 pos)
{
  Chunk chunk = worldChunkProvider.generateOrLoadChunk(pos);
  chunks.add(chunk);
}

Does that help?

  • Longor1996

Oh I see, I’ve been going around it all the wrong way that whole time. Thanks! I might have some questions later, but I’m ok for now!

So I decided to implement a system where the chunks that are within a certain distance to the player are rendered. Obviously, this is a big optimization. I have already figured out the distance formula (easiest one out there), but I have no idea what to do with the value. On a side not, I have no idea why, but sometimes I get a non number from the formula, which I guess makes sense if I’m standing on the chunk as the distance value would be tiny. Here’s my code:


if (mobManager.getP().isHasMoved()) {
			for (int x = 0; x < cx; x++) {
				for (int y = 0; y < cy; y++) {
					for (int z = 0; z < cz; z++) {
						float distance = (float) Math.sqrt((chunks[x][y][z].getCenterPos().x * chunks[x][y][z].getCenterPos().x - mobManager.getP().getPosition().x * mobManager.getP().getPosition().x) + (chunks[x][y][z].getCenterPos().y * chunks[x][y][z].getCenterPos().y - mobManager.getP().getPosition().y * mobManager.getP().getPosition().y) + (chunks[x][y][z].getCenterPos().z * chunks[x][y][z].getCenterPos().z - mobManager.getP().getPosition().z * mobManager.getP().getPosition().z));
						if (distance < 32)
							chunks[x][y][z].render();
						System.out.println(distance);
					}
				}
			}
		}

What happens right now is basically this:

I mean, how does that even happen? A nornal world is just flat, its 2 chunks deep and 10x10 wide and long. I quite honestly do not know why this is happening. How could a chunk that is far away from the player be rendering, if the ones before it aren’t, like in the picture
Edit: For clarification. getP() returns the player. cx, cy, cz are just the limits of the chunk array. The getCenterPos() just returns the center of the chunk.

Nevermind, I’m an idiot. I was doing everything wrong, for future reference, here is how I fixed the problem:


if (Math.abs(chunks[x][y][z].getCenterPos().x - (int) -mobManager.getP().getPosition().x) < 32) {
							if (Math.abs(chunks[x][y][z].getCenterPos().z - (int) -mobManager.getP().getPosition().z) < 32) {
								chunks[x][y][z].render();
							}
						}

Works perfectly now!

I’m pretty sure you’re mistaken here. You have a reference or assem dump to support this?