void AutoWeld() { // Automatically weld any vertices that have been brought too close together if (primaryTargetBrush != null && selectedVertices.Count > 0) { float autoWeldTolerance = 0.001f; Dictionary <Brush, List <Vertex> > refinedSelections = new Dictionary <Brush, List <Vertex> >(); foreach (PrimitiveBrush brush in targetBrushes) { refinedSelections.Add(brush, SelectedVerticesOfBrush(brush)); } bool selectionCleared = false; foreach (PrimitiveBrush brush in targetBrushes) { Polygon[] sourcePolygons = brush.GetPolygons().DeepCopy(); List <Vertex> allVertices = new List <Vertex>(); for (int i = 0; i < sourcePolygons.Length; i++) { allVertices.AddRange(sourcePolygons[i].Vertices); } Polygon[] newPolygons = VertexUtility.WeldNearbyVertices(autoWeldTolerance, sourcePolygons, allVertices); if (newPolygons != null && newPolygons.Length != sourcePolygons.Length) { Undo.RecordObject(brush.transform, "Auto Weld Vertices"); Undo.RecordObject(brush, "Auto Weld Vertices"); if (!selectionCleared) { ClearSelection(); selectionCleared = true; } brush.SetPolygons(newPolygons); SelectVertices(brush, newPolygons, refinedSelections[brush]); } } } }
public override void OnInspectorGUI() { float drawableWidth = EditorGUIUtility.currentViewWidth; drawableWidth -= 42; // Take some off for scroll bars and padding PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => ((PrimitiveBrush)item).BrushType).ToArray(); PrimitiveBrushType?activeType = (selectedTypes.Length == 1) ? (PrimitiveBrushType?)selectedTypes[0] : null; using (new NamedVerticalScope("Type")) { GUILayout.BeginHorizontal(); float areaWidth = drawableWidth - 18; int buttonWidth = Mathf.RoundToInt(areaWidth / 5f); int stretchButtonWidth = Mathf.RoundToInt(areaWidth - buttonWidth * 4); // To ensure a justified alignment one button must be stretched slightly int buttonHeight = 50; GUIStyle brushButtonStyle = new GUIStyle(GUI.skin.button); brushButtonStyle.imagePosition = ImagePosition.ImageAbove; brushButtonStyle.fontSize = 10; GUIStyle labelStyle = new GUIStyle(GUI.skin.label); labelStyle.alignment = TextAnchor.LowerCenter; labelStyle.fontSize = brushButtonStyle.fontSize; bool shortMode = (areaWidth < 260); // Whether certain words need to be abbreviated to fit in the box DrawBrushButton(PrimitiveBrushType.Cube, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); DrawBrushButton(PrimitiveBrushType.Prism, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); DrawBrushButton(PrimitiveBrushType.Cylinder, activeType, brushButtonStyle, labelStyle, stretchButtonWidth, buttonHeight, shortMode); DrawBrushButton(PrimitiveBrushType.Sphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); DrawBrushButton(PrimitiveBrushType.IcoSphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); DrawBrushButton(PrimitiveBrushType.Cone, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); GUI.enabled = true; // Reset GUI enabled so that the next items aren't disabled GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (activeType.HasValue) { GUILayout.Label("Active: " + selectedTypes[0]); } else { GUILayout.Label("Active: Mixed"); } if (activeType.HasValue) { EditorGUIUtility.labelWidth = 60; EditorGUIUtility.fieldWidth = 50; EditorGUI.BeginChangeCheck(); if (activeType.Value == PrimitiveBrushType.Prism) { EditorGUILayout.PropertyField(prismSideCountProp, new GUIContent("Sides")); } else if (activeType.Value == PrimitiveBrushType.Cylinder) { EditorGUILayout.PropertyField(cylinderSideCountProp, new GUIContent("Sides")); } else if (activeType.Value == PrimitiveBrushType.Sphere) { EditorGUILayout.PropertyField(sphereSideCountProp, new GUIContent("Sides")); } else if (activeType.Value == PrimitiveBrushType.IcoSphere) { EditorGUILayout.PropertyField(icoSphereIterationCountProp, new GUIContent("Iterations")); } else if (activeType.Value == PrimitiveBrushType.Cone) { EditorGUILayout.PropertyField(coneSideCountProp, new GUIContent("Sides")); } if (EditorGUI.EndChangeCheck()) { // One of the properties has changed serializedObject.ApplyModifiedProperties(); ResetPolygonsKeepScale(); } } GUILayout.EndHorizontal(); } using (new NamedVerticalScope("Size")) { if (GUILayout.Button(new GUIContent("Reset Bounds", "Resets the bounds of the brush to [2,2,2]"))) { ResetBounds(); } GUILayout.BeginHorizontal(); GUI.SetNextControlName("rescaleTextbox"); scaleString = EditorGUILayout.TextField(scaleString); bool keyboardEnter = Event.current.isKey && Event.current.keyCode == KeyCode.Return && Event.current.type == EventType.KeyUp && GUI.GetNameOfFocusedControl() == "rescaleTextbox"; if (GUILayout.Button("Scale", GUILayout.MaxWidth(drawableWidth / 3f)) || keyboardEnter) { // Try to parse a Vector3 scale from the input string Vector3 scaleVector3; if (StringHelper.TryParseScale(scaleString, out scaleVector3)) { // None of the scale components can be zero if (scaleVector3.x != 0 && scaleVector3.y != 0 && scaleVector3.z != 0) { // Rescale all the brushes Undo.RecordObjects(targets, "Scale Polygons"); foreach (var thisBrush in targets) { BrushUtility.Scale((PrimitiveBrush)thisBrush, scaleVector3); } } } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUI.SetNextControlName("resizeTextbox"); resizeString = EditorGUILayout.TextField(resizeString); keyboardEnter = Event.current.isKey && Event.current.keyCode == KeyCode.Return && Event.current.type == EventType.KeyUp && GUI.GetNameOfFocusedControl() == "resizeTextbox"; if (GUILayout.Button("Resize", GUILayout.MaxWidth(drawableWidth / 3f)) || keyboardEnter) { // Try to parse a Vector3 scale from the input string Vector3 resizeVector3; if (StringHelper.TryParseScale(resizeString, out resizeVector3)) { // None of the size components can be zero if (resizeVector3.x != 0 && resizeVector3.y != 0 && resizeVector3.z != 0) { // Rescale all the brushes so that the local bounds is the same size as the resize vector Undo.RecordObjects(targets, "Resize Polygons"); PrimitiveBrush[] brushes = BrushTargets.Cast <PrimitiveBrush>().ToArray(); foreach (PrimitiveBrush brush in brushes) { BrushUtility.Resize(brush, resizeVector3); } } } } GUILayout.EndHorizontal(); } using (new NamedVerticalScope("Rotation")) { GUILayout.Label("Align up direction", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); if (GUILayout.Button("X")) { AlignUpToAxis(new Vector3(1, 0, 0), false); } if (GUILayout.Button("Y")) { AlignUpToAxis(new Vector3(0, 1, 0), false); } if (GUILayout.Button("Z")) { AlignUpToAxis(new Vector3(0, 0, 1), false); } GUILayout.EndHorizontal(); GUILayout.Label("Align up direction (keep positions)", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); if (GUILayout.Button("X")) { AlignUpToAxis(new Vector3(1, 0, 0), true); } if (GUILayout.Button("Y")) { AlignUpToAxis(new Vector3(0, 1, 0), true); } if (GUILayout.Button("Z")) { AlignUpToAxis(new Vector3(0, 0, 1), true); } GUILayout.EndHorizontal(); } using (new NamedVerticalScope("Misc")) { // Import Row GUILayout.BeginHorizontal(); sourceMesh = EditorGUILayout.ObjectField(sourceMesh, typeof(Mesh), false) as Mesh; if (GUILayout.Button("Import", GUILayout.MaxWidth(drawableWidth / 3f))) { if (sourceMesh != null) { Undo.RecordObjects(targets, "Import Polygons From Mesh"); Polygon[] polygons = BrushFactory.GeneratePolygonsFromMesh(sourceMesh).ToArray(); bool convex = GeometryHelper.IsBrushConvex(polygons); if (!convex) { Debug.LogError("Concavities detected in imported mesh. This may result in issues during CSG, please change the source geometry so that it is convex"); } foreach (var thisBrush in targets) { ((PrimitiveBrush)thisBrush).SetPolygons(polygons, true); } } } GUILayout.EndHorizontal(); // Shell Row GUILayout.BeginHorizontal(); if (shellDistance == 0) { shellDistance = CurrentSettings.PositionSnapDistance; } shellDistance = EditorGUILayout.FloatField("Distance", shellDistance); if (GUILayout.Button("Shell", GUILayout.MaxWidth(drawableWidth / 3f))) { List <GameObject> newSelection = new List <GameObject>(); foreach (var thisBrush in targets) { GameObject newObject = ((PrimitiveBrush)thisBrush).Duplicate(); Polygon[] polygons = newObject.GetComponent <PrimitiveBrush>().GetPolygons(); VertexUtility.DisplacePolygons(polygons, -shellDistance); Bounds newBounds = newObject.GetComponent <PrimitiveBrush>().GetBounds(); // Verify the new geometry if (GeometryHelper.IsBrushConvex(polygons) && newBounds.GetSmallestExtent() > 0) { Undo.RegisterCreatedObjectUndo(newObject, "Shell"); newSelection.Add(newObject); } else { // Produced a concave brush, delete it and pretend nothing happened GameObject.DestroyImmediate(newObject); Debug.LogWarning("Could not shell " + thisBrush.name + " as shelled geometry would not be valid. Try lowering the shell distance and attempt Shell again."); } } if (newSelection.Count > 0) { Selection.objects = newSelection.ToArray(); } } GUILayout.EndHorizontal(); // Split Intersecting Row if (GUILayout.Button("Split Intersecting Brushes")) { // Chop up the intersecting brushes by the brush planes, ideally into as few new brushes as possible PrimitiveBrush[] brushes = BrushTargets.Cast <PrimitiveBrush>().ToArray(); BrushUtility.SplitIntersecting(brushes); } // BrushOrder brushOrder = BrushTarget.GetBrushOrder(); // string positionString = string.Join(",", brushOrder.Position.Select(item => item.ToString()).ToArray()); // GUILayout.Label(positionString, EditorStyles.boldLabel); //List<BrushCache> intersections = ((PrimitiveBrush)BrushTarget).BrushCache.IntersectingVisualBrushCaches; //GUILayout.Label("Intersecting brushes " + intersections.Count, EditorStyles.boldLabel); //for (int i = 0; i < intersections.Count; i++) //{ // GUILayout.Label(intersections[i].Mode.ToString(), EditorStyles.boldLabel); //} } base.OnInspectorGUI(); }
public void DrawBrushTypeField() { GUILayout.BeginHorizontal(); PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => item.BrushType).ToArray(); if (overridenBrushType.HasValue) { selectedTypes = new PrimitiveBrushType[] { overridenBrushType.Value }; } PrimitiveBrushType?newType = SabreGUILayout.EnumPopupMixed("Brush Type", selectedTypes); if (newType.HasValue) { overridenBrushType = newType; if (newType.Value == PrimitiveBrushType.Prism) { GUILayout.Label("Sides", SabreGUILayout.GetForeStyle(), GUILayout.Width(30)); EditorGUILayout.PropertyField(prismSideCountProp, new GUIContent("")); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); } else if (newType.Value == PrimitiveBrushType.Cylinder) { GUILayout.Label("Sides", SabreGUILayout.GetForeStyle(), GUILayout.Width(30)); EditorGUILayout.PropertyField(cylinderSideCountProp, new GUIContent("")); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); } else if (newType.Value == PrimitiveBrushType.Sphere) { GUILayout.Label("Sides", SabreGUILayout.GetForeStyle(), GUILayout.Width(30)); EditorGUILayout.PropertyField(sphereSideCountProp, new GUIContent("")); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); } } if (GUILayout.Button("Reset Polygons")) { Undo.RecordObjects(targets, "Reset Polygons"); foreach (var thisBrush in targets) { if (overridenBrushType.HasValue) { ((PrimitiveBrush)thisBrush).BrushType = overridenBrushType.Value; } ((PrimitiveBrush)thisBrush).ResetPolygons(); ((PrimitiveBrush)thisBrush).Invalidate(true); } overridenBrushType = null; } GUILayout.EndHorizontal(); if (GUILayout.Button("Shell")) { List <GameObject> newSelection = new List <GameObject>(); foreach (var thisBrush in targets) { GameObject newObject = ((PrimitiveBrush)thisBrush).Duplicate(); Polygon[] polygons = newObject.GetComponent <PrimitiveBrush>().GetPolygons(); VertexUtility.DisplacePolygons(polygons, -CurrentSettings.PositionSnapDistance); Bounds newBounds = newObject.GetComponent <PrimitiveBrush>().GetBounds(); // Verify the new geometry if (GeometryHelper.IsBrushConvex(polygons) && newBounds.GetSmallestExtent() > 0) { Undo.RegisterCreatedObjectUndo(newObject, "Shell"); newSelection.Add(newObject); } else { // Produced a concave brush, delete it and pretend nothing happened GameObject.DestroyImmediate(newObject); Debug.LogWarning("Could not shell " + thisBrush.name + " as shelled geometry would not be valid. Try lowering Pos Snapping and attempt Shell again."); } } if (newSelection.Count > 0) { Selection.objects = newSelection.ToArray(); } } }
void OnToolbarGUI(int windowID) { GUILayout.Label("Vertex", SabreGUILayout.GetTitleStyle()); // Button should only be enabled if there are any vertices selected GUI.enabled = selectedVertices.Count > 0; if (GUILayout.Button("Connect", EditorStyles.miniButton)) { if (selectedVertices != null) { // Cache selection Dictionary <Brush, List <Vertex> > refinedSelections = new Dictionary <Brush, List <Vertex> >(); foreach (PrimitiveBrush brush in targetBrushes) { refinedSelections.Add(brush, SelectedVerticesOfBrush(brush)); } ClearSelection(); foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Connect Vertices"); Undo.RecordObject(brush, "Connect Vertices"); List <Edge> newEdges; // Polygon[] newPolygons = VertexUtility.ConnectVertices(brush.GetPolygons(), refinedSelections[brush], out newEdge); Polygon[] newPolygons = VertexUtility.ConnectVertices(brush.GetPolygons(), refinedSelections[brush], out newEdges); if (newPolygons != null) { brush.SetPolygons(newPolygons); for (int i = 0; i < newEdges.Count; i++) { SelectEdges(brush, newPolygons, newEdges[i]); } } } } } if (GUILayout.Button("Weld Selection To Mid-Point", EditorStyles.miniButton)) { if (selectedVertices != null) { Dictionary <Brush, List <Vertex> > refinedSelections = new Dictionary <Brush, List <Vertex> >(); foreach (PrimitiveBrush brush in targetBrushes) { refinedSelections.Add(brush, SelectedVerticesOfBrush(brush)); } ClearSelection(); foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Weld Vertices"); Undo.RecordObject(brush, "Weld Vertices"); Polygon[] newPolygons = VertexUtility.WeldVerticesToCenter(brush.GetPolygons(), refinedSelections[brush]); if (newPolygons != null) { brush.SetPolygons(newPolygons); } SelectVertices(brush, newPolygons, refinedSelections[brush]); } } } EditorGUILayout.BeginHorizontal(); weldTolerance = EditorGUILayout.FloatField(weldTolerance); if (GUILayout.Button("Weld with Tolerance", EditorStyles.miniButton)) { if (selectedVertices != null) { Dictionary <Brush, List <Vertex> > refinedSelections = new Dictionary <Brush, List <Vertex> >(); foreach (PrimitiveBrush brush in targetBrushes) { refinedSelections.Add(brush, SelectedVerticesOfBrush(brush)); } ClearSelection(); foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Weld Vertices"); Undo.RecordObject(brush, "Weld Vertices"); Polygon[] newPolygons = VertexUtility.WeldNearbyVertices(weldTolerance, brush.GetPolygons(), refinedSelections[brush]); if (newPolygons != null) { brush.SetPolygons(newPolygons); } SelectVertices(brush, newPolygons, refinedSelections[brush]); } } } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Global Snap", EditorStyles.miniButton)) { foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Snap Vertices"); Undo.RecordObject(brush, "Snap Vertices"); } SnapSelectedVertices(true); } if (GUILayout.Button("Local Snap", EditorStyles.miniButton)) { foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Snap Vertices"); Undo.RecordObject(brush, "Snap Vertices"); } SnapSelectedVertices(false); } EditorGUILayout.EndHorizontal(); GUILayout.Label("Edge", SabreGUILayout.GetTitleStyle()); GUI.enabled = selectedEdges.Count > 0; if (GUILayout.Button("Connect Mid-Points", EditorStyles.miniButton)) { if (selectedEdges != null) { List <Edge> selectedEdgesCopy = new List <Edge>(selectedEdges); ClearSelection(); foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Connect Mid-Points"); Undo.RecordObject(brush, "Connect Mid-Points"); Polygon[] newPolygons; List <Edge> newEdges; if (EdgeUtility.SplitPolygonsByEdges(brush.GetPolygons(), selectedEdgesCopy, out newPolygons, out newEdges)) { brush.SetPolygons(newPolygons); for (int i = 0; i < newEdges.Count; i++) { SelectEdges(brush, newPolygons, newEdges[i]); } } } } } if (GUILayout.Button("Split", EditorStyles.miniButton)) { if (selectedEdges != null) { List <KeyValuePair <Vertex, Brush> > newSelectedVertices = new List <KeyValuePair <Vertex, Brush> >(); foreach (PrimitiveBrush brush in targetBrushes) { Undo.RecordObject(brush.transform, "Split Edge"); Undo.RecordObject(brush, "Split Edge"); Polygon[] polygons = brush.GetPolygons(); for (int j = 0; j < selectedEdges.Count; j++) { // First check if this edge actually belongs to the brush Brush parentBrush = selectedVertices[selectedEdges[j].Vertex1]; if (parentBrush == brush) { for (int i = 0; i < polygons.Length; i++) { Vertex newVertex; if (EdgeUtility.SplitPolygonAtEdge(polygons[i], selectedEdges[j], out newVertex)) { newSelectedVertices.Add(new KeyValuePair <Vertex, Brush>(newVertex, brush)); } } } } brush.Invalidate(true); } ClearSelection(); for (int i = 0; i < newSelectedVertices.Count; i++) { Brush brush = newSelectedVertices[i].Value; Vertex vertex = newSelectedVertices[i].Key; SelectVertices(brush, brush.GetPolygons(), new List <Vertex>() { vertex }); } } } }
List <PrimitiveBrush> AutoWeld() { // Track the brushes that welding has changed List <PrimitiveBrush> changedBrushes = new List <PrimitiveBrush>(); // Automatically weld any vertices that have been brought too close together if (primaryTargetBrush != null && selectedVertices.Count > 0) { float autoWeldTolerance = 0.001f; Dictionary <Brush, List <Vertex> > refinedSelections = new Dictionary <Brush, List <Vertex> >(); foreach (PrimitiveBrush brush in targetBrushes) { refinedSelections.Add(brush, SelectedVerticesOfBrush(brush)); } bool selectionCleared = false; foreach (PrimitiveBrush brush in targetBrushes) { Polygon[] sourcePolygons = brush.GetPolygons(); // Make a copy so that we can differentiate newPolygons from the original, since welding updates affected polygons in place Polygon[] sourcePolygonsCopy = sourcePolygons.DeepCopy(); List <Vertex> allVertices = new List <Vertex>(); for (int i = 0; i < sourcePolygonsCopy.Length; i++) { allVertices.AddRange(sourcePolygonsCopy[i].Vertices); } Polygon[] newPolygons = VertexUtility.WeldNearbyVertices(autoWeldTolerance, sourcePolygonsCopy, allVertices); bool hasChanged = false; if (newPolygons.Length != sourcePolygons.Length) { hasChanged = true; } if (!hasChanged) { for (int i = 0; i < sourcePolygons.Length; i++) { if (sourcePolygons[i].Vertices.Length != newPolygons[i].Vertices.Length) { hasChanged = true; break; } } } if (hasChanged) { Undo.RecordObject(brush.transform, "Auto Weld Vertices"); Undo.RecordObject(brush, "Auto Weld Vertices"); if (!selectionCleared) { ClearSelection(); selectionCleared = true; } brush.SetPolygons(newPolygons); SelectVertices(brush, newPolygons, refinedSelections[brush]); // Brush has changed so mark it to be returned changedBrushes.Add(brush); } } } // Return the brushes that welding has changed return(changedBrushes); }