public override void Invalidate(bool polygonsChanged) { //////////////////////////////////////////////////////////////////// // a little hack to detect the user manually resizing the bounds. // // we use this to automatically add steps for barnaby. // // it's probably good to build a more 'official' way to detect // // user scaling events in compound brushes sometime. // if (m_LastKnownExtents != localBounds.extents) // { // // undo any position movement. // transform.localPosition = m_LastKnownPosition; // } // //////////////////////////////////////////////////////////////////// Bounds csgBounds = new Bounds(); // nothing to do except copy csg information to our child brushes. if (!isDirty) { for (int i = 0; i < BrushCount; i++) { generatedBrushes[i].Mode = this.Mode; generatedBrushes[i].IsNoCSG = this.IsNoCSG; generatedBrushes[i].IsVisible = this.IsVisible; generatedBrushes[i].HasCollision = this.HasCollision; generatedBrushes[i].Invalidate(true); csgBounds.Encapsulate(generatedBrushes[i].GetBounds()); } // apply the generated csg bounds. localBounds = csgBounds; m_LastKnownExtents = localBounds.extents; m_LastKnownPosition = transform.localPosition; return; } base.Invalidate(polygonsChanged); isDirty = false; // build the polygons from the project. if (m_LastBuiltPolygons == null) { m_LastBuiltPolygons = BuildConvexPolygons(); } // iterate through the brushes we received: int brushCount = BrushCount; // force nocsg when creating a flat polygon sheet as sabrecsg doesn't support it. if (extrudeMode == ExtrudeMode.CreatePolygon) { this.IsNoCSG = true; } 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; // local variables. Quaternion rot; Polygon[] outputPolygons; switch (extrudeMode) { // generate a flat 2d polygon. case ExtrudeMode.CreatePolygon: GenerateNormals(m_LastBuiltPolygons[i]); GenerateUvCoordinates(m_LastBuiltPolygons[i], false); Polygon poly1 = m_LastBuiltPolygons[i].DeepCopy(); poly1.Flip(); generatedBrushes[i].SetPolygons(new Polygon[] { poly1 }); break; // generate 3d cube-ish shapes that revolve around the pivot. case ExtrudeMode.RevolveShape: int labpIndex = i % m_LastBuiltPolygons.Count; Polygon poly2 = m_LastBuiltPolygons[labpIndex].DeepCopy(); poly2.Flip(); GenerateUvCoordinates(poly2, false); foreach (Vertex v in poly2.Vertices) { float step = 360.0f / project.revolve360; v.Position = RotatePointAroundPivot(v.Position, new Vector3(((project.revolveDistance / 8.0f) * project.extrudeScale.x) + ((project.revolveRadius * project.extrudeScale.x) / 8.0f), 0.0f, 0.0f), new Vector3(0.0f, (project.revolveDirection ? 0 : 180) + ((i / m_LastBuiltPolygons.Count) * step), 0.0f)); } Polygon nextPoly = m_LastBuiltPolygons[labpIndex].DeepCopy(); nextPoly.Flip(); foreach (Vertex v in nextPoly.Vertices) { float step = 360.0f / project.revolve360; v.Position = RotatePointAroundPivot(v.Position, new Vector3(((project.revolveDistance / 8.0f) * project.extrudeScale.x) + ((project.revolveRadius * project.extrudeScale.x) / 8.0f), 0.0f, 0.0f), new Vector3(0.0f, (project.revolveDirection ? 0 : 180) + (((i / m_LastBuiltPolygons.Count) * step) + step), 0.0f)); } GenerateNormals(poly2); List <Polygon> polygons = new List <Polygon>() { poly2 }; List <Vertex> backPolyVertices = new List <Vertex>(); Edge[] myEdges = poly2.GetEdges(); Edge[] nextEdges = nextPoly.GetEdges(); for (int j = 0; j < myEdges.Length; j++) { Edge myEdge = myEdges[j]; Edge nextEdge = nextEdges[j]; Polygon newPoly = new Polygon(new Vertex[] { new Vertex(myEdge.Vertex1.Position, Vector3.zero, Vector2.zero), new Vertex(nextEdge.Vertex1.Position, Vector3.zero, Vector2.zero), new Vertex(nextEdge.Vertex2.Position, Vector3.zero, Vector2.zero), new Vertex(myEdge.Vertex2.Position, Vector3.zero, Vector2.zero), }, null, false, false); backPolyVertices.Add(nextEdge.Vertex1); GenerateNormals(newPoly); if (newPoly.Plane.normal == Vector3.zero) { continue; // discard single line, can happen in the center of the shape. } GenerateUvCoordinates(newPoly, false); polygons.Add(newPoly); } Polygon backPoly = new Polygon(backPolyVertices.ToArray(), null, false, false); backPoly.Flip(); GenerateNormals(backPoly); GenerateUvCoordinates(backPoly, false); polygons.Add(backPoly); generatedBrushes[i].SetPolygons(polygons.ToArray()); break; // generate a 3d cube-ish shape. case ExtrudeMode.ExtrudeShape: GenerateNormals(m_LastBuiltPolygons[i]); SurfaceUtility.ExtrudePolygon(m_LastBuiltPolygons[i], project.extrudeDepth, out outputPolygons, out rot); foreach (Polygon poly in outputPolygons) { GenerateUvCoordinates(poly, false); } generatedBrushes[i].SetPolygons(outputPolygons); break; // generate a 3d cone-ish shape. case ExtrudeMode.ExtrudePoint: GenerateNormals(m_LastBuiltPolygons[i]); 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); break; // generate a 3d trapezoid-ish shape. case ExtrudeMode.ExtrudeBevel: GenerateNormals(m_LastBuiltPolygons[i]); 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); break; } generatedBrushes[i].Invalidate(true); csgBounds.Encapsulate(generatedBrushes[i].GetBounds()); } // apply the generated csg bounds. localBounds = csgBounds; m_LastKnownExtents = localBounds.extents; m_LastKnownPosition = transform.localPosition; }
public override void Invalidate(bool polygonsChanged) { //////////////////////////////////////////////////////////////////// // a little hack to detect the user manually resizing the bounds. // // we use this to automatically add steps for barnaby. // // it's probably good to build a more 'official' way to detect // // user scaling events in compound brushes sometime. // if (m_LastKnownExtents != localBounds.extents) // { // // undo any position movement. // transform.localPosition = m_LastKnownPosition; // } // //////////////////////////////////////////////////////////////////// Bounds csgBounds = new Bounds(); // force nocsg when creating a flat polygon sheet as sabrecsg doesn't support it. if (extrudeMode == ExtrudeMode.CreatePolygon) { this.IsNoCSG = true; } // force nocsg when revolving with a sloped spiral as there are non-planar polygons. if (extrudeMode == ExtrudeMode.RevolveShape && project.revolveSpiralSloped && project.globalPivot.position.y != 0) { this.IsNoCSG = true; } // nothing to do except copy csg information to our child brushes. if (!isDirty) { for (int i = 0; i < BrushCount; i++) { generatedBrushes[i].Mode = this.Mode; generatedBrushes[i].IsNoCSG = this.IsNoCSG; generatedBrushes[i].IsVisible = this.IsVisible; generatedBrushes[i].HasCollision = this.HasCollision; generatedBrushes[i].Invalidate(true); csgBounds.Encapsulate(generatedBrushes[i].GetBounds()); } // apply the generated csg bounds. localBounds = csgBounds; m_LastKnownExtents = localBounds.extents; m_LastKnownPosition = transform.localPosition; return; } base.Invalidate(polygonsChanged); isDirty = false; // build the polygons from the project. if (m_LastBuiltPolygons == null) { m_LastBuiltPolygons = BuildConvexPolygons(); } // iterate through the brushes we received: int brushCount = BrushCount; 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; // local variables. Quaternion rot; Polygon[] outputPolygons; switch (extrudeMode) { // generate a flat 2d polygon. case ExtrudeMode.CreatePolygon: GenerateNormals(m_LastBuiltPolygons[i]); GenerateUvCoordinates(m_LastBuiltPolygons[i], false); Polygon poly1 = m_LastBuiltPolygons[i].DeepCopy(); poly1.Flip(); generatedBrushes[i].SetPolygons(new Polygon[] { poly1 }); break; // generate 3d cube-ish shapes that revolve around the pivot and spirals up or down. case ExtrudeMode.RevolveShape: float spiralHeight = ((((project.globalPivot.position.y * project.extrudeScale.y) / 8.0f) * (i / m_LastBuiltPolygons.Count)) / project.revolve360) * (project.revolve360 / project.revolveSteps); float spiralStep = ((((project.globalPivot.position.y * project.extrudeScale.y) / 8.0f)) / project.revolve360) * (project.revolve360 / project.revolveSteps); int labpIndex = i % m_LastBuiltPolygons.Count; Polygon poly2 = m_LastBuiltPolygons[labpIndex].DeepCopy(); poly2.Flip(); GenerateUvCoordinates(poly2, false); foreach (Vertex v in poly2.Vertices) { float step = 360.0f / project.revolve360; v.Position = new Vector3(0, -spiralHeight, 0) + RotatePointAroundPivot(v.Position, new Vector3(((project.revolveDistance / 8.0f) * project.extrudeScale.x) + ((project.revolveRadius * project.extrudeScale.x) / 8.0f), 0.0f, 0.0f), new Vector3(0.0f, ((i / m_LastBuiltPolygons.Count) * step), 0.0f)); } GenerateNormals(poly2); Polygon nextPoly = m_LastBuiltPolygons[labpIndex].DeepCopy(); nextPoly.Flip(); foreach (Vertex v in nextPoly.Vertices) { float step = 360.0f / project.revolve360; v.Position = new Vector3(0, -spiralHeight - (project.revolveSpiralSloped ? spiralStep : 0), 0) + RotatePointAroundPivot(v.Position, new Vector3(((project.revolveDistance / 8.0f) * project.extrudeScale.x) + ((project.revolveRadius * project.extrudeScale.x) / 8.0f), 0.0f, 0.0f), new Vector3(0.0f, (((i / m_LastBuiltPolygons.Count) * step) + step), 0.0f)); } List <Polygon> polygons = new List <Polygon>() { poly2 }; List <Vertex> backPolyVertices = new List <Vertex>(); Edge[] myEdges = poly2.GetEdges(); Edge[] nextEdges = nextPoly.GetEdges(); for (int j = 0; j < myEdges.Length; j++) { Edge myEdge = myEdges[j]; Edge nextEdge = nextEdges[j]; Polygon newPoly = new Polygon(new Vertex[] { new Vertex(myEdge.Vertex1.Position, Vector3.zero, Vector2.zero), new Vertex(nextEdge.Vertex1.Position, Vector3.zero, Vector2.zero), new Vertex(nextEdge.Vertex2.Position, Vector3.zero, Vector2.zero), new Vertex(myEdge.Vertex2.Position, Vector3.zero, Vector2.zero), }, null, false, false); backPolyVertices.Add(nextEdge.Vertex1); GenerateNormals(newPoly); if (newPoly.Plane.normal == Vector3.zero) { continue; // discard single line, can happen in the center of the shape. } GenerateUvCoordinates(newPoly, false); polygons.Add(newPoly); } Polygon backPoly = new Polygon(backPolyVertices.ToArray(), null, false, false); backPoly.Flip(); GenerateNormals(backPoly); GenerateUvCoordinates(backPoly, false); polygons.Add(backPoly); generatedBrushes[i].SetPolygons(polygons.ToArray()); break; // generate a 3d cube-ish shape. case ExtrudeMode.ExtrudeShape: GenerateNormals(m_LastBuiltPolygons[i]); SurfaceUtility.ExtrudePolygon(m_LastBuiltPolygons[i], project.extrudeDepth, out outputPolygons, out rot); foreach (Polygon poly in outputPolygons) { GenerateUvCoordinates(poly, false); } generatedBrushes[i].SetPolygons(outputPolygons); break; // generate a 3d cone-ish shape. case ExtrudeMode.ExtrudePoint: GenerateNormals(m_LastBuiltPolygons[i]); 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); break; // generate a 3d trapezoid-ish shape. case ExtrudeMode.ExtrudeBevel: GenerateNormals(m_LastBuiltPolygons[i]); 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); break; } // we invalidate every brush after hidden surface removal. } // we exclude hidden faces automatically. // this step will automatically optimize NoCSG output the same way additive brushes would have. // 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++) { for (int j = 0; j < brushCount; j++) { // 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()) { foreach (Polygon pb in generatedBrushes[j].GetPolygons()) { // 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) { pa.UserExcludeFromFinal = true; pb.UserExcludeFromFinal = true; } } } } // invalidate every brush. generatedBrushes[i].Invalidate(true); csgBounds.Encapsulate(generatedBrushes[i].GetBounds()); } // apply the generated csg bounds. localBounds = csgBounds; m_LastKnownExtents = localBounds.extents; m_LastKnownPosition = transform.localPosition; // update the generated name in the hierarchy. UpdateGeneratedHierarchyName(); }