diff --git a/Scripts/Editor/Scene/RealtimeCSGTarget.FixedExtrude.cs b/Scripts/Editor/Scene/RealtimeCSGTarget.FixedExtrude.cs
index 966ea68..f132dd3 100644
--- a/Scripts/Editor/Scene/RealtimeCSGTarget.FixedExtrude.cs
+++ b/Scripts/Editor/Scene/RealtimeCSGTarget.FixedExtrude.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
using UnityEngine;
@@ -22,9 +22,18 @@ private void FixedExtrude_Rebuild()
for (int i = 0; i < polygonMeshesCount; i++)
{
var polygonMesh = polygonMeshes[i];
- var planes = polygonMesh.ToMaterialPlanes();
- var brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
+ // try the direct PolygonMesh -> brush path first
+ var brush = ExternalRealtimeCSG.CreateBrushFromPolygonMesh(parent, "Shape Editor Brush", polygonMesh);
+ if (brush != null)
+ {
+ brush.transform.SetParent(parent, false);
+ continue;
+ }
+
+ // fall back to plane-based brush creation
+ var planes = polygonMesh.ToMaterialPlanes();
+ brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
if (brush != null)
brush.transform.SetParent(parent, false);
}
@@ -35,4 +44,4 @@ private void FixedExtrude_Rebuild()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Editor/Scene/RealtimeCSGTarget.LinearStaircase.cs b/Scripts/Editor/Scene/RealtimeCSGTarget.LinearStaircase.cs
index db1a563..17ec049 100644
--- a/Scripts/Editor/Scene/RealtimeCSGTarget.LinearStaircase.cs
+++ b/Scripts/Editor/Scene/RealtimeCSGTarget.LinearStaircase.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
using UnityEngine;
@@ -33,9 +33,18 @@ private void LinearStaircase_Rebuild()
for (int i = 0; i < polygonMeshesCount; i++)
{
var polygonMesh = polygonMeshes[i];
- var planes = polygonMesh.ToMaterialPlanes();
- var brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
+ // try the direct PolygonMesh -> brush path first
+ var brush = ExternalRealtimeCSG.CreateBrushFromPolygonMesh(parent, "Shape Editor Brush", polygonMesh);
+ if (brush != null)
+ {
+ brush.transform.SetParent(parent, false);
+ continue;
+ }
+
+ // fall back to plane-based brush creation
+ var planes = polygonMesh.ToMaterialPlanes();
+ brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
if (brush != null)
brush.transform.SetParent(parent, false);
}
@@ -46,4 +55,4 @@ private void LinearStaircase_Rebuild()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveChopped.cs b/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveChopped.cs
index a6a5f4e..9291e05 100644
--- a/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveChopped.cs
+++ b/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveChopped.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
using UnityEngine;
@@ -35,9 +35,18 @@ private void RevolveChopped_Rebuild()
for (int i = 0; i < polygonMeshesCount; i++)
{
var polygonMesh = polygonMeshes[i];
- var planes = polygonMesh.ToMaterialPlanes();
- var brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
+ // try the direct PolygonMesh -> brush path first
+ var brush = ExternalRealtimeCSG.CreateBrushFromPolygonMesh(parent, "Shape Editor Brush", polygonMesh);
+ if (brush != null)
+ {
+ brush.transform.SetParent(parent, false);
+ continue;
+ }
+
+ // fall back to plane-based brush creation
+ var planes = polygonMesh.ToMaterialPlanes();
+ brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
if (brush != null)
brush.transform.SetParent(parent, false);
}
@@ -48,4 +57,4 @@ private void RevolveChopped_Rebuild()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveExtrude.cs b/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveExtrude.cs
index bbdf6e6..1a38b52 100644
--- a/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveExtrude.cs
+++ b/Scripts/Editor/Scene/RealtimeCSGTarget.RevolveExtrude.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
using UnityEngine;
@@ -42,9 +42,18 @@ private void RevolveExtrude_Rebuild()
for (int i = 0; i < polygonMeshesCount; i++)
{
var polygonMesh = polygonMeshes[i];
- var planes = polygonMesh.ToMaterialPlanes();
- var brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
+ // try the direct PolygonMesh -> brush path first
+ var brush = ExternalRealtimeCSG.CreateBrushFromPolygonMesh(parent, "Shape Editor Brush", polygonMesh);
+ if (brush != null)
+ {
+ brush.transform.SetParent(parent, false);
+ continue;
+ }
+
+ // fall back to plane-based brush creation
+ var planes = polygonMesh.ToMaterialPlanes();
+ brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
if (brush != null)
brush.transform.SetParent(parent, false);
}
@@ -55,4 +64,4 @@ private void RevolveExtrude_Rebuild()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Editor/Scene/RealtimeCSGTarget.ScaledExtrude.cs b/Scripts/Editor/Scene/RealtimeCSGTarget.ScaledExtrude.cs
index 28b33f1..6796f95 100644
--- a/Scripts/Editor/Scene/RealtimeCSGTarget.ScaledExtrude.cs
+++ b/Scripts/Editor/Scene/RealtimeCSGTarget.ScaledExtrude.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
using UnityEngine;
@@ -30,9 +30,18 @@ private void ScaledExtrude_Rebuild()
for (int i = 0; i < polygonMeshesCount; i++)
{
var polygonMesh = polygonMeshes[i];
- var planes = polygonMesh.ToMaterialPlanes();
- var brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
+ // try the direct PolygonMesh -> brush path first
+ var brush = ExternalRealtimeCSG.CreateBrushFromPolygonMesh(parent, "Shape Editor Brush", polygonMesh);
+ if (brush != null)
+ {
+ brush.transform.SetParent(parent, false);
+ continue;
+ }
+
+ // fall back to plane-based brush creation
+ var planes = polygonMesh.ToMaterialPlanes();
+ brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
if (brush != null)
brush.transform.SetParent(parent, false);
}
@@ -43,4 +52,4 @@ private void ScaledExtrude_Rebuild()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Editor/Scene/RealtimeCSGTarget.SplineExtrude.cs b/Scripts/Editor/Scene/RealtimeCSGTarget.SplineExtrude.cs
index 86125ed..f90b0fc 100644
--- a/Scripts/Editor/Scene/RealtimeCSGTarget.SplineExtrude.cs
+++ b/Scripts/Editor/Scene/RealtimeCSGTarget.SplineExtrude.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
using UnityEngine;
@@ -30,9 +30,18 @@ private void SplineExtrude_Rebuild()
for (int i = 0; i < polygonMeshesCount; i++)
{
var polygonMesh = polygonMeshes[i];
- var planes = polygonMesh.ToMaterialPlanes();
- var brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
+ // try the direct PolygonMesh -> brush path first
+ var brush = ExternalRealtimeCSG.CreateBrushFromPolygonMesh(parent, "Shape Editor Brush", polygonMesh);
+ if (brush != null)
+ {
+ brush.transform.SetParent(parent, false);
+ continue;
+ }
+
+ // fall back to plane-based brush creation
+ var planes = polygonMesh.ToMaterialPlanes();
+ brush = ExternalRealtimeCSG.CreateBrushFromPlanes("Shape Editor Brush", planes.planes, GetMaterials(planes.materials), polygonMesh.booleanOperator);
if (brush != null)
brush.transform.SetParent(parent, false);
}
@@ -53,7 +62,7 @@ private MathEx.Spline3 GetSpline3()
private Vector3[] GetLocalChildPoints()
{
int childCount = transform.childCount;
- Vector3[] points = new Vector3[childCount];
+ var points = new Vector3[childCount];
for (int i = 0; i < childCount; i++)
points[i] = transform.GetChild(i).localPosition;
return points;
@@ -61,4 +70,4 @@ private Vector3[] GetLocalChildPoints()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Utilities/ExternalRealtimeCSG.cs b/Scripts/Utilities/ExternalRealtimeCSG.cs
index a984837..22274c3 100644
--- a/Scripts/Utilities/ExternalRealtimeCSG.cs
+++ b/Scripts/Utilities/ExternalRealtimeCSG.cs
@@ -1,4 +1,4 @@
-#if UNITY_EDITOR
+#if UNITY_EDITOR
// contains source code from https://github.com/LogicalError/realtime-CSG-for-unity (see Licenses/RealtimeCSG.txt).
@@ -110,6 +110,51 @@ public class ExternalRealtimeCSG
///
private static PropertyInfo materialUtilityWallMaterialProperty = null;
+ ///
+ /// The cached GeometryUtility type after initialization.
+ ///
+ private static Type geometryUtility = null;
+
+ ///
+ /// The cached GeometryUtility.CalcPolygonPlane method after initialization.
+ ///
+ private static MethodInfo calcPolygonPlaneMethod = null;
+
+ ///
+ /// The cached GeometryUtility.CalculateTangents method after initialization.
+ ///
+ private static MethodInfo calculateTangentsMethod = null;
+
+ ///
+ /// The cached ControlMeshUtility type after initialization.
+ ///
+ private static Type controlMeshUtility = null;
+
+ ///
+ /// The cached ControlMeshUtility.Validate method after initialization.
+ ///
+ private static MethodInfo validateControlMeshMethod = null;
+
+ ///
+ /// The cached ShapeUtility.EnsureInitialized method after initialization.
+ ///
+ private static MethodInfo ensureInitializedShapeMethod = null;
+
+ ///
+ /// The cached CSGSettings.DefaultMaterial property after initialization.
+ ///
+ private static PropertyInfo csgSettingsDefaultMaterialProperty = null;
+
+ ///
+ /// The cached CSGSettings.DefaultTexGenFlags property after initialization.
+ ///
+ private static PropertyInfo csgSettingsDefaultTexGenFlagsProperty = null;
+
+ ///
+ /// The cached MathConstants.oneVector3 field after initialization.
+ ///
+ private static FieldInfo oneVector3Field = null;
+
///
/// Used to store whether an initialization error occured.
///
@@ -189,6 +234,42 @@ public static bool IsAvailable()
materialUtilityWallMaterialProperty = materialUtility.GetProperty("WallMaterial");
if (materialUtilityWallMaterialProperty == null) { initializationError = true; return false; }
+ geometryUtility = GetType("RealtimeCSG.GeometryUtility");
+ if (geometryUtility == null) { initializationError = true; return false; }
+
+ calcPolygonPlaneMethod = geometryUtility.GetMethodByName("CalcPolygonPlane", "controlMesh", "polygonIndex");
+ if (calcPolygonPlaneMethod == null) { initializationError = true; return false; }
+
+ calculateTangentsMethod = geometryUtility.GetMethodByName("CalculateTangents", "normal", out _, out _);
+ if (calculateTangentsMethod == null) { initializationError = true; return false; }
+
+ controlMeshUtility = GetType("RealtimeCSG.ControlMeshUtility");
+ if (controlMeshUtility == null) { initializationError = true; return false; }
+
+ validateControlMeshMethod = controlMeshUtility.GetMethodByName("Validate", "controlMesh", "shape");
+ if (validateControlMeshMethod == null) { initializationError = true; return false; }
+
+ var shapeUtility = GetType("RealtimeCSG.ShapeUtility");
+ if (shapeUtility == null) { initializationError = true; return false; }
+
+ ensureInitializedShapeMethod = shapeUtility.GetMethodByName("EnsureInitialized", "shape");
+ if (ensureInitializedShapeMethod == null) { initializationError = true; return false; }
+
+ var csgSettings = GetType("RealtimeCSG.CSGSettings");
+ if (csgSettings == null) { initializationError = true; return false; }
+
+ csgSettingsDefaultMaterialProperty = csgSettings.GetProperty("DefaultMaterial");
+ if (csgSettingsDefaultMaterialProperty == null) { initializationError = true; return false; }
+
+ csgSettingsDefaultTexGenFlagsProperty = csgSettings.GetProperty("DefaultTexGenFlags");
+ if (csgSettingsDefaultTexGenFlagsProperty == null) { initializationError = true; return false; }
+
+ var mathConstants = GetType("RealtimeCSG.MathConstants");
+ if (mathConstants == null) { initializationError = true; return false; }
+
+ oneVector3Field = mathConstants.GetField("oneVector3");
+ if (oneVector3Field == null) { initializationError = true; return false; }
+
initializationSuccess = true;
return true;
}
@@ -233,7 +314,7 @@ public static Material WallMaterial
get
{
if (!IsAvailable()) return null;
- return (Material)materialUtilityWallMaterialProperty.GetValue(materialUtility);
+ return (Material)csgSettingsDefaultMaterialProperty.GetValue(null);
}
}
@@ -286,6 +367,188 @@ public static MonoBehaviour CreateBrushFromPlanes(string brushName, Plane[] plan
return (MonoBehaviour)brush;
}
+ ///
+ /// Creates a RealtimeCSG brush directly from a PolygonMesh by building the half-edge
+ /// connectivity data (ControlMesh) and matching Shape/Surface/TexGen data, bypassing the
+ /// plane-intersection approach which can fail on certain mesh topologies.
+ ///
+ /// The parent transform for the created brush.
+ /// The name of the brush.
+ /// The PolygonMesh to convert.
+ /// A CSGBrush component on success, or null on failure.
+ public static MonoBehaviour CreateBrushFromPolygonMesh(Transform parent, string brushName, PolygonMesh mesh)
+ {
+ if (!IsAvailable()) return null;
+
+ // skip degenerate meshes
+ if (mesh == null || mesh.Count == 0) return null;
+
+ // build unique vertex list preserving insertion order
+ var vertexSet = new HashSet();
+ var uniqueVertices = new List();
+ foreach (var polygon in mesh)
+ {
+ foreach (var vertex in polygon)
+ {
+ if (vertexSet.Add(vertex.position))
+ uniqueVertices.Add(vertex.position);
+ }
+ }
+
+ // build half-edge index arrays: one edge per polygon vertex
+ var polygonCount = mesh.Count;
+ var edgeIndices = new List();
+ foreach (var polygon in mesh)
+ {
+ for (int j = 0; j < polygon.Count; j++)
+ edgeIndices.Add(0); // placeholder, filled below
+ }
+
+ // build half-edges with polygon/vertex indices (twin set to -1 for now)
+ var halfEdges = new List