Skip to content
This repository has been archived by the owner on Feb 11, 2024. It is now read-only.

2DSE: Extrude anything into a single concave NoCSG brush. #110

Merged
merged 3 commits into from
May 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindowPopup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public enum PopupMode
public int revolveSteps = 4;
public bool revolveSpiralSloped = false;
public Vector2Int GlobalPivotPosition_Position;
public bool convexBrushes = true;

private Action<ShapeEditorWindowPopup> onApply;

Expand All @@ -50,6 +51,7 @@ public ShapeEditorWindowPopup(PopupMode popupMode, ShapeEditor.Project project,
revolve360 = project.revolve360;
revolveSteps = project.revolveSteps;
revolveSpiralSloped = project.revolveSpiralSloped;
convexBrushes = project.convexBrushes;
GlobalPivotPosition_Position = project.globalPivot.position;

this.onApply = (self) =>
Expand All @@ -59,27 +61,32 @@ public ShapeEditorWindowPopup(PopupMode popupMode, ShapeEditor.Project project,
{
case PopupMode.CreatePolygon:
project.extrudeScale = extrudeScale;
project.convexBrushes = convexBrushes;
break;

case PopupMode.RevolveShape:
project.extrudeScale = extrudeScale;
project.convexBrushes = convexBrushes;
project.revolve360 = revolve360;
project.revolveSteps = revolveSteps;
project.revolveSpiralSloped = revolveSpiralSloped;
break;

case PopupMode.ExtrudeShape:
project.extrudeScale = extrudeScale;
project.convexBrushes = convexBrushes;
project.extrudeDepth = extrudeDepth;
break;

case PopupMode.ExtrudePoint:
project.extrudeScale = extrudeScale;
project.convexBrushes = convexBrushes;
project.extrudeDepth = extrudeDepth;
break;

case PopupMode.ExtrudeBevel:
project.extrudeScale = extrudeScale;
project.convexBrushes = convexBrushes;
project.extrudeDepth = extrudeDepth;
project.extrudeClipDepth = extrudeClipDepth;
break;
Expand All @@ -93,28 +100,29 @@ public ShapeEditorWindowPopup(PopupMode popupMode, ShapeEditor.Project project,

public override Vector2 GetWindowSize()
{
// + 18 for every element
switch (popupMode)
{
case PopupMode.BezierDetailLevel:
return new Vector2(205, 140);

case PopupMode.GlobalPivotPosition:
return new Vector2(300, 50 + 18);
return new Vector2(300, 68);

case PopupMode.CreatePolygon:
return new Vector2(300, 50 + 18);
return new Vector2(300, 50 + 36);

case PopupMode.RevolveShape:
return new Vector2(300, 86 + 18 + 18);
return new Vector2(300, 104 + 36);

case PopupMode.ExtrudeShape:
return new Vector2(300, 68 + 18);
return new Vector2(300, 68 + 36);

case PopupMode.ExtrudePoint:
return new Vector2(300, 68 + 18);
return new Vector2(300, 68 + 36);

case PopupMode.ExtrudeBevel:
return new Vector2(300, 86 + 18);
return new Vector2(300, 86 + 36);

default:
return new Vector2(300, 150);
Expand All @@ -124,12 +132,14 @@ public override Vector2 GetWindowSize()
public override void OnGUI(Rect rect)
{
bool hasScale = true;
bool hasConvexBrushes = true;
string accept = "";
switch (popupMode)
{
case PopupMode.BezierDetailLevel:
GUILayout.Label("Bezier Detail Level", EditorStyles.boldLabel);
hasScale = false;
hasConvexBrushes = false;
accept = "Apply";

GUILayout.BeginHorizontal(EditorStyles.toolbar);
Expand Down Expand Up @@ -184,6 +194,7 @@ public override void OnGUI(Rect rect)
case PopupMode.GlobalPivotPosition:
GUILayout.Label("Global Pivot Position", EditorStyles.boldLabel);
hasScale = false;
hasConvexBrushes = false;
accept = "Set Position";

#if !UNITY_2017_2_OR_NEWER
Expand Down Expand Up @@ -211,10 +222,7 @@ public override void OnGUI(Rect rect)
revolveSteps = EditorGUILayout.IntField("Steps", revolveSteps);
if (revolveSteps < 1) revolveSteps = 1;

EditorGUILayout.BeginHorizontal();
GUILayout.Label("NoCSG Only");
revolveSpiralSloped = GUILayout.Toggle(revolveSpiralSloped, "Sloped Spiral", EditorStyles.toolbarButton);
EditorGUILayout.EndHorizontal();
revolveSpiralSloped = EditorGUILayout.Toggle("Sloped Spiral", revolveSpiralSloped);

// steps can't be more than 360.
if (revolveSteps > revolve360) revolveSteps = revolve360;
Expand Down Expand Up @@ -248,6 +256,11 @@ public override void OnGUI(Rect rect)
break;
}

if (hasConvexBrushes)
{
convexBrushes = EditorGUILayout.Toggle("Convex Brushes", convexBrushes);
}

if (hasScale)
{
EditorGUIUtility.wideMode = true;
Expand Down
6 changes: 6 additions & 0 deletions Scripts/Brushes/CompoundBrushes/ShapeEditor/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ public class Project
[SerializeField]
public bool revolveSpiralSloped = false;

/// <summary>
/// Whether the shape uses Convex Decomposition or Concave Shapes.
/// </summary>
[SerializeField]
public bool convexBrushes = true;

/// <summary>
/// Clones this project and returns the copy.
/// </summary>
Expand Down
137 changes: 104 additions & 33 deletions Scripts/Brushes/CompoundBrushes/ShapeEditor/ShapeEditorBrush.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ public override int BrushCount
{
get
{
// if the user desires a single concave brush we return 1.
if (!project.convexBrushes)
return 1;

// we already know the amount of brushes we need.
if (!isDirty)
return desiredBrushCount;
Expand Down Expand Up @@ -149,10 +153,14 @@ public override void Invalidate(bool polygonsChanged)
if (extrudeMode == ExtrudeMode.RevolveShape && project.revolveSpiralSloped && project.globalPivot.position.y != 0)
this.IsNoCSG = true;

// force nocsg when using concave brushes as sabrecsg doesn't support it.
if (!project.convexBrushes)
this.IsNoCSG = true;

// nothing to do except copy csg information to our child brushes.
if (!isDirty)
{
for (int i = 0; i < BrushCount; i++)
for (int i = 0; i < (project.convexBrushes ? desiredBrushCount : 1); i++)
{
generatedBrushes[i].Mode = this.Mode;
generatedBrushes[i].IsNoCSG = this.IsNoCSG;
Expand All @@ -175,16 +183,22 @@ public override void Invalidate(bool polygonsChanged)
if (m_LastBuiltPolygons == null)
m_LastBuiltPolygons = BuildConvexPolygons();

// prepare a list of polygons for concave brushes.
List<Polygon> concavePolygons = null;
if (!project.convexBrushes)
concavePolygons = new List<Polygon>();

// iterate through the brushes we received:
int brushCount = BrushCount;
int brushCount = desiredBrushCount;

// iterate through the brushes we received:
for (int i = 0; i < brushCount; i++)
{
// copy our csg information to our child brushes.
generatedBrushes[i].Mode = this.Mode;
generatedBrushes[i].IsNoCSG = this.IsNoCSG;
generatedBrushes[i].IsVisible = this.IsVisible;
generatedBrushes[i].HasCollision = this.HasCollision;
generatedBrushes[project.convexBrushes ? i : 0].Mode = this.Mode;
generatedBrushes[project.convexBrushes ? i : 0].IsNoCSG = this.IsNoCSG;
generatedBrushes[project.convexBrushes ? i : 0].IsVisible = this.IsVisible;
generatedBrushes[project.convexBrushes ? i : 0].HasCollision = this.HasCollision;

// local variables.
Quaternion rot;
Expand All @@ -198,7 +212,11 @@ public override void Invalidate(bool polygonsChanged)
GenerateUvCoordinates(m_LastBuiltPolygons[i], false);
Polygon poly1 = m_LastBuiltPolygons[i].DeepCopy();
poly1.Flip();
generatedBrushes[i].SetPolygons(new Polygon[] { poly1 });

if (project.convexBrushes)
generatedBrushes[i].SetPolygons(new Polygon[] { poly1 });
else
concavePolygons.Add(poly1);
break;

// generate 3d cube-ish shapes that revolve around the pivot and spirals up or down.
Expand Down Expand Up @@ -254,7 +272,10 @@ public override void Invalidate(bool polygonsChanged)
GenerateUvCoordinates(backPoly, false);
polygons.Add(backPoly);

generatedBrushes[i].SetPolygons(polygons.ToArray());
if (project.convexBrushes)
generatedBrushes[i].SetPolygons(polygons.ToArray());
else
concavePolygons.AddRange(polygons);
break;

// generate a 3d cube-ish shape.
Expand All @@ -263,7 +284,10 @@ public override void Invalidate(bool polygonsChanged)
SurfaceUtility.ExtrudePolygon(m_LastBuiltPolygons[i], project.extrudeDepth, out outputPolygons, out rot);
foreach (Polygon poly in outputPolygons)
GenerateUvCoordinates(poly, false);
generatedBrushes[i].SetPolygons(outputPolygons);
if (project.convexBrushes)
generatedBrushes[i].SetPolygons(outputPolygons);
else
concavePolygons.AddRange(outputPolygons);
break;

// generate a 3d cone-ish shape.
Expand All @@ -272,7 +296,10 @@ public override void Invalidate(bool polygonsChanged)
ExtrudePolygonToPoint(m_LastBuiltPolygons[i], project.extrudeDepth, new Vector2((project.globalPivot.position.x * project.extrudeScale.x) / 8.0f, -(project.globalPivot.position.y * project.extrudeScale.y) / 8.0f), out outputPolygons, out rot);
foreach (Polygon poly in outputPolygons)
GenerateUvCoordinates(poly, false);
generatedBrushes[i].SetPolygons(outputPolygons);
if (project.convexBrushes)
generatedBrushes[i].SetPolygons(outputPolygons);
else
concavePolygons.AddRange(outputPolygons);
break;

// generate a 3d trapezoid-ish shape.
Expand All @@ -281,7 +308,10 @@ public override void Invalidate(bool polygonsChanged)
ExtrudePolygonBevel(m_LastBuiltPolygons[i], project.extrudeDepth, project.extrudeClipDepth / project.extrudeDepth, new Vector2((project.globalPivot.position.x * project.extrudeScale.x) / 8.0f, -(project.globalPivot.position.y * project.extrudeScale.y) / 8.0f), out outputPolygons, out rot);
foreach (Polygon poly in outputPolygons)
GenerateUvCoordinates(poly, false);
generatedBrushes[i].SetPolygons(outputPolygons);
if (project.convexBrushes)
generatedBrushes[i].SetPolygons(outputPolygons);
else
concavePolygons.AddRange(outputPolygons);
break;
}

Expand All @@ -293,42 +323,83 @@ public override void Invalidate(bool polygonsChanged)
// it also excludes a couple faces that CSG doesn't exclude due to floating point precision errors.
// the latter is especially noticable with complex revolved shapes.

// compare each brush to another brush:
for (int i = 0; i < brushCount; i++)
// hidden surface removal for convex brushes.
if (project.convexBrushes)
{
for (int j = 0; j < brushCount; j++)
// compare each brush to another brush:
for (int i = 0; i < brushCount; i++)
{
// can't check for hidden faces on the same brush.
if (i == j) continue;

// compare each polygon on brush i to each polygon on brush j:
foreach (Polygon pa in generatedBrushes[i].GetPolygons())
for (int j = 0; j < brushCount; j++)
{
foreach (Polygon pb in generatedBrushes[j].GetPolygons())
// can't check for hidden faces on the same brush.
if (i == j) continue;

// compare each polygon on brush i to each polygon on brush j:
foreach (Polygon pa in generatedBrushes[i].GetPolygons())
{
// check they both have this polygon:
bool identical = true;
foreach (Vertex va in pa.Vertices)
foreach (Polygon pb in generatedBrushes[j].GetPolygons())
{
if (!pb.Vertices.Any(vb => vb.Position == va.Position))
// check they both have this polygon:
bool identical = true;
foreach (Vertex va in pa.Vertices)
{
if (!pb.Vertices.Any(vb => vb.Position == va.Position))
{
identical = false;
break;
}
}
// identical polygons on both brushes means it can be excluded:
if (identical)
{
identical = false;
break;
pa.UserExcludeFromFinal = true;
pb.UserExcludeFromFinal = true;
}
}
// identical polygons on both brushes means it can be excluded:
if (identical)
}
}

// invalidate every brush.
generatedBrushes[i].Invalidate(true);
csgBounds.Encapsulate(generatedBrushes[i].GetBounds());
}
}

// hidden surface removal for a concave brush.
else
{
List<Polygon> concavePolygonsCopy = concavePolygons.ToList();

// compare each polygon and find duplicates:
foreach (Polygon pa in concavePolygonsCopy)
{
foreach (Polygon pb in concavePolygonsCopy)
{
// can't be the same polygon.
if (pa == pb) continue;

// check they both have this polygon:
bool identical = true;
foreach (Vertex va in pa.Vertices)
{
if (!pb.Vertices.Any(vb => vb.Position == va.Position))
{
pa.UserExcludeFromFinal = true;
pb.UserExcludeFromFinal = true;
identical = false;
break;
}
}
// identical polygons on both brushes means it can be excluded:
if (identical)
{
concavePolygons.Remove(pa);
concavePolygons.Remove(pb);
}
}
}

// invalidate every brush.
generatedBrushes[i].Invalidate(true);
csgBounds.Encapsulate(generatedBrushes[i].GetBounds());
// invalidate the brush.
generatedBrushes[0].SetPolygons(concavePolygons.ToArray());
csgBounds.Encapsulate(generatedBrushes[0].GetBounds());
}

// apply the generated csg bounds.
Expand Down