void OnGUI()
    {
        if (calculatorThread != null)
        {
            GUI.enabled = false;
        }
        MeshFilter newMesh;

        newMesh = (MeshFilter)EditorGUILayout.ObjectField("Object: ", meshFilter, typeof(MeshFilter), true);
        if (newMesh != meshFilter)
        {
            meshFilter = newMesh;
            Reset();
            return;
        }
        GUI.enabled = true;

        if (mesh == null)
        {
            return;
        }

        GUILayout.Label(string.Format("Size: {0} x {1} x {2}", size.x, size.y, size.z));

        if (GUILayout.Toggle(drawFull, "Show Blocks", "Button") != drawFull)
        {
            drawFull = !drawFull;
            SceneView.RepaintAll();
        }

        DisplayGUI();

        CalculateGUI();

        ConfigGUI();

        PaletteGUI();

        CompositionGUI();

        StructureDetailsGUI();

        ExportGUI();

        float x0 = editorSettings.invertX ? Mathf.Ceil(mesh.bounds.max.x) - 0.5f : Mathf.Floor(mesh.bounds.min.x) + 0.5f;
        float y0 = editorSettings.invertY ? Mathf.Ceil(mesh.bounds.max.y) - 0.5f : Mathf.Floor(mesh.bounds.min.y) + 0.5f;
        float z0 = editorSettings.invertZ ? Mathf.Ceil(mesh.bounds.max.z) - 0.5f : Mathf.Floor(mesh.bounds.min.z) + 0.5f;


        posx = new Vector3(
            x0 + (AABBGenerator.RelativeX(pos, editorSettings.axisOrder, size) * (editorSettings.invertX ? -1 : 1)),
            y0 + (AABBGenerator.RelativeY(pos, editorSettings.axisOrder, size) * (editorSettings.invertY ? -1 : 1)),
            z0 + (AABBGenerator.RelativeZ(pos, editorSettings.axisOrder, size) * (editorSettings.invertZ ? -1 : 1))
            );
    }
    void Export()
    {
        string path = EditorUtility.SaveFilePanelInProject("Export structure...", "struct_file", "json", "Export...");

        if (path.Length == 0)
        {
            return;
        }

        JsonStructure toExport = new JsonStructure();

        if (structure.structureUID == string.Empty)
        {
            Debug.LogError("Missing Structure ID! Check Structure Details category.");
            return;
        }
        toExport.uniqueName = structure.structureUID;
        toExport.width      = (int)size.x;
        toExport.height     = (int)size.y;
        toExport.length     = (int)size.z;

        toExport.palette = new List <JsonPaletteEntry>();
        foreach (PaletteEntry entry in structure.palette)
        {
            if (!entry.isValid)
            {
                Debug.LogError("Invalid palette entry! Check Palette category.");
                return;
            }
            toExport.palette.Add(new JsonPaletteEntry(entry.isOreDict? "ore" : entry.mod, entry.name, entry.identifier, entry.meta));
        }

        Block master = structure.validFullBlocks.FirstOrDefault(x => x.pos == structure.masterPos);

        if (master == null)
        {
            Debug.LogError("Master block not set or missing palette value! Check Composition category.");
            return;
        }

        PaletteEntry masterPaletteEntry = structure.palette.FirstOrDefault(x => x.identifier == master.paletteValue);

        if (masterPaletteEntry == null)
        {
            Debug.LogError("Master block is missing valid palette value! Check Composition and Palette categories.");
            return;
        }

        toExport.master = new JsonMaster(
            calculator.BlockPosToLocalX(master.pos),
            calculator.BlockPosToLocalY(master.pos),
            calculator.BlockPosToLocalZ(master.pos),
            masterPaletteEntry.isOreDict ? "ore" :
            masterPaletteEntry.mod, masterPaletteEntry.name, masterPaletteEntry.meta);

        toExport.pointsOfInterest = new List <JsonPoI>();
        foreach (PoI poi in structure.pointsOfInterest)
        {
            if (structure.ignoredPositions2.Contains(poi.pos) || !structure.validFullBlocks.Any(x => x.pos == poi.pos))
            {
                continue;
            }
            toExport.pointsOfInterest.Add(new JsonPoI()
            {
                position = poi.pos, name = poi.id, facing = poi.facing
            });
        }

        int firstDimension  = AABBGenerator.FirstDimension((int)size.x, (int)size.y, (int)size.z, editorSettings.axisOrder);
        int secondDimension = AABBGenerator.SecondDimension((int)size.x, (int)size.y, (int)size.z, editorSettings.axisOrder);
        int thirdDimension  = AABBGenerator.ThirdDimension((int)size.x, (int)size.y, (int)size.z, editorSettings.axisOrder);

        toExport.structure = new List <string>();
        string emptyLine = new string(' ', firstDimension);

        for (int line = 0; line < secondDimension * thirdDimension; line++)
        {
            toExport.structure.Add(emptyLine);
        }
        foreach (Block block in structure.validFullBlocks)
        {
            if (structure.ignoredPositions2.Contains(block.pos))
            {
                continue;
            }
            if (block.IsInvalid())
            {
                Debug.LogError(string.Format("Block {0} is missing valid palette value! Check Composition and Palette categories.", block.pos));
                return;
            }
            int firstPos  = AABBGenerator.RelativeX(block.pos, editorSettings.axisOrder, size);
            int secondPos = AABBGenerator.RelativeZ(block.pos, editorSettings.axisOrder, size);
            int thirdPos  = AABBGenerator.RelativeY(block.pos, editorSettings.axisOrder, size);
            //Debug.Log(string.Format("{0}, {1}, {2}, {3}, {4}", firstPos, secondPos, thirdPos, toExport.structure[thirdPos * secondDimension + secondPos].Length, block.paletteValue));
            toExport.structure[thirdPos * secondDimension + secondPos] = ReplaceAt(toExport.structure[thirdPos * secondDimension + secondPos], firstPos, block.paletteValue);
        }

        toExport.AABB = new List <byte>();
        List <Box>[] orderedList = new List <Box> [(int)(size.x * size.y * size.z)];

        foreach (BlockPosition aabb in structure.allSubAABBs)
        {
            if (orderedList[aabb.pos] == null)
            {
                orderedList[aabb.pos] = new List <Box>();
            }
            orderedList[aabb.pos].Add(aabb.normalizedBox);
        }

        Box fullBox = new Box(0, 0, 0, 16, 16, 16);

        StringBuilder builder = new StringBuilder();

        for (int index = 0; index < orderedList.Length; index++)
        {
            if (structure.ignoredPositions2.Contains(index) || orderedList[index] == null)  //no collision boxes
            {
                builder.Append("null");
            }
            else if (orderedList[index].Count == 1 && orderedList[index][0] == fullBox)     //full collision box
            {
                builder.Append("[]");
            }
            else     //partial collision boxes
            {
                List <string> floats = new List <string>();
                foreach (Box box in orderedList[index])
                {
                    floats.Add(box.ToString(editorSettings.outputFormat));
                }
                builder.Append("[" + string.Join(",", floats) + "]");
            }
            builder.Append(",");
        }

        builder.Length -= 1;

        string json = JsonUtility.ToJson(toExport, false);

        json = json.Insert(json.LastIndexOf('[') + 1, builder.ToString());

        using (StreamWriter writer = new StreamWriter(path)) {
            writer.Write(json);
            writer.Flush();
            writer.Close();
        }
    }