public static ChunkJobResult CreateChunk(ChunkJob Job)
        {
            ChunkJobResult result = new ChunkJobResult();

            try {
                Stopwatch s = new Stopwatch();
                s.Start();
                SE.OpenSimplexNoise noise = new SE.OpenSimplexNoise(1);

                result.Error       = null;
                result.OriginalJob = Job;
                result.DebugPrint  = "";


                ExtractionInput input = new ExtractionInput();
                input.Isovalue   = 0;
                input.LODSides   = Job.LOD;
                input.Resolution = new Util.Vector3i(Job.Resolution, Job.Resolution, Job.Resolution);
                input.Size       = new Vector3(Job.CellSize, Job.CellSize, Job.CellSize);

                int numTimesSampled = 0;

                input.Sample = (float x, float y, float z) => {
                    numTimesSampled++;
                    float res = sample(noise, x + Job.Min.x, y + Job.Min.y, z + Job.Min.z);
                    return(res);
                };

                ExtractionResult ExResult = SurfaceExtractor.ExtractSurface(input);
                result.Result = ExResult;



                s.Stop();
                result.ProcessingTime = s.ElapsedMilliseconds;
            }
            catch (System.Exception exc) {
                result.Error = "Error in thread " + Job.ThreadID + ": " + exc.Message + ", Stacktrace: " + exc.StackTrace;
            }
            return(result);
        }
    public static ExtractionResult ExtractSurface(ExtractionInput input)
    {
        Debug.Log("SurfaceExtractor ExtractSurface ran");
        ExtractionResult r = new ExtractionResult();

        List <GridCell> cells = new List <GridCell>();
        List <GridCell> debugTransitionCells1S = new List <GridCell>();
        List <GridCell> debugTransitionCells2S = new List <GridCell>();
        List <GridCell> debugTransitionCells3S = new List <GridCell>();

        GridCell cell = new GridCell();

        cell.points = new Point[8];
        for (int i = 0; i < 8; i++)
        {
            cell.points[i]          = new Point();
            cell.points[i].position = new Vector3();
        }

        List <Vector3> vertices = new List <Vector3>();

        Vector3[] OFFSETS =
        {
            new Vector3(0f, 0f, 0f), new Vector3(1f, 0f, 0f), new Vector3(1f, 1f, 0f), new Vector3(0f, 1f, 0f),
            new Vector3(0f, 0f, 1f), new Vector3(1f, 0f, 1f), new Vector3(1f, 1f, 1f), new Vector3(0f, 1f, 1f)
        };

        // Generate Regular Cells
        for (int x = 0; x < input.Resolution.x; x++)
        {
            for (int y = 0; y < input.Resolution.y; y++)
            {
                for (int z = 0; z < input.Resolution.z; z++)
                {
                    byte edgeSides = 0;
                    if (x == 0)
                    {
                        edgeSides |= 1;
                    }
                    if (x == input.Resolution.x - 1)
                    {
                        edgeSides |= 2;
                    }
                    if (y == 0)
                    {
                        edgeSides |= 4;
                    }
                    if (y == input.Resolution.y - 1)
                    {
                        edgeSides |= 8;
                    }
                    if (z == 0)
                    {
                        edgeSides |= 16;
                    }
                    if (z == input.Resolution.z - 1)
                    {
                        edgeSides |= 32;
                    }

                    byte lod = (byte)(input.LODSides & edgeSides);

                    // cell is regular
                    if (lod == 0)
                    {
                        for (int i = 0; i < 8; i++)
                        {
                            cell.points[i].position = new Vector3(input.Size.x * (x + OFFSETS[i].x),
                                                                  input.Size.y * (y + OFFSETS[i].y),
                                                                  input.Size.z * (z + OFFSETS[i].z));

                            cell.points[i].density = input.Sample(cell.points[i].position.x, cell.points[i].position.y, cell.points[i].position.z);
                        }
                        cells.Add(cell.Clone());
                        SE.Polyganiser.Polyganise(cell, vertices, input.Isovalue);
                    }
                }
            }
        }

        // Generate Transition Cells
        for (int x = 0; x < input.Resolution.x / 2; x++)
        {
            for (int y = 0; y < input.Resolution.y / 2; y++)
            {
                for (int z = 0; z < input.Resolution.z / 2; z++)
                {
                    byte edgeSides = 0;
                    if (x == 0)
                    {
                        edgeSides |= 1;
                    }
                    if (x == (input.Resolution.x / 2) - 1)
                    {
                        edgeSides |= 2;
                    }
                    if (y == 0)
                    {
                        edgeSides |= 4;
                    }
                    if (y == (input.Resolution.y / 2) - 1)
                    {
                        edgeSides |= 8;
                    }
                    if (z == 0)
                    {
                        edgeSides |= 16;
                    }
                    if (z == (input.Resolution.z / 2) - 1)
                    {
                        edgeSides |= 32;
                    }

                    byte lod = (byte)(input.LODSides & edgeSides);

                    // Is transition cell
                    if (lod != 0)
                    {
                        Vector3 min = new Vector3(input.Size.x * x * 2,
                                                  input.Size.y * y * 2,
                                                  input.Size.z * z * 2);

                        GridCell debugTransitionCell = new GridCell();
                        debugTransitionCell.points = new Point[8];
                        for (int i = 0; i < 8; i++)
                        {
                            debugTransitionCell.points[i].position = min + OFFSETS[i] * input.Size.x * 2f;
                        }

                        TransitionCellResult tCellRes = ProcessTransitionCell(lod, min, input.Size, input.Sample);
                        for (int i = 0; i < tCellRes.cells.Length; i++)
                        {
                            cells.Add(tCellRes.cells[i]);
                            SE.Polyganiser.Polyganise(tCellRes.cells[i], vertices, input.Isovalue);
                        }

                        if (tCellRes.numLODFaces == 1)
                        {
                            debugTransitionCells1S.Add(debugTransitionCell);
                        }
                        else if (tCellRes.numLODFaces == 2)
                        {
                            debugTransitionCells2S.Add(debugTransitionCell);
                        }
                        else if (tCellRes.numLODFaces == 3)
                        {
                            debugTransitionCells3S.Add(debugTransitionCell);
                        }
                    }
                }
            }
        }

        r.Triangles = new int[vertices.Count];
        for (int i = 0; i < vertices.Count; i++)
        {
            r.Triangles[i] = i;
        }
        r.Vertices = vertices.ToArray();
        r.Cells    = cells;
        r.DebugTransitionCells1S = debugTransitionCells1S;
        r.DebugTransitionCells2S = debugTransitionCells2S;
        r.DebugTransitionCells3S = debugTransitionCells3S;

        return(r);
    }