loads of cubes

Blitz3D Forums/Blitz3D Programming/loads of cubes

Warner(Posted 2010) [#1]
I'm working on something similair to minecraft: http://minecraft.net/
However, I'm having trouble rendering big amounts of cubes.
I tried creating a single-mesh and a scenegraph. Also I tried to see what sides of the cubes were redundant, because the cubes have neighbours.
Still, I just can't seem to find a good, fast and stable solution.
Has anybody got any tips/suggestions on how to improve the level performance?


Ross C(Posted 2010) [#2]
I'm surprised single surface cubes didn't work. how many cubes are you using?


Warner(Posted 2010) [#3]
Single surface does help a lot, indeed. But I'm trying to create as much cubes as possible. For now, I'm trying out different amount of cubes, such as 64x64x64 or 32x32x32. The computers I'm working with are not too fast, btw.
I wonder how they did this in Minecraft? It has a lot of cubes:



Kryzon(Posted 2010) [#4]
When saying "scenegraph" you mean you're hiding the cubes that are completely occluded, right?


Ross C(Posted 2010) [#5]
You may need to build a system whereby the cubes are added and any two cube faces that touch, are deleted from the mesh (ie rebuild the mesh, minus those tris). A 3d dimensional array should do this. Something like:

Dim array(300,300,300)

And simply store a 1 for cube here, or a zero for none. Then build the mesh using this data, skipping putting tris in, where adjacent 1's exist in the mesh. This could maybe exist as an optimise button, so as not to slow it down even more.


Warner(Posted 2010) [#6]
@Kryzon
When saying "scenegraph" do you mean you're hiding the cubes that are completely occluded, right?

Well erm, no .. I didn't realise that is what "scenegraph" means. What I intended to say is: I'm using an array of level chunks. The array is used to hide chunks that are far away. The advantage of this is that not all level chunks need to be iterated (no For..Each Chunk to check for EntityDistance), only those chunks that are surrounding the camera.

@Ross, I did do that allready. I made three surfaces, one for each X/Y/Z direction, because when I tried to add 32x32x32 cubes minus overlapping sides to one surface, the program just stopped creating new triangles as some point.
Even with that system, the program becomes slow after adding this big amount of cubes. Maybe I would need to optimise further, merging multiple cubes into a bigger cube. Would CSG help out here. I did experiment with that a few months ago.

Occlusion, I haven't looked into. Might be a good idea. Is that difficult? :) Sure, I guess. I can imagine it works best when walking through the level, but not so good if you're watching it from above (bird-view, as in the picture above).


Kryzon(Posted 2010) [#7]
Well, what happens is this:

Every Renderworld we call, Blitz3D sends to the graphic card every [not hidden] mesh that has it's bounding-box inside the camera's frustrum.

In the graphic card's pipeline, the view will be clipped and then every polygon that is occluded by others will be culled. This takes time.

We need to do everything possible in our applications to minimize the "useless" occlusion culling that the graphic card will do (by 'useless' I mean, sending to the graphic card entities that are entirely occluded by others, and will only consume GPU time to be discarded from rendering).

Therefore, you could try and spend some time learning about PVS (Potentially Visible Set) methods like Octree or your own you make up (prioritizing Conservative algorithms, IMO). I only know this much, so I can't help you with this.
But yeah, like you said, it is difficult - nonetheless, it's worth the try.
It might improve the performance of your game a lot.

EDIT: I don't feel quite comfortable with manipulating meshes in real time (joining cubes in real-time to spare polygons, or even use pre-made, "proxy" meshes). I think it's much easier to just hide everything your PVS system judges to be 100% occluded (with the good ol' HideEntity) . That way those occluded bricks won't be sent to the graphic card at all.
If you are using a Single-Surface system, you can hide every occluded brick by placing all of their vertices behind the camera and at the exact same position (all 8 of them) - if I remember correctly, these two conditions help the graphic card discard the polygons faster. Polygons that have all vertices at the same position won't be rendered.

Can I just go off-topic and say that this music track totally kicks ass?


Jiffy(Posted 2010) [#8]
1 cube has 12 tris
a 2x2x2 cube is 8 cubes
8 cubes have 96 tris

Take a 1x1x1 cube, double the size, texture it as if 2x2x2, and save 84 tris

You won't be able to do this everywhere, but often.

2x2x1 (3 orientations), 4x4x4, 4x4x1 (3 orientations), ect are all potential candidates.

Should help.

[Ed]
Kryzon:
1. This could be part of the build sequence, not the render pipeline- unless this game has destructive dynamics (which it may).
2. Even for a 4x4x4 set, a PVS system will give 32x(faces) tris- as compared to 2x(faces) tris. Not staggering, but easily adding up & setting the max performance bar- which (as you know) for blitz isn't as high as it should be.


GIB3D(Posted 2010) [#9]
For your information, Blockland does the same kind of thing with cubes. I also tried doing something similar in my Gorilla game but didn't realize at the time that More Surfaces = More Lag. If two cubes are touching, the surfaces that are facing each other are deleted. What could have possibly made it faster is if I joined all the surfaces into one surface(or mesh?). I tried adding them all to one mesh but it didn't work out too well because I didn't know what I was doing.


Kryzon(Posted 2010) [#10]
I thought every cube had to be individually manipulable. I tried the original game now.

I guess the loading part is where you position the cubes, adding them up through some CSG algorithm (while keeping UVs intact - you'd have to use a texture with all the necessary tiles inside it).

Then for every block that need to be destroyed, you'd Subtract it (using a reference-only block unit) from the major structure.

To avoid CSG artifacts, you should keep everything coordinate-related (including sizes and scales) to an Integer level, so as to avoid floating point imprecisions.


Rroff(Posted 2010) [#11]
Minecraft (and similiar games) use voxels to decide what to render, etc. you'd need a massive array and raycast into the array to decide what to render - to massively simplify it.


Jiffy(Posted 2010) [#12]
They're all cubes. CSG is overkill.
Just do your xyz nested loop over the worldspace, check current & all coord +1's. if all are the same, mark the corner off as a 2x2, the balance as ('-1' maybe?) & skip 2. Skip any partially marked. When done check the 2x2's in the same way to promote to 4x4s, etc. After that you just drop the right size cube to your 'corners', texturing it as if it the right number of cubes.
more or less.
Cubes are also so regular a specialized occlusion routine could be made to 'pseudo-raycast' for culling- without actually raycasting- all 'objects' dimensions are 'known'- they're all cubes- the same one in fact, just in different places. I think this is the foundation of the 'Cube' engine in fact (tell me you didn't see that one coming)


Kryzon(Posted 2010) [#13]
I don't think CSG is overkill, especially if you need to remove cubes. It's just another approach.

If he needs to be able to remove cubes (say, if an explosion happens and some cubes are destroyed), then converting clusters into simpler, bigger meshes like you say will make it harder to "deconstruct" them to break them apart.
What he'll have to test is if freeing these optimized meshes and restoring all the cube units they were made up by is an improvement over CSG subtractions.

I guess you could run that optimization and turn clusters of cubes into simpler meshes, but then allowing CSG to carve these structures when necessary.
If you do it mathematically perfect (since, like you said, the basic cube unit has its size and position known), there shouldn't be any reasons for artifacts to appear.


Jiffy(Posted 2010) [#14]
If the game is destructive at all, you're definitely right. I'm not sure what he's up to- and depending on design and level size- he may not be able to do what he wants 'in real time' in blitz. Anyway, options are good.


xtremegamr(Posted 2010) [#15]
I'm not sure if someone has said this before, but maybe you should try quad trees. It's how the Cube2 engine works, though it seems like you are trying to make bigger levels than Cube2 could ever support.

Here's an excellent article that explains quad trees: http://www.gamedev.net/reference/programming/features/quadtrees/

Hope this helps.

[Edit]: You may also want to try adjusting the fog\view distance.


Jiffy(Posted 2010) [#16]
Cube uses Quadtree
Cube2 uses Octree
http://cubeengine.com/cube.php4
http://cubeengine.com/

As far as level size goes...
http://eisenstern.com/


Warner(Posted 2010) [#17]
Thanks for all suggestions, I tried them out. I wrote a mesh optimizer that leaves out shared sides, and a merge function that replaces series of cubes with a big cube. I made an octree test. I also wrote a voxel raytracer, first in blitz3d, then in bmax, and finally in devc++. These voxel raytracers are really cool stuff! When I have more time, I'll look further into them.
Now I ended up with a very simple solution: using a grid of cubes around the camera. Used CopyEntity to place them. When the user is walking through the world, the grid is updated with ShowEntity/HideEntity using the data in a big array. It won't allow people to see the world from far above -and I put the fog distance pretty close - but for what it is needed for, it should do.
Thanks for all suggestions.



Warner(Posted 2010) [#18]
For those interested, here is the code I wrote for the voxel raycaster. I converted it back from c++ to b3d, actually without much speed difference.
Controls: arrow keys+A/Z.



Kryzon(Posted 2010) [#19]
I think I speak for a lot of people here - Thank you for sharing your code!


Nate the Great(Posted 2010) [#20]
wow warner, I must say that is surprisingly very very similare tggggo this:

http://www.blitzbasic.com/Community/posts.php?topic=89419

nice job


Warner(Posted 2010) [#21]
That's cool. However, my PC is to slow to run it properly. Indeed, it looks quite similar, code-wise. I can't get over it how cool I find raytracers. Still looking into ways of optimizing, though. I found a method that mipmaps the scene data on beforehand. As the ray gets further away from it's origin, the level data precision is decreased. That way, the ray can get a lot further in the same time period. Still, I'm not at the point were I can run 640x480, which I would really like. Thanks for the link!


Imperium(Posted 2013) [#22]
I love raycast stuff but it's something most modern hardware just can't do very well. I'd rather see a modern game mimic the style of an early shooter instead of trying to get this archaic algorithm to behave correctly. Not that I have any issues with this code.

If anyone ends up releasing a true raycast game I must play it!


jfk EO-11110(Posted 2013) [#23]
You wouldn't render the gfx with a raycaster these days, but for eg. a visibility check it's ok.
Btw I don't understand why warners code seems to use a recursive castray function. Today I'd rather use a binary search.


_PJ_(Posted 2013) [#24]
Obviously if there's cubes with the same texture arranged in a cuboid, this can all be simplified as a single cuboid (max 6 tris), with single, repeated texture.
Or is this possibility unlikely and too far removed from the calculations you ARE using to be of any real help? Since of course, it would "destroy" the individual cubes...