private void OnGUI()
        {
            // Find max label width.
            var maxLabelWidth = 110.0f;

            for (int i = 0; i < lxfml.groups.Length; i++)
            {
                var size = EditorStyles.boldLabel.CalcSize(new GUIContent(lxfml.groups[i].name));
                maxLabelWidth = Mathf.Max(maxLabelWidth, size.x);
            }

            minSize = new Vector2(leftMargin + maxLabelWidth + 360, 226);
            maxSize = new Vector2(leftMargin + maxLabelWidth + 360, 2000);

            scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false);

            GUI.Box(new Rect(20, 20, 100, 100), logoTexture);

            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 20.0f, 100.0f), "Colliders", "Add colliders to bricks.");
            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 50.0f, 100.0f), "Connectivity", "Add connectivity to bricks. Connectivity requires colliders.");

            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 100.0f, 100.0f), "Static", "Make bricks static.");
            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 130.0f, 100.0f), "Lightmapped", "Add lightmap UVs to bricks. Bricks must be static to be lightmapped.");

            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 180.0f, 100.0f), "Randomize Rotation", "Slightly rotate bricks to improve realism.");
            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 230.0f, 100.0f), "Prefer Legacy", "Prefer legacy geometry over new geometry.");
            CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 280.0f, 100.0f), "LOD", "LOD 0 includes chamfered edges.\nLOD 1 does not.\nLOD 2 simplifies knobs.");

            // Reserve the space for the GUILayout scroll view.
            GUILayout.Space(135.0f);
            var nextY = 135.0f;

            var showAllBoolsUI = model == null && group == null && lxfml.groups.Length > 1; // When importing a new model, just check if there is more than one lxfml group.

            showAllBoolsUI |= model != null && importSettings.Count > 1;                    // When reimporting an entire model, check if the existing model has more than one group.
            if (showAllBoolsUI)
            {
                CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 15.0f, nextY), importSettings, "colliders", "connectivity", null);
                CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 45.0f, nextY), importSettings, "connectivity", null, "colliders");

                CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 95.0f, nextY), importSettings, "isStatic", "lightmapped", null);
                CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 125.0f, nextY), importSettings, "lightmapped", null, "isStatic");

                CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 175.0f, nextY), importSettings, "randomizeRotation");
                CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 225.0f, nextY), importSettings, "preferLegacy");
                CreateAllLODsUI(new Vector2(leftMargin + maxLabelWidth + 275.0f, nextY), importSettings);

                // Reserve the space for the GUILayout scroll view.
                GUILayout.Space(25.0f);
                nextY += 25.0f;
            }

            var collidersOrConnectivityWhilePreferringLegacy = false;

            for (int i = 0; i < lxfml.groups.Length; i++)
            {
                var showGroup = group == null && model == null;              // When importing a new model, show all groups.
                showGroup |= model != null && importSettings.ContainsKey(i); // When reimporting an entire model, only show groups already in the existing model.
                showGroup |= group != null && i == group.number;             // When reimporting a model group, only show that group.
                if (showGroup)
                {
                    GUI.Label(new Rect(leftMargin, nextY, maxLabelWidth, 20.0f), lxfml.groups[i].name);

                    CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 15.0f, nextY), importSettings, "colliders", i, "connectivity", null);
                    CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 45.0f, nextY), importSettings, "connectivity", i, null, "colliders");

                    CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 95.0f, nextY), importSettings, "isStatic", i, "lightmapped", null);
                    CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 125.0f, nextY), importSettings, "lightmapped", i, null, "isStatic");

                    CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 175.0f, nextY), importSettings, "randomizeRotation", i);
                    CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 225.0f, nextY), importSettings, "preferLegacy", i);
                    CreateLODUI(new Vector2(leftMargin + maxLabelWidth + 275.0f, nextY), importSettings, i);

                    if ((importSettings[i].colliders || importSettings[i].connectivity) && importSettings[i].preferLegacy)
                    {
                        collidersOrConnectivityWhilePreferringLegacy = true;
                    }

                    // Reserve the space for the GUILayout scroll view.
                    GUILayout.Space(20.0f);
                    nextY += 20.0f;
                }
            }

            if (collidersOrConnectivityWhilePreferringLegacy)
            {
                EditorGUI.HelpBox(new Rect(leftMargin, nextY, position.width - leftMargin - 20.0f, 38.0f), "Legacy parts might not contain colliders or connectivity information.", MessageType.Warning);
                // Reserve the space for the GUILayout scroll view.
                GUILayout.Space(42.0f);
                nextY += 42.0f;
            }

            // Reserve the space for the GUILayout scroll view.
            GUILayout.Space(5.0f);
            nextY += 5.0f;

            // Only show pivot option when not reimporting group.
            if (group == null)
            {
                GUI.Label(new Rect(leftMargin, nextY, maxLabelWidth, 20.0f), "Pivot");
                pivot = (Model.Pivot)EditorGUI.EnumPopup(new Rect(leftMargin + maxLabelWidth + 15.0f, nextY, 126.0f, 16.0f), pivot);

                // Reserve the space for the GUILayout scroll view.
                GUILayout.Space(25.0f);
                nextY += 25.0f;
            }

            // Create the right import/reimport button and handle the import/reimport based on the three cases:
            // - Reimport model
            // - Reimport model group
            // - Import model
            bool importPressed;

            if (model)
            {
                // ----------------------
                // Reimport entire model.
                // ----------------------
                importPressed = GUI.Button(new Rect(leftMargin, nextY, maxLabelWidth + 15.0f + 126.0f, 32.0f), "Reimport Model");

                if (importPressed)
                {
                    // Register undo.
                    Undo.RegisterFullObjectHierarchyUndo(model.gameObject, "Reimport");
                    var oldPivot = model.pivot;
                    model.pivot = pivot;

                    // Update the path if it is new.
                    if (filePath != null)
                    {
                        model.absoluteFilePath = filePath;
                        model.relativeFilePath = PathUtils.GetRelativePath(Directory.GetCurrentDirectory(), filePath);
                    }

                    ModelImporter.ReimportModel(lxfml, model, oldPivot, importSettings);

                    var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
                    if (prefabStage != null)
                    {
                        EditorSceneManager.MarkSceneDirty(prefabStage.scene);
                    }
                    SceneBrickBuilder.MarkSceneDirty();
                }
            }
            else if (group)
            {
                // ---------------------
                // Reimport model group.
                // ---------------------
                importPressed = GUI.Button(new Rect(leftMargin, nextY, maxLabelWidth + 15.0f + 126.0f, 32.0f), "Reimport Model Group");

                if (importPressed)
                {
                    // Register undo.
                    Undo.RegisterFullObjectHierarchyUndo(group.gameObject, "Reimport");

                    ModelImporter.ReimportModelGroup(lxfml, group, importSettings[group.number], true);

                    var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
                    if (prefabStage != null)
                    {
                        EditorSceneManager.MarkSceneDirty(prefabStage.scene);
                    }
                    SceneBrickBuilder.MarkSceneDirty();
                }
            }
            else
            {
                // ----------------------
                // Import model.
                // ----------------------
                importPressed = GUI.Button(new Rect(leftMargin, nextY, maxLabelWidth + 15.0f + 126.0f, 32.0f), "Import Model");

                if (importPressed)
                {
                    // Check for connectivity updates.
                    ConnectivityVersionChecker.CheckForUpdates();

                    model = ModelImporter.InstantiateModel(lxfml, filePath, pivot, importSettings).GetComponent <Model>();
                    var camera = SceneView.lastActiveSceneView.camera;
                    if (camera)
                    {
                        var cameraRay = new Ray(camera.transform.position, camera.transform.forward);

                        var bricksInModel = model.GetComponentsInChildren <Brick>();
                        var bricks        = new HashSet <Brick>(bricksInModel);
                        var sourceBrick   = bricks.First();

                        BrickBuildingUtility.AlignBricks(sourceBrick, bricks, BrickBuildingUtility.ComputeBounds(bricks), sourceBrick.transform.position, Vector3.zero, cameraRay,
                                                         new Plane(Vector3.up, Vector3.zero), 100.0f, out Vector3 offset, out Vector3 alignedOffset, out _, out _);

                        var offsetPosition = model.transform.position + alignedOffset;

                        model.transform.position = offsetPosition;

                        Selection.activeGameObject = model.gameObject;
                        Physics.SyncTransforms();
                    }
                    SceneBrickBuilder.SyncAndUpdateBrickCollision(true);
                }
            }

            // Reserve the space for the GUILayout scroll view.
            GUILayout.Space(36.0f);
            nextY += 36.0f;

            // List tracked errors.
            foreach (var trackedError in trackedErrors)
            {
                EditorGUI.HelpBox(new Rect(leftMargin, nextY, maxLabelWidth + 340.0f, 38.0f), trackedError.Key, MessageType.Warning);
                // Reserve the space for the GUILayout scroll view.
                GUILayout.Space(42.0f);
                nextY += 42.0f;

                foreach (var id in trackedError.Value)
                {
                    GUI.Label(new Rect(leftMargin, nextY, maxLabelWidth + 340.0f, 16.0f), id);
                    // Reserve the space for the GUILayout scroll view.
                    GUILayout.Space(20.0f);
                    nextY += 20.0f;
                }
            }

            GUILayout.EndScrollView();

            if (importPressed)
            {
                this.Close();
            }
        }
        private void DrawMouldingColour(int colourID, int listIndex, bool multipleValues)
        {
            var position = EditorGUILayout.GetControlRect();

            // Draw label
            position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Moulding Colour " + listIndex));

            if (multipleValues)
            {
                // Create box and tooltip.
                GUI.Box(position, new GUIContent("", "Multiple colours selected"));
                var colorRect = new Rect(position.x + 1.0f, position.y + 1.0f, position.width - 2.0f, position.height - 2.0f);
                var lineRect  = new Rect(position.x + 1.0f, position.y + 1.0f + colorRect.height / 2.0f, 10.0f, 1.0f);
                EditorGUI.DrawRect(colorRect, new Color32(209, 209, 209, 255));
                EditorGUI.DrawRect(lineRect, Color.grey);
            }
            else
            {
                var mouldingColourID = (MouldingColour.Id)colourID;
                var colour           = MouldingColour.GetColour(mouldingColourID);

                // Create box and tooltip.
                GUI.Box(position, new GUIContent("", ObjectNames.NicifyVariableName((int)mouldingColourID + " - " + mouldingColourID.ToString())));

                // Draw rects with colour.
                var colorRect = new Rect(position.x + 1.0f, position.y + 1.0f, position.width - 2.0f, position.height - 2.0f - alphaBarHeight);
                var alphaRect = new Rect(position.x + 1.0f, position.y + 1.0f + colorRect.height, Mathf.Round((position.width - 2.0f) * colour.a), alphaBarHeight);
                var blackRect = new Rect(position.x + 1.0f + alphaRect.width, position.y + 1.0f + colorRect.height, position.width - 2.0f - alphaRect.width, alphaBarHeight);
                EditorGUI.DrawRect(colorRect, new Color(colour.r, colour.g, colour.b));
                EditorGUI.DrawRect(alphaRect, Color.white);
                EditorGUI.DrawRect(blackRect, Color.black);
            }

            // Detect click.
            if (Event.current.type == EventType.MouseDown)
            {
                if (position.Contains(Event.current.mousePosition))
                {
                    MouldingColourPicker.Show((c) =>
                    {
                        // Collect all parts to record for undo.
                        foreach (var target in targets)
                        {
                            var brickTarget = (Brick)target;
                            Undo.RegisterFullObjectHierarchyUndo(brickTarget.gameObject, "Changed Material");

                            int indexOffset = 0;
                            foreach (var partTarget in brickTarget.parts)
                            {
                                if (indexOffset <= listIndex && listIndex < indexOffset + partTarget.materialIDs.Count)
                                {
                                    Undo.RegisterFullObjectHierarchyUndo(partTarget.gameObject, "Changed Material");

                                    break;
                                }
                                indexOffset += partTarget.materialIDs.Count;
                            }
                        }

                        // Collect all parts to change the material.
                        foreach (var target in targets)
                        {
                            var brickTarget = (Brick)target;
                            int indexOffset = 0;
                            foreach (var partTarget in brickTarget.parts)
                            {
                                if (indexOffset <= listIndex && listIndex < indexOffset + partTarget.materialIDs.Count)
                                {
                                    // Update material ID.
                                    partTarget.materialIDs[listIndex - indexOffset] = (int)MouldingColour.GetId(c);

                                    // Collect all materials.
                                    LXFMLDoc.Brick.Part.Material[] materials = new LXFMLDoc.Brick.Part.Material[partTarget.materialIDs.Count];
                                    for (var i = 0; i < partTarget.materialIDs.Count; ++i)
                                    {
                                        materials[i] = new LXFMLDoc.Brick.Part.Material()
                                        {
                                            colorId = partTarget.materialIDs[i], shaderId = 0
                                        };
                                    }

                                    // Update the part materials.
                                    ModelImporter.SetMaterials(partTarget, materials, partTarget.legacy);

                                    // Update knobs and tubes of this brick and the bricks it is connected to directly.
                                    var connectedBricks = partTarget.brick.GetConnectedBricks(false);
                                    connectedBricks.Add(partTarget.brick);
                                    foreach (var brick in connectedBricks)
                                    {
                                        foreach (var part in brick.parts)
                                        {
                                            if (!part.legacy && part.connectivity)
                                            {
                                                foreach (var connectionField in part.connectivity.connectionFields)
                                                {
                                                    foreach (var connection in connectionField.connections)
                                                    {
                                                        connection.UpdateKnobsAndTubes();
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    break;
                                }

                                indexOffset += partTarget.materialIDs.Count;
                            }
                        }

                        // Collect all parts to record prefab instance modifications.
                        foreach (var target in targets)
                        {
                            var brickTarget = (Brick)target;
                            if (PrefabUtility.IsPartOfAnyPrefab(brickTarget.gameObject))
                            {
                                PrefabUtility.RecordPrefabInstancePropertyModifications(brickTarget.gameObject);
                            }

                            int indexOffset = 0;
                            foreach (var partTarget in brickTarget.parts)
                            {
                                if (indexOffset <= listIndex && listIndex < indexOffset + partTarget.materialIDs.Count)
                                {
                                    if (PrefabUtility.IsPartOfAnyPrefab(partTarget.gameObject))
                                    {
                                        PrefabUtility.RecordPrefabInstancePropertyModifications(partTarget.gameObject);
                                    }
                                    break;
                                }
                                indexOffset += partTarget.materialIDs.Count;
                            }
                        }
                    },
                                              false,
                                              false,
                                              true
                                              );
                }
            }
        }
        private static bool UpdateConnections(Brick[] bricks)
        {
            var updated = false;

            foreach (var brick in bricks)
            {
                if (PrefabUtility.IsPartOfPrefabInstance(brick))
                {
                    continue;
                }

                foreach (var part in brick.parts)
                {
                    var designID = part.designID.ToString();
                    if (!PartUtility.CheckIfConnectivityForPartIsUnpacked(designID))
                    {
                        PartUtility.UnpackConnectivityForPart(designID);
                    }

                    var connectivity = part.connectivity;
                    if (!connectivity)
                    {
                        // Unsupported or legacy.
                        continue;
                    }

                    if (connectivity.version == currentVersion)
                    {
                        // Already up to date.
                        continue;
                    }

                    var connectivityToInstantiate = PartUtility.LoadConnectivityPrefab(designID);
                    if (connectivityToInstantiate)
                    {
                        GameObject.DestroyImmediate(connectivity.gameObject, true);
                        var connectivityGO = UnityEngine.Object.Instantiate(connectivityToInstantiate);
                        connectivityGO.name = "Connectivity";
                        connectivityGO.transform.SetParent(part.transform, false);
                        var connectivityComp = connectivityGO.GetComponent <Connectivity>();
                        part.connectivity = connectivityComp;
                        part.brick.totalBounds.Encapsulate(connectivityComp.extents);
                        connectivityComp.part = part;

                        updated = true;

                        foreach (var tube in part.tubes)
                        {
                            tube.connections.Clear();
                            tube.field = null;
                        }

                        foreach (var knob in part.knobs)
                        {
                            knob.field           = null;
                            knob.connectionIndex = -1;
                        }

                        foreach (var field in connectivityComp.connectionFields)
                        {
                            foreach (var connection in field.connections)
                            {
                                ModelImporter.MatchConnectionWithKnob(connection, part.knobs);
                                ModelImporter.MatchConnectionWithTubes(connection, part.tubes);
                            }
                        }
                    }
                }
            }
            return(updated);
        }
        private void DrawMouldingColour(SerializedProperty property, GUIContent label)
        {
            var position = EditorGUILayout.GetControlRect();

            // Draw label
            position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

            if (property.hasMultipleDifferentValues)
            {
                // Create box and tooltip.
                GUI.Box(position, new GUIContent("", "Multiple colours selected"));
                var colorRect = new Rect(position.x + 1.0f, position.y + 1.0f, position.width - 2.0f, position.height - 2.0f);
                var lineRect  = new Rect(position.x + 1.0f, position.y + 1.0f + colorRect.height / 2.0f, 10.0f, 1.0f);
                EditorGUI.DrawRect(colorRect, new Color32(209, 209, 209, 255));
                EditorGUI.DrawRect(lineRect, Color.grey);
            }
            else
            {
                var mouldingColourId = (MouldingColour.Id)property.intValue;
                var colour           = MouldingColour.GetColour(mouldingColourId);

                // Create box and tooltip.
                GUI.Box(position, new GUIContent("", ObjectNames.NicifyVariableName((int)mouldingColourId + " - " + mouldingColourId.ToString())));

                // Draw rects with colour.
                var colorRect = new Rect(position.x + 1.0f, position.y + 1.0f, position.width - 2.0f, position.height - 2.0f - alphaBarHeight);
                var alphaRect = new Rect(position.x + 1.0f, position.y + 1.0f + colorRect.height, Mathf.Round((position.width - 2.0f) * colour.a), alphaBarHeight);
                var blackRect = new Rect(position.x + 1.0f + alphaRect.width, position.y + 1.0f + colorRect.height, position.width - 2.0f - alphaRect.width, alphaBarHeight);
                EditorGUI.DrawRect(colorRect, new Color(colour.r, colour.g, colour.b));
                EditorGUI.DrawRect(alphaRect, Color.white);
                EditorGUI.DrawRect(blackRect, Color.black);
            }

            // Detect click.
            if (Event.current.type == EventType.MouseDown)
            {
                if (position.Contains(Event.current.mousePosition))
                {
                    MouldingColourPicker.Show((c) =>
                    {
                        property.intValue = (int)MouldingColour.GetId(c);

                        // Collect all materials.
                        LXFMLDoc.Brick.Part.Material[] materials = new LXFMLDoc.Brick.Part.Material[materialIDsProp.arraySize];
                        for (var i = 0; i < materialIDsProp.arraySize; ++i)
                        {
                            materials[i] = new LXFMLDoc.Brick.Part.Material()
                            {
                                colorId = materialIDsProp.GetArrayElementAtIndex(i).intValue, shaderId = 0
                            };
                        }

                        // Run through all targeted parts and record them for undo.
                        foreach (var target in targets)
                        {
                            var partTarget = (Part)target;

                            // Register state before updating materials.
                            Undo.RegisterFullObjectHierarchyUndo(partTarget.gameObject, "Changed Material");
                        }

                        // Run through all targeted parts and update them.
                        foreach (var target in targets)
                        {
                            var partTarget = (Part)target;

                            // Update the part materials.
                            ModelImporter.SetMaterials(partTarget, materials, legacyProp.boolValue);

                            // Apply the new colour to the serialized property.
                            property.serializedObject.ApplyModifiedProperties();

                            // Update knobs and tubes of this brick and the bricks it is connected to directly.
                            var connectedBricks = partTarget.brick.GetConnectedBricks(false);
                            connectedBricks.Add(partTarget.brick);
                            foreach (var brick in connectedBricks)
                            {
                                foreach (var part in brick.parts)
                                {
                                    if (!part.legacy)
                                    {
                                        foreach (var connectionField in part.connectivity.connectionFields)
                                        {
                                            foreach (var connection in connectionField.connections)
                                            {
                                                connection.UpdateKnobsAndTubes();
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                                              false,
                                              false,
                                              true
                                              );
                }
            }
        }