 Still in need of a RIGGER!        :: Project Civil :: Voxel Engine Development Blog Share

4 - Chunk by Chunk (Part 2) AuthorMessage

CoreDev CEO  Posts : 66
Join date : 2012-09-07
Location : Sydney, Australia 20121016 4 - Chunk by Chunk (Part 2) Alright, I am back. Yes I have been lazy since my last post, leaving it unfinished, but now I am writing the conclusion to the first chapter, the actual Voxel part of the engine.What are we covering now?Yes right, we are now covering everything to do with the actual meshes , verticies re-arangement and face rendering.The chunk is not made up of 1000 individual blocks. This may get you thinking, "But the chunk size is 10x10x10".So let me get this straight, the chunk is actually one mesh, it is one object!The chunk does not contain 100's of blocks as objects, but as values, remember the last post where we set the 3-dimensional array :Code:private var chunk:int[,,] = new int[s,s,s];This variable "chunk" is a 10 by 10 by 10 array...... 1000 "spots". Catching on now?Thus if we take the [,,] from the code it will actualy give us the "blocks" position in the chunk in values of [x,y,z]. e.g chunk[0,0,1] would be the local position of the block [0,0,1].But really to understand you need to see the code.So here is the code decriptionNow this next piece of code is MASSIVE in the source if you looked, but don't worry, it is less daunting when you look at it in parts, it is basically repeating itself.IF you are struggling to read the code(too bunched etc) Please zoom out in your browser ("ctrl" "-")Code:function ChunkRender(chunk:int[,,]){var vertices :List. = new List.();var uvs:List. = new List.();var triangles:List. = new List.();var vertexIndex:int;var top:int;var north:int;var east:int;var south:int;var west:int;var bottom:int;GetComponent(MeshFilter).mesh.Clear();for (var x = 0; x< s; x )for (var y = 0; y< s; y )for (var z = 0; z < s; z ){var block = chunk[x, y, z];if (y==s-1){top = 0;}else{top = chunk[x, y 1, z];}if (y==0){bottom = 0;}else{bottom = chunk[x, y - 1, z];}if (z==s-1){north = 0;}else{north = chunk[x, y, z 1];}if (z==0){south = 0;}else{south = chunk[x, y, z-1];}if (x==s-1){east = 0;}else{east = chunk[x 1, y, z];}if (x==0){west = 0;}else{west = chunk[x-1, y, z];}// we are checking the top face of the block, so see if the top is exposedif (block == 1 && top == 0){vertexIndex = vertices.Count;vertices.Add(new Vector3(x, y 1, z));vertices.Add(new Vector3(x, y 1, z 1));vertices.Add(new Vector3(x 1, y 1, z 1));vertices.Add(new Vector3(x 1, y 1, z));// first triangle for the block toptriangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);// second triangle for the block toptriangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);// add UVuvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}if (block == 1 && north == 0){vertexIndex = vertices.Count;vertices.Add(new Vector3(x, y, z 1));vertices.Add(new Vector3(x 1, y, z 1));vertices.Add(new Vector3(x 1, y 1, z 1));vertices.Add(new Vector3(x, y 1 , z 1));// first triangle for the block toptriangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);// second triangle for the block toptriangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);// add UVuvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}if (block == 1 && east == 0){vertexIndex = vertices.Count;vertices.Add(new Vector3(x 1, y, z));vertices.Add(new Vector3(x 1, y 1, z));vertices.Add(new Vector3(x 1, y 1, z 1));vertices.Add(new Vector3(x 1, y , z 1));// first triangle for the block toptriangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);// second triangle for the block toptriangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);// add UVuvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}if (block == 1 && south == 0){vertexIndex = vertices.Count;vertices.Add(new Vector3(x, y, z));vertices.Add(new Vector3(x, y 1, z));vertices.Add(new Vector3(x 1, y 1, z));vertices.Add(new Vector3(x 1, y , z));// first triangle for the block toptriangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);// second triangle for the block toptriangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);// add UVuvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}if (block == 1 && west == 0){vertexIndex = vertices.Count;vertices.Add(new Vector3(x, y, z 1));vertices.Add(new Vector3(x, y 1, z 1));vertices.Add(new Vector3(x, y 1, z));vertices.Add(new Vector3(x, y , z));// first triangle for the block toptriangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);// second triangle for the block toptriangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);// add UVuvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}if (block == 1 && bottom == 0){vertexIndex = vertices.Count;vertices.Add(new Vector3(x, y, z));vertices.Add(new Vector3(x 1, y, z));vertices.Add(new Vector3(x 1, y, z 1));vertices.Add(new Vector3(x, y , z 1));// first triangle for the block toptriangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);// second triangle for the block toptriangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);// add UVuvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}}var mesh : Mesh = GetComponent(MeshFilter).mesh;mesh.vertices = vertices.ToArray();mesh.triangles = triangles.ToArray();mesh.uv = uvs.ToArray();mesh.RecalculateNormals();GetComponent(MeshCollider).sharedMesh = null;GetComponent(MeshCollider).sharedMesh = mesh;}That is the whole rest of the script!So lets break it DOWN!Code:function ChunkRender(chunk:int[,,]){This just creates a new function ChunkRender taking a 3-dimensional array that contains integers.Code:var vertices :List. = new List.();Creates an empty List of Vector3's. This will contain the locations of all the points/verticies.Code:var uvs:List. = new List.();Creates another empty list (Vector2) that will contain the Uvs values, relating to the textures being drawn. (Vector2, because a side is only two dimensional)Code:var triangles:List. = new List.();Yet another list, this time of just integers holding the amount of trianglesCode:var vertexIndex:int;This is used later in the code relating to the triangles variable.Code: var top:int;var north:int;var east:int;var south:int;var west:int;var bottom:int;Pretty self explainitory, these are the block's surrounding blocks. Why an int? Because with our current code a block can either be a 0 or 1, 0 being air and 1 being a solid.Code:GetComponent(MeshFilter).mesh.Clear();This is important as it resets the mesh to nothing, a blank canvas if you will.The following code now rebuilds the mesh!Code: for (var x = 0; x< s; x )for (var y = 0; y< s; y )for (var z = 0; z < s; z ){Runs 3 for loops, self explainitory once again. It runs it untill x,y,z are all equal to the chunksize (10). First loop runs ten times running the next loop 10 times and running the next loop 10 times, thus equalling our 1000 "blocks".Code:var block = chunk[x, y, z];The variable block holds the current "block" values throughout the loop.Code:if (y==s-1){top = 0;} else{top = chunk[x, y 1, z];}if (y==0){bottom = 0;}else{bottom = chunk[x, y - 1, z];}if (z==s-1){north = 0;}else{north = chunk[x, y, z 1];}if (z==0){south = 0;}else{south = chunk[x, y, z-1];}if (x==s-1){east = 0;}else{east = chunk[x 1, y, z];}if (x==0){west = 0;}else{west = chunk[x-1, y, z];}Once again quite simple, the if part is just determining wether the block adjacent is air. Thus making it a 0. Else it will set its adjacents to the correct chunk. e.g {east = chunk[x 1, y, z];}Here the east value is plus one on the x axisNext I am going to go over one of the face checking sequences (if statements) and leave the rest for you to view in the source and figure out, as tat will expand your knowledge much further.Code: if (block == 1 && top == 0){This code just checks if the current block is NOT air(0) and if the above block IS air(1).Reason for the top needing to equal 0 is because the rest of the code is only run if the current blocks face is visible.Code: vertexIndex = vertices.Count;vertices.Add(new Vector3(x, y 1, z));vertices.Add(new Vector3(x, y 1, z 1));vertices.Add(new Vector3(x 1, y 1, z 1));vertices.Add(new Vector3(x 1, y 1, z));triangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);triangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);uvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));}A long piece of code, this just sets the boundries. I will look at this piece by piece.Code:vertexIndex = verticies.Count;vertexIndex is how many array values are in the variable verticies(which is an array), basically returning the size of the array.Code:vertices.Add(new Vector3(x, y 1, z));vertices.Add(new Vector3(x, y 1, z 1));vertices.Add(new Vector3(x 1, y 1, z 1));vertices.Add(new Vector3(x 1, y 1, z));Now this is where the MAGIC HAPPENS! This sets where the faces need to be drawn, this is so confusing I DREW A PICTURE! (Not sure if tis 100% accurate) This picture shows where the values line up to, seeing as we are only drawing the top face, it is the one shown.Code:triangles.Add(vertexIndex);triangles.Add(vertexIndex 1);triangles.Add(vertexIndex 2);triangles.Add(vertexIndex 2);triangles.Add(vertexIndex 3);triangles.Add(vertexIndex);Now this code will probably confuse you, you go "There are only 2 triangles in a face!", but you see the variable is a int and each triangle has 3 verticies thus the addition is done 6 times.6/3 = 2. 2 Triangles .The reason we add vertexIndex is because it determines where in the 100 blocks across the x and z axis, the triangle needs to be.The first 3 determine the first triangle and the second 3 the second respectively.Code:uvs.Add(Vector2 (0, 0));uvs.Add(Vector2 (0, 1));uvs.Add(Vector2 (1, 1));uvs.Add(Vector2 (1, 0));Completely to do with the textures, it outlines the four verticies(as a square has 4 corners) in which the texture needs to be drawn too. Here is another picture  Basically it will stretch the texture to fit those points, the (0,0) of the texture will slot into the (0,0) of the face.What you need to do now?Before we continue, you need to figure out the rest of the sides, using your own knowledge or the source. This is ESSENTIAL before we continue!Hopefully, that helps you visualise the rest of the sides, I have given you 1 out of 6, try and figure out the rest of them (bottom, south,north,east,west) .Continuing on......We are on the home stretch.All that really needs to be done now is to re-create the mesh as we have a TON of values...... but we have not done anything with them, the next code does this.Code:var mesh : Mesh = GetComponent(MeshFilter).mesh;mesh.vertices = vertices.ToArray();mesh.triangles = triangles.ToArray();mesh.uv = uvs.ToArray();mesh.RecalculateNormals();This script applies all of our new values to the new mesh. RecalculateNormals makes sure the values are applied and that they are smooth.Why ToArray()?This is beacuse the uv, triangles AND verticies of a mesh are in array format.Code:GetComponent(MeshCollider).sharedMesh = null;GetComponent(MeshCollider).sharedMesh = mesh;}sharedMesh is the physics collider of the mesh, we first set it to null, then reset it by making it equal to the new mesh that uses our values.Finished....... AlmostI say almost because most of you will not marvel at the awesomeness of the script, but see it as useless because you can't dig...... That's why I am writing up a bonus part right now using my own code NOT the sources, as my script suports multiple chunks.I shall post it now, and explain later This is in a seperate script attached to the camera. The camera ALSO needs a line renderer attached to it (Found in "Components/Effects")Code:#pragma strictprivate var lineRenderer : LineRenderer;lineRenderer = Camera.main.transform.GetComponent(LineRenderer);var chunkScr : ChunkRender;function Update () {if (Input.GetMouseButtonUp(0)) {lineRenderer.SetPosition(0, Camera.main.transform.position);lineRenderer.SetPosition(1, Camera.main.transform.position);}if (Input.GetMouseButton(0)){var ray:Ray = Camera.main.ScreenPointToRay(Input.mousePosition);var hit:RaycastHit;if (Physics.Raycast(ray,hit, 100)){var hx:int = hit.point.x - hit.transform.position.x;var hy:int = hit.point.y-0.2 - hit.transform.position.y; var hz:int = hit.point.z - hit.transform.position.z;lineRenderer.SetPosition(0, Camera.main.transform.position+Vector3(0,-1,0));lineRenderer.SetPosition(1, hit.point);chunkScr = hit.transform.GetComponent(ChunkRender);chunkScr.chunk[hx,hy,hz] = 0;chunkScr.ChunkRender(chunkScr.chunk);}}}End of chapter 1! If you guys liked this chapter, please leave a comment asking for more Hell if I get enough comments I may even make a video series.But before you move onto the next chapter, I challenge you to:Create a different block size for your chunksMake several different types of blocks that spawnMake placeable blocks (Hard one!)Play around with it and try to understand even more!Also I do not like to nag but Please sign up to this forum!It will help you keep in touch with us and all that is going on, you can also send me a personal message and I will help you.But most of all you can follow our voxel game Civil!Last edited by landon912 on Tue Oct 16, 2012 2:35 pm; edited 1 time in total (Reason for editing : Fixed 2 errors :))    4 - Chunk by Chunk (Part 2) :: Comments Re: 4 - Chunk by Chunk (Part 2) on Tue Oct 16, 2012 2:30 pm by landon912 Nice long post, fixed some spelling errors for u  Re: 4 - Chunk by Chunk (Part 2) on Fri Nov 23, 2012 4:15 am by ForgottenCheese I'm getting the following errors in Unity, when I try to copy and paste the camera script."Assets/mChunkRender/Scripts/Cam.js(32,42): BCE0120: 'ChunkRender.chunk' is inaccessible due to its protection level.""Assets/mChunkRender/Scripts/Cam.js(33,63): BCE0120: 'ChunkRender.chunk' is inaccessible due to its protection level."These two lines are the issue:chunkScr.chunk[hx,hy,hz] = 0; chunkScr.ChunkRender(chunkScr.chunk); Re: 4 - Chunk by Chunk (Part 2) on Fri Nov 23, 2012 12:21 pm by Myhijim Myhijim wrote:Change Code:private var chunk:int[,,] = new int[s,s,s];To Code:var chunk:int[,,] = new int[s,s,s]; Re: 4 - Chunk by Chunk (Part 2) on Sat Nov 24, 2012 12:02 am by ForgottenCheese Thanks for the quick reply, although I don't see what the camera script actually does?Also I've been able to change the size of the voxels in the chunk but they overlap each other, any pointers? Re: 4 - Chunk by Chunk (Part 2) on Sat Nov 24, 2012 12:04 am by Myhijim Yes I also reduced my block size, It gets slightly complex.Uhhh the camera script actually allows digging. However I have made a better one since.I have to go now but I will help you out soon, as I made my voxels 0.5 of the size.Basically you need to times all the values in the Vert addition by whatever size block you want (0.5 in my case).I have another script coming soon. Look at this:http://forum.unity3d.com/threads/158783-Civil-A-3D-Voxel-World Re: 4 - Chunk by Chunk (Part 2) on Sat Nov 24, 2012 12:54 am by ForgottenCheese Myhijim wrote:Yes I also reduced my block size, It gets slightly complex.Uhhh the camera script actually allows digging. However I have made a better one since.I have to go now but I will help you out soon, as I made my voxels 0.5 of the size.Basically you need to times all the values in the Vert addition by whatever size block you want (0.5 in my case).I have another script coming soon. Look at this:Yeah I did the same thing with the verts, except I doubled all of them. They're always all the same same so putting in a var would be better. I'm gonna try experimenting with that a bit.As for the cam script, it was already possible to delete voxels.Anyway read your post on the Unity forums, and it's kinda freaky because my name is James, and I'm 15 years old. Re: 4 - Chunk by Chunk (Part 2) on Sat Nov 24, 2012 7:59 am by Myhijim Haha no way, where are you from?How long you been coding? Re: 4 - Chunk by Chunk (Part 2) on Mon Nov 26, 2012 1:30 am by ForgottenCheese I'm from Philadelphia, been coding for like 4-6 months. How about you? Re: 4 - Chunk by Chunk (Part 2) on Mon Nov 26, 2012 10:12 pm by Myhijim I've been "properly" coding for about 7 months, I used to use game maker though haha.I'll put up a new tut in a few days.
 Re: 4 - Chunk by Chunk (Part 2) by Sponsored content

4 - Chunk by Chunk (Part 2) Page 1 of 1

Permissions in this forum:You cannot reply to topics in this forum Jump to: Select a forum||--Community|   |--News|   |--Unity3D Tutorials|   |--About/Help|   |--Off Topic|   |--Our Friends|   |--RezoneD Game|   |--Development Blog|   |--About|   |--Development Build|   |--Versions|   |--Game Discussion|   |--Wish List|   |--Game Help|   |--Project Civil    |--About    |--Voxel Engine Development Blog