public void ManageSeeThrough()
        {
            VoxelSetHidden(occludedIndices, occludedIndicesCount, false);
            Camera cam = currentCamera;

            if (cam == null)
            {
                return;
            }
            Vector3 camPos = cam.transform.position;

            if (seeThroughTarget == null)
            {
                if (characterController != null)
                {
                    seeThroughTarget = characterController.gameObject;
                }
                if (seeThroughTarget == null)
                {
                    return;
                }
            }
            Vector3 targetPos = seeThroughTarget.transform.position;

            if (occludedIndices == null || occludedIndices.Length == 0)
            {
                occludedIndices = new VoxelIndex[256];
            }
            if (occludedChunks == null || occludedChunks.Length == 0)
            {
                occludedChunks = new VoxelChunk[20];
            }

            int chunkCount = LineCast(targetPos, camPos, occludedChunks);

            // Add surrounding chunks
            int flag = Time.frameCount;

            for (int k = 0; k < chunkCount; k++)
            {
                VoxelChunk chunk = occludedChunks [k];
                chunk.tempFlag = flag;
            }

            int lineChunks = chunkCount;

            for (int k = 0; k < lineChunks; k++)
            {
                VoxelChunk chunk = occludedChunks [k];
                VoxelChunk n     = chunk.top;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.bottom;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.left;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.right;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.forward;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.back;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
            }

            float   distToTarget = Vector3.Distance(targetPos, camPos);
            Vector3 cylinderAxis = (camPos - targetPos) / distToTarget;
            float   radiusSqr    = seeThroughRadius * seeThroughRadius;
            float   minY         = targetPos.y + seeThroughHeightOffset;

            occludedIndicesCount = 0;
            Vector3 voxelPosition = Misc.vector3zero;

            for (int k = 0; k < chunkCount; k++)
            {
                VoxelChunk chunk        = occludedChunks [k];
                Vector3    chunkPosBase = chunk.position;
                chunkPosBase.x = chunkPosBase.x - 8 + 0.5f;
                chunkPosBase.y = chunkPosBase.y - 8 + 0.5f;
                chunkPosBase.z = chunkPosBase.z - 8 + 0.5f;
                for (int voxelIndex = 0; voxelIndex < chunk.voxels.Length; voxelIndex++)
                {
                    if (chunk.voxels [voxelIndex].hasContent == 1)
                    {
                        int py = voxelIndex >> 8;
                        voxelPosition.y = py + chunkPosBase.y;
                        if (voxelPosition.y < minY)
                        {
                            continue;
                        }
                        int pz = (voxelIndex & 0xF0) >> 4;
                        int px = voxelIndex & 0xF;
                        voxelPosition.x = px + chunkPosBase.x;
                        voxelPosition.z = pz + chunkPosBase.z;

                        // Check if it's inside the cylinder
                        Vector3 v = voxelPosition;
                        v.x -= targetPos.x;
                        v.y -= targetPos.y;
                        v.z -= targetPos.z;
                        float cylinderDist = v.x * cylinderAxis.x + v.y * cylinderAxis.y + v.z * cylinderAxis.z;                         // Vector3.Dot (v, cylinderAxis);
                        if (cylinderDist < 0 || cylinderDist > distToTarget)
                        {
                            continue;
                        }

                        v.x -= cylinderDist * cylinderAxis.x;
                        v.y -= cylinderDist * cylinderAxis.y;
                        v.z -= cylinderDist * cylinderAxis.z;
                        float orthDistanceSqr = v.x * v.x + v.y * v.y + v.z * v.z;                         // (v - cylinderDist * cylinderAxis).sqrMagnitude;

                        if (orthDistanceSqr < radiusSqr)
                        {
                            if (occludedIndicesCount >= occludedIndices.Length)
                            {
                                occludedIndices = occludedIndices.Extend();
                            }
                            occludedIndices [occludedIndicesCount].chunk      = chunk;
                            occludedIndices [occludedIndicesCount].voxelIndex = voxelIndex;
                            occludedIndicesCount++;
                        }
                    }
                }
            }
            VoxelSetHidden(occludedIndices, occludedIndicesCount, true);
        }
        void InitClouds()
        {
            if (cloudsChunks == null)
            {
                cloudsChunks = new List <VoxelChunk> (1000);
            }
            else
            {
                cloudsChunks.Clear();
            }
            if (!enableClouds || draftModeActive || world.cloudVoxel == null)
            {
                return;
            }

            Texture2D noise = Resources.Load <Texture2D> ("VoxelPlay/Textures/Noise");

            Color32[] noises = noise.GetPixels32();

            int tw = noise.width;
            int tz = noise.height;

            Vector3 pos;

            pos.y = CLOUDS_SPECIAL_ALTITUDE;

            VoxelChunk chunk, lastChunk = null;
            int        voxelIndex;

            for (int z = 0; z < 1024; z++)
            {
                pos.z = z;
                int trow  = ((z / 4 + tz / 2) % tz) * tw;
                int trow2 = (z / 2 % tz) * tw;
                for (int x = 0; x < 1024; x++)
                {
                    pos.x = x;
                    int tindex  = trow + ((x / 4 + tw / 2) % tw);
                    int tindex2 = trow2 + (x / 2 % tw);
                    int r1      = noises [tindex].r;
                    int r2      = noises [tindex2].r;
                    int r       = (r1 + r2) / 2;
                    if (r < world.cloudCoverage)
                    {
                        Vector3 dpos = pos;
                        dpos.x -= 512;
                        dpos.z -= 512;
                        VoxelSingleSet(dpos, world.cloudVoxel, out chunk, out voxelIndex, Misc.color32White);
                        if (chunk != null && chunk != lastChunk)
                        {
                            chunk.cannotBeReused = true;
                            lastChunk            = chunk;
                            if (!cloudsChunks.Contains(chunk))
                            {
                                cloudsChunks.Add(chunk);
                            }
                        }
                    }
                    if (r < world.cloudCoverage * 19 / 20)
                    {
                        Vector3 dpos = pos;
                        dpos.x -= 512;
                        dpos.z -= 512;
                        dpos.y--;
                        VoxelSingleSet(dpos, world.cloudVoxel, out chunk, out voxelIndex, Misc.color32White);
                        if (chunk != null && chunk != lastChunk)
                        {
                            chunk.cannotBeReused = true;
                            lastChunk            = chunk;
                            if (!cloudsChunks.Contains(chunk))
                            {
                                cloudsChunks.Add(chunk);
                            }
                        }
                    }
                }
            }

            int chunksCount = cloudsChunks.Count;

            if (chunksCount == 0)
            {
                return;
            }

            if (cloudsRoot == null)
            {
                cloudsRoot           = new GameObject(CLOUDS_ROOT_NAME);
                cloudsRoot.hideFlags = HideFlags.DontSave;
                cloudsRoot.transform.hierarchyCapacity = 1000;
                cloudsRoot.transform.SetParent(worldRoot, false);
                Vector3 initialPosition = cameraMain != null ? cameraMain.transform.position : Misc.vector3zero;
                cloudsRoot.transform.position = initialPosition;
                cloudsRoot.AddComponent <VoxelCloudsAnimator> ().cloudChunks = cloudsChunks;
            }

            for (int k = 0; k < chunksCount; k++)
            {
                if (cloudsChunks [k] == null)
                {
                    continue;
                }
                ChunkRequestRefresh(cloudsChunks [k], false, true);
                Transform tc = cloudsChunks [k].transform;
                pos    = cloudsChunks [k].position;
                pos.x += 0.5f;
                pos.y  = world.cloudAltitude + 0.5f;
                pos.z += 0.5f;                 // avoid z-fight
                cloudsChunks [k].position = pos;
                tc.position = pos;
                tc.SetParent(cloudsRoot.transform, true);
            }
        }
        /// <summary>
        /// Computes light propagation. Only Sun light. Other light sources like torches are handled in the shader itself.
        /// </summary>e
        /// <returns><c>true</c>, if lightmap was built, <c>false</c> if no changes or light detected.</returns>
        /// <param name="chunk">Chunk.</param>
        void ComputeLightmap(VoxelChunk chunk)
        {
            if (!effectiveGlobalIllumination)
            {
                return;
            }

            Voxel[] voxels = chunk.voxels;
            if (voxels == null)
            {
                return;
            }

            bool isAboveSurface    = chunk.isAboveSurface;
            int  lightmapSignature = 0;            // used to detect lightmap changes that trigger mesh rebuild

            tempLightmapIndex = -1;

            // Get top chunk but only if it has been rendered at least once.
            // means that the top chunk is not available which in the case of surface will switch to the heuristic of heightmap (see else below)
            VoxelChunk topChunk            = chunk.top;
            bool       topChunkIsAccesible = topChunk && topChunk.isPopulated;

            if (topChunkIsAccesible)
            {
                for (int z = 0; z <= 15; z++)
                {
                    int bottom = z * ONE_Z_ROW;
                    int top    = bottom + 15 * ONE_Y_ROW;
                    for (int x = 0; x <= 15; x++, bottom++, top++)
                    {
                        byte light = topChunk.voxels [bottom].light;
                        lightmapSignature += light;
                        if (voxels [top].opaque < FULL_OPAQUE)
                        {
                            if (light > voxels [top].light)
                            {
                                voxels [top].light = light;
                                tempLightmapPos [++tempLightmapIndex] = top;
                            }
                        }
                    }
                }
            }
            else if (isAboveSurface)
            {
                for (int z = 0; z <= 15; z++)
                {
                    int top = 15 * ONE_Y_ROW + z * ONE_Z_ROW;
                    for (int x = 0; x <= 15; x++, top++)
                    {
                        if (voxels [top].opaque < FULL_OPAQUE)
                        {
                            byte light = (byte)15;
                            if (voxels [top].light != light)
                            {
                                voxels [top].light = light;
                                tempLightmapPos [++tempLightmapIndex] = top;
                            }
                        }
                    }
                }
            }

            // Check bottom chunk
            VoxelChunk bottomChunk            = chunk.bottom;
            bool       bottomChunkIsAccesible = bottomChunk && bottomChunk.isPopulated;

            if (bottomChunkIsAccesible)
            {
                for (int z = 0; z <= 15; z++)
                {
                    int bottom = z * ONE_Z_ROW;
                    int top    = bottom + 15 * ONE_Y_ROW;
                    for (int x = 0; x <= 15; x++, bottom++, top++)
                    {
                        byte light = bottomChunk.voxels [top].light;
                        lightmapSignature += light;
                        if (voxels [bottom].opaque < FULL_OPAQUE)
                        {
                            if (light > voxels [bottom].light)
                            {
                                voxels [bottom].light = light;
                                tempLightmapPos [++tempLightmapIndex] = bottom;
                            }
                        }
                    }
                }
            }
            else if (isAboveSurface && (waterLevel == 0 || chunk.position.y - 8 > waterLevel))
            {
                for (int z = 0; z <= 15; z++)
                {
                    int bottom = z * ONE_Z_ROW;
                    for (int x = 0; x <= 15; x++, bottom++)
                    {
                        if (voxels [bottom].opaque < FULL_OPAQUE)
                        {
                            byte light = (byte)15;
                            if (voxels [bottom].light != light)
                            {
                                voxels [bottom].light = light;
                                tempLightmapPos [++tempLightmapIndex] = bottom;
                            }
                        }
                    }
                }
            }

            // Check left face
            VoxelChunk leftChunk            = chunk.left;
            bool       leftChunkIsAccesible = leftChunk && leftChunk.isPopulated;

            if (leftChunkIsAccesible)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int left  = y * ONE_Y_ROW;
                    int right = left + 15;
                    for (int z = 0; z <= 15; z++, right += ONE_Z_ROW, left += ONE_Z_ROW)
                    {
                        byte light = leftChunk.voxels [right].light;
                        lightmapSignature += light;
                        if (voxels [left].opaque < FULL_OPAQUE)
                        {
                            if (light > voxels [left].light)
                            {
                                voxels [left].light = light;
                                tempLightmapPos [++tempLightmapIndex] = left;
                            }
                        }
                    }
                }
            }
            else if (isAboveSurface)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int left = y * ONE_Y_ROW;
                    for (int z = 0; z <= 15; z++, left += ONE_Z_ROW)
                    {
                        if (voxels [left].opaque < FULL_OPAQUE)
                        {
                            byte light = (byte)15;
                            if (voxels [left].light != light)
                            {
                                voxels [left].light = light;
                                tempLightmapPos [++tempLightmapIndex] = left;
                            }
                        }
                    }
                }
            }


            // Check right face
            VoxelChunk rightChunk            = chunk.right;
            bool       rightChunkIsAccesible = rightChunk && rightChunk.isPopulated;

            if (rightChunkIsAccesible)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int left  = y * ONE_Y_ROW;
                    int right = left + 15;
                    for (int z = 0; z <= 15; z++, left += ONE_Z_ROW, right += ONE_Z_ROW)
                    {
                        byte light = rightChunk.voxels [left].light;
                        lightmapSignature += light;
                        if (voxels [right].opaque < FULL_OPAQUE)
                        {
                            if (light > voxels [right].light)
                            {
                                voxels [right].light = light;
                                tempLightmapPos [++tempLightmapIndex] = right;
                            }
                        }
                    }
                }
            }
            else if (isAboveSurface)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int right = y * ONE_Y_ROW + 15;
                    for (int z = 0; z <= 15; z++, right += ONE_Z_ROW)
                    {
                        if (voxels [right].opaque < FULL_OPAQUE)
                        {
                            byte light = (byte)15;
                            if (voxels [right].light != light)
                            {
                                voxels [right].light = light;
                                tempLightmapPos [++tempLightmapIndex] = right;
                            }
                        }
                    }
                }
            }

            // Check forward face
            VoxelChunk forwardChunk            = chunk.forward;
            bool       forwardChunkIsAccesible = forwardChunk && forwardChunk.isPopulated;

            if (forwardChunkIsAccesible)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int back    = y * ONE_Y_ROW;
                    int forward = back + 15 * ONE_Z_ROW;
                    for (int x = 0; x <= 15; x++, back++, forward++)
                    {
                        byte light = forwardChunk.voxels [back].light;
                        lightmapSignature += light;
                        if (voxels [forward].opaque < FULL_OPAQUE)
                        {
                            if (light > voxels [forward].light)
                            {
                                voxels [forward].light = light;
                                tempLightmapPos [++tempLightmapIndex] = forward;
                            }
                        }
                    }
                }
            }
            else if (isAboveSurface)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int forward = y * ONE_Y_ROW + 15 * ONE_Z_ROW;
                    for (int x = 0; x <= 15; x++, forward++)
                    {
                        if (voxels [forward].opaque < FULL_OPAQUE)
                        {
                            byte light = (byte)15;
                            if (voxels [forward].light != light)
                            {
                                voxels [forward].light = light;
                                tempLightmapPos [++tempLightmapIndex] = forward;
                            }
                        }
                    }
                }
            }

            // Check back face
            VoxelChunk backChunk            = chunk.back;
            bool       backChunkIsAccesible = backChunk && backChunk.isPopulated;

            if (backChunkIsAccesible)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int back    = y * ONE_Y_ROW;
                    int forward = back + 15 * ONE_Z_ROW;
                    for (int x = 0; x <= 15; x++, back++, forward++)
                    {
                        byte light = backChunk.voxels [forward].light;
                        lightmapSignature += light;
                        if (voxels [back].opaque < FULL_OPAQUE)
                        {
                            if (light > voxels [back].light)
                            {
                                voxels [back].light = light;
                                tempLightmapPos [++tempLightmapIndex] = back;
                            }
                        }
                    }
                }
            }
            else if (isAboveSurface)
            {
                for (int y = 15; y >= 0; y--)
                {
                    int back = y * ONE_Y_ROW;
                    for (int x = 0; x <= 15; x++, back++)
                    {
                        if (voxels [back].opaque < FULL_OPAQUE)
                        {
                            byte light = (byte)15;
                            if (voxels [back].light != light)
                            {
                                voxels [back].light = light;
                                tempLightmapPos [++tempLightmapIndex] = back;
                            }
                        }
                    }
                }
            }

            int index = 0;
            int notIsAboveSurfaceReduction, isAboveSurfaceReduction;

            if (isAboveSurface)
            {
                isAboveSurfaceReduction    = 1;
                notIsAboveSurfaceReduction = 0;
            }
            else
            {
                isAboveSurfaceReduction    = 0;
                notIsAboveSurfaceReduction = 1;
            }

            while (index <= tempLightmapIndex)
            {
                // Pop element
                int  voxelIndex = tempLightmapPos [index];
                byte light      = voxels [voxelIndex].light;
                index++;

                if (light <= voxels [voxelIndex].opaque)
                {
                    continue;
                }
                int reducedLight = light - voxels [voxelIndex].opaque;

                // Spread light

                // down
                reducedLight -= notIsAboveSurfaceReduction;
                if (reducedLight == 0)
                {
                    continue;
                }

                int py = voxelIndex / ONE_Y_ROW;

                if (py == 0)
                {
                    if (bottomChunkIsAccesible)
                    {
                        int up = voxelIndex + 15 * ONE_Y_ROW;
                        if (bottomChunk.voxels [up].light < reducedLight)
                        {
                            bottomChunkIsAccesible = false;
                            ChunkRequestRefresh(bottomChunk, false, false);
                        }
                    }
                }
                else
                {
                    int down = voxelIndex - ONE_Y_ROW;
                    if (voxels [down].light < reducedLight && voxels [down].opaque < FULL_OPAQUE)
                    {
                        voxels [down].light       = (byte)reducedLight;
                        lightmapSignature        += reducedLight;
                        tempLightmapPos [--index] = down;
                    }
                }

                reducedLight -= isAboveSurfaceReduction;
                if (reducedLight == 0)
                {
                    continue;
                }

                int remy = voxelIndex - py * ONE_Y_ROW;
                int pz   = remy / ONE_Z_ROW;
                int px   = remy - pz * ONE_Z_ROW;

                if (chunk.position.x == 520 && chunk.position.y == 8 && chunk.position.z == -296)
                {
                    if (py == 11 && px == 11 && pz == 10)
                    {
                        int jj = 9;
                        jj++;
                    }
                }


                // backwards
                if (pz == 0)
                {
                    if (backChunkIsAccesible)
                    {
                        int forward = voxelIndex + 15 * ONE_Z_ROW;
                        if (backChunk.voxels [forward].light < reducedLight)
                        {
                            backChunkIsAccesible = false;
                            ChunkRequestRefresh(backChunk, false, false);
                        }
                    }
                }
                else
                {
                    int back = voxelIndex - ONE_Z_ROW;
                    if (voxels [back].light < reducedLight && voxels [back].opaque < FULL_OPAQUE)
                    {
                        voxels [back].light = (byte)reducedLight;
                        lightmapSignature  += reducedLight;
                        tempLightmapPos [++tempLightmapIndex] = back;
                    }
                }

                // forward
                if (pz == 15)
                {
                    if (forwardChunkIsAccesible)
                    {
                        int back = voxelIndex - 15 * ONE_Z_ROW;
                        if (forwardChunk.voxels [back].light < reducedLight)
                        {
                            forwardChunkIsAccesible = false;
                            ChunkRequestRefresh(forwardChunk, false, false);
                        }
                    }
                }
                else
                {
                    int forward = voxelIndex + ONE_Z_ROW;
                    if (voxels [forward].light < reducedLight && voxels [forward].opaque < FULL_OPAQUE)
                    {
                        voxels [forward].light = (byte)reducedLight;
                        lightmapSignature     += reducedLight;
                        tempLightmapPos [++tempLightmapIndex] = forward;
                    }
                }

                // left
                if (px == 0)
                {
                    if (leftChunkIsAccesible)
                    {
                        int right = voxelIndex + 15;
                        if (leftChunk.voxels [right].light < reducedLight)
                        {
                            leftChunkIsAccesible = false;
                            ChunkRequestRefresh(leftChunk, false, false);
                        }
                    }
                }
                else
                {
                    int left = voxelIndex - 1;
                    if (voxels [left].light < reducedLight && voxels [left].opaque < FULL_OPAQUE)
                    {
                        voxels [left].light = (byte)reducedLight;
                        lightmapSignature  += reducedLight;
                        tempLightmapPos [++tempLightmapIndex] = left;
                    }
                }

                // right
                if (px == 15)
                {
                    if (rightChunkIsAccesible)
                    {
                        int left = voxelIndex - 15;
                        if (rightChunk.voxels [left].light < reducedLight)
                        {
                            rightChunkIsAccesible = false;
                            ChunkRequestRefresh(rightChunk, false, false);
                        }
                    }
                }
                else
                {
                    int right = voxelIndex + 1;
                    if (voxels [right].light < reducedLight && voxels [right].opaque < FULL_OPAQUE)
                    {
                        voxels [right].light = (byte)reducedLight;
                        lightmapSignature   += reducedLight;
                        tempLightmapPos [++tempLightmapIndex] = right;
                    }
                }

                // up
                if (py == 15)
                {
                    if (topChunkIsAccesible)
                    {
                        int down = voxelIndex - 15 * ONE_Y_ROW;
                        if (topChunk.voxels [down].light < reducedLight)
                        {
                            topChunkIsAccesible = false;
                            ChunkRequestRefresh(topChunk, false, false);
                        }
                    }
                }
                else
                {
                    int up = voxelIndex + ONE_Y_ROW;
                    if (voxels [up].light < reducedLight && voxels [up].opaque < FULL_OPAQUE)
                    {
                        voxels [up].light  = (byte)reducedLight;
                        lightmapSignature += reducedLight;
                        tempLightmapPos [++tempLightmapIndex] = up;
                    }
                }
            }

            if (lightmapSignature != chunk.lightmapSignature)
            {
                // There're changes, so annotate this chunk mesh to be rebuilt
                chunk.lightmapSignature = lightmapSignature;
                chunk.needsMeshRebuild  = true;
            }

            chunk.lightmapIsClear = false;
        }
        bool HitVoxelFast(Vector3 origin, Vector3 direction, int damage, out VoxelHitInfo hitInfo, float maxDistance = 0, int damageRadius = 1, bool addParticles = true, bool playSound = true, bool allowDamageEvent = true)
        {
            RayCastFast(origin, direction, out hitInfo, maxDistance, false, 0, ColliderTypes.IgnorePlayer);
            VoxelChunk chunk = hitInfo.chunk;

            if (chunk == null || hitInfo.voxelIndex < 0)
            {
                lastHitInfo.chunk      = null;
                lastHitInfo.voxelIndex = -1;
                return(false);
            }

            lastHitInfo = hitInfo;
            DamageVoxelFast(ref hitInfo, damage, addParticles, playSound, allowDamageEvent);

            bool button1Pressed = input.GetButton(InputButtonNames.Button1);
            bool button2Pressed = input.GetButton(InputButtonNames.Button2);

            if ((button1Pressed || button2Pressed) && OnVoxelClick != null)
            {
                OnVoxelClick(chunk, hitInfo.voxelIndex, button1Pressed ? 0 : 1, hitInfo);
            }

            if (damageRadius > 1)
            {
                Vector3 otherPos;
                Vector3 explosionPosition = hitInfo.voxelCenter + hitInfo.normal * damageRadius;
                damageRadius--;

                for (int y = -damageRadius; y <= damageRadius; y++)
                {
                    otherPos.y = lastHitInfo.voxelCenter.y + y;
                    for (int z = -damageRadius; z <= damageRadius; z++)
                    {
                        otherPos.z = lastHitInfo.voxelCenter.z + z;
                        for (int x = -damageRadius; x <= damageRadius; x++)
                        {
                            if (x == 0 && z == 0 && y == 0)
                            {
                                continue;
                            }
                            VoxelChunk otherChunk;
                            int        otherIndex;
                            otherPos.x = lastHitInfo.voxelCenter.x + x;
                            if (GetVoxelIndex(otherPos, out otherChunk, out otherIndex, false))
                            {
                                if (GetVoxelVisibility(otherChunk, otherIndex))
                                {
                                    FastVector.NormalizedDirection(ref explosionPosition, ref otherPos, ref direction);
                                    if (RayCast(explosionPosition, direction, out hitInfo))
                                    {
                                        DamageVoxelFast(ref hitInfo, damage, addParticles, playSound, allowDamageEvent);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
 /// <summary>
 /// Paints the terrain inside the chunk defined by its central "position"
 /// </summary>
 /// <returns><c>true</c>, if terrain was painted, <c>false</c> otherwise.</returns>
 public abstract bool PaintChunk(VoxelChunk chunk);
        /// <summary>
        /// Clears chunk state before returning it to the pool. This method is called when this chunk is reused.
        /// </summary>
        public void PrepareForReuse(byte light)
        {
            isAboveSurface         = false;
            needsMeshRebuild       = false;
            isPopulated            = false;
            inqueue                = false;
            modified               = false;
            renderState            = ChunkRenderState.Pending;
            allowTrees             = true;
            lightmapSignature      = -1;
            frustumCheckIteration  = 0;
            navMesh                = null;
            navMeshSourceIndex     = -1;
            inconclusiveNeighbours = 0;
            renderingFrame         = -1;
            visibleDistanceStatus  = ChunkVisibleDistanceStatus.Unknown;
            lightmapIsClear        = false;

            if (items != null)
            {
                for (int k = 0; k < items.count; k++)
                {
                    Item item = items.values [k];
                    if (item != null && item.gameObject != null)
                    {
                        DestroyImmediate(item.gameObject);
                    }
                }
                items.Clear();
            }


            if (_left != null)
            {
                _left.right = null;
                _left       = null;
            }
            if (_right != null)
            {
                _right.left = null;
                _right      = null;
            }
            if (_forward != null)
            {
                _forward.back = null;
                _forward      = null;
            }
            if (_back != null)
            {
                _back.forward = null;
                _back         = null;
            }
            if (_top != null)
            {
                _top.bottom = null;
                _top        = null;
            }
            if (_bottom != null)
            {
                _bottom.top = null;
                _bottom     = null;
            }
            ClearVoxels(light);
            mr.enabled = false;
            if (mc != null)
            {
                mc.enabled = false;
            }
            gameObject.SetActive(true);
        }
        void ApplyRule(Vector3 position, VoxelChunk chunk, int voxelIndex, ref VoxelDefinition voxelDefinition, ref Color32 tintColor)
        {
            if (rules == null || env == null || voxelDefinition != this.placingVoxel)
            {
                return;
            }

            VoxelChunk otherChunk;
            int        otherIndex;

            VoxelDefinition[] otherVoxelDefinitions;

            for (int k = 0; k < rules.Length; k++)
            {
                if (!rules [k].enabled)
                {
                    continue;
                }
                Vector3 pos = position + rules [k].offset;
                bool    res = false;
                switch (rules [k].condition)
                {
                case TileRuleCondition.IsEmpty:
                    res = env.IsEmptyAtPosition(pos);
                    break;

                case TileRuleCondition.IsOcuppied:
                    res = !env.IsEmptyAtPosition(pos);
                    break;

                case TileRuleCondition.Equals:
                    if (env.GetVoxelIndex(pos, out otherChunk, out otherIndex, false))
                    {
                        res = otherChunk.voxels [otherIndex].typeIndex == voxelDefinition.index;
                    }
                    break;

                case TileRuleCondition.IsAny:
                    otherVoxelDefinitions = rules [k].compareVoxelDefinitionSet;
                    if (otherVoxelDefinitions != null && env.GetVoxelIndex(pos, out otherChunk, out otherIndex, false))
                    {
                        int otherTypeIndex = otherChunk.voxels [otherIndex].typeIndex;
                        for (int j = 0; j < otherVoxelDefinitions.Length; j++)
                        {
                            if (otherVoxelDefinitions [j].index == otherTypeIndex)
                            {
                                res = true;
                                break;
                            }
                        }
                    }
                    break;

                case TileRuleCondition.Always:
                    res = true;
                    break;
                }

                if (res)
                {
                    switch (rules [k].action)
                    {
                    case TileRuleAction.CancelPlacement:
                        voxelDefinition = null;
                        break;

                    case TileRuleAction.Replace:
                        voxelDefinition = rules [k].replacementSingle;
                        break;

                    case TileRuleAction.Random:
                        otherVoxelDefinitions = rules [k].replacementSet;
                        if (otherVoxelDefinitions != null && otherVoxelDefinitions.Length > 0)
                        {
                            voxelDefinition = otherVoxelDefinitions [UnityEngine.Random.Range(0, otherVoxelDefinitions.Length)];
                        }
                        break;

                    case TileRuleAction.Cycle:
                        otherVoxelDefinitions = rules [k].replacementSet;
                        if (otherVoxelDefinitions != null && otherVoxelDefinitions.Length > 0)
                        {
                            voxelDefinition = otherVoxelDefinitions [cycleIndex++];
                            if (cycleIndex >= otherVoxelDefinitions.Length)
                            {
                                cycleIndex = 0;
                            }
                        }
                        break;
                    }
                    return;                     // rule executed, exit
                }
            }
        }
 /// <summary>
 /// Internal method that puts a voxel in a given position. This method does not inform to neighbours. Only used by non-contiguous structures, like trees or vegetation.
 /// For terrain or large scale buildings, use VoxelPlaceFast.
 /// </summary>
 /// <param name="position">Position.</param>
 /// <param name="voxelType">Voxel type.</param>
 /// <param name="chunk">Chunk.</param>
 /// <param name="voxelIndex">Voxel index.</param>
 void VoxelSingleSet(Vector3 position, VoxelDefinition voxelType, out VoxelChunk chunk, out int voxelIndex)
 {
     VoxelSingleSet(position, voxelType, out chunk, out voxelIndex, Misc.color32White);
 }
        /// <summary>
        /// Converts a voxel into dynamic type
        /// </summary>
        /// <param name="chunk">Chunk.</param>
        /// <param name="voxelIndex">Voxel index.</param>
        GameObject VoxelSetDynamic(VoxelChunk chunk, int voxelIndex, bool addRigidbody, float duration)
        {
            if (chunk == null || chunk.voxels [voxelIndex].hasContent == 0)
            {
                return(null);
            }

            VoxelDefinition vd = voxelDefinitions [chunk.voxels [voxelIndex].typeIndex];

            if (!vd.renderType.supportsDynamic())
            {
                return(null);
            }

            VoxelPlaceholder placeholder = GetVoxelPlaceholder(chunk, voxelIndex, true);

            if (placeholder == null)
            {
                return(null);
            }

            // Add rigid body
            if (addRigidbody)
            {
                Rigidbody rb = placeholder.GetComponent <Rigidbody> ();
                if (rb == null)
                {
                    placeholder.rb = placeholder.gameObject.AddComponent <Rigidbody> ();
                }
            }

            // If it's a custom model ignore it as it's already a gameobject
            if (placeholder.modelMeshFilter != null)
            {
                return(placeholder.gameObject);
            }

            VoxelDefinition vdDyn = vd.dynamicDefinition;

            if (vdDyn == null)
            {
                // Setup and save voxel definition
                vd.dynamicDefinition         = vdDyn = ScriptableObject.CreateInstance <VoxelDefinition> ();
                vdDyn.isDynamic              = true;
                vdDyn.doNotSave              = true;
                vdDyn.staticDefinition       = vd;
                vdDyn.renderType             = RenderType.Custom;
                vdDyn.textureIndexBottom     = vd.textureIndexBottom;
                vdDyn.textureIndexSide       = vd.textureIndexSide;
                vdDyn.textureIndexTop        = vd.textureIndexTop;
                vdDyn.textureThumbnailTop    = vd.textureThumbnailTop;
                vdDyn.textureThumbnailSide   = vd.textureThumbnailSide;
                vdDyn.textureThumbnailBottom = vd.textureThumbnailBottom;
                vdDyn.scale             = vd.scale;
                vdDyn.offset            = vd.offset;
                vdDyn.offsetRandomRange = vd.offsetRandomRange;
                vdDyn.rotation          = vd.rotation;
                vdDyn.rotationRandomY   = vd.rotationRandomY;
                vdDyn.sampleColor       = vd.sampleColor;
                vdDyn.promotesTo        = vd.promotesTo;
                vdDyn.playerDamageDelay = vd.playerDamageDelay;
                vdDyn.playerDamage      = vd.playerDamage;
                vdDyn.pickupSound       = vd.pickupSound;
                vdDyn.landingSound      = vd.landingSound;
                vdDyn.jumpSound         = vd.jumpSound;
                vdDyn.impactSound       = vd.impactSound;
                vdDyn.footfalls         = vd.footfalls;
                vdDyn.destructionSound  = vd.destructionSound;
                vdDyn.canBeCollected    = vd.canBeCollected;
                vdDyn.dropItem          = GetItemDefinition(ItemCategory.Voxel, vd);
                vdDyn.buildSound        = vd.buildSound;
                vdDyn.navigatable       = true;
                vdDyn.windAnimation     = false;
                vdDyn.model             = MakeDynamicCubeFromVoxel(chunk, voxelIndex);
                vdDyn.name = vd.name + " (Dynamic)";
                AddVoxelTextures(vdDyn);
            }

            // Clear any vegetation on top if voxel can be moved (has a rigidbody) to avoid floating grass block
            if (placeholder.rb != null)
            {
                VoxelChunk topChunk;
                int        topIndex;
                if (GetVoxelIndex(chunk, voxelIndex, 0, 1, 0, out topChunk, out topIndex))
                {
                    if (topChunk.voxels [topIndex].hasContent == 1 && voxelDefinitions [topChunk.voxels [topIndex].typeIndex].renderType == RenderType.CutoutCross)
                    {
                        VoxelDestroyFast(topChunk, topIndex);
                    }
                }
            }
            Color32 color = chunk.voxels [voxelIndex].color;

            chunk.voxels [voxelIndex].Set(vdDyn, color);

            if (duration > 0)
            {
                placeholder.SetCancelDynamic(duration);
            }

            // Refresh neighbours
            RebuildNeighbours(chunk, voxelIndex);

            return(placeholder.gameObject);
        }
        void ManageSeeThrough()
        {
            SetArrays(false);
            Camera cam = currentCamera;

            if (cam == null)
            {
                return;
            }
            Vector3 camPos = cam.transform.position;

            if (seeThroughTarget == null)
            {
                if (characterController != null)
                {
                    seeThroughTarget = characterController.gameObject;
                }
                if (seeThroughTarget == null)
                {
                    return;
                }
            }
            Vector3 targetPos = seeThroughTarget.transform.position;

            // Exclude any voxel above roof from rendering
            float   radiusSqr = seeThroughRadius * seeThroughRadius;
            Vector3 cylinderAxis;
            float   distToTarget = Vector3.Distance(targetPos, camPos);

            cylinderAxis.x = (camPos.x - targetPos.x) / distToTarget;
            cylinderAxis.y = (camPos.y - targetPos.y) / distToTarget;
            cylinderAxis.z = (camPos.z - targetPos.z) / distToTarget;
            Shader.SetGlobalVector("_VPSeeThroughData", new Vector4(-cylinderAxis.x, -cylinderAxis.y, -cylinderAxis.z, radiusSqr));

            alphaOccludedIndicesCount = 0;

            // Add surrounding chunks
            int chunkCount = LineCast(targetPos, camPos, occludedChunks);
            int flag       = Time.frameCount;

            for (int k = 0; k < chunkCount; k++)
            {
                VoxelChunk chunk = occludedChunks [k];
                chunk.tempFlag = flag;
            }

            int lineChunks = chunkCount;

            for (int k = 0; k < lineChunks; k++)
            {
                VoxelChunk chunk = occludedChunks [k];
                VoxelChunk n     = chunk.top;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.bottom;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.left;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.right;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.forward;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
                n = chunk.back;
                if (n != null && n.tempFlag != flag)
                {
                    if (chunkCount >= occludedChunks.Length)
                    {
                        occludedChunks = occludedChunks.Extend();
                    }
                    occludedChunks [chunkCount++] = n;
                    n.tempFlag = flag;
                }
            }

            float   minY          = targetPos.y + seeThroughHeightOffset;
            Vector3 voxelPosition = Misc.vector3zero;

            for (int k = 0; k < chunkCount; k++)
            {
                VoxelChunk chunk        = occludedChunks [k];
                Vector3    chunkPosBase = chunk.position;
                chunkPosBase.x = chunkPosBase.x - CHUNK_HALF_SIZE + 0.5f;
                chunkPosBase.y = chunkPosBase.y - CHUNK_HALF_SIZE + 0.5f;
                chunkPosBase.z = chunkPosBase.z - CHUNK_HALF_SIZE + 0.5f;
                for (int voxelIndex = 0; voxelIndex < chunk.voxels.Length; voxelIndex++)
                {
                    if (chunk.voxels [voxelIndex].hasContent == 1)
                    {
                        int py = voxelIndex / ONE_Y_ROW;
                        voxelPosition.y = py + chunkPosBase.y;
                        if (voxelPosition.y < minY)
                        {
                            continue;
                        }
                        int pz = (voxelIndex - py * ONE_Y_ROW) / ONE_Z_ROW;
                        int px = voxelIndex & (CHUNK_SIZE - 1);
                        voxelPosition.x = px + chunkPosBase.x;
                        voxelPosition.z = pz + chunkPosBase.z;

                        // Check if it's inside the cylinder
                        Vector3 v = voxelPosition;
                        v.x -= targetPos.x;
                        v.y -= targetPos.y;
                        v.z -= targetPos.z;
                        // v is the voxel position from targetPos
                        // cylinderDist is the distance from targetPos to the projected vector v on the cylinder axis
                        float cylinderDist = v.x * cylinderAxis.x + v.y * cylinderAxis.y + v.z * cylinderAxis.z;
                        if (cylinderDist < 1 || cylinderDist > distToTarget)
                        {
                            continue;
                        }

                        v.x -= cylinderDist * cylinderAxis.x;
                        v.y -= cylinderDist * cylinderAxis.y;
                        v.z -= cylinderDist * cylinderAxis.z;
                        // this is the distance between the voxel position and the projected point calculated above = distance from the voxel to the axis of the cylinder
                        float orthDistanceSqr = v.x * v.x + v.y * v.y + v.z * v.z;

                        if (orthDistanceSqr < radiusSqr)
                        {
                            if (alphaOccludedIndicesCount >= alphaOccludedIndices.Length)
                            {
                                alphaOccludedIndices = alphaOccludedIndices.Extend();
                            }
                            alphaOccludedIndices [alphaOccludedIndicesCount].chunk      = chunk;
                            alphaOccludedIndices [alphaOccludedIndicesCount].voxelIndex = voxelIndex;
                            alphaOccludedIndicesCount++;
                        }
                    }
                }
            }
            SetArrays(true);
        }
Beispiel #11
0
 void CheckChunksVisibleDistanceLoop(long maxFrameTime)
 {
     try {
         bool eventOut = OnChunkExitVisibleDistance != null;
         bool eventIn  = OnChunkEnterVisibleDistance != null;
         int  max      = checkChunksVisibleDistanceIndex + 200;
         if (max >= chunksPoolLoadIndex)
         {
             max = chunksPoolLoadIndex;
         }
         float visibleDistanceSqr = (_visibleChunksDistance + 1) * CHUNK_SIZE;
         visibleDistanceSqr *= visibleDistanceSqr;
         while (checkChunksVisibleDistanceIndex < max)
         {
             VoxelChunk chunk = chunksPool[checkChunksVisibleDistanceIndex];
             if (chunk.isRendered && !chunk.isCloud)
             {
                 float dist = FastVector.SqrMaxDistanceXorZ(ref chunk.position, ref currentAnchorPos);
                 if (dist > visibleDistanceSqr)
                 {
                     if (chunk.visibleDistanceStatus != ChunkVisibleDistanceStatus.OutOfVisibleDistance)
                     {
                         chunk.visibleDistanceStatus = ChunkVisibleDistanceStatus.OutOfVisibleDistance;
                         if (unloadFarChunks || eventOut)
                         {
                             if (unloadFarChunks)
                             {
                                 chunk.gameObject.SetActive(false);
                             }
                             if (eventOut)
                             {
                                 OnChunkExitVisibleDistance(chunk);
                             }
                             if (stopWatch.ElapsedMilliseconds >= maxFrameTime)
                             {
                                 break;
                             }
                         }
                     }
                 }
                 else if (chunk.visibleDistanceStatus != ChunkVisibleDistanceStatus.WithinVisibleDistance)
                 {
                     chunk.visibleDistanceStatus = ChunkVisibleDistanceStatus.WithinVisibleDistance;
                     if (unloadFarChunks || eventOut)
                     {
                         if (unloadFarChunks)
                         {
                             chunk.gameObject.SetActive(true);
                         }
                         if (eventIn)
                         {
                             OnChunkEnterVisibleDistance(chunk);
                         }
                         if (stopWatch.ElapsedMilliseconds >= maxFrameTime)
                         {
                             break;
                         }
                     }
                 }
             }
             checkChunksVisibleDistanceIndex++;
         }
         if (checkChunksVisibleDistanceIndex >= chunksPoolLoadIndex)
         {
             checkChunksVisibleDistanceIndex = -1;
         }
     } catch (Exception ex) {
         ShowExceptionMessage(ex);
     }
 }
        void SaveGameBinaryFormat_9(BinaryWriter bw)
        {
            if (cachedChunks == null)
            {
                return;
            }
            // Build a table with all voxel definitions used in modified chunks
            InitSaveGameStructs();
            int voxelDefinitionsCount = 0;
            int itemDefinitionsCount  = 0;
            int numChunks             = 0;

            // Pack used voxel and item definitions
            foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks)
            {
                if (kv.Value == null)
                {
                    continue;
                }
                VoxelChunk chunk = kv.Value.chunk;
                if (chunk != null && chunk.modified)
                {
                    numChunks++;
                    if (chunk.voxels != null)
                    {
                        VoxelDefinition last = null;
                        for (int k = 0; k < chunk.voxels.Length; k++)
                        {
                            VoxelDefinition vd = chunk.voxels [k].type;
                            if (vd == null || vd == last || vd.isDynamic || vd.doNotSave)
                            {
                                continue;
                            }
                            last = vd;
                            if (!saveVoxelDefinitionsDict.ContainsKey(vd))
                            {
                                saveVoxelDefinitionsDict [vd] = voxelDefinitionsCount++;
                                saveVoxelDefinitionsList.Add(vd.name);
                            }
                        }
                    }
                    if (chunk.items != null)
                    {
                        ItemDefinition last = null;
                        for (int k = 0; k < chunk.items.count; k++)
                        {
                            Item item = chunk.items.values [k];
                            if (item == null)
                            {
                                continue;
                            }
                            ItemDefinition id = item.itemDefinition;
                            if (id == null || id == last)
                            {
                                continue;
                            }
                            last = id;
                            if (!saveItemDefinitionsDict.ContainsKey(id))
                            {
                                saveItemDefinitionsDict [id] = itemDefinitionsCount++;
                                saveItemDefinitionsList.Add(id.name);
                            }
                        }
                    }
                    if (chunk.lightSources != null)
                    {
                        ItemDefinition last = null;
                        for (int k = 0; k < chunk.lightSources.Count; k++)
                        {
                            ItemDefinition id = chunk.lightSources [k].itemDefinition;
                            if (id == null || id == last)
                            {
                                continue;
                            }
                            last = id;
                            if (!saveItemDefinitionsDict.ContainsKey(id))
                            {
                                saveItemDefinitionsDict [id] = itemDefinitionsCount++;
                                saveItemDefinitionsList.Add(id.name);
                            }
                        }
                    }
                }
            }

            // Header
            bw.Write(SAVE_FILE_CURRENT_FORMAT);
            // Character controller transform position
            if (characterController != null)
            {
                EncodeVector3Binary(bw, characterController.transform.position);
                // Character controller transform rotation
                EncodeVector3Binary(bw, characterController.transform.rotation.eulerAngles);
            }
            else
            {
                EncodeVector3Binary(bw, Misc.vector3zero);
                EncodeVector3Binary(bw, Misc.vector3zero);
            }
            // Character controller's camera local rotation
            if (cameraMain != null)
            {
                EncodeVector3Binary(bw, cameraMain.transform.localRotation.eulerAngles);
            }
            else
            {
                EncodeVector3Binary(bw, Misc.vector3zero);
            }
            // Add voxel definitions table
            int vdCount = saveVoxelDefinitionsList.Count;

            bw.Write((Int16)vdCount);
            for (int k = 0; k < vdCount; k++)
            {
                bw.Write(saveVoxelDefinitionsList [k]);
            }
            // Add item definitions table
            int idCount = saveItemDefinitionsList.Count;

            bw.Write((Int16)idCount);
            for (int k = 0; k < idCount; k++)
            {
                bw.Write(saveItemDefinitionsList [k]);
            }
            // Add modified chunks
            bw.Write(numChunks);
            foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks)
            {
                if (kv.Value == null)
                {
                    continue;
                }
                VoxelChunk chunk = kv.Value.chunk;
                if (chunk != null && chunk.modified)
                {
                    ToggleHiddenVoxels(chunk, true);
                    WriteChunkData_9(bw, chunk);
                    ToggleHiddenVoxels(chunk, false);
                }
            }
            // Add gameobjects
            VoxelPlaySaveThis [] gos = FindObjectsOfType <VoxelPlaySaveThis> ();
            bw.Write((Int16)gos.Length);
            Dictionary <string, string> data = new Dictionary <string, string> ();

            for (int k = 0; k < gos.Length; k++)
            {
                VoxelPlaySaveThis go = gos [k];
                if (string.IsNullOrEmpty(go.prefabResourcesPath))
                {
                    continue;
                }
                bw.Write(go.prefabResourcesPath);
                bw.Write(go.name);
                EncodeVector3Binary(bw, go.transform.position);
                EncodeVector3Binary(bw, go.transform.eulerAngles);
                EncodeVector3Binary(bw, go.transform.localScale);
                data.Clear();
                go.SendMessage("OnSaveGame", data);
                //go.GetData (data);
                Int16 dataCount = (Int16)data.Count;
                bw.Write(dataCount);
                foreach (KeyValuePair <string, string> entry in data)
                {
                    bw.Write(entry.Key);
                    bw.Write(entry.Value);
                }
            }
        }
        void LoadGameBinaryFileFormat_9(BinaryReader br, bool preservePlayerPosition = false)
        {
            // Character controller transform position & rotation
            Vector3 pos = DecodeVector3Binary(br);
            Vector3 characterRotationAngles   = DecodeVector3Binary(br);
            Vector3 cameraLocalRotationAngles = DecodeVector3Binary(br);

            if (!preservePlayerPosition)
            {
                if (characterController != null)
                {
                    characterController.transform.position = pos;
                    characterController.transform.rotation = Quaternion.Euler(characterRotationAngles);
                    cameraMain.transform.localRotation     = Quaternion.Euler(cameraLocalRotationAngles);
                    characterController.UpdateLook();
                }
            }

            InitSaveGameStructs();
            // Read voxel definition table
            int vdCount = br.ReadInt16();

            for (int k = 0; k < vdCount; k++)
            {
                saveVoxelDefinitionsList.Add(br.ReadString());
            }
            // Read item definition table
            int idCount = br.ReadInt16();

            for (int k = 0; k < idCount; k++)
            {
                saveItemDefinitionsList.Add(br.ReadString());
            }

            int             numChunks       = br.ReadInt32();
            VoxelDefinition voxelDefinition = defaultVoxel;
            int             prevVdIndex     = -1;
            Color32         voxelColor      = Misc.color32White;

            for (int c = 0; c < numChunks; c++)
            {
                // Read chunks
                // Get chunk position
                Vector3    chunkPosition  = DecodeVector3Binary(br);
                VoxelChunk chunk          = GetChunkUnpopulated(chunkPosition);
                byte       isAboveSurface = br.ReadByte();
                chunk.isAboveSurface = isAboveSurface == 1;
                chunk.back           = chunk.bottom = chunk.left = chunk.right = chunk.forward = chunk.top = null;
                chunk.allowTrees     = false;
                chunk.modified       = true;
                chunk.isPopulated    = true;
                chunk.voxelSignature = chunk.lightmapSignature = -1;
                chunk.renderState    = ChunkRenderState.Pending;
                SetChunkOctreeIsDirty(chunkPosition, false);
                ChunkClearFast(chunk);
                // Read voxels
                int numWords = br.ReadInt16();
                for (int k = 0; k < numWords; k++)
                {
                    // Voxel definition
                    int vdIndex = br.ReadInt16();
                    if (prevVdIndex != vdIndex)
                    {
                        if (vdIndex >= 0 && vdIndex < vdCount)
                        {
                            voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList [vdIndex]);
                            prevVdIndex     = vdIndex;
                        }
                    }
                    // RGB
                    voxelColor.r = br.ReadByte();
                    voxelColor.g = br.ReadByte();
                    voxelColor.b = br.ReadByte();
                    // Voxel index
                    int voxelIndex = br.ReadInt16();
                    // Repetitions
                    int repetitions = br.ReadInt16();

                    byte flags             = br.ReadByte();
                    byte hasCustomRotation = br.ReadByte();

                    if (voxelDefinition == null)
                    {
                        continue;
                    }

                    // Custom voxel flags
                    if (voxelDefinition.renderType == RenderType.Custom)
                    {
                        flags &= 0xF; // custom voxels do not store texture rotation; their transform has the final rotation
                        if (hasCustomRotation == 1)
                        {
                            Vector3 voxelAngles = DecodeVector3Binary(br);
                            delayedVoxelCustomRotations.Add(GetVoxelPosition(chunkPosition, voxelIndex), voxelAngles);
                        }
                    }
                    for (int i = 0; i < repetitions; i++)
                    {
                        chunk.voxels [voxelIndex + i].Set(voxelDefinition, voxelColor);
                        if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation())
                        {
                            chunk.voxels [voxelIndex + i].SetFlags(flags);
                        }
                    }
                }
                // Read light sources
                int          lightCount = br.ReadInt16();
                VoxelHitInfo hitInfo    = new VoxelHitInfo();
                for (int k = 0; k < lightCount; k++)
                {
                    // Voxel index
                    hitInfo.voxelIndex = br.ReadInt16();
                    // Voxel center
                    hitInfo.voxelCenter = GetVoxelPosition(chunkPosition, hitInfo.voxelIndex);
                    // Normal
                    hitInfo.normal = DecodeVector3Binary(br);
                    hitInfo.chunk  = chunk;
                    // Item definition
                    int itemIndex = br.ReadInt16();
                    if (itemIndex < 0 || itemIndex >= idCount)
                    {
                        continue;
                    }
                    string         itemDefinitionName = saveItemDefinitionsList [itemIndex];
                    ItemDefinition itemDefinition     = GetItemDefinition(itemDefinitionName);
                    TorchAttach(hitInfo, itemDefinition);
                }
                // Read items
                int itemCount = br.ReadInt16();
                for (int k = 0; k < itemCount; k++)
                {
                    // Voxel index
                    int itemIndex = br.ReadInt16();
                    if (itemIndex < 0 || itemIndex >= idCount)
                    {
                        continue;
                    }
                    string  itemDefinitionName = saveItemDefinitionsList [itemIndex];
                    Vector3 itemPosition       = DecodeVector3Binary(br);
                    int     quantity           = br.ReadInt16();
                    ItemSpawn(itemDefinitionName, itemPosition, quantity);
                }
            }
            // Destroy any object with VoxelPlaySaveThis component to avoid repetitions
            VoxelPlaySaveThis [] gos = FindObjectsOfType <VoxelPlaySaveThis> ();
            for (int k = 0; k < gos.Length; k++)
            {
                DestroyImmediate(gos [k].gameObject);
            }
            // Load gameobjects
            int goCount = br.ReadInt16();
            Dictionary <string, string> data = new Dictionary <string, string> ();

            for (int k = 0; k < goCount; k++)
            {
                string     prefabPath = br.ReadString();
                GameObject o          = Resources.Load <GameObject> (prefabPath);
                if (o != null)
                {
                    o      = Instantiate <GameObject> (o) as GameObject;
                    o.name = br.ReadString();
                    VoxelPlaySaveThis go = o.GetComponent <VoxelPlaySaveThis> ();
                    if (go == null)
                    {
                        DestroyImmediate(o);
                        continue;
                    }
                    o.transform.position    = DecodeVector3Binary(br);
                    o.transform.eulerAngles = DecodeVector3Binary(br);
                    o.transform.localScale  = DecodeVector3Binary(br);
                    data.Clear();
                    Int16 dataCount = br.ReadInt16();
                    for (int j = 0; j < dataCount; j++)
                    {
                        string key   = br.ReadString();
                        string value = br.ReadString();
                        data [key] = value;
                    }
                    go.SendMessage("OnLoadGame", data);
                }
            }
        }
Beispiel #14
0
        void LoadSaveBinaryFileFormat_3(BinaryReader br, bool preservePlayerPosition = false)
        {
            // Character controller transform position
            Vector3 v = DecodeVector3Binary(br);

            if (!preservePlayerPosition)
            {
                characterController.transform.position = v;
            }
            // Character controller transform rotation
            Vector3 angles = DecodeVector3Binary(br);

            if (!preservePlayerPosition)
            {
                characterController.transform.rotation = Quaternion.Euler(angles);
            }
            // Character controller's camera local rotation
            angles = DecodeVector3Binary(br);
            if (!preservePlayerPosition)
            {
                cameraMain.transform.localRotation = Quaternion.Euler(angles);
                // Pass initial rotation to mouseLook script
                characterController.GetComponent <VoxelPlayFirstPersonController> ().mouseLook.Init(characterController.transform, cameraMain.transform, null);
            }

            // Read voxel definition table
            InitSaveGameStructs();
            int vdCount = br.ReadInt16();

            for (int k = 0; k < vdCount; k++)
            {
                saveVoxelDefinitionsList.Add(br.ReadString());
            }

            int             numChunks       = br.ReadInt32();
            VoxelDefinition voxelDefinition = defaultVoxel;
            int             prevVdIndex     = -1;
            Color32         voxelColor      = Misc.color32White;

            for (int c = 0; c < numChunks; c++)
            {
                // Read chunks
                int chunkX, chunkY, chunkZ;
                // Get chunk position
                Vector3 chunkPosition = DecodeVector3Binary(br);
                GetChunkCoordinates(chunkPosition, out chunkX, out chunkY, out chunkZ);
                VoxelChunk chunk          = GetChunkUnpopulated(chunkPosition);
                byte       isAboveSurface = br.ReadByte();
                chunk.isAboveSurface = isAboveSurface == 1;
                chunk.back           = chunk.bottom = chunk.left = chunk.right = chunk.forward = chunk.top = null;
                chunk.allowTrees     = false;
                chunk.modified       = true;
                chunk.isPopulated    = true;
                chunk.voxelSignature = chunk.lightmapSignature = -1;
                chunk.renderState    = ChunkRenderState.Pending;
                SetChunkOctreeIsDirty(chunkPosition, false);
                ChunkClearFast(chunk);
                // Read voxels
                int numWords = br.ReadInt16();
                for (int k = 0; k < numWords; k++)
                {
                    // Voxel definition
                    int vdIndex = br.ReadInt16();
                    if (prevVdIndex != vdIndex)
                    {
                        if (vdIndex >= 0 && vdIndex < vdCount)
                        {
                            voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList [vdIndex]);
                            prevVdIndex     = vdIndex;
                        }
                    }
                    // RGB
                    voxelColor.r = br.ReadByte();
                    voxelColor.g = br.ReadByte();
                    voxelColor.b = br.ReadByte();
                    // Voxel index
                    int voxelIndex = br.ReadInt16();
                    // Repetitions
                    int repetitions = br.ReadInt16();

                    if (voxelDefinition == null)
                    {
                        continue;
                    }

                    // Water level (only for transparent)
                    byte waterLevel = 15;
                    if (voxelDefinition.renderType == RenderType.Water)
                    {
                        waterLevel = br.ReadByte();
                    }
                    for (int i = 0; i < repetitions; i++)
                    {
                        chunk.voxels [voxelIndex + i].Set(voxelDefinition, voxelColor);
                        if (voxelDefinition.renderType == RenderType.Water)
                        {
                            chunk.voxels [voxelIndex + i].SetWaterLevel(waterLevel);
                        }
                    }
                }
                // Read light sources
                int          lightCount = br.ReadInt16();
                VoxelHitInfo hitInfo    = new VoxelHitInfo();
                for (int k = 0; k < lightCount; k++)
                {
                    // Voxel index
                    hitInfo.voxelIndex = br.ReadInt16();
                    // Voxel center
                    hitInfo.voxelCenter = GetVoxelPosition(chunkPosition, hitInfo.voxelIndex);
                    // Normal
                    hitInfo.normal = DecodeVector3Binary(br);
                    hitInfo.chunk  = chunk;
                    TorchAttach(hitInfo);
                }
            }
        }
 public bool GetChunkNavMeshIsReady(VoxelChunk chunk)
 {
     return(chunk.navMeshSourceIndex >= 0);
 }
        /// <summary>
        /// Toggles on/off hidden voxels.
        /// </summary>
        void ToggleHiddenVoxels(VoxelChunk chunk, bool show)
        {
            if (chunk == null)
            {
                return;
            }

            FastHashSet <VoxelExtraData> data = chunk.voxelsExtraData;

            if (data == null)
            {
                return;
            }

            int count = data.Count;

            Voxel[] voxels = chunk.voxels;

            for (int k = 0; k < count; k++)
            {
                int voxelIndex = data.entries [k].key;
                if (voxelIndex >= 0)
                {
                    if (data.entries [k].value.hidden)
                    {
                        if (show)
                        {
                            voxels [voxelIndex].typeIndex = data.entries [k].value.hiddenTypeIndex;
                            voxels [voxelIndex].opaque    = data.entries [k].value.hiddenOpaque;
                            voxels [voxelIndex].light     = data.entries [k].value.hiddenLight;
                        }
                        else
                        {
                            ushort typeIndex = voxels [voxelIndex].typeIndex;
                            data.entries [k].value.hiddenTypeIndex = typeIndex;
                            data.entries [k].value.hiddenOpaque    = voxels [voxelIndex].opaque;
                            data.entries [k].value.hiddenLight     = voxels [voxelIndex].light;
                            VoxelDefinition vd = voxelDefinitions [typeIndex];
                            switch (vd.seeThroughMode)
                            {
                            case SeeThroughMode.None:
                                voxels [voxelIndex].typeIndex = 0;                                 // null voxel whose renderType = Empty (but genereate colliders)
                                voxels [voxelIndex].opaque    = 0;
                                break;

                            case SeeThroughMode.ReplaceVoxel:
                                voxels [voxelIndex].typeIndex = vd.seeThroughVoxel.index;                                 // null voxel whose renderType = Empty (but genereate colliders)
                                voxels [voxelIndex].opaque    = vd.seeThroughVoxel.opaque;
                                break;

                            case SeeThroughMode.Transparency:
                                voxels [voxelIndex].typeIndex = (ushort)vd.seeThroughVoxelTempTransp;                                 // null voxel whose renderType = Empty (but genereate colliders)
                                voxels [voxelIndex].opaque    = 0;
                                break;
                            }
                            voxels [voxelIndex].light = 15;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Picks first chunk out of visible distance and not near to the current desired position
        /// </summary>
        void ReuseChunkEntry(Vector3 position)
        {
            bool  valid           = false;
            float visibleDistance = (_visibleChunksDistance + 1) * 16;             // adds one to avoid chunks appearing and disappearing on the border of visible distance
            float minDistance     = 8 * 16;
            int   lastGood        = -1;

            for (int i = 0; i < chunksPool.Length; i++)
            {
                if (++chunksPoolCurrentIndex >= chunksPool.Length - 1)
                {
                    chunksPoolCurrentIndex = chunksPoolFirstReusableIndex;
                }

                VoxelChunk chunk = chunksPool [chunksPoolCurrentIndex];

                // if chunk has been modified or chunk is marked as non reusable, skip it
                if (chunk.modified || chunk.cannotBeReused)
                {
                    continue;
                }

                // if chunk is too near from desired position, skip it
                float dx = position.x - chunk.position.x;
                float dy = position.y - chunk.position.y;
                float dz = position.y - chunk.position.z;
                if (dx >= -minDistance && dx <= minDistance && dy >= -minDistance && dy <= minDistance && dz >= -minDistance && dz <= minDistance)
                {
                    continue;
                }
                lastGood = chunksPoolCurrentIndex;

                // if chunk is within visible distance, skip it
                dx = currentCamPos.x - chunk.position.x;
                dy = currentCamPos.y - chunk.position.y;
                dz = currentCamPos.z - chunk.position.z;
                if (dx >= -visibleDistance && dx <= visibleDistance && dy >= -visibleDistance && dy <= visibleDistance && dz >= -visibleDistance && dz <= visibleDistance)
                {
                    continue;
                }

                // check event confirmation
                bool canUnload = true;
                if (OnChunkUnload != null)
                {
                    OnChunkUnload(chunk, ref canUnload);
                    if (!canUnload)
                    {
                        continue;
                    }
                }

                // chunk seems good, pick it up!
                valid = true;
                break;
            }
            if (lastGood >= 0)
            {
                chunksPoolCurrentIndex = lastGood;
            }
            if (!valid)
            {
                ShowMessage("Reusing visible chunks (Chunks Pool Size value must be increased)");
            }
            VoxelChunk bestChunk = chunksPool [chunksPoolCurrentIndex];

            lock (lockLastChunkFetch) {
                if (bestChunk == lastChunkFetch)
                {
                    lastChunkFetch = null;
                }
            }
            bestChunk.PrepareForReuse(effectiveGlobalIllumination ? (byte)0 : (byte)15);
            // Remove cached chunk at old position
            SetChunkOctreeIsDirty(bestChunk.position, true);
        }
 void VoxelDestroyFastSingle(VoxelChunk chunk, int voxelIndex)
 {
     chunk.voxels [voxelIndex].Clear(effectiveGlobalIllumination ? (byte)0 : (byte)15);
     chunk.modified = true;
 }
Beispiel #19
0
        //TODO: Painting Chunk
        /// <summary>
        /// Paints the terrain inside the chunk defined by its central "position"
        /// </summary>
        /// <returns><c>true</c>, if terrain was painted, <c>false</c> otherwise.</returns>
        public override bool PaintChunk(VoxelChunk chunk)
        {
            Vector3 position = chunk.position;

            if (position.y + VoxelPlayEnvironment.CHUNK_HALF_SIZE < minHeight)
            {
                chunk.isAboveSurface = false;
                return(false);
            }
            int bedrockRow = -1;

            if ((object)bedrockVoxel != null && position.y < minHeight + VoxelPlayEnvironment.CHUNK_HALF_SIZE)
            {
                bedrockRow = (int)(minHeight - (position.y - VoxelPlayEnvironment.CHUNK_HALF_SIZE) + 1) * ONE_Y_ROW - 1;
            }
            position.x -= VoxelPlayEnvironment.CHUNK_HALF_SIZE;
            position.y -= VoxelPlayEnvironment.CHUNK_HALF_SIZE;
            position.z -= VoxelPlayEnvironment.CHUNK_HALF_SIZE;
            Vector3 pos;

            int waterLevel = env.waterLevel > 0 ? env.waterLevel : -1;

            Voxel [] voxels = chunk.voxels;

            bool hasContent     = false;
            bool isAboveSurface = false;

            generation++;
            env.GetHeightMapInfoFast(position.x, position.z, heightChunkData);

            // iterate 256 slice of chunk (z/x plane = 16*16 positions)
            for (int arrayIndex = 0; arrayIndex < VoxelPlayEnvironment.CHUNK_SIZE * VoxelPlayEnvironment.CHUNK_SIZE; arrayIndex++)
            {
                float groundLevel  = heightChunkData [arrayIndex].groundLevel;
                float surfaceLevel = waterLevel > groundLevel ? waterLevel : groundLevel;
                if (surfaceLevel < position.y)
                {
                    // position is above terrain or water
                    isAboveSurface = true;
                    continue;
                }
                BiomeDefinition biome = heightChunkData [arrayIndex].biome;
                if ((object)biome == null)
                {
                    biome = world.defaultBiome;
                    if ((object)biome == null)
                    {
                        continue;
                    }
                }

                int y = (int)(surfaceLevel - position.y);
                if (y >= VoxelPlayEnvironment.CHUNK_SIZE)
                {
                    y = VoxelPlayEnvironment.CHUNK_SIZE - 1;
                }
                pos.y = position.y + y;
                //remember: Array is linear
                pos.x = position.x + (arrayIndex & 0xF);
                pos.z = position.z + (arrayIndex >> 4);

                // Place voxels
                int voxelIndex = y * ONE_Y_ROW + arrayIndex;
                if (pos.y > groundLevel)
                {
                    // water above terrain
                    if (pos.y == surfaceLevel)
                    {
                        isAboveSurface = true;
                    }
                    while (pos.y > groundLevel && voxelIndex >= 0)
                    {
                        voxels [voxelIndex].SetFastWater(waterVoxel);
                        voxelIndex -= ONE_Y_ROW;
                        pos.y--;
                    }
                }
                else if (pos.y == groundLevel)
                {
                    isAboveSurface = true;
                    if (voxels [voxelIndex].hasContent == 0)
                    {
                        if (paintShore && pos.y == waterLevel)
                        {
                            // this is on the shore, place a shoreVoxel
                            voxels [voxelIndex].Set(shoreVoxel);
                        }
                        else
                        {
                            // we're at the surface of the biome => draw the voxel top of the biome and also check for random vegetation and trees
                            voxels [voxelIndex].Set(biome.voxelTop);
#if UNITY_EDITOR
                            if (!env.draftModeActive)
                            {
#endif
                            // Check tree probability
                            if (pos.y > waterLevel)
                            {
                                float rn = WorldRand.GetValue(pos);
                                if (biome.treeDensity > 0 && rn < biome.treeDensity && biome.trees.Length > 0)
                                {
                                    // request one tree at this position
                                    env.RequestTreeCreation(chunk, pos, env.GetTree(biome.trees, rn / biome.treeDensity));
                                }
                                else if (biome.vegetationDensity > 0 && rn < biome.vegetationDensity && biome.vegetation.Length > 0)
                                {
                                    if (voxelIndex >= (VoxelPlayEnvironment.CHUNK_SIZE - 1) * ONE_Y_ROW)
                                    {
                                        // request one vegetation voxel one position above which means the chunk above this one
                                        env.RequestVegetationCreation(chunk.top, voxelIndex - ONE_Y_ROW * (VoxelPlayEnvironment.CHUNK_SIZE - 1), env.GetVegetation(biome, rn / biome.vegetationDensity));
                                    }
                                    else
                                    {
                                        // directly place a vegetation voxel above this voxel
                                        if (env.enableVegetation)
                                        {
                                            voxels [voxelIndex + ONE_Y_ROW].Set(env.GetVegetation(biome, rn / biome.vegetationDensity));
                                            env.vegetationCreated++;
                                        }
                                    }
                                }
                            }
#if UNITY_EDITOR
                        }
#endif
                        }
                        voxelIndex -= ONE_Y_ROW;
                        pos.y--;
                    }
                }

                biome.biomeGeneration = generation;

                // fill hole with water
                while (voxelIndex >= 0 && voxels [voxelIndex].hasContent == 2 && pos.y <= waterLevel)
                {
                    voxels [voxelIndex].SetFastWater(waterVoxel);
                    voxelIndex -= ONE_Y_ROW;
                    pos.y--;
                }

                // Continue filling down
                for (; voxelIndex > bedrockRow; voxelIndex -= ONE_Y_ROW, pos.y--)
                {
                    if (voxels [voxelIndex].hasContent == 0)   // avoid holes
                    {
                        voxels [voxelIndex].SetFastOpaque(biome.voxelDirt);
                    }
                    else if (voxels [voxelIndex].hasContent == 2 && pos.y <= waterLevel)     // hole under water level -> fill with water
                    {
                        voxels [voxelIndex].SetFastWater(waterVoxel);
                    }
                }
                if (bedrockRow >= 0 && voxelIndex >= 0)
                {
                    voxels [voxelIndex].SetFastOpaque(bedrockVoxel);
                }
                hasContent = true;
            }

            // Spawn random ore
            if (addOre)
            {
                // Check if there's any ore in this chunk (randomly)
                float noiseValue = WorldRand.GetValue(chunk.position);
                for (int b = 0; b < world.biomes.Length; b++)
                {
                    BiomeDefinition biome = world.biomes [b];
                    if (biome.biomeGeneration != generation)
                    {
                        continue;
                    }
                    for (int o = 0; o < biome.ores.Length; o++)
                    {
                        if (biome.ores [o].ore == null)
                        {
                            continue;
                        }
                        if (biome.ores [o].probabilityMin <= noiseValue && biome.ores [o].probabilityMax >= noiseValue)
                        {
                            // ore picked; determine the number of veins in this chunk
                            int veinsCount = biome.ores [o].veinsCountMin + (int)(WorldRand.GetValue() * (biome.ores [o].veinsCountMax - biome.ores [o].veinsCountMin + 1f));
                            for (int vein = 0; vein < veinsCount; vein++)
                            {
                                Vector3 veinPos = chunk.position;
                                veinPos.x += vein;
                                // Determine random vein position in the chunk
                                Vector3 v  = WorldRand.GetVector3(veinPos, VoxelPlayEnvironment.CHUNK_SIZE);
                                int     px = (int)v.x;
                                int     py = (int)v.y;
                                int     pz = (int)v.z;
                                veinPos = env.GetVoxelPosition(veinPos, px, py, pz);
                                int oreIndex = py * ONE_Y_ROW + pz * ONE_Z_ROW + px;
                                int veinSize = biome.ores [o].veinMinSize + (oreIndex % (biome.ores [o].veinMaxSize - biome.ores [o].veinMinSize + 1));
                                // span ore vein
                                SpawnOre(chunk, biome.ores [o].ore, veinPos, px, py, pz, veinSize, biome.ores [o].depthMin, biome.ores [o].depthMax);
                            }
                            break;
                        }
                    }
                }
            }

            // Finish, return
            chunk.isAboveSurface = isAboveSurface;
            return(hasContent);
        }
        /// <summary>
        /// Loads game world from a string
        /// </summary>
        /// <returns>True if saveGameData was loaded successfully.</returns>
        /// <param name="preservePlayerPosition">If set to <c>true</c> preserve player position.</param>
        /// <param name="clearScene">If set to <c>true</c> existing chunks will be cleared before loading the game. If set to false, only chunks from the saved game will be replaced.</param>
        public bool LoadGameFromByteArray(byte[] saveGameData, bool preservePlayerPosition, bool clearScene = true)
        {
            bool result = false;

            if (clearScene)
            {
                DestroyAllVoxels();
            }
            else
            {
                // Remove all modified chunks to ensure only loaded chunks are the modified ones
                GetChunks(tempChunks, true);
                int count = tempChunks.Count;
                for (int k = 0; k < count; k++)
                {
                    VoxelChunk chunk = tempChunks [k];
                    if (chunk != null && chunk.modified)
                    {
                        // Restore original contents
                        world.terrainGenerator.PaintChunk(chunk);
                        ChunkRequestRefresh(chunk, true, true);
                        chunk.modified = false;
                    }
                }
            }

            try {
                if (saveGameData == null)
                {
                    return(false);
                }

                // get version
                isLoadingGame = true;
                BinaryReader br      = new BinaryReader(new MemoryStream(saveGameData), Encoding.UTF8);
                byte         version = br.ReadByte();
                switch (version)
                {
                case 3:
                    LoadSaveBinaryFileFormat_3(br, preservePlayerPosition);
                    break;

                case 4:
                    LoadSaveBinaryFileFormat_4(br, preservePlayerPosition);
                    break;

                case 5:
                    LoadGameBinaryFileFormat_5(br, preservePlayerPosition);
                    break;

                case 6:
                    LoadGameBinaryFileFormat_6(br, preservePlayerPosition);
                    break;

                case 7:
                    LoadGameBinaryFileFormat_7(br, preservePlayerPosition);
                    break;

                default:
                    throw new ApplicationException("LoadGameFromArray() does not support this file format.");
                }
                br.Close();
                isLoadingGame    = false;
                saveFileIsLoaded = true;
                result           = true;
            } catch (Exception ex) {
                Debug.LogError("Voxel Play: " + ex.Message);
                result = false;
            }

            isLoadingGame = false;
            shouldCheckChunksInFrustum = true;
            return(result);
        }
        VoxelChunk RayCastFastVoxel(Vector3 origin, Vector3 direction, out VoxelHitInfo hitInfo, float maxDistance = 0, bool createChunksIfNeeded = false, byte minOpaque = 0)
        {
#if DEBUG_RAYCAST
            GameObject o;
#endif

            float maxDistanceSqr = maxDistance == 0 ? 1000 * 1000 : maxDistance * maxDistance;

            // Ray march throuch chunks until hit one loaded chunk
            Vector3    position = origin;
            VoxelChunk chunk    = null;
            hitInfo            = new VoxelHitInfo();
            hitInfo.voxelIndex = -1;

            Vector3 viewDirSign = new Vector3(Mathf.Sign(direction.x), Mathf.Sign(direction.y), Mathf.Sign(direction.z));
            Vector3 viewSign    = (viewDirSign + Misc.vector3one) * 0.5f;          // 0 = left, 1 = right

            float vxz, vzy, vxy;
            if (direction.y != 0)
            {
                float a = direction.x / direction.y;
                float b = direction.z / direction.y;
                vxz = Mathf.Sqrt(1f + a * a + b * b);
            }
            else
            {
                vxz = 1000000f;
            }
            if (direction.x != 0)
            {
                float a = direction.z / direction.x;
                float b = direction.y / direction.x;
                vzy = Mathf.Sqrt(1f + a * a + b * b);
            }
            else
            {
                vzy = 1000000f;
            }
            if (direction.z != 0)
            {
                float a = direction.x / direction.z;
                float b = direction.y / direction.z;
                vxy = Mathf.Sqrt(1f + a * a + b * b);
            }
            else
            {
                vxy = 1000000f;
            }

            Vector3 v3                = new Vector3(vzy, vxz, vxy);
            Vector3 viewSign16        = viewSign * 16f;
            Vector3 viewDirSignOffset = viewDirSign * 0.002f;

            int     chunkX, chunkY, chunkZ;
            int     chunkCount = 0;
            float   t;
            Vector3 normal = Misc.vector3zero, db;
//			bool notFirstVoxel = false;

            while (chunkCount++ < 500)               // safety counter to avoid any potential infinite loop

            // Check max distance
            {
                float distSqr = (position.x - origin.x) * (position.x - origin.x) + (position.y - origin.y) * (position.y - origin.y) + (position.z - origin.z) * (position.z - origin.z);
                if (distSqr > maxDistanceSqr)
                {
                    return(null);
                }


#if DEBUG_RAYCAST
                o = GameObject.CreatePrimitive(PrimitiveType.Cube);
                o.transform.localScale = Misc.Vector3one * 0.15f;
                o.transform.position   = position;
                DestroyImmediate(o.GetComponent <Collider>());
                o.GetComponent <Renderer>().material.color = Color.blue;
#endif

                chunkX = FastMath.FloorToInt(position.x / 16f);
                chunkY = FastMath.FloorToInt(position.y / 16f);
                chunkZ = FastMath.FloorToInt(position.z / 16f);

                chunk = null;
                if (createChunksIfNeeded)
                {
                    chunk = GetChunkOrCreate(chunkX, chunkY, chunkZ);
                }
                else
                {
                    int x00  = WORLD_SIZE_DEPTH * WORLD_SIZE_HEIGHT * (chunkX + WORLD_SIZE_WIDTH);
                    int y00  = WORLD_SIZE_DEPTH * (chunkY + WORLD_SIZE_HEIGHT);
                    int hash = x00 + y00 + chunkZ;
                    chunk = GetChunkIfExists(hash);
                }

                chunkX *= 16;
                chunkY *= 16;
                chunkZ *= 16;

                if (chunk)
                {
                    // Ray-march through chunk
                    Voxel[] voxels     = chunk.voxels;
                    Vector3 inPosition = position;

                    for (int k = 0; k < 64; k++)
                    {
#if DEBUG_RAYCAST
                        o = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                        o.transform.localScale = Misc.Vector3one * 0.1f;
                        o.transform.position   = inPosition;
                        DestroyImmediate(o.GetComponent <Collider>());
                        o.GetComponent <Renderer>().material.color = Color.yellow;
#endif

                        // Check voxel content
                        int fy = FastMath.FloorToInt(inPosition.y);
                        int py = fy - chunkY;
                        int fz = FastMath.FloorToInt(inPosition.z);
                        int pz = fz - chunkZ;
                        int fx = FastMath.FloorToInt(inPosition.x);
                        int px = fx - chunkX;
                        if (px < 0 || px > 15 || py < 0 || py > 15 || pz < 0 || pz > 15)
                        {
                            break;
                        }

                        int voxelIndex = py * ONE_Y_ROW + pz * ONE_Z_ROW + px;
                        if (voxels [voxelIndex].hasContent == 1 && !voxels [voxelIndex].type.ignoresRayCast && (minOpaque == 255 || voxels [voxelIndex].opaque >= minOpaque))
                        {
                            VoxelDefinition vd = voxelDefinitions [voxels [voxelIndex].typeIndex];
                            if (vd.renderType != RenderType.Custom || !vd.modelUsesCollider)
                            {
                                // Check max distance
                                distSqr = (inPosition.x - origin.x) * (inPosition.x - origin.x) + (inPosition.y - origin.y) * (inPosition.y - origin.y) + (inPosition.z - origin.z) * (inPosition.z - origin.z);
                                if (distSqr > maxDistanceSqr)
                                {
                                    return(null);
                                }

                                // Check water level or grass height
                                float voxelHeight = 0;
                                if (vd.renderType == RenderType.Water)
                                {
                                    voxelHeight = voxels [voxelIndex].GetWaterLevel() / 15f;
                                }
                                else if (vd.renderType == RenderType.CutoutCross)
                                {
                                    voxelHeight = vd.scale.y;
                                }
                                bool    hit         = true;
                                Vector3 voxelCenter = new Vector3(chunkX + px + 0.5f, chunkY + py + 0.5f, chunkZ + pz + 0.5f);
                                Vector3 localHitPos = inPosition - voxelCenter;
                                if (voxelHeight > 0 && voxelHeight < 1f && direction.y != 0)
                                {
                                    t = localHitPos.y + 0.5f - voxelHeight;
                                    if (t > 0)
                                    {
                                        t            = t * Mathf.Sqrt(1 + (direction.x * direction.x + direction.z * direction.z) / (direction.y * direction.y));
                                        localHitPos += t * direction;
                                        hit          = localHitPos.x >= -0.5f && localHitPos.x <= 0.5f && localHitPos.z >= -0.5f && localHitPos.z <= 0.5f;
                                    }
                                }
                                if (hit)
                                {
                                    hitInfo             = new VoxelHitInfo();
                                    hitInfo.chunk       = chunk;
                                    hitInfo.voxel       = voxels [voxelIndex];
                                    hitInfo.point       = inPosition - normal;
                                    hitInfo.distance    = Mathf.Sqrt(distSqr);
                                    hitInfo.voxelIndex  = voxelIndex;
                                    hitInfo.voxelCenter = voxelCenter;
                                    if (localHitPos.y >= 0.495)
                                    {
                                        hitInfo.normal = Misc.vector3up;
                                    }
                                    else if (localHitPos.y <= -0.495)
                                    {
                                        hitInfo.normal = Misc.vector3down;
                                    }
                                    else if (localHitPos.x < -0.495)
                                    {
                                        hitInfo.normal = Misc.vector3left;
                                    }
                                    else if (localHitPos.x > 0.495)
                                    {
                                        hitInfo.normal = Misc.vector3right;
                                    }
                                    else if (localHitPos.z < -0.495)
                                    {
                                        hitInfo.normal = Misc.vector3back;
                                    }
                                    else if (localHitPos.z > 0.495)
                                    {
                                        hitInfo.normal = Misc.vector3forward;
                                    }

#if DEBUG_RAYCAST
                                    o = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                                    o.transform.localScale = Misc.Vector3one * 0.15f;
                                    o.transform.position   = inPosition;
                                    DestroyImmediate(o.GetComponent <Collider>());
                                    o.GetComponent <Renderer>().material.color = Color.red;
#endif

                                    return(chunk);
                                }
                            }
                        }

                        db.x = (fx + viewSign.x - inPosition.x) * v3.x;
                        db.y = (fy + viewSign.y - inPosition.y) * v3.y;
                        db.z = (fz + viewSign.z - inPosition.z) * v3.z;

                        db.x = db.x < 0 ? -db.x : db.x;
                        db.y = db.y < 0 ? -db.y : db.y;
                        db.z = db.z < 0 ? -db.z : db.z;

                        t        = db.x;
                        normal.x = viewDirSignOffset.x;
                        normal.y = 0;
                        normal.z = 0;
                        if (db.y < t)
                        {
                            t        = db.y;
                            normal.x = 0;
                            normal.y = viewDirSignOffset.y;
                        }
                        if (db.z < t)
                        {
                            t        = db.z;
                            normal.x = 0;
                            normal.y = 0;
                            normal.z = viewDirSignOffset.z;
                        }

                        inPosition.x += direction.x * t + normal.x;
                        inPosition.y += direction.y * t + normal.y;
                        inPosition.z += direction.z * t + normal.z;

//						notFirstVoxel = true;
                    }
                }

                db.x = (chunkX + viewSign16.x - position.x) * v3.x;
                db.y = (chunkY + viewSign16.y - position.y) * v3.y;
                db.z = (chunkZ + viewSign16.z - position.z) * v3.z;

                db.x = db.x < 0 ? -db.x : db.x;
                db.y = db.y < 0 ? -db.y : db.y;
                db.z = db.z < 0 ? -db.z : db.z;

                t        = db.x;
                normal.x = viewDirSignOffset.x;
                normal.y = 0;
                normal.z = 0;
                if (db.y < t)
                {
                    t        = db.y;
                    normal.x = 0;
                    normal.y = viewDirSignOffset.y;
                }
                if (db.z < t)
                {
                    t        = db.z;
                    normal.x = 0;
                    normal.y = 0;
                    normal.z = viewDirSignOffset.z;
                }

                position.x += direction.x * t + normal.x;
                position.y += direction.y * t + normal.y;
                position.z += direction.z * t + normal.z;

//				notFirstVoxel = true;
            }
            return(null);
        }
        private void FixedUpdate()
        {
            if (!hasCharacterController)
            {
                return;
            }

            float speed;

            GetInput(out speed);

            Vector3 pos = transform.position;

            if (isFlying || isInWater)
            {
                m_MoveDir  = m_Camera.transform.forward * m_Input.y + m_Camera.transform.right * m_Input.x + m_Camera.transform.up * m_Input.z;
                m_MoveDir *= speed;
                if (!isFlying)
                {
                    if (m_MoveDir.y < 0)
                    {
                        m_MoveDir.y += 0.1f * Time.fixedDeltaTime;
                    }
                    if (m_Jump)
                    {
                        // Check if player is next to terrain
                        if (env.CheckCollision(new Vector3(pos.x + m_Camera.transform.forward.x, pos.y, pos.z + m_Camera.transform.forward.z)))
                        {
                            m_MoveDir.y = jumpSpeed * 0.5f;
                            m_Jumping   = true;
                        }
                        m_Jump = false;
                    }
                    else
                    {
                        m_MoveDir += Physics.gravity * gravityMultiplier * Time.fixedDeltaTime * 0.5f;
                    }
                    if (pos.y > waterLevelTop && m_MoveDir.y > 0)
                    {
                        m_MoveDir.y = 0;                         // do not exit water
                    }
                    ProgressSwimCycle(m_CharacterController.velocity, swimSpeed);
                }
            }
            else
            {
                // always move along the camera forward as it is the direction that it being aimed at
                Vector3 desiredMove = transform.forward * m_Input.y + transform.right * m_Input.x;

                // get a normal for the surface that is being touched to move along it
                RaycastHit hitInfo;
                Physics.SphereCast(pos, m_CharacterController.radius, Misc.vector3down, out hitInfo,
                                   characterHeight / 2f, Physics.AllLayers, QueryTriggerInteraction.Ignore);
                desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized;

                m_MoveDir.x = desiredMove.x * speed;
                m_MoveDir.z = desiredMove.z * speed;
                if (m_CharacterController.isGrounded)
                {
                    m_MoveDir.y = -stickToGroundForce;

                    if (m_Jump)
                    {
                        m_MoveDir.y = jumpSpeed;
                        PlayJumpSound();
                        m_Jump    = false;
                        m_Jumping = true;
                    }
                }
                else
                {
                    m_MoveDir += Physics.gravity * gravityMultiplier * Time.fixedDeltaTime;
                }

                UpdateCameraPosition(speed);
                ProgressStepCycle(m_CharacterController.velocity, speed);
            }


            Vector3 finalMove = m_MoveDir * Time.fixedDeltaTime;
            Vector3 newPos    = pos + finalMove;
            bool    canMove   = !limitBoundsEnabled || limitBounds.Contains(newPos);

            if (m_PreviouslyGrounded && !isFlying && isCrouched)
            {
                // check if player is beyond the edge
                Ray ray = new Ray(newPos, Misc.vector3down);
                canMove = Physics.SphereCast(ray, 0.3f, 1f);
                // if player can't move, clamp movement along the edge and check again
                if (!canMove)
                {
                    if (Mathf.Abs(m_MoveDir.z) > Mathf.Abs(m_MoveDir.x))
                    {
                        m_MoveDir.x = 0;
                    }
                    else
                    {
                        m_MoveDir.z = 0;
                    }
                    finalMove  = m_MoveDir * Time.fixedDeltaTime;
                    newPos     = pos + finalMove;
                    ray.origin = newPos;
                    canMove    = Physics.SphereCast(ray, 0.3f, 1f);
                }
            }

            // if constructor is enabled, disable any movement if control key is pressed (reserved for special constructor actions)
            if (env.constructorMode && input.GetButton(InputButtonNames.LeftControl))
            {
                canMove = false;
            }
            if (canMove)
            {
                // autoclimb
                Vector3 dir     = new Vector3(m_MoveDir.x, 0, m_MoveDir.z);
                Vector3 basePos = new Vector3(pos.x, pos.y - characterHeight * 0.25f, pos.z);
                Ray     ray     = new Ray(basePos, dir);
                if (Physics.SphereCast(ray, 0.3f, 1f))
                {
                    m_CharacterController.stepOffset = 1.1f;
                }
                else
                {
                    m_CharacterController.stepOffset = 0.2f;
                }
                m_CollisionFlags = m_CharacterController.Move(finalMove);
            }
            isGrounded = m_CharacterController.isGrounded;

            // Check limits
            if (orbitMode)
            {
                if (FastVector.ClampDistance(ref lookAt, ref pos, minDistance, maxDistance))
                {
                    m_CharacterController.transform.position = pos;
                }
            }

            mouseLook.UpdateCursorLock();

            if (!isGrounded && !isFlying)
            {
                // Check current chunk
                VoxelChunk chunk = env.GetCurrentChunk();
                if (chunk != null && !chunk.isRendered)
                {
                    WaitForCurrentChunk();
                    return;
                }
            }
        }
        /// <summary>
        /// Performs the voxel damage.
        /// </summary>
        /// <returns>The actual damage taken by the voxe.</returns>
        /// <param name="hitInfo">Hit info.</param>
        /// <param name="damage">Damage.</param>
        /// <param name="addParticles">If set to <c>true</c> add particles.</param>
        int DamageVoxelFast(ref VoxelHitInfo hitInfo, int damage, bool addParticles, bool playSound, bool allowDamageEvent = true)
        {
            VoxelChunk chunk = hitInfo.chunk;

            if (hitInfo.voxel.typeIndex == 0)
            {
                return(0);
            }
            VoxelDefinition voxelType = hitInfo.voxel.type;
            byte            voxelTypeResistancePoints = voxelType.resistancePoints;

            if (buildMode)
            {
                if (damage > 0)
                {
                    damage = 255;
                }
            }
            else
            {
                if (voxelTypeResistancePoints == (byte)0)
                {
                    damage = 0;
                }
                else if (voxelTypeResistancePoints == (byte)255)
                {
                    if (playSound)
                    {
                        PlayImpactSound(hitInfo.voxel.type.impactSound, hitInfo.voxelCenter);
                    }
                    damage = 0;
                }
            }

            if (allowDamageEvent && OnVoxelDamaged != null)
            {
                OnVoxelDamaged(chunk, hitInfo.voxelIndex, ref damage);
            }

            if (damage == 0)
            {
                return(0);
            }

            // Gets ambient light near surface
            float voxelLight = GetVoxelLight(hitInfo.point + hitInfo.normal * 0.5f);

            // Get voxel damage indicator GO's name
            bool             destroyed            = voxelType.renderType == RenderType.CutoutCross;
            int              resistancePointsLeft = 0;
            VoxelPlaceholder placeholder          = null;

            if (!destroyed)
            {
                placeholder          = GetVoxelPlaceholder(chunk, hitInfo.voxelIndex, true);
                resistancePointsLeft = placeholder.resistancePointsLeft - damage;
                if (resistancePointsLeft < 0)
                {
                    resistancePointsLeft = 0;
                    destroyed            = true;
                }
                placeholder.resistancePointsLeft = resistancePointsLeft;
            }

            if (voxelType.renderType == RenderType.Empty)
            {
                addParticles = false;
            }

            int particlesAmount;

            if (destroyed)
            {
                // Add recoverable voxel on the scene (not for vegetation)
                if (voxelType.renderType != RenderType.Empty && voxelType.renderType != RenderType.CutoutCross && voxelType.canBeCollected && !buildMode)
                {
                    bool create = true;

                    if (OnVoxelBeforeDropItem != null)
                    {
                        OnVoxelBeforeDropItem(chunk, hitInfo, out create);
                    }
                    if (create)
                    {
                        CreateRecoverableVoxel(hitInfo.voxelCenter, voxelDefinitions [hitInfo.voxel.typeIndex], hitInfo.voxel.color);
                    }
                }

                // Destroy the voxel
                VoxelDestroyFast(chunk, hitInfo.voxelIndex);

                // Check if grass is on top and remove it as well
                VoxelChunk topChunk;
                int        topIndex;
                if (GetVoxelIndex(hitInfo.voxelCenter + Misc.vector3up, out topChunk, out topIndex, false))
                {
                    if (topChunk.voxels [topIndex].typeIndex != 0 && voxelDefinitions [topChunk.voxels [topIndex].typeIndex].renderType == RenderType.CutoutCross)
                    {
                        byte light = topChunk.voxels [topIndex].lightMesh;
                        topChunk.voxels [topIndex].Clear(light);
                        topChunk.modified = true;
                    }
                }

                // Max particles
                particlesAmount = 20;

                if (playSound)
                {
                    PlayDestructionSound(voxelDefinitions [hitInfo.voxel.typeIndex].destructionSound, hitInfo.voxelCenter);
                }
            }
            else
            {
                // Add damage indicator
                if (placeholder == null)
                {
                    placeholder = GetVoxelPlaceholder(chunk, hitInfo.voxelIndex, true);
                }
                if (placeholder.damageIndicator == null)
                {
                    if (damagedVoxelPrefab == null)
                    {
                        damagedVoxelPrefab = Resources.Load <GameObject> ("VoxelPlay/Prefabs/DamagedVoxel");
                    }
                    GameObject g = Instantiate <GameObject> (damagedVoxelPrefab);
                    g.name = DAMAGE_INDICATOR;
                    Transform tDamageIndicator = g.transform;
                    placeholder.damageIndicator = tDamageIndicator.GetComponent <Renderer> ();
                    tDamageIndicator.SetParent(placeholder.transform, false);
                    tDamageIndicator.localPosition = placeholder.bounds.center;
                    tDamageIndicator.localScale    = placeholder.bounds.size * 1.001f;
                }

                int textureIndex = FastMath.FloorToInt((5f * resistancePointsLeft) / voxelTypeResistancePoints);
                if (world.voxelDamageTextures.Length > 0)
                {
                    if (textureIndex >= world.voxelDamageTextures.Length)
                    {
                        textureIndex = world.voxelDamageTextures.Length - 1;
                    }
                    Material mi = placeholder.damageIndicatorMaterial;                     // gets a copy of material the first time it's used
                    mi.mainTexture = world.voxelDamageTextures [textureIndex];
                    mi.SetFloat("_VoxelLight", voxelLight);
                    placeholder.damageIndicator.enabled = true;
                }

                // Particle amount depending of damage
                particlesAmount = (6 - textureIndex) + 3;

                // Sets health recovery for the voxel
                placeholder.StartHealthRecovery(chunk, hitInfo.voxelIndex, world.damageDuration);

                if (playSound)
                {
                    PlayImpactSound(voxelDefinitions [hitInfo.voxel.typeIndex].impactSound, hitInfo.voxelCenter);
                }
            }

            // Add random particles
            if (!addParticles)
            {
                particlesAmount = 0;
            }
            for (int k = 0; k < particlesAmount; k++)
            {
                int ppeIndex = GetParticleFromPool();
                if (ppeIndex < 0)
                {
                    continue;
                }

                // Scale of particle
                Renderer particleRenderer = particlePool [ppeIndex].renderer;
                if (destroyed)
                {
                    if (voxelType.renderType == RenderType.CutoutCross)                         // smaller particles for vegetation
                    {
                        particleRenderer.transform.localScale = Misc.vector3one * Random.Range(0.03f, 0.04f);
                    }
                    else
                    {
                        particleRenderer.transform.localScale = Misc.vector3one * Random.Range(0.04f, 0.1f);
                    }
                }
                else
                {
                    particleRenderer.transform.localScale = Misc.vector3one * Random.Range(0.03f, 0.06f);
                }

                // Set particle texture
                Material instanceMat = particleRenderer.sharedMaterial;
                SetParticleMaterialTextures(instanceMat, voxelDefinitions [hitInfo.voxel.typeIndex], hitInfo.voxel.color);
                instanceMat.mainTextureOffset = new Vector2(Random.value, Random.value);
                instanceMat.mainTextureScale  = Misc.vector2one * 0.05f;
                instanceMat.SetFloat("_VoxelLight", voxelLight);
                instanceMat.SetFloat("_FlashDelay", 0);

                // Set position
                Rigidbody rb = particlePool [ppeIndex].rigidBody;
                if (destroyed)
                {
                    Vector3 expelDir = Random.insideUnitSphere;
                    Vector3 pos      = hitInfo.voxelCenter + expelDir;
                    particleRenderer.transform.position = pos;
                    rb.AddForce(expelDir * (Random.value * 125f));
                }
                else
                {
                    Vector3 pos = hitInfo.point;
                    Vector3 v1  = new Vector3(-hitInfo.normal.y, hitInfo.normal.z, hitInfo.normal.x);
                    Vector3 v2  = new Vector3(-hitInfo.normal.z, hitInfo.normal.x, hitInfo.normal.y);
                    Vector3 dx  = (Random.value - 0.5f) * 0.7f * v1;
                    Vector3 dy  = (Random.value - 0.5f) * 0.7f * v2;
                    particleRenderer.transform.position = pos + hitInfo.normal * 0.001f + dx + dy;
                    rb.AddForce(cameraMain.transform.forward * (Random.value * -125f));
                }
                rb.AddForce(Misc.vector3up * 25f);
                rb.AddTorque(Random.onUnitSphere * 100f);
                rb.useGravity = true;

                // Self-destruct
                particlePool [ppeIndex].destructionTime = Time.time + 2.5f + Random.value;
            }

            return(damage);
        }
Beispiel #24
0
        void SaveGameBinaryFormat_8(BinaryWriter bw)
        {
            if (cachedChunks == null)
            {
                return;
            }
            // Build a table with all voxel definitions used in modified chunks
            InitSaveGameStructs();
            int voxelDefinitionsCount = 0;
            int itemDefinitionsCount  = 0;
            int numChunks             = 0;

            // Pack used voxel and item definitions
            foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks)
            {
                if (kv.Value == null)
                {
                    continue;
                }
                VoxelChunk chunk = kv.Value.chunk;
                if (chunk != null && chunk.modified)
                {
                    numChunks++;
                    if (chunk.voxels != null)
                    {
                        VoxelDefinition last = null;
                        for (int k = 0; k < chunk.voxels.Length; k++)
                        {
                            VoxelDefinition vd = chunk.voxels [k].type;
                            if (vd == null || vd == last || vd.isDynamic || vd.doNotSave)
                            {
                                continue;
                            }
                            last = vd;
                            if (!saveVoxelDefinitionsDict.ContainsKey(vd))
                            {
                                saveVoxelDefinitionsDict [vd] = voxelDefinitionsCount++;
                                saveVoxelDefinitionsList.Add(vd.name);
                            }
                        }
                    }
                    if (chunk.items != null)
                    {
                        ItemDefinition last = null;
                        for (int k = 0; k < chunk.items.count; k++)
                        {
                            Item item = chunk.items.values [k];
                            if (item == null)
                            {
                                continue;
                            }
                            ItemDefinition id = item.itemDefinition;
                            if (id == null || id == last)
                            {
                                continue;
                            }
                            last = id;
                            if (!saveItemDefinitionsDict.ContainsKey(id))
                            {
                                saveItemDefinitionsDict [id] = itemDefinitionsCount++;
                                saveItemDefinitionsList.Add(id.name);
                            }
                        }
                    }
                    if (chunk.lightSources != null)
                    {
                        ItemDefinition last = null;
                        for (int k = 0; k < chunk.lightSources.Count; k++)
                        {
                            ItemDefinition id = chunk.lightSources [k].itemDefinition;
                            if (id == null || id == last)
                            {
                                continue;
                            }
                            last = id;
                            if (!saveItemDefinitionsDict.ContainsKey(id))
                            {
                                saveItemDefinitionsDict [id] = itemDefinitionsCount++;
                                saveItemDefinitionsList.Add(id.name);
                            }
                        }
                    }
                }
            }

            // Header
            bw.Write(SAVE_FILE_CURRENT_FORMAT);
            // Character controller transform position
            if (characterController != null)
            {
                EncodeVector3Binary(bw, characterController.transform.position);
                // Character controller transform rotation
                EncodeVector3Binary(bw, characterController.transform.rotation.eulerAngles);
            }
            else
            {
                EncodeVector3Binary(bw, Misc.vector3zero);
                EncodeVector3Binary(bw, Misc.vector3zero);
            }
            // Character controller's camera local rotation
            if (cameraMain != null)
            {
                EncodeVector3Binary(bw, cameraMain.transform.localRotation.eulerAngles);
            }
            else
            {
                EncodeVector3Binary(bw, Misc.vector3zero);
            }
            // Add voxel definitions table
            int vdCount = saveVoxelDefinitionsList.Count;

            bw.Write((Int16)vdCount);
            for (int k = 0; k < vdCount; k++)
            {
                bw.Write(saveVoxelDefinitionsList [k]);
            }
            // Add item definitions table
            int idCount = saveItemDefinitionsList.Count;

            bw.Write((Int16)idCount);
            for (int k = 0; k < idCount; k++)
            {
                bw.Write(saveItemDefinitionsList [k]);
            }
            // Add modified chunks
            bw.Write(numChunks);
            foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks)
            {
                if (kv.Value == null)
                {
                    continue;
                }
                VoxelChunk chunk = kv.Value.chunk;
                if (chunk != null && chunk.modified)
                {
                    ToggleHiddenVoxels(chunk, true);
                    WriteChunkData_8(bw, chunk);
                    ToggleHiddenVoxels(chunk, false);
                }
            }
        }
        /// <summary>
        /// Paints the terrain inside the chunk defined by its central "position"
        /// </summary>
        /// <returns><c>true</c>, if terrain was painted, <c>false</c> otherwise.</returns>
        public override bool PaintChunk(VoxelChunk chunk)
        {
            Vector3 position = chunk.position;

            if (position.y + VoxelPlayEnvironment.CHUNK_HALF_SIZE < minHeight)
            {
                chunk.isAboveSurface = false;
                return(false);
            }

            int bedrockRow = -1;

            if ((object)bedrockVoxel != null && position.y < minHeight + VoxelPlayEnvironment.CHUNK_HALF_SIZE)
            {
                bedrockRow = (int)(minHeight - (position.y - VoxelPlayEnvironment.CHUNK_HALF_SIZE) + 1) * ONE_Y_ROW;
            }

            position.x -= VoxelPlayEnvironment.CHUNK_HALF_SIZE;
            position.y -= VoxelPlayEnvironment.CHUNK_HALF_SIZE;
            position.z -= VoxelPlayEnvironment.CHUNK_HALF_SIZE;
            Vector3 pos;

            int waterLevel = env.waterLevel > 0 ? env.waterLevel : -1;

            Voxel[] voxels = chunk.voxels;

            bool hasContent     = false;
            bool isAboveSurface = false;

            for (int z = 0; z < VoxelPlayEnvironment.CHUNK_SIZE; z++)
            {
                pos.z = position.z + z;
                int arrayZIndex = z * ONE_Z_ROW;
                for (int x = 0; x < VoxelPlayEnvironment.CHUNK_SIZE; x++)
                {
                    pos.x = position.x + x;
                    HeightMapInfo heightMapInfo = env.GetHeightMapInfoFast(pos.x, pos.z);

                    float groundLevel  = heightMapInfo.groundLevel;
                    float surfaceLevel = waterLevel > groundLevel ? waterLevel : groundLevel;
                    if (surfaceLevel < position.y)
                    {
                        // position is above terrain or water
                        isAboveSurface = true;
                        continue;
                    }

                    int hindex = GetHeightIndex(pos.x, pos.z);
                    if (hindex < 0)
                    {
                        continue;
                    }
                    VoxelDefinition vd = heights [hindex].terrainVoxelTop;
                    if ((object)vd == null)
                    {
                        continue;
                    }

                    int y = (int)(surfaceLevel - position.y);
                    if (y >= VoxelPlayEnvironment.CHUNK_SIZE)
                    {
                        y = (VoxelPlayEnvironment.CHUNK_SIZE - 1);
                    }
                    pos.y = position.y + y;

                    // Place voxels
                    int voxelIndex = y * ONE_Y_ROW + arrayZIndex + x;
                    if (pos.y > groundLevel)
                    {
                        // water above terrain
                        if (pos.y == surfaceLevel)
                        {
                            isAboveSurface = true;
                        }
                        while (pos.y > groundLevel && voxelIndex >= 0)
                        {
                            voxels [voxelIndex].Set(waterVoxel);
                            voxelIndex -= ONE_Y_ROW;
                            pos.y--;
                        }
                    }
                    else if (pos.y == groundLevel)
                    {
                        isAboveSurface = true;
                        if (voxels [voxelIndex].hasContent == 0)
                        {
                            // surface => draw voxel top, vegetation and trees
                            voxels [voxelIndex].Set(vd);
#if UNITY_EDITOR
                            if (!env.draftModeActive)
                            {
#endif
                            // Check tree probability
                            if (pos.y > waterLevel)
                            {
                                ModelDefinition treeModel = heights [hindex].treeModel;
                                if (env.enableTrees && treeModel != null)
                                {
                                    env.RequestTreeCreation(chunk, pos, treeModel);
                                }
                                else if (env.enableVegetation)
                                {
                                    VoxelDefinition vegetation = heights [hindex].vegetationVoxel;
                                    if (vegetation != null)
                                    {
                                        if (voxelIndex >= (VoxelPlayEnvironment.CHUNK_SIZE - 1) * ONE_Y_ROW)
                                        {
                                            env.RequestVegetationCreation(chunk.top, voxelIndex - ONE_Y_ROW * (VoxelPlayEnvironment.CHUNK_SIZE - 1), vegetation);
                                        }
                                        else
                                        {
                                            voxels [voxelIndex + ONE_Y_ROW].Set(vegetation);
                                        }
                                        env.vegetationCreated++;
                                    }
                                }
                            }
#if UNITY_EDITOR
                        }
#endif
                        }
                        voxelIndex -= ONE_Y_ROW;
                    }

                    // Continue filling down
                    vd = heights [hindex].terrainVoxelDirt;
                    while (voxelIndex > bedrockRow)
                    {
                        if (voxels [voxelIndex].hasContent == 0)
                        {
                            voxels [voxelIndex].SetFastOpaque(vd);
                        }
                        voxelIndex -= ONE_Y_ROW;
                    }
                    if (bedrockRow >= 0)
                    {
                        voxels [voxelIndex].SetFastOpaque(bedrockVoxel);
                    }
                    hasContent = true;
                }
            }

            chunk.isAboveSurface = isAboveSurface;
            return(hasContent);
        }
Beispiel #26
0
        void WriteChunkData_8(BinaryWriter bw, VoxelChunk chunk)
        {
            // Chunk position
            EncodeVector3Binary(bw, chunk.position);
            bw.Write(chunk.isAboveSurface ? (byte)1 : (byte)0);

            int             voxelDefinitionIndex = 0;
            VoxelDefinition prevVD = null;


            // Count voxels words
            int k        = 0;
            int numWords = 0;

            while (k < chunk.voxels.Length)
            {
                if (chunk.voxels [k].hasContent == 1)
                {
                    VoxelDefinition voxelDefinition = chunk.voxels [k].type;
                    if (voxelDefinition.isDynamic)
                    {
                        k++;
                        continue;
                    }
                    if (voxelDefinition != prevVD)
                    {
                        if (!saveVoxelDefinitionsDict.TryGetValue(voxelDefinition, out voxelDefinitionIndex))
                        {
                            k++;
                            continue;
                        }
                        prevVD = voxelDefinition;
                    }
                    Color32 tintColor = chunk.voxels [k].color;
                    int     flags     = 0;
                    if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation())
                    {
                        flags = chunk.voxels [k].GetFlags();
                    }
                    k++;
                    while (k < chunk.voxels.Length && chunk.voxels [k].type == voxelDefinition && chunk.voxels [k].color.r == tintColor.r && chunk.voxels [k].color.g == tintColor.g && chunk.voxels [k].color.b == tintColor.b && voxelDefinition.renderType != RenderType.Custom && chunk.voxels [k].GetFlags() == flags)
                    {
                        k++;
                    }
                    numWords++;
                }
                else
                {
                    k++;
                }
            }
            bw.Write((Int16)numWords);

            // Write voxels
            k = 0;
            while (k < chunk.voxels.Length)
            {
                if (chunk.voxels [k].hasContent == 1)
                {
                    int             voxelIndex      = k;
                    VoxelDefinition voxelDefinition = chunk.voxels [k].type;
                    if (voxelDefinition.isDynamic)
                    {
                        k++;
                        continue;
                    }
                    if (voxelDefinition != prevVD)
                    {
                        if (!saveVoxelDefinitionsDict.TryGetValue(voxelDefinition, out voxelDefinitionIndex))
                        {
                            k++;
                            continue;
                        }
                        prevVD = voxelDefinition;
                    }
                    Color32 tintColor = chunk.voxels [k].color;
                    byte    flags     = 0;
                    if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation())
                    {
                        flags = chunk.voxels [k].GetFlags();
                    }
                    int repetitions = 1;
                    k++;
                    while (k < chunk.voxels.Length && chunk.voxels [k].type == voxelDefinition && chunk.voxels [k].color.r == tintColor.r && chunk.voxels [k].color.g == tintColor.g && chunk.voxels [k].color.b == tintColor.b && voxelDefinition.renderType != RenderType.Custom && chunk.voxels [k].GetFlags() == flags)
                    {
                        repetitions++;
                        k++;
                    }
                    bw.Write((Int16)voxelDefinitionIndex);
                    bw.Write(tintColor.r);
                    bw.Write(tintColor.g);
                    bw.Write(tintColor.b);
                    bw.Write((Int16)voxelIndex);
                    bw.Write((Int16)repetitions);
                    bw.Write(flags);
                    if (voxelDefinition.renderType == RenderType.Custom)
                    {
                        // Check rotation
                        VoxelPlaceholder placeholder = GetVoxelPlaceholder(chunk, voxelIndex, false);
                        if (placeholder != null)
                        {
                            Vector3 angles = placeholder.transform.eulerAngles;
                            if (angles.x != 0 || angles.y != 0 || angles.z != 0)
                            {
                                bw.Write((byte)1);                                  // has rotation
                                EncodeVector3Binary(bw, angles);
                            }
                            else
                            {
                                bw.Write((byte)0);
                            }
                        }
                        else
                        {
                            bw.Write((byte)0);
                        }
                    }
                    else
                    {
                        bw.Write((byte)0);
                    }
                }
                else
                {
                    k++;
                }
            }

            // Write number of light sources
            int lightCount = chunk.lightSources != null ? chunk.lightSources.Count : 0;

            bw.Write((Int16)lightCount);
            if (lightCount > 0)
            {
                for (k = 0; k < lightCount; k++)
                {
                    LightSource    lightSource = chunk.lightSources [k];
                    int            voxelIndex  = lightSource.hitInfo.voxelIndex;;
                    Vector3        normal      = lightSource.hitInfo.normal;
                    int            itemIndex   = 0;
                    ItemDefinition id          = lightSource.itemDefinition;
                    if (id != null)
                    {
                        saveItemDefinitionsDict.TryGetValue(id, out itemIndex);
                    }
                    bw.Write((Int16)voxelIndex);
                    EncodeVector3Binary(bw, normal);
                    bw.Write((Int16)itemIndex);
                }
            }

            // Write number of items
            int itemCount = chunk.items != null ? chunk.items.count : 0;

            bw.Write((Int16)itemCount);
            if (itemCount > 0)
            {
                for (k = 0; k < itemCount; k++)
                {
                    Int16   itemIndex    = 0;
                    Int16   itemQuantity = 0;
                    Vector3 itemPosition = Misc.vector3zero;
                    Item    item         = chunk.items.values [k];
                    if (item != null && item.itemDefinition != null)
                    {
                        ItemDefinition id = item.itemDefinition;
                        int            idIndex;
                        if (saveItemDefinitionsDict.TryGetValue(id, out idIndex))
                        {
                            itemIndex    = ((Int16)idIndex);
                            itemPosition = item.transform.position;
                            bw.Write((Int16)item.quantity);
                        }
                    }
                    bw.Write(itemIndex);
                    EncodeVector3Binary(bw, itemPosition);
                    bw.Write(itemQuantity);
                }
            }
        }
Beispiel #27
0
        /// <summary>
        /// Uses geometry-shader-based materials and mesh to render the chunk
        /// </summary>
        void GenerateMeshData_Geo(int jobIndex)
        {
            VoxelChunk chunk = meshJobs [jobIndex].chunk;

            tempChunkVertices    = meshJobs [jobIndex].vertices;
            tempChunkUV0         = meshJobs [jobIndex].uv0;
            tempChunkUV2         = meshJobs [jobIndex].uv2;
            tempChunkColors32    = meshJobs [jobIndex].colors;
            meshColliderVertices = meshJobs [jobIndex].colliderVertices;
            meshColliderIndices  = meshJobs [jobIndex].colliderIndices;
            navMeshVertices      = meshJobs [jobIndex].navMeshVertices;
            navMeshIndices       = meshJobs [jobIndex].navMeshIndices;
            mivs = meshJobs [jobIndex].mivs;

            tempChunkVertices.Clear();
            tempChunkUV0.Clear();
            tempChunkColors32.Clear();
            if (enableColliders)
            {
                meshColliderIndices.Clear();
                meshColliderVertices.Clear();
                if (enableNavMesh)
                {
                    navMeshIndices.Clear();
                    navMeshVertices.Clear();
                }
            }
            mivs.Clear();

            Voxel[] voxels = chunk.voxels;
            Vector4 uvAux, uv2Aux;

            tempChunkUV2.Clear();

            int          chunkUVIndex    = -1;
            int          chunkVoxelCount = 0;
            Color32      tintColor       = Misc.color32White;
            Vector3      pos             = Misc.vector3zero;
            ModelInVoxel miv             = new ModelInVoxel();

            const int V_ONE_Y_ROW = 18 * 18;
            const int V_ONE_Z_ROW = 18;

            int voxelIndex     = 0;
            int voxelSignature = 1;

            for (int y = 0; y < 16; y++)
            {
                int vy = (y + 1) * 18 * 18;
                for (int z = 0; z < 16; z++)
                {
                    int vyz = vy + (z + 1) * 18;
                    for (int x = 0; x < 16; x++, voxelIndex++)
                    {
                        voxels [voxelIndex].lightMesh = voxels [voxelIndex].light;
                        if (voxels [voxelIndex].hasContent != 1)
                        {
                            continue;
                        }

                        int vxyz = vyz + x + 1;

                        int     vindex = vxyz - 1;
                        Voxel[] chunk_middle_middle_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_middle_left       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz + 1;
                        Voxel[] chunk_middle_middle_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_middle_right       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz + V_ONE_Y_ROW;
                        Voxel[] chunk_top_middle_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_middle_middle       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz - V_ONE_Y_ROW;
                        Voxel[] chunk_bottom_middle_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_middle_middle       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz + V_ONE_Z_ROW;
                        Voxel[] chunk_middle_forward_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_forward_middle       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz - V_ONE_Z_ROW;
                        Voxel[] chunk_middle_back_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_back_middle       = virtualChunk [vindex].voxelIndex;

                        // If voxel is surrounded by material, don't render
                        int v1u = chunk_top_middle_middle [top_middle_middle].opaque;
                        int v1f = chunk_middle_forward_middle [middle_forward_middle].opaque;
                        int v1b = chunk_middle_back_middle [middle_back_middle].opaque;
                        int v1l = chunk_middle_middle_left [middle_middle_left].opaque;
                        int v1r = chunk_middle_middle_right [middle_middle_right].opaque;
                        int v1d = chunk_bottom_middle_middle [bottom_middle_middle].opaque;
                        if (v1u + v1f + v1b + v1l + v1r + v1d == 90)                         // 90 = 15 * 6
                        {
                            continue;
                        }

                        // top
                        vindex = vxyz + V_ONE_Y_ROW + V_ONE_Z_ROW - 1;
                        Voxel[] chunk_top_forward_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_forward_left       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_top_forward_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_forward_middle       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_top_forward_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_forward_right       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz + V_ONE_Y_ROW - 1;
                        Voxel[] chunk_top_middle_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_middle_left       = virtualChunk [vindex].voxelIndex;

                        vindex += 2;
                        Voxel[] chunk_top_middle_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_middle_right       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz + V_ONE_Y_ROW - V_ONE_Z_ROW - 1;
                        Voxel[] chunk_top_back_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_back_left       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_top_back_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_back_middle       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_top_back_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     top_back_right       = virtualChunk [vindex].voxelIndex;

                        // middle
                        vindex = vxyz + V_ONE_Z_ROW - 1;
                        Voxel[] chunk_middle_forward_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_forward_left       = virtualChunk [vindex].voxelIndex;

                        vindex += 2;
                        Voxel[] chunk_middle_forward_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_forward_right       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz - V_ONE_Z_ROW - 1;
                        Voxel[] chunk_middle_back_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_back_left       = virtualChunk [vindex].voxelIndex;

                        vindex += 2;
                        Voxel[] chunk_middle_back_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     middle_back_right       = virtualChunk [vindex].voxelIndex;

                        // bottom
                        vindex = vxyz - V_ONE_Y_ROW + V_ONE_Z_ROW - 1;
                        Voxel[] chunk_bottom_forward_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_forward_left       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_bottom_forward_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_forward_middle       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_bottom_forward_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_forward_right       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz - V_ONE_Y_ROW - 1;
                        Voxel[] chunk_bottom_middle_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_middle_left       = virtualChunk [vindex].voxelIndex;

                        vindex += 2;
                        Voxel[] chunk_bottom_middle_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_middle_right       = virtualChunk [vindex].voxelIndex;

                        vindex = vxyz - V_ONE_Y_ROW - V_ONE_Z_ROW - 1;
                        Voxel[] chunk_bottom_back_left = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_back_left       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_bottom_back_middle = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_back_middle       = virtualChunk [vindex].voxelIndex;

                        vindex++;
                        Voxel[] chunk_bottom_back_right = chunk9 [virtualChunk [vindex].chunk9Index];
                        int     bottom_back_right       = virtualChunk [vindex].voxelIndex;


                        pos.y = y - 7.5f;
                        pos.z = z - 7.5f;
                        pos.x = x - 7.5f;

                        voxelSignature += voxelIndex;
                        chunkVoxelCount++;

                        VoxelDefinition       type    = voxelDefinitions [voxels [voxelIndex].typeIndex];
                        FastFixedBuffer <int> indices = tempGeoIndices[type.materialBufferIndex];

                        uvAux.x = type.textureIndexSide;
                        uvAux.y = type.textureIndexTop;
                        uvAux.z = type.textureIndexBottom;
                        uvAux.w = 0;

                        int occlusionX = 0;
                        int occlusionY = 0;
                        int occlusionZ = 0;
                        int occlusionW = 0;
                        int occ        = 0;

                        RenderType rt = type.renderType;
                        switch (rt)
                        {
                        case RenderType.Water:
                        {
                            ++chunkUVIndex;
                            indices.Add(chunkUVIndex);

                            uvAux.w = voxels [voxelIndex].light / 15f;

                            int hf = chunk_middle_forward_middle [middle_forward_middle].GetWaterLevel();
                            int hb = chunk_middle_back_middle [middle_back_middle].GetWaterLevel();
                            int hr = chunk_middle_middle_right [middle_middle_right].GetWaterLevel();
                            int hl = chunk_middle_middle_left [middle_middle_left].GetWaterLevel();

                            // compute water neighbours and account for foam
                            // back
                            occ = chunk_middle_back_middle [middle_back_middle].hasContent;
                            if (occ == 1)
                            {
                                // occlusionX bit = 0 means that face is visible
                                occlusionX |= 1;
                                if (hb == 0)
                                {
                                    occlusionY |= 1;
                                }
                            }

                            // front
                            occ = chunk_middle_forward_middle [middle_forward_middle].hasContent;
                            if (occ == 1)
                            {
                                occlusionX |= (1 << 1);
                                if (hf == 0)
                                {
                                    occlusionY |= 2;
                                }
                            }

                            int wh = voxels [voxelIndex].GetWaterLevel();
                            int th = chunk_top_middle_middle [top_middle_middle].GetWaterLevel();

                            // top (hide only if water level is full or voxel on top is water)
                            if (wh == 15 || th > 0)
                            {
                                occlusionX += ((chunk_top_middle_middle [top_middle_middle].hasContent & 1) << 2);
                            }

                            // down
                            occlusionX += ((chunk_bottom_middle_middle [bottom_middle_middle].hasContent & 1) << 3);

                            // left
                            occ = chunk_middle_middle_left [middle_middle_left].hasContent;
                            if (occ == 1)
                            {
                                occlusionX |= (1 << 4);
                                if (hl == 0)
                                {
                                    occlusionY |= 4;
                                }
                            }
                            // right
                            occ = chunk_middle_middle_right [middle_middle_right].hasContent;
                            if (occ == 1)
                            {
                                occlusionX += (1 << 5);
                                if (hr == 0)
                                {
                                    occlusionY |= 8;
                                }
                            }

                            // If there's water on top, full size
                            if (th > 0)
                            {
                                occlusionW  = 15 + (15 << 4) + (15 << 8) + (15 << 12);                          // full height
                                occlusionY += (1 << 8) + (1 << 10);                                             // neutral/no flow
                            }
                            else
                            {
                                // Get corners heights
                                int hfr = chunk_middle_forward_right [middle_forward_right].GetWaterLevel();
                                int hbr = chunk_middle_back_right [middle_back_right].GetWaterLevel();
                                int hbl = chunk_middle_back_left [middle_back_left].GetWaterLevel();
                                int hfl = chunk_middle_forward_left [middle_forward_left].GetWaterLevel();

                                // corner foam
                                if (type.showFoam)
                                {
                                    if (hbl == 0)
                                    {
                                        occlusionY |= chunk_middle_back_left [middle_back_left].hasContent << 4;
                                    }
                                    if (hfl == 0)
                                    {
                                        occlusionY |= chunk_middle_forward_left [middle_forward_left].hasContent << 5;
                                    }
                                    if (hfr == 0)
                                    {
                                        occlusionY |= chunk_middle_forward_right [middle_forward_right].hasContent << 6;
                                    }
                                    if (hbr == 0)
                                    {
                                        occlusionY |= chunk_middle_back_right [middle_back_right].hasContent << 7;
                                    }
                                }

                                int tf  = chunk_top_forward_middle [top_forward_middle].GetWaterLevel();
                                int tfr = chunk_top_forward_right [top_forward_right].GetWaterLevel();
                                int tr  = chunk_top_middle_right [top_middle_right].GetWaterLevel();
                                int tbr = chunk_top_back_right [top_back_right].GetWaterLevel();
                                int tb  = chunk_top_back_middle [top_back_middle].GetWaterLevel();
                                int tbl = chunk_top_back_left [top_back_left].GetWaterLevel();
                                int tl  = chunk_top_middle_left [top_middle_left].GetWaterLevel();
                                int tfl = chunk_top_forward_left [top_forward_left].GetWaterLevel();

                                // forward right corner
                                if (tf * hf + tfr * hfr + tr * hr > 0)
                                {
                                    hfr = 15;
                                }
                                else
                                {
                                    hfr = wh > hfr ? wh : hfr;
                                    if (hf > hfr)
                                    {
                                        hfr = hf;
                                    }
                                    if (hr > hfr)
                                    {
                                        hfr = hr;
                                    }
                                }
                                // bottom right corner
                                if (tr * hr + tbr * hbr + tb * hb > 0)
                                {
                                    hbr = 15;
                                }
                                else
                                {
                                    hbr = wh > hbr ? wh : hbr;
                                    if (hr > hbr)
                                    {
                                        hbr = hr;
                                    }
                                    if (hb > hbr)
                                    {
                                        hbr = hb;
                                    }
                                }
                                // bottom left corner
                                if (tb * hb + tbl * hbl + tl * hl > 0)
                                {
                                    hbl = 15;
                                }
                                else
                                {
                                    hbl = wh > hbl ? wh : hbl;
                                    if (hb > hbl)
                                    {
                                        hbl = hb;
                                    }
                                    if (hl > hbl)
                                    {
                                        hbl = hl;
                                    }
                                }
                                // forward left corner
                                if (tl * hl + tfl * hfl + tf * hf > 0)
                                {
                                    hfl = 15;
                                }
                                else
                                {
                                    hfl = wh > hfl ? wh : hfl;
                                    if (hl > hfl)
                                    {
                                        hfl = hl;
                                    }
                                    if (hf > hfl)
                                    {
                                        hfl = hf;
                                    }
                                }
                                occlusionW = hfr + (hbr << 4) + (hbl << 8) + (hfl << 12);

                                // flow
                                int fx = hfr + hbr - hfl - hbl;
                                if (fx > 0)
                                {
                                    fx = 2;
                                }
                                else if (fx < 0)
                                {
                                    fx = 0;
                                }
                                else
                                {
                                    fx = 1;
                                }
                                int fz = hfl + hfr - hbl - hbr;
                                if (fz > 0)
                                {
                                    fz = 2;
                                }
                                else if (fz < 0)
                                {
                                    fz = 0;
                                }
                                else
                                {
                                    fz = 1;
                                }

                                occlusionY += (fx << 8) + (fz << 10);
                            }

                            if (!type.showFoam)
                            {
                                occlusionY &= 0xFF00;
                            }
                            pos.y -= 0.5f;
                            tempChunkVertices.Add(pos);
                        }
                        break;

                        case RenderType.CutoutCross:
                        {
                            ++chunkUVIndex;
                            indices.Add(chunkUVIndex);
                            uvAux.w = voxels [voxelIndex].light / 15f;
                            Vector3 aux    = pos;
                            float   random = WorldRand.GetValue(pos);
                            uvAux.w *= 1f + (random - 0.45f) * type.colorVariation;                                     // * (1f + random * 0.3f;  // adds color variation
                            pos.x   += random * 0.5f - 0.25f;
                            aux.x   += 1f;
                            random   = WorldRand.GetValue(aux);
                            pos.z   += random * 0.5f - 0.25f;
                            pos.y   -= random * 0.1f;
                            tempChunkVertices.Add(pos);
                        }
                        break;

                        case RenderType.OpaqueNoAO:
                            ++chunkUVIndex;
                            tempChunkVertices.Add(pos);

                            v1b     = (v1b + 1) >> 4;                           // substitutes a comparisson with FULL_OPAQUE (15)
                            v1f     = (v1f + 1) >> 4;
                            v1u     = (v1u + 1) >> 4;
                            v1d     = (v1d + 1) >> 4;
                            v1l     = (v1l + 1) >> 4;
                            v1r     = (v1r + 1) >> 4;
                            uvAux.w = v1b + (v1f << 1) + (v1u << 2) + (v1d << 3) + (v1l << 4) + (v1r << 5);
                            indices.Add(chunkUVIndex);
                            break;

                        case RenderType.Transp6tex:
                        {
                            ++chunkUVIndex;
                            indices.Add(chunkUVIndex);
                            int rotation = voxels [voxelIndex].GetTextureRotation();
                            uvAux   = type.textureSideIndices [rotation].xyzw;
                            uvAux.w = voxels [voxelIndex].light;

                            // Avoid overlapping faces of same glass material
                            int typeIndex = voxels [voxelIndex].typeIndex;

                            // back
                            if (v1b == FULL_OPAQUE || chunk_middle_back_middle [middle_back_middle].typeIndex == typeIndex)
                            {
                                // occlusionX bit = 0 means that face is visible
                                occlusionX |= 1;
                            }

                            // front
                            if (v1f == FULL_OPAQUE || chunk_middle_forward_middle [middle_forward_middle].typeIndex == typeIndex)
                            {
                                occlusionX |= 2;
                            }

                            // up
                            if (v1u == FULL_OPAQUE || chunk_top_middle_middle [top_middle_middle].typeIndex == typeIndex)
                            {
                                occlusionX |= 4;
                            }

                            // down
                            if (v1d == FULL_OPAQUE || chunk_bottom_middle_middle [bottom_middle_middle].typeIndex == typeIndex)
                            {
                                occlusionX |= 8;
                            }

                            // left
                            if (v1l == FULL_OPAQUE || chunk_middle_middle_left [middle_middle_left].typeIndex == typeIndex)
                            {
                                occlusionX |= 16;
                            }
                            // right
                            if (v1r == FULL_OPAQUE || chunk_middle_middle_right [middle_middle_right].typeIndex == typeIndex)
                            {
                                occlusionX |= 32;
                            }
                            tempChunkVertices.Add(pos);

                            // Pass custom alpha
                            occlusionY = (int)(255 * type.alpha);

                            // Add collider data
                            if (enableColliders)
                            {
                                if (v1b == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Back, x, y, z);
                                }
                                if (v1f == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Forward, x, y, z);
                                }
                                if (v1u == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Top, x, z, y);
                                }
                                if (v1d == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Bottom, x, z, y);
                                }
                                if (v1l == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Left, z, y, x);
                                }
                                if (v1r == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Right, z, y, x);
                                }
                                if (enableNavMesh && type.navigatable)
                                {
                                    if (v1b == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Back, x, y, z);
                                    }
                                    if (v1f == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Forward, x, y, z);
                                    }
                                    if (v1u == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Top, x, z, y);
                                    }
                                    if (v1l == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Left, z, y, x);
                                    }
                                    if (v1r == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Right, z, y, x);
                                    }
                                }
                            }
                        }
                        break;

                        default:                         //case RenderType.Custom:
                            miv.voxelIndex = voxelIndex;
                            miv.vd         = type;
                            mivs.Add(miv);
                            continue;

                        case RenderType.Empty:
                        {
                            v1b = (v1b + 1) >> 4;                                       // substitutes a comparison with FULL_OPAQUE (15)
                            v1f = (v1f + 1) >> 4;
                            v1u = (v1u + 1) >> 4;
                            v1d = (v1d + 1) >> 4;
                            v1l = (v1l + 1) >> 4;
                            v1r = (v1r + 1) >> 4;

                            // Add collider data
                            if (enableColliders)
                            {
                                if (v1b == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Back, x, y, z);
                                }
                                if (v1f == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Forward, x, y, z);
                                }
                                if (v1u == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Top, x, z, y);
                                }
                                if (v1d == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Bottom, x, z, y);
                                }
                                if (v1l == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Left, z, y, x);
                                }
                                if (v1r == 0)
                                {
                                    greedyCollider.AddQuad(FaceDirection.Right, z, y, x);
                                }
                                if (enableNavMesh && type.navigatable)
                                {
                                    if (v1b == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Back, x, y, z);
                                    }
                                    if (v1f == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Forward, x, y, z);
                                    }
                                    if (v1u == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Top, x, z, y);
                                    }
                                    if (v1l == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Left, z, y, x);
                                    }
                                    if (v1r == 0)
                                    {
                                        greedyNavMesh.AddQuad(FaceDirection.Right, z, y, x);
                                    }
                                }
                            }
                        }
                            continue;                             // loop

                        case RenderType.Opaque:
                        case RenderType.Opaque6tex:
                        case RenderType.Cutout:                         // Opaque & Cutout
                            ++chunkUVIndex;
                            tempChunkVertices.Add(pos);

                            if (rt == RenderType.Cutout)
                            {
                                indices.Add(chunkUVIndex);
                            }
                            else
                            {
                                indices.Add(chunkUVIndex);
                                int rotation = voxels [voxelIndex].GetTextureRotation();
                                uvAux = type.textureSideIndices [rotation].xyzw;
                            }

                            if (denseTrees && rt == RenderType.Cutout)
                            {
                                uvAux.w = 0;
                            }
                            else
                            {
                                v1b     = (v1b + 1) >> 4;                               // substitutes a comparisson with FULL_OPAQUE (15)
                                v1f     = (v1f + 1) >> 4;
                                v1u     = (v1u + 1) >> 4;
                                v1d     = (v1d + 1) >> 4;
                                v1l     = (v1l + 1) >> 4;
                                v1r     = (v1r + 1) >> 4;
                                uvAux.w = v1b + (v1f << 1) + (v1u << 2) + (v1d << 3) + (v1l << 4) + (v1r << 5);

                                // Add collider data
                                if (enableColliders && rt != RenderType.Cutout)
                                {
                                    if (v1b == 0)
                                    {
                                        greedyCollider.AddQuad(FaceDirection.Back, x, y, z);
                                    }
                                    if (v1f == 0)
                                    {
                                        greedyCollider.AddQuad(FaceDirection.Forward, x, y, z);
                                    }
                                    if (v1u == 0)
                                    {
                                        greedyCollider.AddQuad(FaceDirection.Top, x, z, y);
                                    }
                                    if (v1d == 0)
                                    {
                                        greedyCollider.AddQuad(FaceDirection.Bottom, x, z, y);
                                    }
                                    if (v1l == 0)
                                    {
                                        greedyCollider.AddQuad(FaceDirection.Left, z, y, x);
                                    }
                                    if (v1r == 0)
                                    {
                                        greedyCollider.AddQuad(FaceDirection.Right, z, y, x);
                                    }
                                    if (enableNavMesh && type.navigatable)
                                    {
                                        if (v1b == 0)
                                        {
                                            greedyNavMesh.AddQuad(FaceDirection.Back, x, y, z);
                                        }
                                        if (v1f == 0)
                                        {
                                            greedyNavMesh.AddQuad(FaceDirection.Forward, x, y, z);
                                        }
                                        if (v1u == 0)
                                        {
                                            greedyNavMesh.AddQuad(FaceDirection.Top, x, z, y);
                                        }
                                        if (v1l == 0)
                                        {
                                            greedyNavMesh.AddQuad(FaceDirection.Left, z, y, x);
                                        }
                                        if (v1r == 0)
                                        {
                                            greedyNavMesh.AddQuad(FaceDirection.Right, z, y, x);
                                        }
                                    }
                                }
                            }
                            if (rt == RenderType.Cutout)
                            {
                                // Add color variation
                                float random = WorldRand.GetValue(pos);
                                int   r      = (int)(255f * (1f + (random - 0.45f) * type.colorVariation));
                                uvAux.w += (r << 6);
                                if (type.windAnimation)
                                {
                                    uvAux.w += 65536;                                     //1 << 16;
                                }
                            }

                            int lu = chunk_top_middle_middle [top_middle_middle].light;
                            int ll = chunk_middle_middle_left [middle_middle_left].light;
                            int lf = chunk_middle_forward_middle [middle_forward_middle].light;
                            int lr = chunk_middle_middle_right [middle_middle_right].light;
                            int lb = chunk_middle_back_middle [middle_back_middle].light;
                            int ld = chunk_bottom_middle_middle [bottom_middle_middle].light;

                                                                                        #if UNITY_EDITOR
                            if (enableSmoothLighting && !draftModeActive)
                            {
                                                                #else
                            if (enableSmoothLighting)
                            {
                                                                #endif
                                int v2r  = chunk_top_middle_right [top_middle_right].light;
                                int v2br = chunk_top_back_right [top_back_right].light;
                                int v2b  = chunk_top_back_middle [top_back_middle].light;
                                int v2bl = chunk_top_back_left [top_back_left].light;
                                int v2l  = chunk_top_middle_left [top_middle_left].light;
                                int v2fl = chunk_top_forward_left [top_forward_left].light;
                                int v2f  = chunk_top_forward_middle [top_forward_middle].light;
                                int v2fr = chunk_top_forward_right [top_forward_right].light;

                                int v1fr = chunk_middle_forward_right [middle_forward_right].light;
                                int v1br = chunk_middle_back_right [middle_back_right].light;
                                int v1bl = chunk_middle_back_left [middle_back_left].light;
                                int v1fl = chunk_middle_forward_left [middle_forward_left].light;

                                int v0r  = chunk_bottom_middle_right [bottom_middle_right].light;
                                int v0br = chunk_bottom_back_right [bottom_back_right].light;
                                int v0b  = chunk_bottom_back_middle [bottom_back_middle].light;
                                int v0bl = chunk_bottom_back_left [bottom_back_left].light;
                                int v0l  = chunk_bottom_middle_left [bottom_middle_left].light;
                                int v0fl = chunk_bottom_forward_left [bottom_forward_left].light;
                                int v0f  = chunk_bottom_forward_middle [bottom_forward_middle].light;
                                int v0fr = chunk_bottom_forward_right [bottom_forward_right].light;

                                // Backwards face
                                // Vertex 0
                                occ         = ((lb + v0b + v0bl + v1bl) >> 2);
                                occlusionX += occ;
                                // Vertex 1
                                occ         = ((lb + v0b + v0br + v1br) >> 2);
                                occlusionX += occ << 4;
                                // Vertex 2
                                occ         = ((lb + v1bl + v2bl + v2b) >> 2);
                                occlusionX += occ << 8;
                                // Vertex 3
                                occ         = ((lb + v2b + v2br + v1br) >> 2);
                                occlusionX += occ << 12;

                                // Forward face
                                // Vertex 5
                                occ         = ((lf + v0f + v0fr + v1fr) >> 2);
                                occlusionX += occ << 16;
                                // Vertex 4
                                occ         = ((lf + v0f + v0fl + v1fl) >> 2);
                                occlusionX += occ << 20;
                                // Vertex 6
                                occ         = ((lf + v1fr + v2fr + v2f) >> 2);
                                occlusionY += occ;
                                // Vertex 7
                                occ         = ((lf + v2f + v2fl + v1fl) >> 2);
                                occlusionY += occ << 4;

                                // Top face
                                // Vertex 2
                                occ         = ((lu + v2b + v2bl + v2l) >> 2);
                                occlusionY += occ << 8;
                                // Vertex 3
                                occ         = ((lu + v2b + v2br + v2r) >> 2);
                                occlusionY += occ << 12;
                                // Vertex 7
                                occ         = ((lu + v2l + v2fl + v2f)) >> 2;
                                occlusionY += occ << 16;
                                // Vertex 6
                                occ         = ((lu + v2r + v2fr + v2f) >> 2);
                                occlusionY += occ << 20;

                                // Left face
                                // Vertex 0
                                occ         = ((ll + v1bl + v0l + v0bl) >> 2);
                                occlusionZ += occ;
                                // Vertex 4
                                occ         = ((ll + v1fl + v0l + v0fl) >> 2);
                                occlusionZ += occ << 4;
                                // Vertex 2
                                occ         = ((ll + v2l + v2bl + v1bl) >> 2);
                                occlusionZ += occ << 8;
                                // Vertex 7
                                occ         = ((ll + v2l + v2fl + v1fl) >> 2);
                                occlusionZ += occ << 12;

                                // Right face
                                // Vertex 1
                                occ         = ((lr + v1br + v0r + v0br) >> 2);
                                occlusionZ += occ << 16;
                                // Vertex 5
                                occ         = ((lr + v1fr + v0r + v0fr) >> 2);
                                occlusionZ += occ << 20;
                                // Vertex 3
                                occ         = ((lr + v2r + v2br + v1br) >> 2);
                                occlusionW += occ;
                                // Vertex 6
                                occ         = ((lr + v2r + v2fr + v1fr) >> 2);
                                occlusionW += occ << 4;

                                // Bottom face
                                // Vertex 0
                                occ         = ((ld + v0b + v0l + v0bl) >> 2);
                                occlusionW += occ << 8;
                                // Vertex 1
                                occ         = ((ld + v0b + v0r + v0br) >> 2);
                                occlusionW += occ << 12;
                                // Vertex 4
                                occ         = ((ld + v0f + v0l + v0fl) >> 2);
                                occlusionW += occ << 16;
                                // Vertex 5
                                occ         = ((ld + v0f + v0r + v0fr) >> 2);
                                occlusionW += occ << 20;
                            }
                            else
                            {
                                occlusionX  = lb;                                // back
                                occlusionX += lf << 4;                           // forward
                                occlusionX += lu << 8;                           // top
                                occlusionX += ll << 12;                          // // left
                                occlusionX += lr << 16;                          // right
                                occlusionX += ld << 20;                          // bottom
                            }
                            break;
                        }
                        tempChunkUV0.Add(uvAux);
                        uv2Aux.x = occlusionX;
                        uv2Aux.y = occlusionY;
                        uv2Aux.z = occlusionZ;
                        uv2Aux.w = occlusionW;
                        tempChunkUV2.Add(uv2Aux);

                        if (enableTinting)
                        {
                            tintColor.r = voxels [voxelIndex].red;
                            tintColor.g = voxels [voxelIndex].green;
                            tintColor.b = voxels [voxelIndex].blue;
                            tempChunkColors32.Add(tintColor);
                        }
                    }
                }
            }

            meshJobs [jobIndex].chunk = chunk;

            // chunkVoxelCount includes MIVs
            int nonMivsCount = chunkUVIndex + 1;
            meshJobs [jobIndex].totalVisibleVoxels = nonMivsCount;

            if (chunkVoxelCount == 0)
            {
                return;
            }

            if (voxelSignature != chunk.voxelSignature)
            {
                chunk.needsColliderRebuild = true;
            }
            chunk.voxelSignature = voxelSignature;

            if (enableColliders)
            {
                if (chunk.needsColliderRebuild)
                {
                    greedyCollider.FlushTriangles(meshColliderVertices, meshColliderIndices);
                    if (enableNavMesh)
                    {
                        greedyNavMesh.FlushTriangles(navMeshVertices, navMeshIndices);
                    }
                }
                else
                {
                    greedyCollider.Clear();
                    greedyNavMesh.Clear();
                }
            }

            // job index 0 lists are used as generic buffers but we need to convert them to array to use SetIndices API :(
            int subMeshCount = 0;
            for (int k = 0; k < MAX_MATERIALS_PER_CHUNK; k++)
            {
                if (tempGeoIndices [k].count > 0)
                {
                    subMeshCount++;
                    meshJobs [jobIndex].buffers [k].indicesArray = tempGeoIndices [k].ToArray();
                    meshJobs [jobIndex].buffers [k].indicesCount = meshJobs [jobIndex].buffers [k].indicesArray.Length;
                }
            }
            meshJobs [jobIndex].subMeshCount = subMeshCount;

            meshJobs [jobIndex].colliderVertices = meshColliderVertices;
            meshJobs [jobIndex].colliderIndices  = meshColliderIndices;

            meshJobs [jobIndex].navMeshVertices = navMeshVertices;
            meshJobs [jobIndex].navMeshIndices  = navMeshIndices;

            meshJobs [jobIndex].mivs = mivs;
        }
    }
 /// <summary>
 /// Clears a chunk
 /// </summary>
 void ChunkClearFast(VoxelChunk chunk)
 {
     chunk.ClearVoxels(noLightValue);
 }
        void LoadGameBinaryFileFormat_7(BinaryReader br, bool preservePlayerPosition = false)
        {
            // Character controller transform position & rotation
            Vector3 pos = DecodeVector3Binary(br);
            Vector3 characterRotationAngles   = DecodeVector3Binary(br);
            Vector3 cameraLocalRotationAngles = DecodeVector3Binary(br);

            if (!preservePlayerPosition)
            {
                if (characterController != null)
                {
                    characterController.transform.position = pos;
                    characterController.transform.rotation = Quaternion.Euler(characterRotationAngles);
                    cameraMain.transform.localRotation     = Quaternion.Euler(cameraLocalRotationAngles);
                    characterController.UpdateLook();
                }
            }

            InitSaveGameStructs();
            // Read voxel definition table
            int vdCount = br.ReadInt16();

            for (int k = 0; k < vdCount; k++)
            {
                saveVoxelDefinitionsList.Add(br.ReadString());
            }
            // Read item definition table
            int idCount = br.ReadInt16();

            for (int k = 0; k < idCount; k++)
            {
                saveItemDefinitionsList.Add(br.ReadString());
            }

            int             numChunks       = br.ReadInt32();
            VoxelDefinition voxelDefinition = defaultVoxel;
            int             prevVdIndex     = -1;
            Color32         voxelColor      = Misc.color32White;

            for (int c = 0; c < numChunks; c++)
            {
                // Read chunks
                // Get chunk position
                Vector3    chunkPosition  = DecodeVector3Binary(br);
                VoxelChunk chunk          = GetChunkUnpopulated(chunkPosition);
                byte       isAboveSurface = br.ReadByte();
                chunk.isAboveSurface = isAboveSurface == 1;
                chunk.back           = chunk.bottom = chunk.left = chunk.right = chunk.forward = chunk.top = null;
                chunk.allowTrees     = false;
                chunk.modified       = true;
                chunk.isPopulated    = true;
                chunk.voxelSignature = chunk.lightmapSignature = -1;
                chunk.renderState    = ChunkRenderState.Pending;
                SetChunkOctreeIsDirty(chunkPosition, false);
                ChunkClearFast(chunk);
                // Read voxels
                int numWords = br.ReadInt16();
                for (int k = 0; k < numWords; k++)
                {
                    // Voxel definition
                    int vdIndex = br.ReadInt16();
                    if (prevVdIndex != vdIndex)
                    {
                        if (vdIndex >= 0 && vdIndex < vdCount)
                        {
                            voxelDefinition = GetVoxelDefinition(saveVoxelDefinitionsList [vdIndex]);
                            prevVdIndex     = vdIndex;
                        }
                    }
                    // RGB
                    voxelColor.r = br.ReadByte();
                    voxelColor.g = br.ReadByte();
                    voxelColor.b = br.ReadByte();
                    // Voxel index
                    int voxelIndex = br.ReadInt16();
                    // Repetitions
                    int repetitions = br.ReadInt16();

                    byte flags             = br.ReadByte();
                    byte hasCustomRotation = br.ReadByte();

                    if (voxelDefinition == null)
                    {
                        continue;
                    }

                    // Custom voxel flags
                    if (voxelDefinition.renderType == RenderType.Custom)
                    {
                        if (hasCustomRotation == 1)
                        {
                            Vector3 voxelAngles = DecodeVector3Binary(br);
                            delayedVoxelCustomRotations.Add(GetVoxelPosition(chunkPosition, voxelIndex), voxelAngles);
                        }
                    }
                    for (int i = 0; i < repetitions; i++)
                    {
                        chunk.voxels [voxelIndex + i].Set(voxelDefinition, voxelColor);
                        if (voxelDefinition.renderType == RenderType.Water || voxelDefinition.renderType.supportsTextureRotation())
                        {
                            chunk.voxels [voxelIndex + i].SetFlags(flags);
                        }
                    }
                }
                // Read light sources
                int          lightCount = br.ReadInt16();
                VoxelHitInfo hitInfo    = new VoxelHitInfo();
                for (int k = 0; k < lightCount; k++)
                {
                    // Voxel index
                    hitInfo.voxelIndex = br.ReadInt16();
                    // Voxel center
                    hitInfo.voxelCenter = GetVoxelPosition(chunkPosition, hitInfo.voxelIndex);
                    // Normal
                    hitInfo.normal = DecodeVector3Binary(br);
                    hitInfo.chunk  = chunk;
                    TorchAttach(hitInfo);
                }
                // Read items
                int itemCount = br.ReadInt16();
                for (int k = 0; k < itemCount; k++)
                {
                    // Voxel index
                    int itemIndex = br.ReadInt16();
                    if (itemIndex < 0 || itemIndex >= idCount)
                    {
                        continue;
                    }
                    string  itemDefinitionName = saveItemDefinitionsList [itemIndex];
                    Vector3 itemPosition       = DecodeVector3Binary(br);
                    int     quantity           = br.ReadInt16();
                    ItemSpawn(itemDefinitionName, itemPosition, quantity);
                }
            }
        }
        void UploadMeshData(int jobIndex)
        {
            VoxelChunk chunk = meshJobs [jobIndex].chunk;

            if (meshJobs [jobIndex].totalVisibleVoxels == 0)
            {
                if (chunk.mf.sharedMesh != null)
                {
                    chunk.mf.sharedMesh.Clear(false);
                }
                chunk.mc.enabled  = false;
                chunk.renderState = ChunkRenderState.RenderingComplete;
                return;
            }

            // Create mesh
                        #if !UNITY_EDITOR
            Mesh mesh;
            if (isMobilePlatform)
            {
                mesh = new Mesh();                  // on mobile will be released mesh data upon uploading to the GPU so the mesh is no longer readable; need to recreate it everytime the chunk is rendered
            }
            else
            {
                mesh = chunk.mf.sharedMesh;
                if (mesh == null)
                {
                    mesh = new Mesh();
                }
                else
                {
                    mesh.Clear();
                }
            }
                        #else
            Mesh mesh = chunk.mf.sharedMesh;
            if (mesh == null)
            {
                mesh = new Mesh();
                chunksDrawn++;
            }
            else
            {
                voxelsCreatedCount -= mesh.vertexCount;
                mesh.Clear();
            }
            voxelsCreatedCount += meshJobs [jobIndex].totalVisibleVoxels;
                        #endif

            // Vertices
            mesh.SetVertices(meshJobs [jobIndex].vertices);

            // UVs, normals, colors
            mesh.SetUVs(0, meshJobs [jobIndex].uv0);
            if (effectiveUseGeometryShaders)
            {
                mesh.SetUVs(1, meshJobs [jobIndex].uv2);
            }
            else
            {
                mesh.SetNormals(meshJobs [jobIndex].normals);
            }
            if (enableTinting)
            {
                mesh.SetColors(meshJobs [jobIndex].colors);
            }

            // Assign materials and submeshes
            int subMeshIndex = -1;
            int matIndex     = 0;
            mesh.subMeshCount = meshJobs [jobIndex].subMeshCount;
            if (mesh.subMeshCount > 0)
            {
                if (effectiveUseGeometryShaders)
                {
                    for (int k = 0; k < MAX_MATERIALS_PER_CHUNK; k++)
                    {
                        if (meshJobs [jobIndex].buffers [k].indicesCount > 0)
                        {
                            subMeshIndex++;
                            mesh.SetIndices(meshJobs [jobIndex].buffers [k].indicesArray, MeshTopology.Points, subMeshIndex, false);
                            matIndex += 1 << k;
                        }
                    }
                }
                else
                {
                    for (int k = 0; k < MAX_MATERIALS_PER_CHUNK; k++)
                    {
                        if (meshJobs [jobIndex].buffers [k].indicesCount > 0)
                        {
                            subMeshIndex++;
                            mesh.SetTriangles(meshJobs [jobIndex].buffers [k].indices, subMeshIndex, false);
                            matIndex += 1 << k;
                        }
                    }
                }

                // Compute material array
                Material[] matArray;
                if (!materialsDict.TryGetValue(matIndex, out matArray))
                {
                    matArray = new Material[mesh.subMeshCount];
                    for (int k = 0, j = 0; k < MAX_MATERIALS_PER_CHUNK; k++)
                    {
                        if (meshJobs [jobIndex].buffers [k].indicesCount > 0)
                        {
                            matArray [j++] = materials [k];
                        }
                    }
                    materialsDict [matIndex] = matArray;
                }
                chunk.mr.sharedMaterials = matArray;

                mesh.bounds = enableCurvature ? Misc.bounds16Stretched : Misc.bounds16;

                chunk.mf.sharedMesh = mesh;

                                #if !UNITY_EDITOR
                if (isMobilePlatform)
                {
                    mesh.UploadMeshData(true);
                }
                                #endif

                if (!chunk.mr.enabled)
                {
                    chunk.mr.enabled = true;
                }
            }

            // Update collider?
            if (enableColliders && chunk.needsColliderRebuild)
            {
                int  colliderVerticesCount = meshJobs [jobIndex].colliderVertices.Count;
                Mesh colliderMesh          = chunk.mc.sharedMesh;
                if (colliderVerticesCount == 0 || !applicationIsPlaying)
                {
                    chunk.mc.enabled = false;
                }
                else
                {
                    if (colliderMesh == null)
                    {
                        colliderMesh = new Mesh();
                    }
                    else
                    {
                        colliderMesh.Clear();
                    }
                    colliderMesh.SetVertices(meshJobs [jobIndex].colliderVertices);
                    colliderMesh.SetTriangles(meshJobs [jobIndex].colliderIndices, 0);
                    chunk.mc.sharedMesh = colliderMesh;
                    chunk.mc.enabled    = true;
                }

                // Update navmesh
                if (enableNavMesh)
                {
                    int  navMeshVerticesCount = meshJobs [jobIndex].navMeshVertices.Count;
                    Mesh navMesh = chunk.navMesh;
                    if (navMesh == null)
                    {
                        navMesh = new Mesh();
                    }
                    else
                    {
                        navMesh.Clear();
                    }
                    navMesh.SetVertices(meshJobs [jobIndex].navMeshVertices);
                    navMesh.SetTriangles(meshJobs [jobIndex].navMeshIndices, 0);
                    chunk.navMesh = navMesh;
                    AddChunkNavMesh(chunk);
                }
            }

            RenderModelsInVoxels(chunk, meshJobs [jobIndex].mivs);

            if (chunk.renderState != ChunkRenderState.RenderingComplete)
            {
                chunk.renderState = ChunkRenderState.RenderingComplete;
                if (OnChunkAfterFirstRender != null)
                {
                    OnChunkAfterFirstRender(chunk);
                }
            }

            if (OnChunkRender != null)
            {
                OnChunkRender(chunk);
            }

            shouldUpdateParticlesLighting = true;
        }