- Home /
Creating a voxel grid from several bitmaps
Hello people
A certain degree of knowledge about Dwarf Fortress is a plus for this question ;)
In short, i'll be recieving some bitmaps, to begin with it'll be colors of black and white like these two maps:
<- this one is supposed to be loaded in twice
And from those two(three) maps, i want to make a script that can generate a 3D scene, so the end result will look like this:
I've thought about using voxels, as it seems to be the best sulution for something like this, as the final scene will be having about 100*250^2 blocks
This is an absolutely obscene amount of blocks, as well as polygons, of that i am aware. Which is why i'm thinking it'd be a good idea to use a voxels, as i've seen how many "blocks" minecraft can handle.
Before you shout "MINECRAFT CLONE" let me remind you that i'm making a visualizer for this game: http://en.wikipedia.org/wiki/Dwarf_Fortress
So yeah, i dont want anyone to write a tutorial for this, i'm more looking for feedback if this is actually possible, and if so, where can i find reading material about it, i've been googling a bit, and cant seem to find anything.
Thanks in advance
/Kacer
Just for all the people who do shout "$$anonymous$$inecraft Clone":
NOT ALL BLOC$$anonymous$$Y VOXEL GRAPHIC GA$$anonymous$$ES ARE $$anonymous$$INECRAFT CLONES! The first game to use voxel graphics was a helicopter sim called Comanche $$anonymous$$aximum Overkill, and nobody shouts "Comanche clone" at $$anonymous$$inecraft, and they didn't even shout "Infini$$anonymous$$er clone", the most recent voxel game before $$anonymous$$ecraft! Voxels are useful for a variety of purposes in games, particularly terrain, and the simplest and fastest way to do so is to make the blocky kind. Tons of games use blocky voxels, many of which were made BEFORE $$anonymous$$INECRAFT! True, $$anonymous$$inecraft did use voxels exceptionally well, but that does NOT mean that every other game that uses them is now somehow a 'clone' or 'ripoff' of $$anonymous$$inecraft. Following that logic, isn't every first person game we play really just a clone of $$anonymous$$aze? $$anonymous$$ojang doesn't even own the algorithm!
In other words, very few games accused of being "$$anonymous$$inecraft clones" actually deserve that shame, and in the meantime we have discouraged many people's development of games or software using voxels, which is a shame, since polygonal meshes are slowly become obsolete as graphics hardware is beco$$anonymous$$g better at rendering volumes.
In other words, those who accuse any blocky game as being a $$anonymous$$ecraft clone are just being ignorant, and unless you are actually attempting to recreate $$anonymous$$inecraft, your game is not going to be a ripoff or a clone.
I just felt that needed to be said.
Answer by Cherno · May 04, 2015 at 10:56 PM
Absolutely possible and not too difficult either.
Here are the basics:
The bitmaps have to be set to read-enabled in their import settings. It would probably be advisable to create another array for the bitmaps so they are in the right order and we don't have to create seperate variables for each:
public Texture2D[] textureArray = new Texture2D[];
private int texture_width = textureArray[0].width;
private int texture_height = textureArray[0].height;
Note that this array has only one dimension. You can populate it with a simple function, or just set it's length in the editor and drag the textures into it in the right order.
Now we need to create our three-dimensional array. Since we only need to care about wether a cell (a position inside the array) is filled or not, we can just use an array with elements of type int. 0 would be clear, and 1 would be filled. The size of the array depends on the size of the bitmaps, of course, but for this example we can just assume that, as in the screenshot, the bitmap is 3x3 pixels, and there are three bitmaps, so our array has a length of 3 in all dimensions:
public int[,,] blocks;
blocks = new int[texture_width, textureArray.Length, texture_height];
Now we will iterate through our block array, one cell at a time, and take the color of the corresponding pixel of the bitmap and convert it to either 0 or 1.
int x = 0;
int y = 0;
int z = 0;
for(x = 0; x < blocks.GetLength(0); x++) {
for(y = 0; y < blocks.GetLength(1); y++) {
for(z = 0; z < blocks.GetLength(2); z++) {
Texture2D currentTexture = textureArray[y];
Color pixelColor = currentTexture.GetPixel(x, z).grayscale;
if(pixelColor.r >= 0.5f) {
blocks[x,y,z] = 0;
}
else {
blocks[x,y,z] = 1;
}
}
}
}
So now we have the basic information we need to start creating our mesh. There are lots of voxel tutorials so IM' just gonna link to the one that got me started. It's in Unity Script but can easily be converted to C# if you want.
It also uses a three-dimensional int array to it should be a perfect fit. Just delete the part where the int values get randomly assigned.
Thank you for the great reply, way more than i had anticipated :D
The guide you've linked, to create voxels, thats just a chunk though, and from the guide it appears that it is limited to 65000 points, do you have a theory as to how i'd load a map like this into several chunks?
This is a whole new chapter in program$$anonymous$$g for me, and i find it quite complicated :)
Yes, you need to implement chunks, but that is rather trivial really. It's basically like this:
The script that generated the mesh sits on a gameObject (one chunk). You pass it the whole block array as well as the starting and ending xyz values. Then you do the normal mesh generation, but you don't iterate through every block in the block array, but rather only through the blocks inside that specific chunk's xyz values.
The chunk objects themselves are referenced in their own three-dimensional array, with this array's dimensions being deter$$anonymous$$ed by the cell dimensions each chunk should have. So if you have a block array with a xyz length of, say, 9, and your chunk should have xyz lengths of 3, then your chunk array would have a xyz length of 3 (9/3).
So to put things in order:
Define chunk array ( GameObject[,,] or ChunkRender[,,])
Iterate through chunk array, instantiating a prefab at each position. The prefab would be a gameObject with meshfilter, meshrenderer, meshcollider, and the ChunkRender script.
Access the instantiated chunk's ChunkRender script and set the xyz starting and end values
When the loops are finished, you can call another function that iterates through the chunk array again and calls the ChunkRender function on their scripts. This is what you would do if you would want to update all the chunks during runtime.
Edit: Oh, and if you want more information... After playing $$anonymous$$ecraft...
Edit2:
int x = 0;
int y = 0;
int z = 0;
int worldSize_x = 9;
int worldSize_y = 9;
int worldSize_z = 9;
int chunkSize_x = 3;
int chunkSize_y = 3;
int chunkSize_z = 3;
ChunkRenderer[,,] chunkArray = new ChunkRenderer[worldSize_x / chunkSize_x,worldSize_y / chunkSize_y,worldSize_z / chunkSize_z];
//create chunks... note that it doesn't matter where the chunk gameobject is located because it's the mesh's vertices that count
for (x = 0; x < chunkArray.GetLength(0); x ++) {
for (y = 0; y < chunkArray.GetLength(1); y++) {
for (z = 0; z < chunkArray.GetLength(2); z++) {
GameObject chunkCur = Instantiate(chunkPrefab, transform.position, Quaternion.identity) as GameObject;
ChunkRenderer chunkRenderer = chunkCur.GetComponent<ChunkRenderer>();
chunkRenderer.start_x = x * chunkSize_x;
chunkRenderer.start_y = y * chunkSize_y;
chunkRenderer.start_z = z * chunkSize_z;
chunkArray[x,y,z] = chunkRenderer ;
}
}
}
//Update all chunks
for (x = 0; x < chunkArray.GetLength(0); x ++) {
for (y = 0; y < chunkArray.GetLength(1); y++) {
for (z = 0; z < chunkArray.GetLength(2); z++) {
chunkArray[x,y,z].ChunkRender(blockArray);
}
}
}
Inside the ChunkRender function, the xyz loop would look like this
for (x = chunkStart_x; x < chunkStart_x + chunkSize_x; x++)
{
for (y = chunkStart_y; y < chunkStart_y + chunkSize_y; y++)
{
for (z = chunkStart_z; z < chunkStart_z + chunkSize_z; z++)
I also edited my original answer, one paragraph waas at the wrong place, this one:
Note that this array has only one dimension. You can populate it with a simple function, or just set it's length in the editor and drag the textures into it in the right order.
Belongs under the code that declares the Texture array.
Wish i could give you more upvotes on this, you're really helping me a lot :)
Answer by $$anonymous$$ · May 04, 2015 at 11:18 PM
First of all huge Dwarf Fortress fan! Love the game. Must dig deeper!!!
Alright on to serious talk...
Is it feasible?
Short answer: Yes
Long answer: I've dabbled in mesh generation, voxel terrain, and such multiple times so I have a little experience but not a lot. Generating the mesh from the heightmaps is going to be the easy bit. It will be optimizing performance that will be hard.
You simply put cannot generate the entire dwarf fortress region at once (even as one mesh) as there will be too much vertices data for the engine to efficiently process so you'll need to create the mesh in chunks.
As well you will want to ensure you do not render any hidden faces and not render any faces that the camera cannot see. (I.E. the back of the mesh) Note Unity does perform optimizations if the face is not visible or if it is outside the camera's view, but I found that it wasn't always enough. (Mind you that may just be my bad coding...)
You can definitely do it but it just has a few layers of hidden complexity that you will need to be aware of before you embark.
If you want to see some code I would be happy to provide some that I wrote a while back.
Some of the tutorials that helped me out: http://studentgamedev.blogspot.co.uk/2013/08/unity-voxel-tutorial-part-1-generating.html https://www.youtube.com/watch?v=bpB4BApnKhM&list=PLbghT7MmckI4qGA0Wm_TZS8LVrqS47I9R (It's 2D but goes over mesh generation well)
Thanks for the answer, i've bookmarked the thread, and will watch the videos later. $$anonymous$$eep an eye on the DF forums if i ever manage to finish this :P