First of all, having cube scene nodes is far too slow (each node is a draw call and we're looking at (size*count)^3 per chunk (or woe the naive "one chunk world" implementation), assuming this is 32^3 then that's 32768 draw calls! You really want as few as possible, 500 draw calls is great - 5000 might push it... thirty two thousand? Give me a break.
Second of all it's memory inefficient.
Third of all nested loops are slower than a single unwrapped loop, you got the order right from a cache perspective though.
What you'd want to ideally do is check for occlusion based on the input data (so you only generate faces adjacent to air blocks) and generate only the appropriate faces, this gives you one mesh per chunk (i.e push a new face onto the current mesh as needed until we've iterated over the chunk structure) then you would ideally want to optimize the mesh using some algorithm such as greedy meshing - but that's optional.
EDIT: I just now noticed 8000 cubes that'd be 20^3, so fine - that's an odd size from a perspective of memory alignment, you'd usually want it to be a power of 8 (8. 16, 32, 64 ... - 24 might work because it's a multiple of 8 so it'll align properly; but this all depends on the chunk structure) but even here we're looking at 8000 draw calls so I'm not surprised you're getting 24 fps.
I have a chunk test where I have over ten million triangles (unculled, so each voxel has all six faces, 12 tris) in 30 draw calls, it hits 60 fps easily even on pretty conservative hardware (note: not stock irrlicht) - can I give you the source? No. It's really ugly and not a good implementation but I can give you a somewhat general-ish implementation without culling
depending on your needs this should work, this pushes back one full cube per voxel for size voxels (size is width*height*length, in your case 8000 - this code would return one draw call as opposed to eight thousand draw calls and should thus be significantly faster) - you can extract what you need for a face and then factor it out to separate methods, called as needed. Or modify this method to generate faces needed based on input parameters - either way.
Code: Select all
for (int i = 0; i < size; i++)
{
//these if statemenets are a REALLY ugly way to unwrap the loops, but it works.
if (x >= CS) //logic to calculate whether to increment Y and reset X
{
x = 0;
y++;
}
if (y >= CS) //logic to calculate whether to increment Z and reset Y
{
y = 0;
z++;
}
if (z >= CS) //logic to calculate whether to reset Z
{
z = 0;
}
//push back 12 vertices, this is optimal for a cube.
//caveat: since this is a "compressed" cube it becomes much harder to do certain skinning related things
//in my use it made sense to conserve memory - you may want to use a proper 24 vertex cube since it's more flexible
buf->Vertices.push_back(video::S3DVertex(x - 0, y + 1, z - 0, -2, 2, -2, c, 0, 0));
buf->Vertices.push_back(video::S3DVertex(x + 1, y + 1, z - 0, 2, 2, -2, c, 1, 0));
buf->Vertices.push_back(video::S3DVertex(x + 1, y - 0, z - 0, 2, -2, -2, c, 1, 1));
buf->Vertices.push_back(video::S3DVertex(x - 0, y - 0, z - 0, -2, -2, -2, c, 0, 1));
buf->Vertices.push_back(video::S3DVertex(x - 0, y + 1, z + 1, -2, 2, 2, c, 1, 0));
buf->Vertices.push_back(video::S3DVertex(x + 1, y + 1, z + 1, 2, 2, 2, c, 0, 0));
buf->Vertices.push_back(video::S3DVertex(x + 1, y - 0, z + 1, 2, -2, 2, c, 0, 1));
buf->Vertices.push_back(video::S3DVertex(x - 0, y - 0, z + 1, -2, -2, 2, c, 1, 1));
buf->Vertices.push_back(video::S3DVertex(x - 0, y + 1, z - 0, -2, 2, -2, c, 1, 1));
buf->Vertices.push_back(video::S3DVertex(x + 1, y + 1, z - 0, 2, 2, -2, c, 0, 1));
buf->Vertices.push_back(video::S3DVertex(x + 1, y - 0, z - 0, 2, -2, -2, c, 0, 0));
buf->Vertices.push_back(video::S3DVertex(x - 0, y - 0, z - 0, -2, -2, -2, c, 1, 0));
//push back 36 indices
buf->Indices.push_back(numVertices + 0);
buf->Indices.push_back(numVertices + 1);
buf->Indices.push_back(numVertices + 2);
buf->Indices.push_back(numVertices + 2);
buf->Indices.push_back(numVertices + 3);
buf->Indices.push_back(numVertices + 0);
buf->Indices.push_back(numVertices + 7);
buf->Indices.push_back(numVertices + 6);
buf->Indices.push_back(numVertices + 5);
buf->Indices.push_back(numVertices + 5);
buf->Indices.push_back(numVertices + 4);
buf->Indices.push_back(numVertices + 7);
buf->Indices.push_back(numVertices + 4);
buf->Indices.push_back(numVertices + 0);
buf->Indices.push_back(numVertices + 3);
buf->Indices.push_back(numVertices + 3);
buf->Indices.push_back(numVertices + 7);
buf->Indices.push_back(numVertices + 4);
buf->Indices.push_back(numVertices + 1);
buf->Indices.push_back(numVertices + 5);
buf->Indices.push_back(numVertices + 6);
buf->Indices.push_back(numVertices + 6);
buf->Indices.push_back(numVertices + 2);
buf->Indices.push_back(numVertices + 1);
buf->Indices.push_back(numVertices + 9);
buf->Indices.push_back(numVertices + 8);
buf->Indices.push_back(numVertices + 4);
buf->Indices.push_back(numVertices + 4);
buf->Indices.push_back(numVertices + 5);
buf->Indices.push_back(numVertices + 9);
buf->Indices.push_back(numVertices + 11);
buf->Indices.push_back(numVertices + 10);
buf->Indices.push_back(numVertices + 6);
buf->Indices.push_back(numVertices + 6);
buf->Indices.push_back(numVertices + 7);
buf->Indices.push_back(numVertices + 11);
numVertices += 12;
x++;
}
EDIT 2:
CuteAlien wrote:I never coded a Minecraft, but I suppose you don't want to have real cubes at all. But calculate the outer layers. I remember some people talked in older threads about a libraray to calculate the corresponding geometry. Which can then be used in meshbuffers. But ... don't remember the name of that lib.
You're probably thinking of polyvox - it didn't meet my requirements, too much overhead at the scale I'm trying to work with.
But here's the website for that in case OP wants to take the slightly easier-ish route
http://www.volumesoffun.com/