Chunk CreateChunk(Chunk chunk)
        {
            #if UNITY_EDITOR
            if (chunk == null) { Debug.LogError("Chunk must not be null!"); }
            #endif
            tempChunk = chunk;
            tempVoxelPosRel.Reset();

            // normal chunk data
            // TODO can be optimized?
            // TODO different standard landscapes
            int cx = chunk.pos.x * chunkSize;
            int cy = chunk.pos.y * chunkSize;
            int cz = chunk.pos.z * chunkSize;
            //int noAirVoxelCount = 0;
            for (int i = 0, y = -1; y < chunkSizeA1; ++y) {
                for (int z = -1; z < chunkSizeA1; ++z) {
                    for (int x = -1; x < chunkSizeA1; ++x, ++i) {
                        tempVoxels[i] = GetStandardVoxel(x + cx, y + cy, z + cz);
                        //			if (!tempVoxels[i].isAir) noAirVoxelCount++;
                    }
                }
            }

            // load additional voxels data
            for (int i = 0; i < chunk.additionalVoxelsDataNum; ++i) {
                int vi = chunk.additionalVoxelsDataPosIndices[i];
                //	if (!tempVoxels[vi].isAir) noAirVoxelCount--;
                tempVoxels[vi] = chunk.additionalVoxelsDataBoxIndices[i];
                //	if (!tempVoxels[vi].isAir) noAirVoxelCount++;
            }

            /*
            if (noAirVoxelCount == 0)
                return chunk;
            */

            // build chunk
            if (!chunk.built) {
                chunk.m = new Mesh();
                chunk.go = new GameObject("chunk " + chunk.pos);
                chunk.go.transform.parent = chunkParent;
                chunk.go.transform.position = chunk.pos.ToVector() * chunkSize;
                chunk.mf = chunk.go.AddComponent<MeshFilter>();
                chunk.go.AddComponent<MeshRenderer>().material = material;
                chunk.mc = chunk.go.AddComponent<MeshCollider>();
            }

            if (!chunk.built && randomSpawnObjects != null && randomSpawnObjects.Length > 0) {
                for (int y = 0; y < chunkSize; ++y) {
                    for (int z = 0; z < chunkSize; ++z) {
                        for (int x = 0; x < chunkSize; ++x) {
                            if (Random.value > 0.998f && tempVoxels[GetVoxelRelPosIndex(x, y, z)].isAir) {
                                GameObject go = (GameObject)Instantiate(randomSpawnObjects[Random.Range(0, randomSpawnObjects.Length)], new Vector3(x + cx + 0.5f, y + cy + 0.5f, z + cz + 0.5f), Random.rotationUniform);
                                go.transform.parent = chunk.go.transform;
                            }
                        }
                    }
                }
            }

            // create chunk mesh data
            TempMeshData tmd = new TempMeshData(chunkSizeA2P2 * 2);
            int vc = 0; // current vertex count
            int start = (chunkSize + 3) * chunkSizeA2 + 1;
            int index = start;
            for (tempVoxelPosRel.y = 0; tempVoxelPosRel.y < chunkSize; ++tempVoxelPosRel.y, index += chunkSizeA2M2) {
                for (tempVoxelPosRel.z = 0; tempVoxelPosRel.z < chunkSize; ++tempVoxelPosRel.z, index+= 2) {
                    for (tempVoxelPosRel.x = 0; tempVoxelPosRel.x < chunkSize; ++tempVoxelPosRel.x, ++index) {
                        if (!tempVoxels[index].isAir) {
                            tempVoxels[index].Build(tmd, index, ref vc);
                        }
                    }
                }
            }

            // mesh creation
            chunk.m.Clear();
            tmd.Assign(chunk.m);
            chunk.m.Optimize();
            chunk.mf.sharedMesh = chunk.m;
            chunk.mc.sharedMesh = null;
            chunk.mc.sharedMesh = chunk.m;
            chunk.built = true;

            System.GC.Collect();

            return chunk;
        }
 // CREATE OR CHANGE A VOXEL
 void ChangeVoxel(Position3 posAbs, Voxel voxel, bool updateChunk = true, int padX = 0, int padY = 0, int padZ = 0)
 {
     #if UNITY_EDITOR
     if (padX < -1 || padX > 1) { Debug.LogError("Padding (X) too big!"); }
     if (padY < -1 || padY > 1) { Debug.LogError("Padding (Y) too big!"); }
     if (padZ < -1 || padZ > 1) { Debug.LogError("Padding (Z) too big!"); }
     #endif
     Position3 chunkPos = new Position3(
         (posAbs.x < 0 ? (posAbs.x - chunkSize + 1) : posAbs.x) / chunkSize - padX, // TODO: >> instead of /?
         (posAbs.y < 0 ? (posAbs.y - chunkSize + 1) : posAbs.y) / chunkSize - padY, // TODO: >> instead of /?
         (posAbs.z < 0 ? (posAbs.z - chunkSize + 1) : posAbs.z) / chunkSize - padZ // TODO: >> instead of /?
     );
     Chunk curChunk;
     if (!chunks.TryGetValue(chunkPos, out curChunk)) {
         curChunk = new Chunk(chunkPos);
     }
     Position3 posRel = posAbs - (chunkPos * chunkSize);
     int posRelIndex = GetVoxelRelPosIndex(posRel);
     List<int> dataPosInd = new List<int>(); // using List<>s for edit mode only?
     List<Voxel> dataVoxel = new List<Voxel>();
     if (curChunk.additionalVoxelsDataNum != 0) { // get the already created add voxel data
         dataPosInd.AddRange(curChunk.additionalVoxelsDataPosIndices);
         dataVoxel.AddRange(curChunk.additionalVoxelsDataBoxIndices);
     }
     if (dataPosInd.Contains(posRelIndex)) { // box was changed already
         Voxel sb = GetStandardVoxel(posAbs.x, posAbs.y, posAbs.z);
         int dpi = dataPosInd.IndexOf(posRelIndex);
         if (voxel == sb) { // is standard box? -> remove this add data
             dataPosInd.RemoveAt(dpi);
             dataVoxel.RemoveAt(dpi);
             curChunk.additionalVoxelsDataNum--;
         }
         else { // is new box type? -> replace it
             dataVoxel[dpi] = voxel;
         }
     }
     else { // voxel was never changed before? -> add it
         dataPosInd.Add(posRelIndex);
         dataVoxel.Add(voxel);
         curChunk.additionalVoxelsDataNum++;
     }
     curChunk.additionalVoxelsDataPosIndices = dataPosInd.ToArray();
     curChunk.additionalVoxelsDataBoxIndices = dataVoxel.ToArray();
     // somewhere at the limits?
     if (padY == 0 && padZ == 0 && posRel.x == 0) ChangeVoxel(posAbs, voxel, updateChunk, 1);
     else if (padY == 0 && padZ == 0 && posRel.x == chunkSize - 1) ChangeVoxel(posAbs, voxel, updateChunk, -1);
     if (padX == 0 && padZ == 0 && posRel.y == 0) ChangeVoxel(posAbs, voxel, updateChunk, 0, 1);
     else if (padX == 0 && padZ == 0 && posRel.y == chunkSize - 1) ChangeVoxel(posAbs, voxel, updateChunk, 0, -1);
     if (padX == 0 && padY == 0 && posRel.z == 0) ChangeVoxel(posAbs, voxel, updateChunk, 0, 0, 1);
     else if (padX == 0 && padY == 0 && posRel.z == chunkSize - 1) ChangeVoxel(posAbs, voxel, updateChunk, 0, 0, -1);
     // update chunk
     if (updateChunk) {
         CreateChunk(curChunk);
     }
     else {
         tempChunksToGetUpdated.Add(curChunk);
     }
 }
 Chunk CreateChunk(Position3 pos)
 {
     Chunk chunk;
     chunks.TryGetValue(pos, out chunk);
     if (chunk == null) {
         chunk = new Chunk(pos);
     }
     return CreateChunk(chunk);
 }