From cc48864f497ee72bb6439c11ef418f1684500a1c Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 13:25:35 +0100 Subject: [PATCH 1/9] Enable CSG subtract --- Assets/Scripts/model/controller/PeltzerController.cs | 1 + Assets/Scripts/model/main/Features.cs | 2 +- Assets/Scripts/tools/VolumeInserter.cs | 7 +++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Scripts/model/controller/PeltzerController.cs b/Assets/Scripts/model/controller/PeltzerController.cs index ac2fe977..6cd94a83 100644 --- a/Assets/Scripts/model/controller/PeltzerController.cs +++ b/Assets/Scripts/model/controller/PeltzerController.cs @@ -1423,6 +1423,7 @@ public void ChangeMode(ControllerMode newMode, GameObject toolHead = null) break; case ControllerMode.insertStroke: case ControllerMode.insertVolume: + case ControllerMode.subtract: if (Config.Instance.VrHardware == VrHardware.Vive) { defaultTipPointerDefaultLocation = new Vector3(0f, 0f, -0.015f); diff --git a/Assets/Scripts/model/main/Features.cs b/Assets/Scripts/model/main/Features.cs index 6614c0ef..9e2c54d0 100644 --- a/Assets/Scripts/model/main/Features.cs +++ b/Assets/Scripts/model/main/Features.cs @@ -26,7 +26,7 @@ namespace com.google.apps.peltzer.client.model.main public class Features { // If true, CSG subtraction (subtracting one shape from another, also known as "carving") is enabled. - public static bool csgSubtractEnabled = false; + public static bool csgSubtractEnabled = true; // If true, saves creations in the Mogwai object store. public static bool saveToMogwaiObjectStore = true; diff --git a/Assets/Scripts/tools/VolumeInserter.cs b/Assets/Scripts/tools/VolumeInserter.cs index f0ffd092..590a506b 100644 --- a/Assets/Scripts/tools/VolumeInserter.cs +++ b/Assets/Scripts/tools/VolumeInserter.cs @@ -380,8 +380,7 @@ private void CreateNewVolumeMesh(int? oldScaleDeltaToAnimateFrom = null) // Create the primitive. List newMeshes = new List(); // TODO(bug) Replace pink with wireframe - int material = peltzerController.mode == ControllerMode.subtract ? - /* pink wireframe */ MaterialRegistry.PINK_WIREFRAME_ID : peltzerController.currentMaterial; + int material = peltzerController.currentMaterial; Vector3 scale; if (peltzerController.shapesMenu.showingShapeMenu) @@ -845,12 +844,12 @@ private void ControllerEventHandler(object sender, ControllerEventArgs args) { if (peltzerController.mode == ControllerMode.insertVolume) { - peltzerController.ChangeMode(ControllerMode.subtract); peltzerController.shapesMenu.ChangeShapesMenuMaterial(MaterialRegistry.PINK_WIREFRAME_ID); + peltzerController.ChangeMode(ControllerMode.subtract, ObjectFinder.ObjectById("ID_ToolShapes")); } else if (peltzerController.mode == ControllerMode.subtract) { - peltzerController.ChangeMode(ControllerMode.insertVolume); + peltzerController.ChangeMode(ControllerMode.insertVolume, ObjectFinder.ObjectById("ID_ToolShapes")); peltzerController.shapesMenu.ChangeShapesMenuMaterial(peltzerController.currentMaterial); } } From 5e3dec3f68f7857295f6f9fff867406976bbbf82 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 13:36:56 +0100 Subject: [PATCH 2/9] Add the missing CSG ops --- Assets/Scripts/model/csg/CsgOperations.cs | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Assets/Scripts/model/csg/CsgOperations.cs b/Assets/Scripts/model/csg/CsgOperations.cs index 36f797a2..ceb00cbc 100644 --- a/Assets/Scripts/model/csg/CsgOperations.cs +++ b/Assets/Scripts/model/csg/CsgOperations.cs @@ -155,6 +155,42 @@ private static List CsgSubtract(CsgContext ctx, CsgObject leftObj, C return polys; } + /// + /// Perform union on CsgObjects + /// + public static List CsgUnion(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + { + SplitObject(ctx, leftObj, rightObj); + SplitObject(ctx, rightObj, leftObj); + SplitObject(ctx, leftObj, rightObj); + ClassifyPolygons(leftObj, rightObj); + ClassifyPolygons(rightObj, leftObj); + + FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; + List polys = SelectPolygons(leftObj, false, null, PolygonStatus.OUTSIDE, PolygonStatus.SAME); + polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.OUTSIDE)); + + return polys; + } + + /// + /// Perform intersection on CsgObjects + /// + public static List CsgIntersect(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + { + SplitObject(ctx, leftObj, rightObj); + SplitObject(ctx, rightObj, leftObj); + SplitObject(ctx, leftObj, rightObj); + ClassifyPolygons(leftObj, rightObj); + ClassifyPolygons(rightObj, leftObj); + + FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; + List polys = SelectPolygons(leftObj, false, null, PolygonStatus.INSIDE, PolygonStatus.SAME); + polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.INSIDE)); + + return polys; + } + /// /// Select all of the polygons in the object with any of the given statuses. /// From 1b31924d803069539aac921dd1852cdd4ad7df7a Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 13:39:58 +0100 Subject: [PATCH 3/9] Unused imports --- Assets/Scripts/model/csg/CsgOperations.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Assets/Scripts/model/csg/CsgOperations.cs b/Assets/Scripts/model/csg/CsgOperations.cs index ceb00cbc..c2fdb5b9 100644 --- a/Assets/Scripts/model/csg/CsgOperations.cs +++ b/Assets/Scripts/model/csg/CsgOperations.cs @@ -12,14 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Collections.Generic; -using System.Linq; using UnityEngine; using com.google.apps.peltzer.client.model.core; using com.google.apps.peltzer.client.model.util; -using com.google.apps.peltzer.client.model.render; namespace com.google.apps.peltzer.client.model.csg { From 65f4bbe349eb5b9530ea44204fe9e7fa22854b10 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 13:50:46 +0100 Subject: [PATCH 4/9] Support for csg union and intersect in main CSG code --- Assets/Editor/tests/model/csg/CsgCasesTest.cs | 20 ++--- .../tests/model/csg/CsgOperationsTest.cs | 12 +-- Assets/Scripts/model/csg/CsgOperations.cs | 82 ++++++++++++------- 3 files changed, 70 insertions(+), 44 deletions(-) diff --git a/Assets/Editor/tests/model/csg/CsgCasesTest.cs b/Assets/Editor/tests/model/csg/CsgCasesTest.cs index 2d6c3ed2..cb1085b1 100644 --- a/Assets/Editor/tests/model/csg/CsgCasesTest.cs +++ b/Assets/Editor/tests/model/csg/CsgCasesTest.cs @@ -55,7 +55,7 @@ public void TestOne() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -82,7 +82,7 @@ public void TestTwo() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -109,7 +109,7 @@ public void TestThree() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -136,7 +136,7 @@ public void TestFour() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -163,7 +163,7 @@ public void TestFive() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -190,7 +190,7 @@ public void TestSix() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -216,7 +216,7 @@ public void TestSeven() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -242,7 +242,7 @@ public void TestEight() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -269,7 +269,7 @@ public void TestNine() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } [Test] @@ -296,7 +296,7 @@ public void TestTen() Model m = new Model(bounds); m.AddMesh(shape1); - NUnit.Framework.Assert.IsTrue(CsgOperations.SubtractMeshFromModel(m, spatialIndex, shape2)); + NUnit.Framework.Assert.IsTrue(CsgOperations.CsgMeshFromModel(m, spatialIndex, shape2)); } } } \ No newline at end of file diff --git a/Assets/Editor/tests/model/csg/CsgOperationsTest.cs b/Assets/Editor/tests/model/csg/CsgOperationsTest.cs index 82141eac..3b0b68ef 100644 --- a/Assets/Editor/tests/model/csg/CsgOperationsTest.cs +++ b/Assets/Editor/tests/model/csg/CsgOperationsTest.cs @@ -56,7 +56,7 @@ public void TestSubtractFromModel() spatialIndex.AddMesh(meshToAdd); // Now subtract a big cube from the model: - bool subtracted = CsgOperations.SubtractMeshFromModel( + bool subtracted = CsgOperations.CsgMeshFromModel( model, spatialIndex, Primitives.AxisAlignedBox(7, Vector3.zero, Vector3.one, 1)); NUnit.Framework.Assert.IsTrue(subtracted); @@ -71,7 +71,7 @@ public void TestSubtractFromModel() NUnit.Framework.Assert.AreEqual(15, model.GetMesh(toIntersect).faceCount); // Subtract away from the scene to make sure the method returns false. - NUnit.Framework.Assert.IsFalse(CsgOperations.SubtractMeshFromModel(model, spatialIndex, + NUnit.Framework.Assert.IsFalse(CsgOperations.CsgMeshFromModel(model, spatialIndex, Primitives.AxisAlignedBox(7, Vector3.one * -3, Vector3.one, 1))); } @@ -276,10 +276,10 @@ public void SubtractCubeWithinCube() MMesh largeCube = Primitives.AxisAlignedBox(1, Vector3.zero, Vector3.one, 1); // Subtracting large cube from small cube should result in empty space. - NUnit.Framework.Assert.IsNull(CsgOperations.Subtract(smallCube, largeCube)); + NUnit.Framework.Assert.IsNull(CsgOperations.DoCsgOperation(smallCube, largeCube)); // Subtracting small cube from large cube should result in just the large cube with an invisible hole. - MMesh results = CsgOperations.Subtract(largeCube, smallCube); + MMesh results = CsgOperations.DoCsgOperation(largeCube, smallCube); NUnit.Framework.Assert.AreEqual(12, results.faceCount); // Mesh should still be valid: @@ -290,7 +290,7 @@ public void SubtractCubeWithinCube() public void SubtractCubeOverlappingCube() { // Two cubes next to each other, overlapping. - MMesh result = CsgOperations.Subtract( + MMesh result = CsgOperations.DoCsgOperation( Primitives.AxisAlignedBox(1, new Vector3(-1, 0, 0), Vector3.one, 1), Primitives.AxisAlignedBox(2, Vector3.zero, Vector3.one, 1)); @@ -301,7 +301,7 @@ public void SubtractCubeOverlappingCube() public void SubtractSphereOverlappingCube() { // A cube and a sphere, overlapping. - MMesh result = CsgOperations.Subtract( + MMesh result = CsgOperations.DoCsgOperation( Primitives.AxisAlignedBox(1, new Vector3(-1, -0.7f, -0.3f), Vector3.one, 2), Primitives.AxisAlignedIcosphere(2, new Vector3(-.2f, 0, 0), Vector3.one, 1)); diff --git a/Assets/Scripts/model/csg/CsgOperations.cs b/Assets/Scripts/model/csg/CsgOperations.cs index c2fdb5b9..fb74bef0 100644 --- a/Assets/Scripts/model/csg/CsgOperations.cs +++ b/Assets/Scripts/model/csg/CsgOperations.cs @@ -25,22 +25,29 @@ public class CsgOperations { private const float COPLANAR_EPS = 0.001f; + public enum CsgOperation + { + UNION, + INTERSECT, + SUBTRACT + } + /// - /// Subtract a mesh from all intersecting meshes in a model. + /// Performs a CSG operation on all intersecting meshes in a model. /// - /// true if the subtract brush intersects with meshes in the scene. - public static bool SubtractMeshFromModel(Model model, SpatialIndex spatialIndex, MMesh toSubtract) + /// true if the brush intersects with meshes in the scene. + public static bool CsgMeshFromModel(Model model, SpatialIndex spatialIndex, MMesh brush, CsgOperation csgOp = CsgOperation.SUBTRACT) { - Bounds bounds = toSubtract.bounds; + Bounds bounds = brush.bounds; List commands = new List(); HashSet intersectingMeshIds; - if (spatialIndex.FindIntersectingMeshes(toSubtract.bounds, out intersectingMeshIds)) + if (spatialIndex.FindIntersectingMeshes(brush.bounds, out intersectingMeshIds)) { foreach (int meshId in intersectingMeshIds) { MMesh mesh = model.GetMesh(meshId); - MMesh result = Subtract(mesh, toSubtract); + MMesh result = DoCsgOperation(mesh, brush); commands.Add(new DeleteMeshCommand(mesh.id)); // If the result is null, it means the mesh was entirely erased. No need to add a new version back. if (result != null) @@ -66,16 +73,21 @@ public static bool SubtractMeshFromModel(Model model, SpatialIndex spatialIndex, } /// - /// Subtract a mesh from another. Returns a new MMesh that is the result of the subtraction. + /// Performs CSG on two meshes. Returns a new MMesh that is the result of the operation. /// If the result is an empty space, returns null. /// - public static MMesh Subtract(MMesh subtrahend, MMesh minuend) + public static MMesh DoCsgOperation(MMesh brush, MMesh target, CsgOperation csgOp = CsgOperation.SUBTRACT) { - // If the objects don't overlap, just bail out: - - if (!subtrahend.bounds.Intersects(minuend.bounds)) + // If the objects don't overlap, we have two fast paths: + if (!brush.bounds.Intersects(target.bounds)) { - return subtrahend.Clone(); + switch (csgOp) + { + case CsgOperation.INTERSECT: + return null; + case CsgOperation.SUBTRACT: + return brush.Clone(); + } } // Our epsilons aren't very good for operations that are either very small or very big, @@ -84,8 +96,8 @@ public static MMesh Subtract(MMesh subtrahend, MMesh minuend) // // Here's a good article for comparing floating point numbers: // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ - Vector3 operationalCenter = (subtrahend.bounds.center + minuend.bounds.center) / 2.0f; - float averageRadius = (subtrahend.bounds.extents.magnitude + minuend.bounds.extents.magnitude) / 2.0f; + Vector3 operationalCenter = (brush.bounds.center + target.bounds.center) / 2.0f; + float averageRadius = (brush.bounds.extents.magnitude + target.bounds.extents.magnitude) / 2.0f; Vector3 operationOffset = -operationalCenter; float operationScale = 1.0f / averageRadius; if (operationScale < 1.0f) @@ -94,34 +106,48 @@ public static MMesh Subtract(MMesh subtrahend, MMesh minuend) } Bounds operationBounds = new Bounds(); - foreach (int vertexId in subtrahend.GetVertexIds()) + foreach (int vertexId in brush.GetVertexIds()) { - operationBounds.Encapsulate((subtrahend.VertexPositionInModelCoords(vertexId) + operationOffset) * operationScale); + operationBounds.Encapsulate((brush.VertexPositionInModelCoords(vertexId) + operationOffset) * operationScale); } - foreach (int vertexId in minuend.GetVertexIds()) + foreach (int vertexId in target.GetVertexIds()) { - operationBounds.Encapsulate((minuend.VertexPositionInModelCoords(vertexId) + operationOffset) * operationScale); + operationBounds.Encapsulate((target.VertexPositionInModelCoords(vertexId) + operationOffset) * operationScale); } operationBounds.Expand(0.01f); CsgContext ctx = new CsgContext(operationBounds); - CsgObject leftObj = ToCsg(ctx, subtrahend, operationOffset, operationScale); - CsgObject rightObj = ToCsg(ctx, minuend, operationOffset, operationScale); - List result = CsgSubtract(ctx, leftObj, rightObj); - if (result.Count > 0) + CsgObject leftObj = ToCsg(ctx, brush, operationOffset, operationScale); + CsgObject rightObj = ToCsg(ctx, target, operationOffset, operationScale); + List result = null; + + switch (csgOp) + { + case CsgOperation.UNION: + result = CsgUnion(ctx, leftObj, rightObj); + break; + case CsgOperation.INTERSECT: + result = CsgIntersect(ctx, leftObj, rightObj); + break; + case CsgOperation.SUBTRACT: + result = CsgSubtract(ctx, leftObj, rightObj); + break; + } + + if (result != null && result.Count > 0) { HashSet combinedRemixIds = null; - if (subtrahend.remixIds != null || minuend.remixIds != null) + if (brush.remixIds != null || target.remixIds != null) { combinedRemixIds = new HashSet(); - if (subtrahend.remixIds != null) combinedRemixIds.UnionWith(subtrahend.remixIds); - if (minuend.remixIds != null) combinedRemixIds.UnionWith(minuend.remixIds); + if (brush.remixIds != null) combinedRemixIds.UnionWith(brush.remixIds); + if (target.remixIds != null) combinedRemixIds.UnionWith(target.remixIds); } return FromPolys( - subtrahend.id, - subtrahend.offset, - subtrahend.rotation, + brush.id, + brush.offset, + brush.rotation, result, operationOffset, operationScale, From 20dec4a8b75472b95bdeea155737d510f2dbc285 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 13:51:26 +0100 Subject: [PATCH 5/9] Method was renamed in earlier commit --- Assets/Scripts/tools/VolumeInserter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/tools/VolumeInserter.cs b/Assets/Scripts/tools/VolumeInserter.cs index 590a506b..dedbb71b 100644 --- a/Assets/Scripts/tools/VolumeInserter.cs +++ b/Assets/Scripts/tools/VolumeInserter.cs @@ -540,7 +540,7 @@ private void InsertVolumeMesh() } else if (peltzerController.mode == ControllerMode.subtract) { - if (CsgOperations.SubtractMeshFromModel(model, spatialIndex, meshToInsert)) + if (CsgOperations.CsgMeshFromModel(model, spatialIndex, meshToInsert)) { audioLibrary.PlayClip(audioLibrary.deleteSound); peltzerController.TriggerHapticFeedback( From 2f849f6758190be0b0695ff9c215f1c6891f4dba Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 13:57:06 +0100 Subject: [PATCH 6/9] Rename "subtract" mode to "csg" --- .../Scripts/model/controller/ControllerMode.cs | 2 +- .../model/controller/PaletteController.cs | 2 +- .../model/controller/PeltzerController.cs | 6 +++--- Assets/Scripts/model/main/PeltzerMain.cs | 2 +- Assets/Scripts/tools/VolumeInserter.cs | 16 ++++++++-------- Assets/Scripts/tools/utils/HeldMeshes.cs | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Assets/Scripts/model/controller/ControllerMode.cs b/Assets/Scripts/model/controller/ControllerMode.cs index 3197db39..f8d54da6 100644 --- a/Assets/Scripts/model/controller/ControllerMode.cs +++ b/Assets/Scripts/model/controller/ControllerMode.cs @@ -68,7 +68,7 @@ public enum ControllerMode /// /// Mode for deleting meshes via subtraction (csg). /// - subtract, + csg, /// /// Mode for deleting edges. /// diff --git a/Assets/Scripts/model/controller/PaletteController.cs b/Assets/Scripts/model/controller/PaletteController.cs index efdec4c3..02d0082a 100644 --- a/Assets/Scripts/model/controller/PaletteController.cs +++ b/Assets/Scripts/model/controller/PaletteController.cs @@ -538,7 +538,7 @@ public GameObject GetToolheadForMode(ControllerMode mode) switch (mode) { case ControllerMode.insertVolume: - case ControllerMode.subtract: + case ControllerMode.csg: return shapeToolhead; case ControllerMode.insertStroke: return freeformToolhead; diff --git a/Assets/Scripts/model/controller/PeltzerController.cs b/Assets/Scripts/model/controller/PeltzerController.cs index 6cd94a83..3302fba4 100644 --- a/Assets/Scripts/model/controller/PeltzerController.cs +++ b/Assets/Scripts/model/controller/PeltzerController.cs @@ -1423,7 +1423,7 @@ public void ChangeMode(ControllerMode newMode, GameObject toolHead = null) break; case ControllerMode.insertStroke: case ControllerMode.insertVolume: - case ControllerMode.subtract: + case ControllerMode.csg: if (Config.Instance.VrHardware == VrHardware.Vive) { defaultTipPointerDefaultLocation = new Vector3(0f, 0f, -0.015f); @@ -1673,7 +1673,7 @@ public void ChangeTouchpadOverlay(TouchpadOverlay newOverlay) switch (mode) { case ControllerMode.insertVolume: - case ControllerMode.subtract: + case ControllerMode.csg: currentOverlayGO = controllerGeometry.volumeInserterOverlay; break; case ControllerMode.insertStroke: @@ -1721,7 +1721,7 @@ public void ResetTouchpadOverlay() switch (mode) { case ControllerMode.insertVolume: - case ControllerMode.subtract: + case ControllerMode.csg: ChangeTouchpadOverlay(TouchpadOverlay.VOLUME_INSERTER); break; case ControllerMode.insertStroke: diff --git a/Assets/Scripts/model/main/PeltzerMain.cs b/Assets/Scripts/model/main/PeltzerMain.cs index 40144bc3..ed9e13dd 100644 --- a/Assets/Scripts/model/main/PeltzerMain.cs +++ b/Assets/Scripts/model/main/PeltzerMain.cs @@ -1704,7 +1704,7 @@ public bool OperationInProgress() return reshaper.IsReshaping(); case ControllerMode.subdivideFace: return false; - case ControllerMode.subtract: + case ControllerMode.csg: return volumeInserter.IsFilling(); } diff --git a/Assets/Scripts/tools/VolumeInserter.cs b/Assets/Scripts/tools/VolumeInserter.cs index dedbb71b..d49a4832 100644 --- a/Assets/Scripts/tools/VolumeInserter.cs +++ b/Assets/Scripts/tools/VolumeInserter.cs @@ -146,7 +146,7 @@ private void Update() } bool activeMode = (peltzerController.mode == ControllerMode.insertVolume - || peltzerController.mode == ControllerMode.subtract) + || peltzerController.mode == ControllerMode.csg) && !PeltzerMain.Instance.peltzerController.isPointingAtMenu && PeltzerMain.Instance.introChoreographer.introIsComplete; @@ -538,7 +538,7 @@ private void InsertVolumeMesh() HapticFeedback.HapticFeedbackType.FEEDBACK_3, /* durationSeconds */ 0.05f, /* strength */ 0.3f); Primitives.Shape selectedShape = (Primitives.Shape)peltzerController.shapesMenu.CurrentItemId; } - else if (peltzerController.mode == ControllerMode.subtract) + else if (peltzerController.mode == ControllerMode.csg) { if (CsgOperations.CsgMeshFromModel(model, spatialIndex, meshToInsert)) { @@ -686,7 +686,7 @@ private bool IsLongTermScale() private void ControllerEventHandler(object sender, ControllerEventArgs args) { // If we are not in insert or subtract mode, do nothing. - if ((peltzerController.mode != ControllerMode.insertVolume && peltzerController.mode != ControllerMode.subtract) + if ((peltzerController.mode != ControllerMode.insertVolume && peltzerController.mode != ControllerMode.csg) || PeltzerMain.Instance.peltzerController.isPointingAtMenu) { return; @@ -845,9 +845,9 @@ private void ControllerEventHandler(object sender, ControllerEventArgs args) if (peltzerController.mode == ControllerMode.insertVolume) { peltzerController.shapesMenu.ChangeShapesMenuMaterial(MaterialRegistry.PINK_WIREFRAME_ID); - peltzerController.ChangeMode(ControllerMode.subtract, ObjectFinder.ObjectById("ID_ToolShapes")); + peltzerController.ChangeMode(ControllerMode.csg, ObjectFinder.ObjectById("ID_ToolShapes")); } - else if (peltzerController.mode == ControllerMode.subtract) + else if (peltzerController.mode == ControllerMode.csg) { peltzerController.ChangeMode(ControllerMode.insertVolume, ObjectFinder.ObjectById("ID_ToolShapes")); peltzerController.shapesMenu.ChangeShapesMenuMaterial(peltzerController.currentMaterial); @@ -857,13 +857,13 @@ private void ControllerEventHandler(object sender, ControllerEventArgs args) private void ModeChangeEventHandler(ControllerMode oldMode, ControllerMode newMode) { - if (oldMode == ControllerMode.insertVolume || oldMode == ControllerMode.subtract) + if (oldMode == ControllerMode.insertVolume || oldMode == ControllerMode.csg) { peltzerController.shapesMenu.Hide(); UnsetAllHoverTooltips(); } - if (newMode == ControllerMode.insertVolume || newMode == ControllerMode.subtract) + if (newMode == ControllerMode.insertVolume || newMode == ControllerMode.csg) { CreateNewVolumeMesh(); @@ -904,7 +904,7 @@ private void ShapeChangedHandler(int newShapeMenuItemId) private void BlockModeChangedHandler(bool isBlockMode) { - if (peltzerController.mode == ControllerMode.insertVolume || peltzerController.mode == ControllerMode.subtract) + if (peltzerController.mode == ControllerMode.insertVolume || peltzerController.mode == ControllerMode.csg) { CreateNewVolumeMesh(); } diff --git a/Assets/Scripts/tools/utils/HeldMeshes.cs b/Assets/Scripts/tools/utils/HeldMeshes.cs index 20826647..f798d38f 100644 --- a/Assets/Scripts/tools/utils/HeldMeshes.cs +++ b/Assets/Scripts/tools/utils/HeldMeshes.cs @@ -456,7 +456,7 @@ public void StartSnapping(Model model, SpatialIndex spatialIndex) model, spatialIndex, worldSpace, - peltzerController.mode == ControllerMode.subtract, + peltzerController.mode == ControllerMode.csg, out finalVolumePreviewMeshRotation, out previewFace, out coplanarPreviewFaceVerticesAtOrigin, From 0177a1ef6659fcbc8663e08973b41e0920b283e8 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 14:09:49 +0100 Subject: [PATCH 7/9] Allow cycling between CSG ops --- Assets/Scripts/model/csg/CsgOperations.cs | 2 +- Assets/Scripts/tools/VolumeInserter.cs | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Assets/Scripts/model/csg/CsgOperations.cs b/Assets/Scripts/model/csg/CsgOperations.cs index fb74bef0..9f194c2c 100644 --- a/Assets/Scripts/model/csg/CsgOperations.cs +++ b/Assets/Scripts/model/csg/CsgOperations.cs @@ -47,7 +47,7 @@ public static bool CsgMeshFromModel(Model model, SpatialIndex spatialIndex, MMes foreach (int meshId in intersectingMeshIds) { MMesh mesh = model.GetMesh(meshId); - MMesh result = DoCsgOperation(mesh, brush); + MMesh result = DoCsgOperation(mesh, brush, csgOp); commands.Add(new DeleteMeshCommand(mesh.id)); // If the result is null, it means the mesh was entirely erased. No need to add a new version back. if (result != null) diff --git a/Assets/Scripts/tools/VolumeInserter.cs b/Assets/Scripts/tools/VolumeInserter.cs index d49a4832..8d503fac 100644 --- a/Assets/Scripts/tools/VolumeInserter.cs +++ b/Assets/Scripts/tools/VolumeInserter.cs @@ -95,6 +95,7 @@ public class VolumeInserter : MonoBehaviour /// showed enough knowledge of how to snap. /// private int completedSnaps = 0; + private CsgOperations.CsgOperation csgOperation; private const int SNAP_KNOW_HOW_COUNT = 3; /// @@ -540,7 +541,7 @@ private void InsertVolumeMesh() } else if (peltzerController.mode == ControllerMode.csg) { - if (CsgOperations.CsgMeshFromModel(model, spatialIndex, meshToInsert)) + if (CsgOperations.CsgMeshFromModel(model, spatialIndex, meshToInsert, csgOperation)) { audioLibrary.PlayClip(audioLibrary.deleteSound); peltzerController.TriggerHapticFeedback( @@ -846,11 +847,27 @@ private void ControllerEventHandler(object sender, ControllerEventArgs args) { peltzerController.shapesMenu.ChangeShapesMenuMaterial(MaterialRegistry.PINK_WIREFRAME_ID); peltzerController.ChangeMode(ControllerMode.csg, ObjectFinder.ObjectById("ID_ToolShapes")); + csgOperation = CsgOperations.CsgOperation.SUBTRACT; } else if (peltzerController.mode == ControllerMode.csg) { - peltzerController.ChangeMode(ControllerMode.insertVolume, ObjectFinder.ObjectById("ID_ToolShapes")); - peltzerController.shapesMenu.ChangeShapesMenuMaterial(peltzerController.currentMaterial); + switch (csgOperation) + { + case CsgOperations.CsgOperation.SUBTRACT: + csgOperation = CsgOperations.CsgOperation.INTERSECT; + audioLibrary.PlayClip(audioLibrary.swipeRightSound); + peltzerController.TriggerHapticFeedback(); + break; + case CsgOperations.CsgOperation.INTERSECT: + csgOperation = CsgOperations.CsgOperation.UNION; + audioLibrary.PlayClip(audioLibrary.swipeRightSound); + peltzerController.TriggerHapticFeedback(); + break; + case CsgOperations.CsgOperation.UNION: + peltzerController.ChangeMode(ControllerMode.insertVolume, ObjectFinder.ObjectById("ID_ToolShapes")); + peltzerController.shapesMenu.ChangeShapesMenuMaterial(peltzerController.currentMaterial); + break; + } } } } From 88681013580431de11368360e5ad3678ff1bca73 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 22 Jul 2024 14:45:05 +0100 Subject: [PATCH 8/9] Fix normals --- Assets/Scripts/model/csg/CsgOperations.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/model/csg/CsgOperations.cs b/Assets/Scripts/model/csg/CsgOperations.cs index 9f194c2c..24ca9513 100644 --- a/Assets/Scripts/model/csg/CsgOperations.cs +++ b/Assets/Scripts/model/csg/CsgOperations.cs @@ -191,7 +191,7 @@ public static List CsgUnion(CsgContext ctx, CsgObject leftObj, CsgOb FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; List polys = SelectPolygons(leftObj, false, null, PolygonStatus.OUTSIDE, PolygonStatus.SAME); - polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.OUTSIDE)); + polys.AddRange(SelectPolygons(rightObj, false, facePropertiesForNewFaces, PolygonStatus.OUTSIDE)); return polys; } @@ -209,7 +209,7 @@ public static List CsgIntersect(CsgContext ctx, CsgObject leftObj, C FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; List polys = SelectPolygons(leftObj, false, null, PolygonStatus.INSIDE, PolygonStatus.SAME); - polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.INSIDE)); + polys.AddRange(SelectPolygons(rightObj, false, facePropertiesForNewFaces, PolygonStatus.INSIDE)); return polys; } From 1c33bd39a42f44041c114598717808aa7ebfbc63 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 18 Aug 2024 09:54:08 +0100 Subject: [PATCH 9/9] CSG icons --- .../Mogwai/Design/Icons/ic_csg_intersect.png | Bin 0 -> 5269 bytes .../Design/Icons/ic_csg_intersect.png.meta | 116 ++++++++++++++++++ .../Mogwai/Design/Icons/ic_csg_subtract.png | Bin 0 -> 5346 bytes .../Design/Icons/ic_csg_subtract.png.meta | 116 ++++++++++++++++++ Assets/Mogwai/Design/Icons/ic_csg_union.png | Bin 0 -> 3633 bytes .../Mogwai/Design/Icons/ic_csg_union.png.meta | 116 ++++++++++++++++++ 6 files changed, 348 insertions(+) create mode 100644 Assets/Mogwai/Design/Icons/ic_csg_intersect.png create mode 100644 Assets/Mogwai/Design/Icons/ic_csg_intersect.png.meta create mode 100644 Assets/Mogwai/Design/Icons/ic_csg_subtract.png create mode 100644 Assets/Mogwai/Design/Icons/ic_csg_subtract.png.meta create mode 100644 Assets/Mogwai/Design/Icons/ic_csg_union.png create mode 100644 Assets/Mogwai/Design/Icons/ic_csg_union.png.meta diff --git a/Assets/Mogwai/Design/Icons/ic_csg_intersect.png b/Assets/Mogwai/Design/Icons/ic_csg_intersect.png new file mode 100644 index 0000000000000000000000000000000000000000..1ae5b98742c6dabbc93b45f2e69fd82776c8e47a GIT binary patch literal 5269 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 F{{e9SB02y7 literal 0 HcmV?d00001 diff --git a/Assets/Mogwai/Design/Icons/ic_csg_subtract.png.meta b/Assets/Mogwai/Design/Icons/ic_csg_subtract.png.meta new file mode 100644 index 00000000..9bb203ec --- /dev/null +++ b/Assets/Mogwai/Design/Icons/ic_csg_subtract.png.meta @@ -0,0 +1,116 @@ +fileFormatVersion: 2 +guid: 6a1d385b53ecb544289404050bfb66bc +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 16 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mogwai/Design/Icons/ic_csg_union.png b/Assets/Mogwai/Design/Icons/ic_csg_union.png new file mode 100644 index 0000000000000000000000000000000000000000..fd2d56ef7205c184ac5d08cd9f881f2440595729 GIT binary patch literal 3633 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 literal 0 HcmV?d00001 diff --git a/Assets/Mogwai/Design/Icons/ic_csg_union.png.meta b/Assets/Mogwai/Design/Icons/ic_csg_union.png.meta new file mode 100644 index 00000000..ee00709c --- /dev/null +++ b/Assets/Mogwai/Design/Icons/ic_csg_union.png.meta @@ -0,0 +1,116 @@ +fileFormatVersion: 2 +guid: 4f4b5f6433b99af469f6dc26e8b55b7c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 16 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: