Chunk Class for Unity Voxel Engine (Example)

Here’s an example of a Chunk class i use in some variants of the engine. This one is a bit experimental but working OK so far.

chunk

 

using UnityEngine;
using System.Collections.Generic;

public enum ChunkState:int {
    Invalid = 0,
    Base = 1,
    Terra = 10,
    ReGen = 11,
    Light = 15,
    Post_Light = 16,
    Mesh = 20,
    Render = 30   

}
public class LightSource {

    public Lux light;
    public int x;
    public int y;
    public int z;
}

public class Chunk {

    //render mesh data
    public List<int> triangles;
    public List<int>[] subtriangles;
    public List<Vector2> uvs;
    public List<Vector3> vertices;
    public List<Color> colours;
    public List<Vector3> normals;
    public List<Vector4> tangents;
    public List<Lux> lightsources;
    public List<IntVect> sunlitBlocks;

    //collision mesh data
    public List<int> col_triangles;
    public List<Vector3> col_vertices;



    //chunk world position
    public int x;
    public int y;
    public int z;

    public GameObject chunkObject;
    public ChunkState state;

 

    public bool AboveReady(ChunkState s) {
      
        if (y == World.yChunkRadius - 1) { return true; }

        int ts = (int)s;
        int cs = (int)World.GetChunkState(x, y + 1, z);

        if (cs == 0) { return true; }
        if (cs >= ts) { return true; }
        return false;
    }

    public bool SurroundingState(ChunkState cstate)
    {
        ChunkState north = World.GetChunkState(x, y, z + 1);
        if ((int)north < (int)cstate && north != ChunkState.Invalid) { return false; }

        ChunkState south = World.GetChunkState(x, y, z - 1);
        if ((int)south < (int)cstate && south != ChunkState.Invalid) { return false; }

        ChunkState east = World.GetChunkState(x + 1, y, z);
        if ((int)east < (int)cstate && east != ChunkState.Invalid) { return false; }

        ChunkState west = World.GetChunkState(x - 1, y, z);
        if ((int)west < (int)cstate && west != ChunkState.Invalid) { return false; }
        if (!AboveReady(cstate)) { return false; }

        /*
        ChunkState stack;
        for (int i = 0; i < World.yChunkRadius; i++)
        {
            stack = World.GetChunkState(x, i, z);
            if ((int)stack < (int)cstate && stack != ChunkState.Invalid) { return false; }
        }
        */


        return true;
    }

    public void Init(int _x, int _y, int _z)
    {
        x = _x; y = _y; z = _z;
        state = ChunkState.Base;

    }
    public void ReBuild() {
        sunlitBlocks = new List<IntVect>();
        col_vertices = new List<Vector3>();
        col_triangles = new List<int>();
        vertices = new List<Vector3>();
        triangles = new List<int>();
        uvs = new List<Vector2>();
        colours = new List<Color>();
        normals = new List<Vector3>();
        tangents = new List<Vector4>();

        subtriangles = new List<int>[Mats.matCount];
        for (int n = 0; n < Mats.matCount; n++) { subtriangles[n] = new List<int>(); }

    }





}

Creating a grid mesh programmatically in Unity 3D

using System;
using System.Collections.Generic;
using UnityEngine;

public static class Grid
{

    public static void Create(bool withCollision)
    {
        GameObject gridObject = new GameObject();
        gridObject.name = "Grid";
        gridObject.transform.position = Vector3.zero;

        List<int> triangles = new List<int>();
        List<Vector3> vertices = new List<Vector3>();
        List<Vector2> uvs = new List<Vector2>();

        List<int> collision_triangles = new List<int>();
        List<Vector3> collision_vertices = new List<Vector3>();

        int vertexIndex = 0;
        int collisionVertexIndex = 0;

        int count = 64; // n+1 grid lines (n = even number)
        int n = count / 2; // halve count for +/- iteration

        float w = 0.05f; //line width
        float s = 1.0f; //width of space

        Vector3 v1;
        Vector3 v2;
        Vector3 v3;
        Vector3 v4;


        //Collision mesh

        if (withCollision)
        {
            v1 = new Vector3(-n, 0, -n);
            v2 = new Vector3(-n, 0, n);
            v3 = new Vector3(n, 0, n);
            v4 = new Vector3(n, 0, -n);

            collision_vertices.Add(v1);
            collision_vertices.Add(v2);
            collision_vertices.Add(v3);
            collision_vertices.Add(v4);

            collision_triangles.Add(collisionVertexIndex);
            collision_triangles.Add((collisionVertexIndex + 1));
            collision_triangles.Add((collisionVertexIndex + 2));
            collision_triangles.Add((collisionVertexIndex + 2));
            collision_triangles.Add((collisionVertexIndex + 3));
            collision_triangles.Add(collisionVertexIndex);

        }


        //Grid mesh
        for (int x = -n; x < n + 1; x++)
        {

            v1 = new Vector3((x * s), 0, -n);
            v2 = new Vector3((x * s), 0, n);
            v3 = new Vector3((x * s) + w, 0, n);
            v4 = new Vector3((x * s) + w, 0, -n);

            vertexIndex = vertices.Count;

            vertices.Add(v1);
            vertices.Add(v2);
            vertices.Add(v3);
            vertices.Add(v4);

            triangles.Add(vertexIndex);
            triangles.Add((vertexIndex + 1));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 3));
            triangles.Add(vertexIndex);

            uvs.AddRange(Mats.Generic);

            //back face
            vertexIndex = vertices.Count;

            vertices.Add(v4);
            vertices.Add(v3);
            vertices.Add(v2);
            vertices.Add(v1);

            triangles.Add(vertexIndex);
            triangles.Add((vertexIndex + 1));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 3));
            triangles.Add(vertexIndex);

            uvs.AddRange(Mats.Generic);

        }


        for (int z = -n; z < n + 1; z++)
        {

            v1 = new Vector3(-n, 0, (z * s));
            v2 = new Vector3(n, 0, (z * s));
            v3 = new Vector3(n, 0, (z * s) + w);
            v4 = new Vector3(-n, 0, (z * s) + w);

            vertexIndex = vertices.Count;

            vertices.Add(v1);
            vertices.Add(v2);
            vertices.Add(v3);
            vertices.Add(v4);

            triangles.Add(vertexIndex);
            triangles.Add((vertexIndex + 1));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 3));
            triangles.Add(vertexIndex);

            uvs.AddRange(Mats.Generic);

            //back face
            vertexIndex = vertices.Count;

            vertices.Add(v4);
            vertices.Add(v3);
            vertices.Add(v2);
            vertices.Add(v1);

            triangles.Add(vertexIndex);
            triangles.Add((vertexIndex + 1));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 2));
            triangles.Add((vertexIndex + 3));
            triangles.Add(vertexIndex);

            uvs.AddRange(Mats.Generic);

        }

        Mesh mesh = new Mesh();
        mesh.vertices = vertices.ToArray();
        mesh.triangles = triangles.ToArray();
        mesh.uv = uvs.ToArray();

        mesh.RecalculateNormals();
        mesh.Optimize();

        MeshFilter meshFilter = gridObject.AddComponent<MeshFilter>();
        MeshRenderer meshRenderer = gridObject.AddComponent<MeshRenderer>();

        if (withCollision)
        {
            MeshCollider meshCollider = gridObject.AddComponent<MeshCollider>();
            Mesh collision_mesh = new Mesh();
            collision_mesh.vertices = collision_vertices.ToArray();
            collision_mesh.triangles = collision_triangles.ToArray();
            meshCollider.sharedMesh = collision_mesh;
        }

        meshRenderer.material = Mats.Grid();
        meshFilter.sharedMesh = mesh;

    }

}

Accessing strings in c++ from c#

Scenario; You may have some c++ code you wish to output string data to be used in a c# application.

#include <stdio.h>
#include <string.h>

extern "C"
{
	__declspec(dllexport) int GetString(char* buf,int n)
	{
		strncpy(buf,"my string in cpp", n);
		return 0;
	}
}
using System.Runtime.InteropServices;

[DllImport("mycppdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int GetString(StringBuilder buffer, int capacity);

void csharp(){
StringBuilder sb = new StringBuilder(1000);
GetString(sb, sb.Capacity);
string mycppstring = sb.toString();
}

SQL to JSON with C#

This seems to work quite well. If there is a more efficient way please let me know.

string SQLtoJSON(string _sqlCon, string _sqlQuery) 
{
 using (SqlConnection con = new SqlConnection(_sqlCon))
 {
 using (SqlCommand cmd = new SqlCommand(_sqlQuery, con))
 {
 using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
 {

 DataTable sqlData = new DataTable();
 adapter.Fill(sqlData); 

System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
 List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>(); 
Dictionary<string, object> row;

 foreach (DataRow dr in sqlData.Rows) 
{ 
row = new Dictionary<string, object>();
 foreach (DataColumn col in sqlData.Columns)
 {
 row.Add(col.ColumnName, dr[col]);
 } 
rows.Add(row); 
} 
return serializer.Serialize(rows); } } } } 

void SendJSON(string json) {
 Response.Clear();
 Response.ContentType = "application/json; charset=utf-8"; 
Response.Write(json); 
Response.End();
 }

 

Mesh Optimization

Below is my custom version of a greedy mesh optimizer for voxel data in Unity3d translated from JavaScript to c# using the original awesome mind bending code of Mikola Lysenko.

meshopt-1

This is useful if you need to use mesh for collision data and it can drastically improve speed when building/rebuilding game objects. I’ve added a method of excluding some data from generating mesh which is great for blocks which the player should fall through.

Don’t forget to include the original copyright if you use it in your project or share it. Feel free to also credit this site if you wish.

It’s worth noting that there is a kind of “bug” in Mikola’s code which is possibly only apparent when used with Unity3d/c# which returns incorrect normals on 3/6 sides of the cube. This is corrected in the c# below.

If you have a more efficient mesh optimizer you’d like to share. please do! dev@nuzly.com

c#

// The MIT License (MIT)
//
// Copyright (c) 2012-2013 Mikola Lysenko
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

 public static Mesh ReduceMesh(Chunk chunk)
    {
        List vertices = new List();
        List elements = new List();
        List uvs = new List();
        List colours = new List();

        List noCollision = World.noCollision;

        int size = World.CHUNK_SIZE;

        //Sweep over 3-axes
        for (int d = 0; d < 3; d++)
        {

            int i, j, k, l, w, h, u = (d + 1) % 3, v = (d + 2) % 3;

            int[] x = new int[3];
            int[] q = new int[3];
            int[] mask = new int[(size + 1) * (size + 1)];

            q[d] = 1;

            for (x[d] = -1; x[d] < size; )
            {

                // Compute the mask
                int n = 0;
                for (x[v] = 0; x[v] < size; ++x[v])
                {
                    for (x[u] = 0; x[u] < size; ++x[u], ++n)
                    {
              

                        int a = 0; if (0 <= x[d]) { a = (int)World.GetBlock(chunk, x[0], x[1], x[2]).Type; if (noCollision.IndexOf(a)!=-1) { a = 0; } }
                        int b = 0; if (x[d] < size - 1) { b = (int)World.GetBlock(chunk, x[0] + q[0], x[1] + q[1], x[2] + q[2]).Type; if (noCollision.IndexOf(b) != -1) { b = 0; } }                             if (a != -1 && b != -1 && a == b) { mask[n] = 0; }                             else if (a > 0)
                            {
                                a = 1;
                                mask[n] = a;
                            }

                            else
                            {
                                b = 1;
                                mask[n] = -b;
                            }

                        }

                   
                }

                // Increment x[d]
                ++x[d];

                // Generate mesh for mask using lexicographic ordering
                n = 0;
                for (j = 0; j < size; ++j)
                {
                    for (i = 0; i < size; )                     {                         var c = mask[n];                         if (c > -3)
                        {
                            // Compute width
                            for (w = 1; c == mask[n + w] && i + w < size; ++w) { }

                            // Compute height
                            bool done = false;
                            for (h = 1; j + h < size; ++h)
                            {
                                for (k = 0; k < w; ++k)                                 {                                     if (c != mask[n + k + h * size])                                     {                                         done = true;                                         break;                                     }                                 }                                 if (done) break;                             }                             // Add quad                             bool flip = false;                             x[u] = i;                             x[v] = j;                             int[] du = new int[3];                             int[] dv = new int[3];                             if (c > -1)
                            {
                                du[u] = w;
                                dv[v] = h;
                            }
                            else
                            {
                                flip = true;
                                c = -c;
                                du[u] = w;
                                dv[v] = h;
                            }


                            Vector3 v1 = new Vector3(x[0], x[1], x[2]);
                            Vector3 v2 = new Vector3(x[0] + du[0], x[1] + du[1], x[2] + du[2]);
                            Vector3 v3 = new Vector3(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]);
                            Vector3 v4 = new Vector3(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]);

                            if (c > 0 && !flip)
                            {
                                AddFace(v1, v2, v3, v4, vertices, elements, 0);
                            }

                            if (flip)
                            {
                                AddFace(v4, v3, v2, v1, vertices, elements, 0);
                            }

                            // Zero-out mask
                            for (l = 0; l < h; ++l)
                                for (k = 0; k < w; ++k)
                                {
                                    mask[n + k + l * size] = 0;
                                }

                            // Increment counters and continue
                            i += w; n += w;
                        }

                        else
                        {
                            ++i;
                            ++n;
                        }
                    }
                }
            }
        }

        Mesh mesh = new Mesh();
        mesh.Clear();
        mesh.vertices = vertices.ToArray();
        mesh.triangles = elements.ToArray();
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
     

        return mesh;

    }
    private static void AddFace(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, List vertices, List elements, int order)
    {
        if (order == 0)
        {
            int index = vertices.Count;

            vertices.Add(v1);
            vertices.Add(v2);
            vertices.Add(v3);
            vertices.Add(v4);

            elements.Add(index);
            elements.Add(index + 1);
            elements.Add(index + 2);
            elements.Add(index + 2);
            elements.Add(index + 3);
            elements.Add(index);

        }

        if (order == 1)
        {
            int index = vertices.Count;

            vertices.Add(v1);
            vertices.Add(v2);
            vertices.Add(v3);
            vertices.Add(v4);

            elements.Add(index);
            elements.Add(index + 3);
            elements.Add(index + 2);
            elements.Add(index + 2);
            elements.Add(index + 1);
            elements.Add(index);

        }

JavaScript

// The MIT License (MIT)
//
// Copyright (c) 2012-2013 Mikola Lysenko
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

function GreedyMesh(volume, dims) {
  function f(i,j,k) {
    return volume[i + dims[0] * (j + dims[1] * k)];
  }
  //Sweep over 3-axes
  var quads = [];
  for(var d=0; d<3; ++d) {
    var i, j, k, l, w, h
      , u = (d+1)%3
      , v = (d+2)%3
      , x = [0,0,0]
      , q = [0,0,0]
      , mask = new Int32Array(dims[u] * dims[v]);
    q[d] = 1;
    for(x[d]=-1; x[d]<dims[d]; ) {
      //Compute mask
      var n = 0;
      for(x[v]=0; x[v]<dims[v]; ++x[v])
      for(x[u]=0; x[u]<dims[u]; ++x[u]) {
        mask[n++] =
          (0    <= x[d]      ? f(x[0],      x[1],      x[2])      : false) !=
          (x[d] <  dims[d]-1 ? f(x[0]+q[0], x[1]+q[1], x[2]+q[2]) : false);
      }
      //Increment x[d]
      ++x[d];
      //Generate mesh for mask using lexicographic ordering
      n = 0;
      for(j=0; j<dims[v]; ++j)
      for(i=0; i<dims[u]; ) {
        if(mask[n]) {
          //Compute width
          for(w=1; mask[n+w] && i+w<dims[u]; ++w) {
          }
          //Compute height (this is slightly awkward
          var done = false;
          for(h=1; j+h<dims[v]; ++h) {
            for(k=0; k<w; ++k) {
              if(!mask[n+k+h*dims[u]]) {
                done = true;
                break;
              }
            }
            if(done) {
              break;
            }
          }
          //Add quad
          x[u] = i;  x[v] = j;
          var du = [0,0,0]; du[u] = w;
          var dv = [0,0,0]; dv[v] = h;
          quads.push([
              [x[0],             x[1],             x[2]            ]
            , [x[0]+du[0],       x[1]+du[1],       x[2]+du[2]      ]
            , [x[0]+du[0]+dv[0], x[1]+du[1]+dv[1], x[2]+du[2]+dv[2]]
            , [x[0]      +dv[0], x[1]      +dv[1], x[2]      +dv[2]]
          ]);
          //Zero-out mask
          for(l=0; l<h; ++l)
          for(k=0; k<w; ++k) {
            mask[n+k+l*dims[u]] = false;
          }
          //Increment counters and continue
          i += w; n += w;
        } else {
          ++i;    ++n;
        }
      }
    }
  }
  return quads;
}