public sealed override void OnInspectorGUI()
        {
            // group editing:
            if (ShowGroupInspector)
            {
                using (new NamedVerticalScope("Group"))
                {
                    GUILayout.BeginHorizontal();

                    // find whether we are currently inside of a group:
                    GroupBrush group = null;
                    if (BrushTarget.transform.parent)
                    {
                        group = BrushTarget.transform.parent.GetComponent <GroupBrush>();
                    }

                    // we are in a group:
                    if (group != null)
                    {
                        if (GUILayout.Button("Select Group"))
                        {
                            // select the group.
                            Selection.objects = new Object[] { BrushTarget.transform.parent.gameObject };
                        }
                    }

                    if (GUILayout.Button("Create Group"))
                    {
                        // create a group.
                        TransformHelper.GroupSelection();
                    }

                    GUILayout.EndHorizontal();
                }
            }

            // volume editing:
            if (BrushTargets.Any(b => b.Mode == CSGMode.Volume))
            {
                using (NamedVerticalScope scope = new NamedVerticalScope("Volume"))
                {
                    scope.WikiLink = "Brush-Volumes";

                    // find all of the volume types in the project:
                    List <System.Type> volumeTypes = Volume.FindAllInAssembly();
                    if (volumeTypes.Count == 0)
                    {
                        EditorGUILayout.LabelField("No volume types could be found!");
                    }
                    else
                    {
                        // find all of the volume brushes that are currently selected.
                        BrushBase[] volumeBrushes = BrushTargets.Where(b => b.Mode == CSGMode.Volume).ToArray();
                        BrushBase   volumeTarget  = volumeBrushes.Contains(BrushTarget) ? BrushTarget : volumeBrushes.Last();

                        // make sure all volume brushes are of the same type (for multi-editing).
                        System.Type brushTargetVolumeType = volumeTarget.Volume ? volumeTarget.Volume.GetType() : null;
                        if (volumeBrushes.Length > 1 && !volumeBrushes.All(b => b.Volume.GetType() == brushTargetVolumeType))
                        {
                            EditorGUILayout.LabelField("Cannot multi-edit volumes of different types!");
                        }
                        else
                        {
                            // let the user pick a volume type:
                            int selected = 0;
                            if (volumeTarget.Volume != null)
                            {
                                for (int i = 0; i < volumeTypes.Count; i++)
                                {
                                    selected = i;
                                    if (volumeTarget.Volume.GetType() == volumeTypes[i])
                                    {
                                        break;
                                    }
                                }
                            }
                            selected = EditorGUILayout.Popup("Volume Type", selected, volumeTypes.Select(v => v.Name + " (" + v.Namespace + ")").ToArray());

                            // set the brush volume type:
                            for (int i = 0; i < volumeBrushes.Length; i++)
                            {
                                BrushBase target = volumeBrushes[i];

                                // if the brush does not have a volume yet or the wrong one, create the selected type now:
                                if (target.Volume == null || target.Volume.GetType() != volumeTypes[selected])
                                {
                                    target.Volume = (Volume)ScriptableObject.CreateInstance(volumeTypes[selected]);
                                    if (serializedObject.targetObject != null)
                                    {
                                        serializedObject.ApplyModifiedProperties();
                                        System.Array.ForEach(volumeBrushes, item => item.Invalidate(true));
                                    }
                                }
                            }

                            // custom volume inspector:
                            if (volumeTarget.Volume.OnInspectorGUI(volumeBrushes.Select(b => b.Volume).ToArray()))
                            {
                                if (serializedObject.targetObject != null)
                                {
                                    serializedObject.ApplyModifiedProperties();
                                    System.Array.ForEach(volumeBrushes, item => item.Invalidate(true));
                                }
                            }
                        }
                    }
                }
            }

            // custom inspector:
            DoInspectorGUI();

            // generic brush editing:
            using (NamedVerticalScope scope = new NamedVerticalScope("Order"))
            {
                scope.WikiLink = "Tutorial-1#additive-and-subtractive-brushes";

                List <BrushBase> orderedTargets = BrushTargets.ToList();
                orderedTargets.RemoveAll(item => (item == null));
                orderedTargets.Sort((x, y) => x.transform.GetSiblingIndex().CompareTo(y.transform.GetSiblingIndex()));

                if (GUILayout.Button("Set As First"))
                {
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        // REVERSED
                        BrushBase thisBrush = orderedTargets[orderedTargets.Count - 1 - i];

                        Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                        thisBrush.transform.SetAsFirstSibling();
                    }

                    // Force all the brushes to recalculate their intersections and get ready for rebuilding
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        if (orderedTargets[i] is PrimitiveBrush)
                        {
                            ((PrimitiveBrush)orderedTargets[i]).RecalculateIntersections();
                            ((PrimitiveBrush)orderedTargets[i]).BrushCache.SetUnbuilt();
                        }
                    }
                }

                if (GUILayout.Button("Send Earlier"))
                {
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        BrushBase thisBrush = orderedTargets[i];

                        int siblingIndex = thisBrush.transform.GetSiblingIndex();
                        if (siblingIndex > 0)
                        {
                            Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                            siblingIndex--;
                            thisBrush.transform.SetSiblingIndex(siblingIndex);
                        }
                    }

                    // Force all the brushes to recalculate their intersections and get ready for rebuilding
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        if (orderedTargets[i] is PrimitiveBrush)
                        {
                            ((PrimitiveBrush)orderedTargets[i]).RecalculateIntersections();
                            ((PrimitiveBrush)orderedTargets[i]).BrushCache.SetUnbuilt();
                        }
                    }
                }

                if (GUILayout.Button("Send Later"))
                {
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        // REVERSED
                        BrushBase thisBrush = orderedTargets[orderedTargets.Count - 1 - i];

                        int siblingIndex = thisBrush.transform.GetSiblingIndex();
                        Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                        siblingIndex++;
                        thisBrush.transform.SetSiblingIndex(siblingIndex);
                    }

                    // Force all the brushes to recalculate their intersections and get ready for rebuilding
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        if (orderedTargets[i] is PrimitiveBrush)
                        {
                            ((PrimitiveBrush)orderedTargets[i]).RecalculateIntersections();
                            ((PrimitiveBrush)orderedTargets[i]).BrushCache.SetUnbuilt();
                        }
                    }
                }

                if (GUILayout.Button("Set As Last"))
                {
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        BrushBase thisBrush = orderedTargets[i];

                        Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                        thisBrush.transform.SetAsLastSibling();
                    }

                    // Force all the brushes to recalculate their intersections and get ready for rebuilding
                    for (int i = 0; i < orderedTargets.Count; i++)
                    {
                        if (orderedTargets[i] is PrimitiveBrush)
                        {
                            ((PrimitiveBrush)orderedTargets[i]).RecalculateIntersections();
                            ((PrimitiveBrush)orderedTargets[i]).BrushCache.SetUnbuilt();
                        }
                    }
                }

                if (serializedObject.targetObject != null)
                {
                    serializedObject.ApplyModifiedProperties();
                }
            }
        }
示例#2
0
        public override void OnInspectorGUI()
        {
            CSGModel csgModel = (CSGModel)target;

            // Ensure the default material is set
            csgModel.EnsureDefaultMaterialSet();

            DrawDefaultInspector();

            this.serializedObject.Update();
            using (NamedVerticalScope scope = new NamedVerticalScope("Build Settings"))
            {
                scope.WikiLink = "Build-Settings";

                EditorGUIUtility.fieldWidth = 0;
                EditorGUIUtility.labelWidth = 160;

                EditorGUILayout.PropertyField(generateCollisionMeshesProperty, new GUIContent("Generate Collision Meshes"));
                EditorGUILayout.PropertyField(generateTangentsProperty, new GUIContent("Generate Tangents"));

                EditorGUILayout.PropertyField(generateLightmapUVsProperty, new GUIContent("Generate Lightmap UVs"));
                EditorGUIUtility.labelWidth = 125;

                GUI.enabled           = generateLightmapUVsProperty.boolValue;
                EditorGUI.indentLevel = 1;
                EditorGUILayout.PropertyField(unwrapAngleErrorProperty, new GUIContent("Unwrap Angle Error"));
                EditorGUILayout.PropertyField(unwrapAreaErrorProperty, new GUIContent("Unwrap Area Error"));
                EditorGUILayout.PropertyField(unwrapHardAngleProperty, new GUIContent("Unwrap Hard Angle"));
                EditorGUILayout.PropertyField(unwrapPackMarginProperty, new GUIContent("Unwrap Pack Margin"));
                EditorGUI.indentLevel       = 0;
                EditorGUIUtility.labelWidth = 0;
                GUI.enabled = true;

                EditorGUILayout.PropertyField(shadowCastingModeProperty, new GUIContent("Shadow Casting Mode"));
                EditorGUILayout.PropertyField(reflectionProbeUsageProperty, new GUIContent("Reflection Probes"));

                // Experimental build settings to enable features that are not yet completely stable
                GUILayout.Label("Experimental", EditorStyles.boldLabel);
                EditorGUI.indentLevel = 1;

                EditorGUILayout.PropertyField(optimizeGeometryProperty, new GUIContent("Optimize Geometry"));
                EditorGUILayout.PropertyField(saveMeshesAsAssetsProperty, new GUIContent("Save Meshes As Assets"));
                EditorGUI.indentLevel = 0;
            }

            using (new NamedVerticalScope("Default Material"))
            {
                PhysicMaterial lastPhysicsMaterial = defaultPhysicsMaterialProperty.objectReferenceValue as PhysicMaterial;

                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(defaultPhysicsMaterialProperty, new GUIContent("Default Physics Material"));
                if (EditorGUI.EndChangeCheck())
                {
                    PhysicMaterial newPhysicsMaterial = defaultPhysicsMaterialProperty.objectReferenceValue as PhysicMaterial;

                    // Update the built mesh colliders that use the old material
                    UpdatePhysicsMaterial(lastPhysicsMaterial, newPhysicsMaterial);
                }

                // Track the last visual material, so that if the user changes it we can update built renderers instantly
                Material lastVisualMaterial = defaultVisualMaterialProperty.objectReferenceValue as Material;

                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(defaultVisualMaterialProperty, new GUIContent("Default Visual Material"));
                if (EditorGUI.EndChangeCheck())
                {
                    // User has changed the material, so grab the new material
                    Material newVisualMaterial = defaultVisualMaterialProperty.objectReferenceValue as Material;
                    // EnsureDefaultMaterialSet hasn't had a chance to run yet, so make sure we have a solid material reference
                    if (newVisualMaterial == null)
                    {
                        newVisualMaterial = csgModel.GetDefaultFallbackMaterial();
                        defaultVisualMaterialProperty.objectReferenceValue = newVisualMaterial;
                    }

                    // Update the built renderers that use the old material, also update source brush polygons
                    UpdateVisualMaterial(lastVisualMaterial, newVisualMaterial);

                    // Update the last build's default material because we don't need to build again
                    lastBuildDefaultVisualMaterialProperty.objectReferenceValue = newVisualMaterial;
                }

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Lit Texture (No Tint)"))
                {
                    Material newVisualMaterial = AssetDatabase.LoadMainAssetAtPath(CSGModel.GetSabreCSGPath() + "Resources/Materials/Default_Map.mat") as Material;
                    SetVisualMaterial(lastVisualMaterial, newVisualMaterial);
                }
                if (GUILayout.Button("Lit Vertex Tint"))
                {
                    Material newVisualMaterial = AssetDatabase.LoadMainAssetAtPath(CSGModel.GetSabreCSGPath() + "Resources/Materials/Default_LitWithTint.mat") as Material;
                    SetVisualMaterial(lastVisualMaterial, newVisualMaterial);
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Unlit Vertex Color"))
                {
                    Material newVisualMaterial = AssetDatabase.LoadMainAssetAtPath(CSGModel.GetSabreCSGPath() + "Resources/Materials/Default_VertexColor.mat") as Material;
                    SetVisualMaterial(lastVisualMaterial, newVisualMaterial);
                }
                if (GUILayout.Button("Lit Vertex Color"))
                {
                    Material newVisualMaterial = AssetDatabase.LoadMainAssetAtPath(CSGModel.GetSabreCSGPath() + "Resources/Materials/Default_VertexColorLit.mat") as Material;
                    SetVisualMaterial(lastVisualMaterial, newVisualMaterial);
                }
                EditorGUILayout.EndHorizontal();
            }

            using (NamedVerticalScope scope = new NamedVerticalScope("Export"))
            {
                scope.WikiLink = "Build-Settings#exporting-obj-files";

                if (GUILayout.Button("Export All To OBJ"))
                {
                    csgModel.ExportOBJ(false);
                }

                if (GUILayout.Button("Export Selected To OBJ"))
                {
                    csgModel.ExportOBJ(true);
                }
            }

            using (new NamedVerticalScope("Import"))
            {
                GuiLayoutBeginImporterSection(SabreCSGResources.ImporterUnrealGoldTexture, "Unreal Gold Importer", "Henry de Jongh");

                importerUnrealGoldScale = EditorGUILayout.IntField("Scale", importerUnrealGoldScale);
                if (importerUnrealGoldScale < 1)
                {
                    importerUnrealGoldScale = 1;
                }

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Import Unreal Gold Map (*.t3d)"))
                {
                    try
                    {
                        string path = EditorUtility.OpenFilePanel("Import Unreal Gold Map", "", "t3d");
                        if (path.Length != 0)
                        {
                            EditorUtility.DisplayProgressBar("SabreCSG: Importing Unreal Gold Map", "Parsing Unreal Text File (*.t3d)...", 0.0f);
                            var importer = new Importers.UnrealGold.T3dImporter();
                            var map      = importer.Import(path);
                            Importers.UnrealGold.T3dMapConverter.Import(csgModel, map, importerUnrealGoldScale);
                        }
                    }
                    catch (Exception ex)
                    {
                        EditorUtility.ClearProgressBar();
                        EditorUtility.DisplayDialog("Unreal Gold Map Import", "An exception occurred while importing the map:\r\n" + ex.Message, "Ohno!");
                    }
                }
                if (GUILayout.Button("?", GUILayout.Width(16)))
                {
                    EditorUtility.DisplayDialog("Unreal Gold Importer", "This importer was created using Unreal Gold 227 (http://oldunreal.com/).\n\nImportant Notes:\n* It will try to find the materials in your project automatically. First it looks for the full name like 'PlayrShp.Ceiling.Hullwk' then the last word 'Hullwk'. The latter option could cause some false positives, try creating a material with the full name if this happens.\n* This importer requires you to place a massive additive cube around your whole level as Unreal Editor uses the subtractive workflow.\n\nKnown Issues:\n* Concave brushes may cause corruptions.", "Okay");
                }
                EditorGUILayout.EndHorizontal();
                GuiLayoutEndImporterSection();

                //
                EditorGUILayout.Space();
                //

                GuiLayoutBeginImporterSection(SabreCSGResources.ImporterValveMapFormat2006Texture, "Source Engine 2006 Importer", "Henry de Jongh");

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Import Source Engine Map (*.vmf)"))
                {
                    try
                    {
                        string path = EditorUtility.OpenFilePanel("Import Source Engine Map", "", "vmf");
                        if (path.Length != 0)
                        {
                            EditorUtility.DisplayProgressBar("SabreCSG: Importing Source Engine Map", "Parsing Valve Map Format File (*.vmf)...", 0.0f);
                            var importer = new Importers.ValveMapFormat2006.VmfImporter();
                            var map      = importer.Import(path);
                            Importers.ValveMapFormat2006.VmfWorldConverter.Import(csgModel, map);
                        }
                    }
                    catch (Exception ex)
                    {
                        EditorUtility.ClearProgressBar();
                        EditorUtility.DisplayDialog("Source Engine Map Import", "An exception occurred while importing the map:\r\n" + ex.Message, "Ohno!");
                    }
                }

                if (GUILayout.Button("?", GUILayout.Width(16)))
                {
                    EditorUtility.DisplayDialog("Source Engine 2006 Importer", "This importer was created using Source SDK maps and Hammer 4.1.\n\nImportant Notes:\n* It will try to find the materials in your project automatically. First it looks for the full name with forward slashes '/' replaced by periods '.' like 'BRICK.BRICKFLOOR001A' then the last word 'BRICKFLOOR001A'. The latter option could cause some false positives, try creating a material with the full name if this happens.", "Okay");
                }
                EditorGUILayout.EndHorizontal();
                GuiLayoutEndImporterSection();

                //
                EditorGUILayout.Space();
                //

                GuiLayoutBeginImporterSection(SabreCSGResources.ImporterQuake1Texture, "Quake 1 Importer", "Jasmine Mickle");

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Import Quake 1 Map (*.map)"))
                {
                    try
                    {
                        string path = EditorUtility.OpenFilePanel("Import Quake 1 Map", "", "map");
                        if (path.Length != 0)
                        {
                            EditorUtility.DisplayProgressBar("SabreCSG: Importing Quake 1 Map", "Parsing Quake 1 Map File (*.map)...", 0.0f);
                            var importer = new Importers.Quake1.MapImporter();
                            var map      = importer.Import(path);
                            Importers.Quake1.MapWorldConverter.Import(csgModel, map);
                        }
                    }
                    catch (Exception ex)
                    {
                        EditorUtility.ClearProgressBar();
                        EditorUtility.DisplayDialog("Quake 1 Map Import", "An exception occurred while importing the map:\r\n" + ex.Message, "Ohno!");
                    }
                }

                if (GUILayout.Button("?", GUILayout.Width(16)))
                {
                    EditorUtility.DisplayDialog("Quake 1 Importer", "This importer (parser by Henry de Jongh) was created using Trenchbroom 2.0.6.\n\nImportant Notes:\n* It will try to find the materials in your project automatically. It will replace any leading '*' with '#' like '#teleport'.\n\nKnown Issues:\n* 45 degree angled walls may not have correct UV texture coordinates (are not correctly picking the dominant axis because there are two).\n* Negative vertex coordinates may not have correct UV texture coordinates (feel free to contribute improved UV mapping code if you know how it works, we had to guess most of the maths).", "Okay");
                }
                EditorGUILayout.EndHorizontal();
                GuiLayoutEndImporterSection();
            }

            using (new NamedVerticalScope("Stats"))
            {
                BuildMetrics buildMetrics = csgModel.BuildMetrics;

                GUILayout.Label("Vertices: " + buildMetrics.TotalVertices);
                GUILayout.Label("Triangles: " + buildMetrics.TotalTriangles);
                GUILayout.Label("Meshes: " + buildMetrics.TotalMeshes);
                GUILayout.Label("Build Time: " + buildMetrics.BuildTime.ToString());
            }

            // Make sure any serialize property changes are committed to the underlying Unity Object
            bool anyApplied = this.serializedObject.ApplyModifiedProperties();

            if (anyApplied)
            {
                // Make sure that nothing else is included in the Undo, as an immediate rebuild may take place and
                // we don't want the rebuild's lastBuildSettings being included in the undo step
                Undo.IncrementCurrentGroup();
            }
        }