Still in need of a RIGGER!
 
Back to the forumHomeFAQMemberlistUsergroupsRegisterLog inSearch

Share | 
 

 4 - Chunk by Chunk (Part 2)

View previous topic View next topic Go down 
AuthorMessage
Myhijim
CoreDev CEO
CoreDev CEO


Posts : 66
Join date : 2012-09-07
Location : Sydney, Australia

20121016
Post4 - 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 decription

Now 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 exposed
if (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 top
triangles.Add(vertexIndex);
triangles.Add(vertexIndex 1);
triangles.Add(vertexIndex 2);
// second triangle for the block top
triangles.Add(vertexIndex 2);
triangles.Add(vertexIndex 3);
triangles.Add(vertexIndex);
// add UV
uvs.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 top
triangles.Add(vertexIndex);
triangles.Add(vertexIndex 1);
triangles.Add(vertexIndex 2);
// second triangle for the block top
triangles.Add(vertexIndex 2);
triangles.Add(vertexIndex 3);
triangles.Add(vertexIndex);
// add UV
uvs.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 top
triangles.Add(vertexIndex);
triangles.Add(vertexIndex 1);
triangles.Add(vertexIndex 2);
// second triangle for the block top
triangles.Add(vertexIndex 2);
triangles.Add(vertexIndex 3);
triangles.Add(vertexIndex);
// add UV
uvs.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 top
triangles.Add(vertexIndex);
triangles.Add(vertexIndex 1);
triangles.Add(vertexIndex 2);
// second triangle for the block top
triangles.Add(vertexIndex 2);
triangles.Add(vertexIndex 3);
triangles.Add(vertexIndex);
// add UV
uvs.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 top
triangles.Add(vertexIndex);
triangles.Add(vertexIndex 1);
triangles.Add(vertexIndex 2);
// second triangle for the block top
triangles.Add(vertexIndex 2);
triangles.Add(vertexIndex 3);
triangles.Add(vertexIndex);
// add UV
uvs.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 top
triangles.Add(vertexIndex);
triangles.Add(vertexIndex 1);
triangles.Add(vertexIndex 2);
// second triangle for the block top
triangles.Add(vertexIndex 2);
triangles.Add(vertexIndex 3);
triangles.Add(vertexIndex);
// add UV
uvs.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 triangles

Code:
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 axis


Next 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 Very Happy


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....... Almost

I 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 king

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 strict

private 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!cheers

If you guys liked this chapter, please leave a comment asking for more Smile 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 chunks
  • Make several different types of blocks that spawn
  • Make 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 :))
Back to top Go down
View user profile http://coredev.makingforum.com
Share this post on: Excite BookmarksDiggRedditDel.icio.usGoogleLiveSlashdotNetscapeTechnoratiStumbleUponNewsvineFurlYahooSmarking

4 - Chunk by Chunk (Part 2) :: Comments

Re: 4 - Chunk by Chunk (Part 2)
Post on Tue Oct 16, 2012 2:30 pm by landon912
Nice long post, fixed some spelling errors for u Smile
Re: 4 - Chunk by Chunk (Part 2)
Post 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)
Post 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)
Post 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)
Post 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)
Post 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)
Post 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)
Post 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)
Post 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)
Post  by Sponsored content
 

4 - Chunk by Chunk (Part 2)

View previous topic View next topic Back to top 

Page 1 of 1

Permissions in this forum:You cannot reply to topics in this forum
 :: Project Civil :: Voxel Engine Development Blog-
Jump to: