public static void InsertEdgeLoop(PrimitiveBrush brush, Plane localClipPlane) { // Clip the polygons against the plane List <Polygon> polygonsFront; List <Polygon> polygonsBack; if (PolygonFactory.SplitPolygonsByPlane(new List <Polygon>(brush.GetPolygons()), localClipPlane, true, out polygonsFront, out polygonsBack)) { List <Polygon> allPolygons = new List <Polygon>(); // Concat back into one list allPolygons.AddRange(polygonsFront); allPolygons.AddRange(polygonsBack); // Remove the inserted polygons for (int i = 0; i < allPolygons.Count; i++) { if (allPolygons[i].ExcludeFromFinal) { allPolygons.RemoveAt(i); i--; } } // Update the brush with the new polygons brush.SetPolygons(allPolygons.ToArray(), true); } }
public override void Invalidate(bool polygonsChanged) { generatedBrushes.RemoveAll(item => item == null); if (generatedBrushes.Count > BrushCount) { // Trim off the extraneous brushes for (int i = BrushCount; i < generatedBrushes.Count; i++) { if (generatedBrushes[i] != null) { DestroyImmediate(generatedBrushes[i].gameObject); } } generatedBrushes.RemoveRange(BrushCount, generatedBrushes.Count - BrushCount); } else if (generatedBrushes.Count < BrushCount) { // Add in new brushes to fill the gap while (generatedBrushes.Count < BrushCount) { PrimitiveBrush newBrush = CreateBrush(); newBrush.transform.SetParent(this.transform, false); newBrush.SetBrushController(this); generatedBrushes.Add(newBrush); } } }
/// <summary> /// Scales the brush by a local Vector3 scale from its pivot /// </summary> /// <param name="brush">The brush to be rescaled</param> /// <param name="rescaleValue">Local scale to apply</param> public static void Scale(PrimitiveBrush brush, Vector3 scaleValue) { Polygon[] polygons = brush.GetPolygons(); for (int i = 0; i < polygons.Length; i++) { Polygon polygon = polygons[i]; polygons[i].CalculatePlane(); Vector3 previousPlaneNormal = polygons[i].Plane.normal; int vertexCount = polygon.Vertices.Length; Vector3[] newPositions = new Vector3[vertexCount]; Vector2[] newUV = new Vector2[vertexCount]; for (int j = 0; j < vertexCount; j++) { newPositions[j] = polygon.Vertices[j].Position; newUV[j] = polygon.Vertices[j].UV; } for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; Vector3 newPosition = vertex.Position.Multiply(scaleValue); newPositions[j] = newPosition; newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition); } // Apply all the changes to the polygon for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; vertex.Position = newPositions[j]; vertex.UV = newUV[j]; } // Polygon geometry has changed, inform the polygon that it needs to recalculate its cached plane polygons[i].CalculatePlane(); Vector3 newPlaneNormal = polygons[i].Plane.normal; // Find the rotation from the original polygon plane to the new polygon plane Quaternion normalRotation = Quaternion.FromToRotation(previousPlaneNormal, newPlaneNormal); // Rotate all the vertex normals by the new rotation for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; vertex.Normal = normalRotation * vertex.Normal; } } #if UNITY_EDITOR EditorHelper.SetDirty(brush); #endif brush.Invalidate(true); }
/// <summary> /// Resizes the brush so that it's local bounds match the specified extents /// </summary> /// <param name="brush">The brush to be resized</param> /// <param name="rescaleValue">The extents to match</param> public static void Resize(PrimitiveBrush brush, Vector3 resizeValue) { Bounds bounds = brush.GetBounds(); // Calculate the rescale vector required to change the bounds to the resize vector Vector3 rescaleVector3 = resizeValue.Divide(bounds.size); Scale(brush, rescaleVector3); }
/// <summary> /// Create a brush at the origin using a specified set of polygons /// </summary> /// <returns>The custom brush game object.</returns> /// <param name="polygons">Polygons.</param> public GameObject CreateCustomBrush(Polygon[] polygons) { GameObject brushObject = new GameObject("AppliedBrush"); brushObject.transform.parent = this.transform; PrimitiveBrush primitiveBrush = brushObject.AddComponent <PrimitiveBrush>(); primitiveBrush.SetPolygons(polygons, true); return(brushObject); }
public GameObject CreateBrush(PrimitiveBrushType brushType) { GameObject brushObject = new GameObject("AppliedBrush"); brushObject.transform.parent = this.transform; PrimitiveBrush primitiveBrush = brushObject.AddComponent <PrimitiveBrush>(); primitiveBrush.BrushType = brushType; primitiveBrush.ResetPolygons(); return(brushObject); }
/// <summary> /// Creates a brush under the CSG Model with the specified attributes. /// </summary> /// <returns>The created game object.</returns> /// <param name="brushType">Brush type.</param> /// <param name="localPosition">Local position of the brush's transform</param> /// <param name="localSize">Local bounds size of the brush (Optional, defaults to 2,2,2).</param> /// <param name="localRotation">Local rotation of the brush (Optional, defaults to identity quaternion).</param> /// <param name="material">Material to apply to all faces, (Optional, defaults to null for default material).</param> /// <param name="csgMode">Whether the brush is additive or subtractive (Optional, defaults to additive).</param> /// <param name="brushName">Name for the game object (Optional, defaults to "AppliedBrush").</param> public GameObject CreateBrush(PrimitiveBrushType brushType, Vector3 localPosition, Vector3 localSize = default(Vector3), Quaternion localRotation = default(Quaternion), Material material = null, CSGMode csgMode = CSGMode.Add, string brushName = null) { GameObject brushObject; if (!string.IsNullOrEmpty(brushName)) { brushObject = new GameObject(brushName); } else { brushObject = new GameObject(""); } brushObject.transform.localScale = this.transform.lossyScale; brushObject.transform.parent = this.transform; brushObject.transform.localPosition = localPosition; if (localRotation != default(Quaternion)) { brushObject.transform.localRotation = localRotation; } PrimitiveBrush primitiveBrush = brushObject.AddComponent <PrimitiveBrush>(); primitiveBrush.BrushType = brushType; primitiveBrush.Mode = csgMode; primitiveBrush.ResetPolygons(); if (localSize != default(Vector3) && localSize != new Vector3(2, 2, 2)) { BrushUtility.Resize(primitiveBrush, localSize); } else { // Resize automatically invalidates a brush with changed polygons set, if no resize took place we still need to make sure it happens primitiveBrush.Invalidate(true); } if (material != null) { SurfaceUtility.SetAllPolygonsMaterials(primitiveBrush, material); } return(brushObject); }
/// <summary> /// Rebuilds the volume, creating or deleting the volume component and applying new settings. /// </summary> internal void RebuildVolume() { // volumes can only be primitive brushes at the moment. if (GetType() != typeof(PrimitiveBrush)) { return; } PrimitiveBrush self = (PrimitiveBrush)this; // remove volumes from brushes that are no longer volumes: if (Mode != CSGMode.Volume && Volume != null) { // set volume handle to null. Volume = null; // delete any built volume. Transform volume1 = transform.Find(Constants.GameObjectVolumeComponentIdentifier); if (volume1 != null) { GameObject.DestroyImmediate(volume1.gameObject); } } // generate all of the volume brushes: if (Mode == CSGMode.Volume && Volume != null) { // remove any existing built volume: Transform volume2 = transform.Find(Constants.GameObjectVolumeComponentIdentifier); if (volume2 != null) { GameObject.DestroyImmediate(volume2.gameObject); } // create the game object with convex mesh collider: Mesh mesh = new Mesh(); BrushFactory.GenerateMeshFromPolygonsFast(self.GetPolygons(), ref mesh, 0.0f); GameObject gameObject = CreateVolumeMeshCollider(mesh); gameObject.transform.position = transform.position; gameObject.transform.rotation = transform.rotation; // execute custom volume generation code: Volume.OnCreateVolume(gameObject); } }
/// <summary> /// Applies a clip or split operation to a brush by supplying a plane local to the brush. Clipping allows you to cut away and discard parts of the brush, while splitting allows you to split a brush in two. /// </summary> /// <returns>If keepBothSides is <c>true</c>, this returns the second brush if one was created.</returns> /// <param name="brush">Brush to clip/split</param> /// <param name="localPlane">Local plane to clip/split against</param> /// <param name="keepBothSides">If set to <c>true</c> split the brush and keep both sides.</param> /// <param name="resetPivots">If set to <c>true</c> make sure the pivot clipped brush and any new brush are at their center.</param> public static GameObject ApplyClipPlane(PrimitiveBrush brush, Plane localPlane, bool keepBothSides, bool resetPivots = true) { // Clip the polygons against the plane List <Polygon> polygonsFront; List <Polygon> polygonsBack; List <Polygon> polygonList = new List <Polygon>(brush.GetPolygons()); if (PolygonFactory.SplitPolygonsByPlane(polygonList, localPlane, false, out polygonsFront, out polygonsBack)) { // Update the brush with the new polygons brush.SetPolygons(polygonsFront.ToArray(), true); GameObject newObject = null; // If they have decided to split instead of clip, create a second brush with the other side if (keepBothSides) { newObject = brush.Duplicate(); // Finally give the new brush the other set of polygons newObject.GetComponent <PrimitiveBrush>().SetPolygons(polygonsBack.ToArray(), true); newObject.transform.SetSiblingIndex(brush.transform.GetSiblingIndex()); // Reset the new brush's pivot if (resetPivots) { newObject.GetComponent <PrimitiveBrush>().ResetPivot(); } } // Can't reset the first brush pivot until after the second brush is made, otherwise the second // brush is effectively translated twice, ending up in the wrong position if (resetPivots) { brush.ResetPivot(); } return(newObject); } else { return(null); } }
private void OnMouseMove(SceneView sceneView, Event e) { // if the user stopped holding shift we cancel the automatic height. if (!e.shift) { startedSubtract = false; } UpdateCSGMode(e); if (selectingHeight) { // Mouse movement while selecting depth controls prism height OnMouseMoveSelectHeight(sceneView, e); } else { // Not currently drawing, so update the active polygon to whatever they are currently hovering over if (drawMode == DrawMode.None) { PolygonRaycastHit?hit = CalculateHitPolygon(e.mousePosition); if (hit.HasValue) { activePolygon = hit.Value.Polygon; activeBrush = hit.Value.GameObject.GetComponent <PrimitiveBrush>(); } else { activePolygon = null; activeBrush = null; } } // Find the snapped hover point based on any active polygon or the grid Vector3?hitPoint = GetHitPoint(e.mousePosition); if (hitPoint.HasValue) { hoverPoint = hitPoint.Value; SceneView.RepaintAll(); } } }
/// <summary> /// Creates a brush under the CSG Model with the specified attributes. /// </summary> /// <returns>The created game object.</returns> /// <param name="brushType">Brush type.</param> /// <param name="localPosition">Local position of the brush's transform</param> /// <param name="localSize">Local bounds size of the brush (Optional, defaults to 2,2,2).</param> /// <param name="localRotation">Local rotation of the brush (Optional, defaults to identity quaternion).</param> /// <param name="material">Material to apply to all faces, (Optional, defaults to null for default material).</param> /// <param name="csgMode">Whether the brush is additive or subtractive (Optional, defaults to additive).</param> /// <param name="brushName">Name for the game object (Optional, defaults to "AppliedBrush").</param> public GameObject CreateBrush(PrimitiveBrushType brushType, Vector3 localPosition, Vector3 localSize = default(Vector3), Quaternion localRotation = default(Quaternion), Material material = null, CSGMode csgMode = CSGMode.Add, string brushName = null) { GameObject brushObject; if (!string.IsNullOrEmpty(brushName)) { brushObject = new GameObject(brushName); } else { brushObject = new GameObject("AppliedBrush"); } brushObject.transform.parent = this.transform; brushObject.transform.localPosition = localPosition; if (localRotation != default(Quaternion)) { brushObject.transform.localRotation = localRotation; } PrimitiveBrush primitiveBrush = brushObject.AddComponent <PrimitiveBrush>(); primitiveBrush.BrushType = brushType; primitiveBrush.Mode = csgMode; primitiveBrush.ResetPolygons(); if (localSize != default(Vector3) && localSize != new Vector3(2, 2, 2)) { BrushUtility.Resize(primitiveBrush, localSize); // primitiveBrush.Invalidate(true); } if (material != null) { SurfaceUtility.SetAllPolygonsMaterials(primitiveBrush, material); } return(brushObject); }
void OnSelectionChanged() { bool anyCSGObjectsSelected = false; bool anyNonCSGSelected = false; List <CSGModel> foundModels = new List <CSGModel>(); Dictionary <CSGModel, List <UnityEngine.Object> > selectedBrushes = new Dictionary <CSGModel, List <UnityEngine.Object> >(); for (int i = 0; i < Selection.gameObjects.Length; i++) { PrimitiveBrush primitiveBrush = Selection.gameObjects[i].GetComponent <PrimitiveBrush>(); CSGModel csgModel = Selection.gameObjects[i].GetComponent <CSGModel>(); if (primitiveBrush != null) { csgModel = primitiveBrush.GetCSGModel() as CSGModel; if (!foundModels.Contains(csgModel)) { foundModels.Add(csgModel); selectedBrushes[csgModel] = new List <UnityEngine.Object>(); } selectedBrushes[csgModel].Add(primitiveBrush.gameObject); } if (csgModel != null) { anyCSGObjectsSelected = true; if (!foundModels.Contains(csgModel)) { foundModels.Add(csgModel); selectedBrushes[csgModel] = new List <UnityEngine.Object>(); } } else { CSGModel[] parentCSGModels = Selection.gameObjects[i].GetComponentsInParent <CSGModel>(true); if (parentCSGModels.Length > 0) { csgModel = parentCSGModels[0]; if (Selection.gameObjects[i].GetComponent <MeshFilter>() != null || Selection.gameObjects[i].GetComponent <MeshCollider>() != null) { anyNonCSGSelected = true; } else { anyCSGObjectsSelected = true; if (!foundModels.Contains(csgModel)) { foundModels.Add(csgModel); selectedBrushes[csgModel] = new List <UnityEngine.Object>(); } } } else { anyNonCSGSelected = true; } } } if (anyCSGObjectsSelected) { CSGModel activeModel = null; if (foundModels.Count == 1) { if (!foundModels[0].EditMode) { foundModels[0].EditMode = true; } activeModel = foundModels[0]; } else { bool anyActive = false; for (int i = 0; i < foundModels.Count; i++) { if (foundModels[i].EditMode) { anyActive = true; activeModel = foundModels[i]; break; } } if (!anyActive) { foundModels[0].EditMode = true; activeModel = foundModels[0]; } } if (anyNonCSGSelected && activeModel != null) { deferredSelection = selectedBrushes[activeModel].ToArray(); } } else if (anyNonCSGSelected) { EditMode = false; } if (EditMode) { // Walk backwards until we find the last selected brush for (int i = Selection.gameObjects.Length - 1; i >= 0; i--) { Brush brush = Selection.gameObjects[i].GetComponent <Brush>(); if (brush != null) { lastSelectedBrush = brush; break; } } } }
public void OnBrushDisabled(PrimitiveBrush brush) { polygonsRemoved = true; }
private void OnMouseDown(SceneView sceneView, Event e) { if (drawMode == DrawMode.None || drawMode == DrawMode.Ambiguous) { startedSubtract = e.shift; } UpdateCSGMode(e); if (selectingHeight) { return; } if (e.clickCount == 2) { // Double click, so finish the polygon if (drawMode == DrawMode.PolygonBase) { if (Is3DView && !startedSubtract) { selectingHeight = true; prismHeight = 0; ignoreNextMouseUp = true; SceneView.RepaintAll(); } else { CreateBrush(hitPoints); ResetTool(); sceneView.Repaint(); } } } else { if (drawMode == DrawMode.None) { PolygonRaycastHit?hit = CalculateHitPolygon(e.mousePosition); if (hit.HasValue) { activePolygon = hit.Value.Polygon; activeBrush = hit.Value.GameObject.GetComponent <PrimitiveBrush>(); } else { activePolygon = null; activeBrush = null; } Vector3?hitPoint = GetHitPoint(e.mousePosition); if (hitPoint.HasValue) { downPoint = hitPoint.Value; hoverPoint = downPoint; } hitPoints.Clear(); if (hitPoint.HasValue) { drawMode = DrawMode.Ambiguous; } } else { Vector3?hitPoint = GetHitPoint(e.mousePosition); if (hitPoint.HasValue) { downPoint = hitPoint.Value; hoverPoint = downPoint; } } } }
public void SetSelection(GameObject selectedGameObject, GameObject[] selectedGameObjects) { // Find the selected brush bases List <BrushBase> brushBases = new List <BrushBase>(); for (int i = 0; i < Selection.gameObjects.Length; i++) { BrushBase matchedBrushBase = Selection.gameObjects[i].GetComponent <BrushBase>(); // If we've selected a brush base that isn't a prefab in the project if (matchedBrushBase != null #if UNITY_2018_2_OR_NEWER && !(PrefabUtility.GetCorrespondingObjectFromSource(matchedBrushBase.gameObject) == null #else && !(PrefabUtility.GetPrefabParent(matchedBrushBase.gameObject) == null #endif #if !UNITY_2018_3_OR_NEWER && PrefabUtility.GetPrefabObject(matchedBrushBase.transform) != null)) #else && PrefabUtility.GetPrefabInstanceHandle(selectedGameObject.transform) != null)) #endif { brushBases.Add(matchedBrushBase); } } // Also find an array of brushes (brush bases that aren't primitive brushes will have a null entry) PrimitiveBrush[] primitiveBrushes = brushBases.Select(item => item.GetComponent <PrimitiveBrush>()).ToArray(); // Pick out the first brush base and primitive brush (or null if none) BrushBase newPrimaryBrushBase = null; PrimitiveBrush newPrimaryBrush = null; // Make sure it's not null and that it isn't a prefab in the project if (selectedGameObject != null #if UNITY_2018_2_OR_NEWER && !(PrefabUtility.GetCorrespondingObjectFromSource(selectedGameObject) == null #else && !(PrefabUtility.GetPrefabParent(selectedGameObject) == null #endif #if !UNITY_2018_3_OR_NEWER && PrefabUtility.GetPrefabObject(selectedGameObject.transform) != null)) #else && PrefabUtility.GetPrefabInstanceHandle(selectedGameObject.transform) != null)) #endif { newPrimaryBrushBase = selectedGameObject.GetComponent <BrushBase>(); // brushBases.FirstOrDefault(); newPrimaryBrush = selectedGameObject.GetComponent <PrimitiveBrush>(); // primitiveBrushes.Where(item => item != null).FirstOrDefault(); } // If the primary brush base has changed if (primaryTargetBrushBase != newPrimaryBrushBase || (primaryTargetBrushBase == null && newPrimaryBrushBase != null)) // Special case for undo where references are equal but one is null { primaryTargetBrushBase = newPrimaryBrushBase; if (newPrimaryBrushBase != null) { primaryTargetBrushTransform = newPrimaryBrushBase.transform; } else { primaryTargetBrushTransform = null; } ResetTool(); } BrushBase[] brushBasesArray = brushBases.ToArray(); primaryTargetBrush = newPrimaryBrush; if (!targetBrushBases.ContentsEquals(brushBasesArray)) { OnSelectionChanged(); targetBrushBases = brushBasesArray; targetBrushes = primitiveBrushes; targetBrushTransforms = new Transform[brushBasesArray.Length]; for (int i = 0; i < brushBasesArray.Length; i++) { if (brushBasesArray[i] != null) { targetBrushTransforms[i] = brushBasesArray[i].transform; } else { targetBrushTransforms[i] = null; } } } }
public override void OnInspectorGUI() { // DrawDefaultInspector(); DrawBrushTypeField(); // BrushOrder brushOrder = BrushTarget.GetBrushOrder(); // string positionString = string.Join(",", brushOrder.Position.Select(item => item.ToString()).ToArray()); // GUILayout.Label(positionString, EditorStyles.boldLabel); // List<BrushCache> intersections = BrushTarget.BrushCache.IntersectingVisualBrushCaches; // // for (int i = 0; i < intersections.Count; i++) // { // GUILayout.Label(intersections[i].Mode.ToString(), EditorStyles.boldLabel); // } GUILayout.BeginHorizontal(); GUI.SetNextControlName("rescaleTextbox"); rescaleString = EditorGUILayout.TextField(rescaleString); bool keyboardEnter = Event.current.isKey && Event.current.keyCode == KeyCode.Return && Event.current.type == EventType.KeyUp && GUI.GetNameOfFocusedControl() == "rescaleTextbox"; if (GUILayout.Button("Rescale") || keyboardEnter) { // Try to parse a Vector3 scale from the input string Vector3 rescaleVector3; if (StringHelper.TryParseScale(rescaleString, out rescaleVector3)) { // None of the scale components can be zero if (rescaleVector3.x != 0 && rescaleVector3.y != 0 && rescaleVector3.z != 0) { // Rescale all the brushes Undo.RecordObjects(targets, "Rescale Polygons"); foreach (var thisBrush in targets) { BrushUtility.Rescale((PrimitiveBrush)thisBrush, rescaleVector3); } } } } 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") || 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; foreach (PrimitiveBrush brush in brushes) { BrushUtility.Resize(brush, resizeVector3); } } } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); sourceMesh = EditorGUILayout.ObjectField(sourceMesh, typeof(Mesh), false) as Mesh; if (GUILayout.Button("Import")) { 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(); List <PrimitiveBrush> orderedTargets = BrushTargets.ToList(); 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 PrimitiveBrush 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++) { orderedTargets[i].RecalculateIntersections(); orderedTargets[i].BrushCache.SetUnbuilt(); } } if (GUILayout.Button("Send Earlier")) { for (int i = 0; i < orderedTargets.Count; i++) { PrimitiveBrush 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++) { orderedTargets[i].RecalculateIntersections(); orderedTargets[i].BrushCache.SetUnbuilt(); } } if (GUILayout.Button("Send Later")) { for (int i = 0; i < orderedTargets.Count; i++) { // REVERSED PrimitiveBrush 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++) { orderedTargets[i].RecalculateIntersections(); orderedTargets[i].BrushCache.SetUnbuilt(); } } if (GUILayout.Button("Set As Last")) { for (int i = 0; i < orderedTargets.Count; i++) { PrimitiveBrush 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++) { orderedTargets[i].RecalculateIntersections(); orderedTargets[i].BrushCache.SetUnbuilt(); } } serializedObject.ApplyModifiedProperties(); // GUILayout.Label("UVs", EditorStyles.boldLabel); // // if (GUILayout.Button("Flip XY")) // { // UVUtility.FlipUVsXY(thisBrush.Polygons); // } // // GUILayout.BeginHorizontal(); // if (GUILayout.Button("Flip X")) // { // UVUtility.FlipUVsX(thisBrush.Polygons); // } // if (GUILayout.Button("Flip Y")) // { // UVUtility.FlipUVsY(thisBrush.Polygons); // } // GUILayout.EndHorizontal(); // // GUILayout.BeginHorizontal(); // if (GUILayout.Button("UVs x 2")) // { // UVUtility.ScaleUVs(thisBrush.Polygons, 2f); // } // if (GUILayout.Button("UVs / 2")) // { // UVUtility.ScaleUVs(thisBrush.Polygons, .5f); // } // GUILayout.EndHorizontal(); // Ensure Edit Mode is on // csgModel.EditMode = true; }
private void CreateBrush(List <Vector3> positions) { Polygon sourcePolygon = PolygonFactory.ConstructPolygon(positions, true); // Early out if it wasn't possible to create the polygon if (sourcePolygon == null) { return; } if (activePolygon != null) { for (int i = 0; i < sourcePolygon.Vertices.Length; i++) { Vector2 newUV = GeometryHelper.GetUVForPosition(activePolygon, sourcePolygon.Vertices[i].Position); sourcePolygon.Vertices[i].UV = newUV; } } Vector3 planeNormal = GetActivePlane().normal; // Debug.Log(Vector3.Dot(sourcePolygon.Plane.normal, planeNormal)); // Flip the polygon if the winding order is wrong if (Vector3.Dot(sourcePolygon.Plane.normal, planeNormal) < 0) { sourcePolygon.Flip(); // Need to flip the UVs across the U (X) direction for (int i = 0; i < sourcePolygon.Vertices.Length; i++) { Vector2 uv = sourcePolygon.Vertices[i].UV; uv.x = 1 - uv.x; sourcePolygon.Vertices[i].UV = uv; } } float extrusionDistance = 1; Vector3 positionOffset = Vector3.zero; if (selectingHeight) { extrusionDistance = prismHeight; } else { if (activePolygon != null && activeBrush != null) { extrusionDistance = activeBrush.CalculateExtentsInAxis(planeNormal); } else { Brush lastSelectedBrush = csgModel.LastSelectedBrush; if (lastSelectedBrush != null) { Bounds lastSelectedBrushBounds = lastSelectedBrush.GetBoundsTransformed(); for (int i = 0; i < 3; i++) { if (!planeNormal[i].EqualsWithEpsilon(0)) { if (lastSelectedBrushBounds.size[i] != 0) { extrusionDistance = lastSelectedBrushBounds.size[i]; if (planeNormal[i] > 0) { positionOffset[i] = lastSelectedBrushBounds.center[i] - lastSelectedBrushBounds.extents[i]; } else { positionOffset[i] = lastSelectedBrushBounds.center[i] + lastSelectedBrushBounds.extents[i]; } } } } } } // Subtractions should go through if (csgMode == CSGMode.Subtract) { sourcePolygon.Flip(); } } Quaternion rotation; Polygon[] polygons; SurfaceUtility.ExtrudePolygon(sourcePolygon, extrusionDistance, out polygons, out rotation); GameObject newObject = csgModel.CreateCustomBrush(polygons); PrimitiveBrush newBrush = newObject.GetComponent <PrimitiveBrush>(); newObject.transform.rotation = rotation; newObject.transform.position += positionOffset; if (activePolygon != null && activePolygon.Material != csgModel.GetDefaultMaterial()) { for (int i = 0; i < polygons.Length; i++) { polygons[i].Material = activePolygon.Material; } } // Finally give the new brush the other set of polygons newBrush.SetPolygons(polygons, true); newBrush.Mode = csgMode; newBrush.ResetPivot(); // Use this brush as the basis for drawing the next brush csgModel.SetLastSelectedBrush(newBrush); Undo.RegisterCreatedObjectUndo(newObject, "Draw Brush"); }
public static void SplitIntersecting(PrimitiveBrush[] brushes) { List <Brush> intersections = new List <Brush>(); foreach (PrimitiveBrush brush in brushes) { intersections.AddRange(brush.BrushCache.IntersectingVisualBrushes); } foreach (PrimitiveBrush brush in brushes) { List <PrimitiveBrush> newBrushes = new List <PrimitiveBrush>(); foreach (Brush intersectingBrush in intersections) { PrimitiveBrush brushToClip = (PrimitiveBrush)intersectingBrush; Polygon[] polygons = brush.GetPolygons(); // A brush may have several polygons that share a plane, find all the distinct polygon planes List <Plane> distinctPlanes = new List <Plane>(); for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) { Polygon polygon = polygons[polygonIndex]; Vertex vertex1, vertex2, vertex3; SurfaceUtility.GetPrimaryPolygonDescribers(polygon, out vertex1, out vertex2, out vertex3); Vector3 position1 = vertex1.Position; Vector3 position2 = vertex2.Position; Vector3 position3 = vertex3.Position; // Transform from local to brush to local to intersectingBrush position1 = intersectingBrush.transform.InverseTransformPoint(brush.transform.TransformPoint(position1)); position2 = intersectingBrush.transform.InverseTransformPoint(brush.transform.TransformPoint(position2)); position3 = intersectingBrush.transform.InverseTransformPoint(brush.transform.TransformPoint(position3)); // Calculate plane in intersectingBrush's local space Plane polygonPlane = new Plane(position1, position2, position3); bool found = false; // See if it already exists for (int i = 0; i < distinctPlanes.Count; i++) { if (MathHelper.PlaneEqualsLooser(distinctPlanes[i], polygonPlane)) { found = true; break; } } // Not added to an existing group, so add new if (!found) { // Add a new group for the polygon distinctPlanes.Add(polygonPlane); } } foreach (Plane clipPlane in distinctPlanes) { #if UNITY_EDITOR UnityEditor.Undo.RecordObject(brushToClip, "Split Intersecting Brushes"); UnityEditor.Undo.RecordObject(brushToClip.transform, "Split Intersecting Brushes"); #endif GameObject newObject = ClipUtility.ApplyClipPlane(brushToClip, clipPlane, true, false); if (newObject != null) { #if UNITY_EDITOR UnityEditor.Undo.RegisterCreatedObjectUndo(newObject, "Split Intersecting Brushes"); #endif newBrushes.Add(newObject.GetComponent <PrimitiveBrush>()); } } brushToClip.ResetPivot(); } foreach (PrimitiveBrush newBrush in newBrushes) { newBrush.ResetPivot(); } intersections.AddRange(newBrushes.ConvertAll <Brush>(item => (Brush)item)); } }
public static void Rescale(PrimitiveBrush brush, Vector3 rescaleValue) { Scale(brush, rescaleValue); }
/// <summary> /// Flips brushes along the provided axis /// </summary> /// <param name="primaryTargetBrush">The brush considered to be the pivot brush, for use when localToPrimaryBrush is true</param> /// <param name="targetBrushes">All brushes to be flipped</param> /// <param name="axisIndex">Index of the axis component to flip along, 0 = X, 1 = Y, 2 = Z</param> /// <param name="localToPrimaryBrush">Whether the axis to flip in is local to the primary brush's rotation, if false global orientation is used</param> /// <param name="flipCenter">The point in world space at which to flip the geometry around</param> public static void Flip(PrimitiveBrush primaryTargetBrush, PrimitiveBrush[] targetBrushes, int axisIndex, bool localToPrimaryBrush, Vector3 flipCenter) { foreach (PrimitiveBrush brush in targetBrushes) { Polygon[] polygons = brush.GetPolygons(); for (int i = 0; i < polygons.Length; i++) { for (int j = 0; j < polygons[i].Vertices.Length; j++) { Vertex vertex = polygons[i].Vertices[j]; Vector3 position = vertex.Position; Vector3 normal = vertex.Normal; if (localToPrimaryBrush) { // Rotate the position and normal to be relative to the primary brush's local space position = primaryTargetBrush.transform.InverseTransformDirection(brush.transform.TransformDirection(position)); normal = primaryTargetBrush.transform.InverseTransformDirection(brush.transform.TransformDirection(normal)); } else { // Rotate the position and normal to be relative to the global axis orientation position = brush.transform.TransformDirection(position); normal = brush.transform.TransformDirection(normal); } // Flip in relevant axis position[axisIndex] = -position[axisIndex]; normal[axisIndex] = -normal[axisIndex]; if (localToPrimaryBrush) { // Rotate the position and normal from the primary brush's local space back to the brush's local space position = brush.transform.InverseTransformDirection(primaryTargetBrush.transform.TransformDirection(position)); normal = brush.transform.InverseTransformDirection(primaryTargetBrush.transform.TransformDirection(normal)); } else { // Rotate the position and normal from the global axis orientation back to the brush's original local space position = brush.transform.InverseTransformDirection(position); normal = brush.transform.InverseTransformDirection(normal); } // Set the vertex position and normal to their new values vertex.Position = position; vertex.Normal = normal; } // Because a flip has occurred we need to reverse the winding order Array.Reverse(polygons[i].Vertices); // Polygon plane has probably changed so it should now be recalculated polygons[i].CalculatePlane(); } if (targetBrushes.Length > 0) // Only need to move brushes if there's more than one { // Calculate the difference between the brush position and the center of flipping Vector3 deltaFromCenter = brush.transform.position - flipCenter; if (localToPrimaryBrush) { // Rotate the delta so that it's in the primary brush's local space deltaFromCenter = primaryTargetBrush.transform.InverseTransformDirection(deltaFromCenter); } // Negate the delta, so that the brush position will be flipped to the other side deltaFromCenter[axisIndex] = -deltaFromCenter[axisIndex]; if (localToPrimaryBrush) { // Rotate the delta back to its original space deltaFromCenter = primaryTargetBrush.transform.TransformDirection(deltaFromCenter); } // Set the brush's new position brush.transform.position = flipCenter + deltaFromCenter; } // Notify the brush that it has changed brush.Invalidate(true); } }
public void OnSceneGUI(SceneView sceneView) { Event e = Event.current; // if (e.type == EventType.Repaint) // { // if(CurrentSettings.GridMode == GridMode.SabreCSG) // { // CSGGrid.Activate(); // } // } if (!EditMode) { return; } // Frame rate tracking if (e.type == EventType.Repaint) { currentFrameDelta = Time.realtimeSinceStartup - currentFrameTimestamp; currentFrameTimestamp = Time.realtimeSinceStartup; } // Raw checks for tracking mouse events (use raw so that consumed events are not ignored) if (e.rawType == EventType.MouseDown) { mouseIsDragging = false; mouseIsHeld = true; if (e.button == 0 && GUIUtility.hotControl == 0) { GUIUtility.keyboardControl = 0; } } else if (e.rawType == EventType.MouseDrag) { mouseIsDragging = true; } else if (e.rawType == EventType.MouseUp) { mouseIsHeld = false; } // if (CurrentSettings.BrushesVisible) { // No idea what this line of code means, but it seems to stop normal mouse selection HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive)); } if (EditMode) { // In CSG mode, prevent the normal tools, so that the user must use our tools instead Tools.current = UnityEditor.Tool.None; } int concaveBrushCount = 0; for (int i = 0; i < brushes.Count; i++) { if (brushes[i] != null && !brushes[i].IsBrushConvex) { concaveBrushCount++; } } if (concaveBrushCount > 0) { Toolbar.WarningMessage = concaveBrushCount + " Concave Brush" + (concaveBrushCount > 1 ? "es" : "") + " Detected"; } else { // Toolbar.WarningMessage = ""; } Toolbar.CSGModel = this; Toolbar.OnSceneGUI(sceneView, e); if (e.type == EventType.Repaint) // || e.type == EventType.Layout) { if (tools[CurrentSettings.CurrentMode].BrushesHandleDrawing) { SabreGraphics.GetSelectedBrushMaterial().SetPass(0); // Selection GL.Begin(GL.LINES); Color outlineColor = Color.blue; for (int brushIndex = 0; brushIndex < brushes.Count; brushIndex++) { Brush brush = brushes[brushIndex]; if (brush == null) { continue; } GameObject brushGameObject = brush.gameObject; if (!brushGameObject.activeInHierarchy) { continue; } if (Selection.Contains(brushGameObject)) { if (brushes[brushIndex].Mode == CSGMode.Add) { outlineColor = Color.cyan; } else { outlineColor = Color.yellow; } } else if (CurrentSettings.BrushesVisible) { if (brushes[brushIndex].Mode == CSGMode.Add) { outlineColor = Color.blue; } else { outlineColor = new Color32(255, 130, 0, 255); } } else { continue; } GL.Color(outlineColor); Polygon[] polygons = brush.GetPolygons(); Transform brushTransform = brush.transform; // Brush Outline for (int i = 0; i < polygons.Length; i++) { Polygon polygon = polygons[i]; for (int j = 0; j < polygon.Vertices.Length; j++) { Vector3 position = brushTransform.TransformPoint(polygon.Vertices[j].Position); GL.Vertex(position); if (j < polygon.Vertices.Length - 1) { Vector3 position2 = brushTransform.TransformPoint(polygon.Vertices[j + 1].Position); GL.Vertex(position2); } else { Vector3 position2 = brushTransform.TransformPoint(polygon.Vertices[0].Position); GL.Vertex(position2); } } } } GL.End(); for (int i = 0; i < brushes.Count; i++) { if (brushes[i] is PrimitiveBrush && brushes[i] != null && brushes[i].gameObject.activeInHierarchy) { ((PrimitiveBrush)brushes[i]).OnRepaint(sceneView, e); } } } } if (e.type == EventType.Repaint) { Rect rect = new Rect(0, 0, Screen.width, Screen.height); EditorGUIUtility.AddCursorRect(rect, SabreMouse.ActiveCursor); } // // int hotControl = GUIUtility.hotControl; // if(hotControl != 0) // Debug.Log (hotControl); // Tools.viewTool = ViewTool.None; PrimitiveBrush primitiveBrush = null; if (Selection.activeGameObject != null) { primitiveBrush = Selection.activeGameObject.GetComponent <PrimitiveBrush>(); // primitiveBrush = Selection.activeGameObject.GetComponentInChildren<PrimitiveBrush>(); } List <PrimitiveBrush> primitiveBrushes = new List <PrimitiveBrush>(); for (int i = 0; i < Selection.gameObjects.Length; i++) { PrimitiveBrush[] matchedBrushes = Selection.gameObjects[i].GetComponents <PrimitiveBrush>(); // PrimitiveBrush[] matchedBrushes = Selection.gameObjects[i].GetComponentsInChildren<PrimitiveBrush>(); if (matchedBrushes.Length > 0) { primitiveBrushes.AddRange(matchedBrushes); } } Tool lastTool = activeTool; if (tools.ContainsKey(CurrentSettings.CurrentMode)) { activeTool = tools[CurrentSettings.CurrentMode]; } else { activeTool = null; } if (activeTool != null) { activeTool.CSGModel = this; activeTool.PrimaryTargetBrush = primitiveBrush; activeTool.TargetBrushes = primitiveBrushes.ToArray(); activeTool.OnSceneGUI(sceneView, e); if (activeTool != lastTool) { if (lastTool != null) { lastTool.Deactivated(); } activeTool.ResetTool(); } } // if(e.type == EventType.DragPerform) // { // Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); // // RaycastHit hit = new RaycastHit(); // // int layerMask = 1 << LayerMask.NameToLayer("CSGMesh"); // // Invert the layer mask // layerMask = ~layerMask; // // // Shift mode means only add what they click (clicking nothing does nothing) // if (Physics.Raycast(ray, out hit, float.PositiveInfinity, layerMask)) // { // OnDragDrop(hit.collider.gameObject); // } // } if (e.type == EventType.MouseDown) { } else if (e.type == EventType.MouseDrag) { } else if (e.type == EventType.MouseUp) { OnMouseUp(sceneView, e); SabreMouse.ResetCursor(); } else if (e.type == EventType.KeyDown || e.type == EventType.KeyUp) { OnKeyAction(sceneView, e); } if (CurrentSettings.OverrideFlyCamera) { LinearFPSCam.OnSceneGUI(sceneView); } }