diff --git a/Scripts/Brushes/CompoundBrushes/CurvedStairBrush.cs b/Scripts/Brushes/CompoundBrushes/CurvedStairBrush.cs index 65f374f0..2fcd0819 100644 --- a/Scripts/Brushes/CompoundBrushes/CurvedStairBrush.cs +++ b/Scripts/Brushes/CompoundBrushes/CurvedStairBrush.cs @@ -42,6 +42,22 @@ public class CurvedStairBrush : CompoundBrush [SerializeField] bool counterClockwise = false; + /// Whether the stairs reach down to the bottom. + [SerializeField] + bool fillToBottom = true; + + /// Whether to generate stairs or a curved wall. + [SerializeField] + bool curvedWall = false; + + /// Whether the floor is stairs or a smooth slope. + [SerializeField] + bool slopedFloor = false; + + /// Whether the ceiling is stairs or a smooth slope. + [SerializeField] + bool slopedCeiling = false; + /// The last known extents of the compound brush to detect user resizing the bounds. private Vector3 m_LastKnownExtents; /// The last known position of the compound brush to prevent movement on resizing the bounds. @@ -102,7 +118,6 @@ public override void Invalidate (bool polygonsChanged) // local variables List vertexPositions = new List(); - Plane plane; Vector3 rotateStep = new Vector3(); Vector3 vertex = new Vector3(), newVertex = new Vector3(); float adjustment; @@ -128,7 +143,10 @@ public override void Invalidate (bool polygonsChanged) newVertex = Quaternion.Euler(rotateStep * x) * vertex; vertexPositions.Add(new Vector3(newVertex.x, vertex.z - adjustment, newVertex.y)); - vertex.z += stepHeight; + if (curvedWall) + vertex.z = stepHeight * numSteps; + else + vertex.z += stepHeight; vertexPositions.Add(new Vector3(newVertex.x, vertex.z, newVertex.y)); } @@ -145,7 +163,10 @@ public override void Invalidate (bool polygonsChanged) newVertex = Quaternion.Euler(rotateStep * x) * vertex; vertexPositions.Add(new Vector3(newVertex.x, vertex.z - adjustment, newVertex.y)); - vertex.z += stepHeight; + if (curvedWall) + vertex.z = stepHeight * numSteps; + else + vertex.z += stepHeight; vertexPositions.Add(new Vector3(newVertex.x, vertex.z, newVertex.y)); } @@ -183,6 +204,14 @@ public override void Invalidate (bool polygonsChanged) index3 = 3; } + // we force NoCSG mode if special NoCSG operators are used. + if (curvedWall) { slopedFloor = false; slopedCeiling = false; } + if (fillToBottom) { slopedCeiling = false; } + if (slopedFloor || slopedCeiling) + { + this.IsNoCSG = true; + } + // we calculate the bounds of the output csg. Bounds csgBounds = new Bounds(); @@ -207,20 +236,34 @@ public override void Invalidate (bool polygonsChanged) // | Back | Left | Right | Front | Bottom | Top | // +--------+--------+--------+--------+--------+--------+ + + // retrieve the vertices of the top polygon. Vertex[] vertices = polygons[5].Vertices; // step top. - vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2]; - vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 1]; - vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1]; - vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 2]; + if (slopedFloor) + { + // create a smooth slope. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2 + 1]; + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 1]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1]; + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 2 + 1]; + } + else + { + // create blocky stairs. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2]; + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 1]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1]; + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 2]; + } + + // calculate a normal using a virtual plane. + GenerateNormals(polygons[5]); // update uv coordinates to prevent distortions using barnaby's genius utilities. - vertices[index0].UV = GeometryHelper.GetUVForPosition(polygons[5], vertexPositions[outerStart + (i * 2) + 2]); - vertices[index1].UV = GeometryHelper.GetUVForPosition(polygons[5], vertexPositions[outerStart + (i * 2) + 1]); - vertices[index2].UV = GeometryHelper.GetUVForPosition(polygons[5], vertexPositions[innerStart + (i * 2) + 1]); - vertices[index3].UV = GeometryHelper.GetUVForPosition(polygons[5], vertexPositions[innerStart + (i * 2) + 2]); + GenerateUvCoordinates(polygons[5]); @@ -228,17 +271,34 @@ public override void Invalidate (bool polygonsChanged) vertices = polygons[3].Vertices; // step front. - vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 1]; - vertices[index1].Position = vertexPositions[bottomOuterStart + i]; - vertices[index2].Position = vertexPositions[bottomInnerStart + i]; - vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1]; + if (fillToBottom) + { + // fill downwards to the bottom. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 1]; + vertices[index1].Position = vertexPositions[bottomOuterStart + i]; + vertices[index2].Position = vertexPositions[bottomInnerStart + i]; + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1]; + } + else + { + // fill downwards to the step height. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 1]; + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1]; + } + if (slopedCeiling) + { + // create a smooth ceiling slope. + vertices[index1].Position -= new Vector3(0.0f, stepHeight, 0.0f); + vertices[index2].Position -= new Vector3(0.0f, stepHeight, 0.0f); + } // calculate a normal using a virtual plane. - plane = new Plane(vertices[index1].Position, vertices[index2].Position, vertices[index3].Position); - vertices[index0].Normal = plane.normal; - vertices[index1].Normal = plane.normal; - vertices[index2].Normal = plane.normal; - vertices[index3].Normal = plane.normal; + GenerateNormals(polygons[3]); + + // update uv coordinates to prevent distortions using barnaby's genius utilities. + GenerateUvCoordinates(polygons[3]); @@ -246,17 +306,38 @@ public override void Invalidate (bool polygonsChanged) vertices = polygons[1].Vertices; // inner curve. - vertices[index0].Position = vertexPositions[bottomInnerStart + i + 1]; - vertices[index1].Position = vertexPositions[innerStart + (i * 2) + 2]; - vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1]; - vertices[index3].Position = vertexPositions[bottomInnerStart + i]; + if (fillToBottom) + { + // fill downwards to the bottom. + vertices[index0].Position = vertexPositions[bottomInnerStart + i + 1]; + vertices[index1].Position = vertexPositions[innerStart + (i * 2) + 2]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1]; + vertices[index3].Position = vertexPositions[bottomInnerStart + i]; + } + else + { + // fill downwards to the step height. + vertices[index0].Position = vertexPositions[innerStart + (i * 2) + 2] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index1].Position = vertexPositions[innerStart + (i * 2) + 2]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 1]; + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight, 0.0f); + } + if (slopedFloor) + { + // create a smooth floor slope. + vertices[index1].Position = vertexPositions[innerStart + (i * 2) + 2 + 1]; + } + if (slopedCeiling) + { + // create a smooth ceiling slope. + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight * 2.0f, 0.0f); + } // calculate a normal using a virtual plane. - plane = new Plane(vertices[index1].Position, vertices[index2].Position, vertices[index3].Position); - vertices[index0].Normal = plane.normal; - vertices[index1].Normal = plane.normal; - vertices[index2].Normal = plane.normal; - vertices[index3].Normal = plane.normal; + GenerateNormals(polygons[1]); + + // update uv coordinates to prevent distortions using barnaby's genius utilities. + GenerateUvCoordinates(polygons[1]); @@ -264,17 +345,38 @@ public override void Invalidate (bool polygonsChanged) vertices = polygons[2].Vertices; // outer curve. - vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2]; - vertices[index1].Position = vertexPositions[bottomOuterStart + i + 1]; - vertices[index2].Position = vertexPositions[bottomOuterStart + i]; - vertices[index3].Position = vertexPositions[outerStart + (i * 2) + 1]; + if (fillToBottom) + { + // fill downwards to the bottom. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2]; + vertices[index1].Position = vertexPositions[bottomOuterStart + i + 1]; + vertices[index2].Position = vertexPositions[bottomOuterStart + i]; + vertices[index3].Position = vertexPositions[outerStart + (i * 2) + 1]; + } + else + { + // fill downwards to the step height. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2]; + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 2] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index2].Position = vertexPositions[outerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index3].Position = vertexPositions[outerStart + (i * 2) + 1]; + } + if (slopedFloor) + { + // create a smooth floor slope. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2 + 1]; + } + if (slopedCeiling) + { + // create a smooth ceiling slope. + vertices[index2].Position = vertexPositions[outerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight * 2.0f, 0.0f); + } // calculate a normal using a virtual plane. - plane = new Plane(vertices[index1].Position, vertices[index2].Position, vertices[index3].Position); - vertices[index0].Normal = plane.normal; - vertices[index1].Normal = plane.normal; - vertices[index2].Normal = plane.normal; - vertices[index3].Normal = plane.normal; + GenerateNormals(polygons[2]); + + // update uv coordinates to prevent distortions using barnaby's genius utilities. + GenerateUvCoordinates(polygons[2]); @@ -282,16 +384,34 @@ public override void Invalidate (bool polygonsChanged) vertices = polygons[4].Vertices; // bottom. - vertices[index0].Position = vertexPositions[bottomOuterStart + i]; - vertices[index1].Position = vertexPositions[bottomOuterStart + i + 1]; - vertices[index2].Position = vertexPositions[bottomInnerStart + i + 1]; - vertices[index3].Position = vertexPositions[bottomInnerStart + i]; + if (fillToBottom) + { + // fill downwards to the bottom. + vertices[index0].Position = vertexPositions[bottomOuterStart + i]; + vertices[index1].Position = vertexPositions[bottomOuterStart + i + 1]; + vertices[index2].Position = vertexPositions[bottomInnerStart + i + 1]; + vertices[index3].Position = vertexPositions[bottomInnerStart + i]; + } + else + { + // fill downwards to the step height. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 2] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 2] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight, 0.0f); + } + if (slopedCeiling) + { + // create a smooth ceiling slope. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight * 2.0f, 0.0f); + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 1] - new Vector3(0.0f, stepHeight * 2.0f, 0.0f); + } + + // calculate a normal using a virtual plane. + GenerateNormals(polygons[4]); // update uv coordinates to prevent distortions using barnaby's genius utilities. - vertices[index0].UV = GeometryHelper.GetUVForPosition(polygons[4], vertexPositions[bottomOuterStart + i]); - vertices[index1].UV = GeometryHelper.GetUVForPosition(polygons[4], vertexPositions[bottomOuterStart + i + 1]); - vertices[index2].UV = GeometryHelper.GetUVForPosition(polygons[4], vertexPositions[bottomInnerStart + i + 1]); - vertices[index3].UV = GeometryHelper.GetUVForPosition(polygons[4], vertexPositions[bottomInnerStart + i]); + GenerateUvCoordinates(polygons[4]); @@ -299,17 +419,36 @@ public override void Invalidate (bool polygonsChanged) vertices = polygons[0].Vertices; // back panel. - vertices[index0].Position = vertexPositions[bottomOuterStart + i + 1]; - vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 2]; - vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 2]; - vertices[index3].Position = vertexPositions[bottomInnerStart + i + 1]; + if (fillToBottom) + { + // fill downwards to the bottom. + vertices[index0].Position = vertexPositions[bottomOuterStart + i + 1]; + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 2]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 2]; + vertices[index3].Position = vertexPositions[bottomInnerStart + i + 1]; + } + else + { + // fill downwards to the step height. + vertices[index0].Position = vertexPositions[outerStart + (i * 2) + 2] - new Vector3(0.0f, stepHeight, 0.0f); + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 2]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 2]; + vertices[index3].Position = vertexPositions[innerStart + (i * 2) + 2] - new Vector3(0.0f, stepHeight, 0.0f); + } + if (slopedFloor) + { + // create a smooth floor slope. + vertices[index1].Position = vertexPositions[outerStart + (i * 2) + 2 + 1]; + vertices[index2].Position = vertexPositions[innerStart + (i * 2) + 2 + 1]; + } // calculate a normal using a virtual plane. - plane = new Plane(vertices[index1].Position, vertices[index2].Position, vertices[index3].Position); - vertices[index0].Normal = plane.normal; - vertices[index1].Normal = plane.normal; - vertices[index2].Normal = plane.normal; - vertices[index3].Normal = plane.normal; + GenerateNormals(polygons[0]); + + // update uv coordinates to prevent distortions using barnaby's genius utilities. + GenerateUvCoordinates(polygons[0]); + + generatedBrushes[i].Invalidate(true); csgBounds.Encapsulate(generatedBrushes[i].GetBounds()); @@ -320,6 +459,23 @@ public override void Invalidate (bool polygonsChanged) m_LastKnownExtents = localBounds.extents; m_LastKnownPosition = transform.localPosition; } + + /// + /// Generates the UV coordinates for a automatically. + /// + /// The polygon to be updated. + private void GenerateUvCoordinates(Polygon polygon) + { + foreach (Vertex vertex in polygon.Vertices) + vertex.UV = GeometryHelper.GetUVForPosition(polygon, vertex.Position); + } + + private void GenerateNormals(Polygon polygon) + { + Plane plane = new Plane(polygon.Vertices[1].Position, polygon.Vertices[2].Position, polygon.Vertices[3].Position); + foreach (Vertex vertex in polygon.Vertices) + vertex.Normal = plane.normal; + } } } diff --git a/Scripts/Brushes/CompoundBrushes/Editor/CurvedStairBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/CurvedStairBrushInspector.cs index dcd30aab..8a234335 100644 --- a/Scripts/Brushes/CompoundBrushes/Editor/CurvedStairBrushInspector.cs +++ b/Scripts/Brushes/CompoundBrushes/Editor/CurvedStairBrushInspector.cs @@ -18,6 +18,10 @@ public class CurvedStairBrushInspector : CompoundBrushInspector SerializedProperty numSteps; SerializedProperty addToFirstStep; SerializedProperty counterClockwise; + SerializedProperty fillToBottom; + SerializedProperty curvedWall; + SerializedProperty slopedFloor; + SerializedProperty slopedCeiling; protected override void OnEnable() { @@ -30,10 +34,16 @@ protected override void OnEnable() numSteps = serializedObject.FindProperty("numSteps"); addToFirstStep = serializedObject.FindProperty("addToFirstStep"); counterClockwise = serializedObject.FindProperty("counterClockwise"); + fillToBottom = serializedObject.FindProperty("fillToBottom"); + curvedWall = serializedObject.FindProperty("curvedWall"); + slopedFloor = serializedObject.FindProperty("slopedFloor"); + slopedCeiling = serializedObject.FindProperty("slopedCeiling"); } public override void OnInspectorGUI() { + bool oldBool; + using (new NamedVerticalScope("Curved Stair")) { EditorGUI.BeginChangeCheck(); @@ -75,17 +85,62 @@ public override void OnInspectorGUI() if (EditorGUI.EndChangeCheck()) ApplyAndInvalidate(); - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(addToFirstStep); - if (addToFirstStep.floatValue < 0.0f) - addToFirstStep.floatValue = 0.0f; - if (EditorGUI.EndChangeCheck()) + // can only use additional height if fill to bottom is enabled. + if (fillToBottom.boolValue) + { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(addToFirstStep); + if (addToFirstStep.floatValue < 0.0f) + addToFirstStep.floatValue = 0.0f; + if (EditorGUI.EndChangeCheck()) + ApplyAndInvalidate(); + } + + EditorGUILayout.Space(); + + EditorGUILayout.BeginHorizontal(); + + counterClockwise.boolValue = GUILayout.Toggle(oldBool = counterClockwise.boolValue, "Counter Clockwise", EditorStyles.toolbarButton); + if (counterClockwise.boolValue != oldBool) ApplyAndInvalidate(); - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(counterClockwise); - if (EditorGUI.EndChangeCheck()) + fillToBottom.boolValue = GUILayout.Toggle(oldBool = fillToBottom.boolValue, "Fill To Bottom", EditorStyles.toolbarButton); + if (fillToBottom.boolValue != oldBool) ApplyAndInvalidate(); + + curvedWall.boolValue = GUILayout.Toggle(oldBool = curvedWall.boolValue, "Curved Wall", EditorStyles.toolbarButton); + if (curvedWall.boolValue != oldBool) + ApplyAndInvalidate(); + + EditorGUILayout.EndHorizontal(); + } + + // can only use nocsg slopes if build torus is disabled. + if (!curvedWall.boolValue) + { + using (new NamedVerticalScope("Curved Stair: NoCSG Operators")) + { + if (slopedFloor.boolValue || slopedCeiling.boolValue) + { + EditorGUILayout.HelpBox("The surface of the slope is non-planar. This means there are triangulated bumpy seams (look closely with your camera). NoCSG will be forced for this compound brush as the CSG engine cannot handle this shape properly.", MessageType.Warning); + } + + EditorGUILayout.BeginHorizontal(); + + slopedFloor.boolValue = GUILayout.Toggle(oldBool = slopedFloor.boolValue, "Sloped Floor", EditorStyles.toolbarButton); + if (slopedFloor.boolValue != oldBool) + ApplyAndInvalidate(); + + // can only use ceiling slopes if it isn't filled to the bottom. + if (!fillToBottom.boolValue) + { + slopedCeiling.boolValue = GUILayout.Toggle(oldBool = slopedCeiling.boolValue, "Sloped Ceiling", EditorStyles.toolbarButton); + if (slopedCeiling.boolValue != oldBool) + ApplyAndInvalidate(); + } + + EditorGUILayout.EndHorizontal(); + } } base.OnInspectorGUI();