public virtual void Init(int threadId, int poolSize, VoxelPlayEnvironment env)
        {
            this.threadId        = threadId;
            this.env             = env;
            this.enableColliders = env.enableColliders;
            this.enableNavMesh   = env.enableNavMesh;
            this.enableTinting   = env.enableTinting;
            this.virtualChunk    = env.virtualChunk;
            this.denseTrees      = env.denseTrees;
            bool lowMemoryMode = env.lowMemoryMode;

            faceColors = new Color32 [4];
            for (int k = 0; k < faceColors.Length; k++)
            {
                faceColors[k] = Misc.color32White;
            }


#if UNITY_WEBGL
            meshJobs = new MeshJobData[384];
#else
            meshJobs = new MeshJobData [poolSize];
#endif
            meshJobMeshLastIndex                = poolSize - 1;
            meshJobMeshDataGenerationIndex      = poolSize - 1;
            meshJobMeshDataGenerationReadyIndex = poolSize - 1;
            meshJobMeshUploadIndex              = poolSize - 1;
            int initialCapacity = 15000;
            for (int k = 0; k < meshJobs.Length; k++)
            {
                meshJobs [k].vertices = Misc.GetList <Vector3> (lowMemoryMode, initialCapacity);
                meshJobs [k].uv0      = Misc.GetList <Vector4> (lowMemoryMode, initialCapacity);
                meshJobs [k].colors   = Misc.GetList <Color32> (lowMemoryMode, enableTinting ? initialCapacity : 4);
                meshJobs [k].buffers  = new MeshJobBuffer [VoxelPlayEnvironment.MAX_MATERIALS_PER_CHUNK];
                meshJobs [k].normals  = Misc.GetList <Vector3> (lowMemoryMode, initialCapacity);
                for (int j = 0; j < meshJobs [k].buffers.Length; j++)
                {
                    meshJobs [k].buffers [j].indices = new List <int> ();
                }
                if (enableColliders)
                {
                    meshJobs [k].colliderVertices = Misc.GetList <Vector3> (lowMemoryMode, 2700);
                    meshJobs [k].colliderIndices  = Misc.GetList <int> (lowMemoryMode, 4000);
                }
                if (enableNavMesh)
                {
                    meshJobs [k].navMeshVertices = Misc.GetList <Vector3> (lowMemoryMode, 2700);
                    meshJobs [k].navMeshIndices  = Misc.GetList <int> (lowMemoryMode, 4000);
                }
                meshJobs [k].mivs = new FastList <ModelInVoxel> ();
            }

            greedyCollider  = new VoxelPlayGreedyMesher();
            greedyNavMesh   = new VoxelPlayGreedyMesher();
            chunk9          = new Voxel [27] [];
            neighbourChunks = new VoxelChunk [27];
        }
        void InitRenderer()
        {
            draftModeActive = !applicationIsPlaying && editorDraftMode;
                        #if UNITY_WEBGL
            effectiveUseGeometryShaders = false;
                        #else
            effectiveUseGeometryShaders = useGeometryShaders && !isMobilePlatform && SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Metal;
                        #endif

            // Init materials
            Material matTerrainOpaque, matTerrainCutout, matTerrainWater, matTerrainTransp, matTerrainCutxss, matTerrainOpNoAO;

            Material matTriangleOpaque = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Opaque"));
            Material matTriangleCutout = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Cutout"));

            if (effectiveUseGeometryShaders)
            {
                matTerrainOpaque = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Opaque"));
                matTerrainCutout = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Cutout"));
                if (shadowsOnWater && !draftModeActive)
                {
                    matTerrainWater = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Water"));
                }
                else
                {
                    matTerrainWater = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Water No Shadows"));
                }
                matTerrainCutxss = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Cutout Cross"));
                matTerrainOpNoAO = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Opaque No AO"));
                if (doubleSidedGlass)
                {
                    matTerrainTransp = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Transp Double Sided"));
                }
                else
                {
                    matTerrainTransp = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Geo Transp"));
                }
            }
            else
            {
                matTerrainOpaque = matTriangleOpaque;
                matTerrainCutout = matTriangleCutout;
                if (shadowsOnWater && !draftModeActive)
                {
                    matTerrainWater = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Water"));
                }
                else
                {
                    matTerrainWater = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Water No Shadows"));
                }
                matTerrainCutxss = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Cutout Cross"));
                if (doubleSidedGlass)
                {
                    matTerrainTransp = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Transp Double Sided"));
                }
                else
                {
                    matTerrainTransp = Instantiate <Material> (Resources.Load <Material> ("VoxelPlay/Materials/VP Voxel Triangle Transp"));
                }
                matTerrainOpNoAO = matTerrainOpaque;
            }

            // Init system arrays and structures
            if (materialsDict == null)
            {
                materialsDict = new Dictionary <int, Material[]> ();
            }
            else
            {
                materialsDict.Clear();
            }

            materials = new Material[MAX_MATERIALS_PER_CHUNK];
            materials [INDICES_BUFFER_OPAQUE] = matTerrainOpaque;
            materials [INDICES_BUFFER_CUTOUT] = matTerrainCutout;
            materials [INDICES_BUFFER_CUTXSS] = matTerrainCutxss;
            materials [INDICES_BUFFER_WATER]  = matTerrainWater;
            materials [INDICES_BUFFER_TRANSP] = matTerrainTransp;
            materials [INDICES_BUFFER_OPNOAO] = matTerrainOpNoAO;

            if (materialIndices == null)
            {
                materialIndices = new Dictionary <Material, int> ();
            }
            else
            {
                materialIndices.Clear();
            }
            materialIndices [matTerrainOpaque] = INDICES_BUFFER_OPAQUE;
            materialIndices [matTerrainCutout] = INDICES_BUFFER_CUTOUT;
            materialIndices [matTerrainCutxss] = INDICES_BUFFER_CUTXSS;
            materialIndices [matTerrainWater]  = INDICES_BUFFER_WATER;
            materialIndices [matTerrainTransp] = INDICES_BUFFER_TRANSP;
            materialIndices [matTerrainOpNoAO] = INDICES_BUFFER_OPNOAO;

            // Triangle opaque and cutout are always loaded because dynamic voxels requires them
            if (useGeometryShaders)
            {
                INDICES_BUFFER_OPAQUE_TRIANGLE = 6;
                INDICES_BUFFER_CUTOUT_TRIANGLE = 7;
                lastBufferIndex = 7;
            }
            else
            {
                INDICES_BUFFER_OPAQUE_TRIANGLE = INDICES_BUFFER_OPAQUE;
                INDICES_BUFFER_CUTOUT_TRIANGLE = INDICES_BUFFER_CUTOUT;
                lastBufferIndex = 5;
            }
            materials [INDICES_BUFFER_OPAQUE_TRIANGLE] = matTriangleOpaque;
            materials [INDICES_BUFFER_CUTOUT_TRIANGLE] = matTriangleCutout;


            modelMeshColors = new List <Color32> (128);
            tempVertices    = new List <Vector3> (36);
            tempNormals     = new List <Vector3> (36);
            tempUVs         = new List <Vector4> (36);
            tempIndices     = new int[36];
            tempColors      = new List <Color32> (36);

            InitMeshJobsPool();

            Voxel.Empty.light = noLightValue;
            InitVirtualChunk();

            greedyCollider = new VoxelPlayGreedyMesher();
            greedyNavMesh  = new VoxelPlayGreedyMesher();
            greedyNoAO     = new VoxelPlayGreedyMesher(true);
            greedyCutout   = new VoxelPlayGreedyMesher(true);

            instancingManager = new VoxelPlayInstancingRendererManager(this);

            VoxelPlayLightManager lightManager = currentCamera.GetComponent <VoxelPlayLightManager> ();
            if (lightManager == null)
            {
                currentCamera.gameObject.AddComponent <VoxelPlayLightManager> ();
            }
            else
            {
                lightManager.enabled = true;
            }
            StartGenerationThread();
        }