コード例 #1
0
        public override void PerformWeld()
        {
            // New position for the vertices is their center
            Vector3 newPosition = Vector3.zero;

            for (int i = 0; i < sourceVertices.Count; i++)
            {
                newPosition += sourceVertices[i].Position;
            }
            newPosition /= sourceVertices.Count;

            // Update all the selected vertices UVs
            for (int i = 0; i < sourceVertices.Count; i++)
            {
                Polygon polygon = vertexPolygonMappings[sourceVertices[i]];
                sourceVertices[i].UV = GeometryHelper.GetUVForPosition(polygon, newPosition);
            }

            // Update all the selected vertices to their new position
            for (int i = 0; i < sourceVertices.Count; i++)
            {
                sourceVertices[i].Position = newPosition;
            }
        }
コード例 #2
0
        internal static bool SplitPolygonsByPlane(List <Polygon> polygons,       // Source polygons that will be split
                                                  Plane splitPlane,
                                                  bool excludeNewPolygons,       // Whether new polygons should be marked as excludeFromBuild
                                                  out List <Polygon> polygonsFront,
                                                  out List <Polygon> polygonsBack)
        {
            polygonsFront = new List <Polygon>();
            polygonsBack  = new List <Polygon>();

            // First of all make sure splitting actually needs to occur (we'll get bad issues if
            // we try splitting geometry when we don't need to)
            if (!GeometryHelper.PolygonsIntersectPlane(polygons, splitPlane))
            {
                return(false);
            }

            // These are the vertices that will be used in the new caps
            List <Vertex> newVertices = new List <Vertex>();

            for (int polygonIndex = 0; polygonIndex < polygons.Count; polygonIndex++)
            {
                Polygon.PolygonPlaneRelation planeRelation = Polygon.TestPolygonAgainstPlane(polygons[polygonIndex], splitPlane);

                // Polygon has been found to span both sides of the plane, attempt to split into two pieces
                if (planeRelation == Polygon.PolygonPlaneRelation.Spanning)
                {
                    Polygon frontPolygon;
                    Polygon backPolygon;
                    Vertex  newVertex1;
                    Vertex  newVertex2;

                    // Attempt to split the polygon
                    if (Polygon.SplitPolygon(polygons[polygonIndex], out frontPolygon, out backPolygon, out newVertex1, out newVertex2, splitPlane))
                    {
                        // If the split algorithm was successful (produced two valid polygons) then add each polygon to
                        // their respective points and track the intersection points
                        polygonsFront.Add(frontPolygon);
                        polygonsBack.Add(backPolygon);

                        newVertices.Add(newVertex1);
                        newVertices.Add(newVertex2);
                    }
                    else
                    {
                        // Two valid polygons weren't generated, so use the valid one
                        if (frontPolygon != null)
                        {
                            planeRelation = Polygon.PolygonPlaneRelation.InFront;
                        }
                        else if (backPolygon != null)
                        {
                            planeRelation = Polygon.PolygonPlaneRelation.Behind;
                        }
                        else
                        {
                            planeRelation = Polygon.PolygonPlaneRelation.InFront;

                            Debug.LogError("Polygon splitting has resulted in two zero area polygons. This is unhandled.");
                            //							Polygon.PolygonPlaneRelation secondplaneRelation = Polygon.TestPolygonAgainstPlane(polygons[polygonIndex], splitPlane);
                        }
                    }
                }

                // If the polygon is on one side of the plane or the other
                if (planeRelation != Polygon.PolygonPlaneRelation.Spanning)
                {
                    // Make sure any points that are coplanar on non-straddling polygons are still used in polygon
                    // construction
                    for (int vertexIndex = 0; vertexIndex < polygons[polygonIndex].Vertices.Length; vertexIndex++)
                    {
                        if (Polygon.ComparePointToPlane(polygons[polygonIndex].Vertices[vertexIndex].Position, splitPlane) == Polygon.PointPlaneRelation.On)
//						if(Polygon.ComparePointToPlane2(polygons[polygonIndex].Vertices[vertexIndex].Position, splitPlane) == Polygon.PointPlaneRelation.On)
                        {
                            newVertices.Add(polygons[polygonIndex].Vertices[vertexIndex]);
                        }
                    }

                    if (planeRelation == Polygon.PolygonPlaneRelation.Behind)
                    {
                        polygonsBack.Add(polygons[polygonIndex]);
                    }
                    else
                    {
                        polygonsFront.Add(polygons[polygonIndex]);
                    }
                }
            }

            // If any splits occured or coplanar vertices are found. (For example if you're splitting a sphere at the
            // equator then no polygons will be split but there will be a bunch of coplanar vertices!)
            if (newVertices.Count > 0 &&
                polygonsBack.Count >= 3 &&
                polygonsFront.Count >= 3)
            {
                // HACK: This code is awful, because we end up with lots of duplicate vertices
                List <Vector3> positions = new List <Vector3>(newVertices.Count);
                for (int i = 0; i < newVertices.Count; i++)
                {
                    positions.Add(newVertices[i].Position);
                }

                Polygon newPolygon = PolygonFactory.ConstructPolygon(positions, true);

                // Assuming it was possible to create a polygon
                if (newPolygon != null)
                {
                    if (!MathHelper.PlaneEqualsLooser(newPolygon.Plane, splitPlane))
                    {
                        // Polygons are sometimes constructed facing the wrong way, possibly due to a winding order
                        // mismatch. If the two normals are opposite, flip the new polygon
                        if (Vector3.Dot(newPolygon.Plane.normal, splitPlane.normal) < -0.9f)
                        {
                            newPolygon.Flip();
                        }
                    }

                    newPolygon.ExcludeFromFinal = excludeNewPolygons;

                    polygonsFront.Add(newPolygon);

                    newPolygon = newPolygon.DeepCopy();
                    newPolygon.Flip();

                    newPolygon.ExcludeFromFinal = excludeNewPolygons;


                    if (newPolygon.Plane.normal == Vector3.zero)
                    {
                        Debug.LogError("Invalid Normal! Shouldn't be zero. This is unexpected since extraneous positions should have been removed!");
                        //						Polygon fooNewPolygon = PolygonFactory.ConstructPolygon(positions, true);
                    }

                    polygonsBack.Add(newPolygon);
                }
                return(true);
            }
            else
            {
                // It wasn't possible to create the polygon, for example the constructed polygon was too small
                // This could happen if you attempt to clip the tip off a long but thin brush, the plane-polyhedron test
                // would say they intersect but in reality the resulting polygon would be near zero area
                return(false);
            }
        }
コード例 #3
0
        public void DrawBrushTypeField()
        {
            GUILayout.BeginHorizontal();
            PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => item.BrushType).ToArray();

            if (overridenBrushType.HasValue)
            {
                selectedTypes = new PrimitiveBrushType[] { overridenBrushType.Value };
            }

            PrimitiveBrushType?newType = SabreGUILayout.EnumPopupMixed("Brush Type", selectedTypes);

            if (newType.HasValue)
            {
                overridenBrushType = newType;

                if (newType.Value == PrimitiveBrushType.Prism)
                {
                    GUILayout.Label("Sides", SabreGUILayout.GetForeStyle(), GUILayout.Width(30));
                    EditorGUILayout.PropertyField(prismSideCountProp, new GUIContent(""));
                    GUILayout.EndHorizontal();

                    GUILayout.BeginHorizontal();
                }
                else if (newType.Value == PrimitiveBrushType.Cylinder)
                {
                    GUILayout.Label("Sides", SabreGUILayout.GetForeStyle(), GUILayout.Width(30));
                    EditorGUILayout.PropertyField(cylinderSideCountProp, new GUIContent(""));
                    GUILayout.EndHorizontal();

                    GUILayout.BeginHorizontal();
                }

                else if (newType.Value == PrimitiveBrushType.Sphere)
                {
                    GUILayout.Label("Sides", SabreGUILayout.GetForeStyle(), GUILayout.Width(30));
                    EditorGUILayout.PropertyField(sphereSideCountProp, new GUIContent(""));
                    GUILayout.EndHorizontal();

                    GUILayout.BeginHorizontal();
                }
            }

            if (GUILayout.Button("Reset Polygons"))
            {
                Undo.RecordObjects(targets, "Reset Polygons");
                foreach (var thisBrush in targets)
                {
                    if (overridenBrushType.HasValue)
                    {
                        ((PrimitiveBrush)thisBrush).BrushType = overridenBrushType.Value;
                    }
                    ((PrimitiveBrush)thisBrush).ResetPolygons();
                    ((PrimitiveBrush)thisBrush).Invalidate(true);
                }

                overridenBrushType = null;
            }
            GUILayout.EndHorizontal();

            if (GUILayout.Button("Shell"))
            {
                List <GameObject> newSelection = new List <GameObject>();
                foreach (var thisBrush in targets)
                {
                    GameObject newObject = ((PrimitiveBrush)thisBrush).Duplicate();
                    Polygon[]  polygons  = newObject.GetComponent <PrimitiveBrush>().GetPolygons();
                    VertexUtility.DisplacePolygons(polygons, -CurrentSettings.PositionSnapDistance);
                    Bounds newBounds = newObject.GetComponent <PrimitiveBrush>().GetBounds();
                    // Verify the new geometry
                    if (GeometryHelper.IsBrushConvex(polygons) &&
                        newBounds.GetSmallestExtent() > 0)
                    {
                        Undo.RegisterCreatedObjectUndo(newObject, "Shell");
                        newSelection.Add(newObject);
                    }
                    else
                    {
                        // Produced a concave brush, delete it and pretend nothing happened
                        GameObject.DestroyImmediate(newObject);
                        Debug.LogWarning("Could not shell " + thisBrush.name + " as shelled geometry would not be valid. Try lowering Pos Snapping and attempt Shell again.");
                    }
                }

                if (newSelection.Count > 0)
                {
                    Selection.objects = newSelection.ToArray();
                }
            }
        }
コード例 #4
0
        public void SnapSelectedVertices(bool isAbsoluteGrid)
        {
            // So we know which polygons need to have their normals recalculated
            List <Polygon> affectedPolygons = new List <Polygon>();

            foreach (PrimitiveBrush brush in targetBrushes)
            {
                Polygon[] polygons = brush.GetPolygons();

                for (int i = 0; i < polygons.Length; i++)
                {
                    Polygon polygon = polygons[i];

                    int vertexCount = polygon.Vertices.Length;

                    Vector3[] newPositions = new Vector3[vertexCount];
                    Vector2[] newUV        = new Vector2[vertexCount];

                    for (int j = 0; j < vertexCount; j++)
                    {
                        newPositions[j] = polygon.Vertices[j].Position;
                        newUV[j]        = polygon.Vertices[j].UV;
                    }

                    bool polygonAffected = false;
                    for (int j = 0; j < vertexCount; j++)
                    {
                        Vertex vertex = polygon.Vertices[j];
                        if (selectedVertices.ContainsKey(vertex))
                        {
                            Vector3 newPosition = vertex.Position;

                            float snapDistance = CurrentSettings.PositionSnapDistance;
                            if (isAbsoluteGrid)
                            {
                                newPosition = brush.transform.TransformPoint(newPosition);
                            }
                            newPosition = MathHelper.RoundVector3(newPosition, snapDistance);
                            if (isAbsoluteGrid)
                            {
                                newPosition = brush.transform.InverseTransformPoint(newPosition);
                            }

                            newPositions[j] = newPosition;

                            newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition);

                            polygonAffected = true;
                        }
                    }

                    if (polygonAffected)
                    {
                        affectedPolygons.Add(polygon);
                    }

                    // Apply all the changes to the polygon
                    for (int j = 0; j < vertexCount; j++)
                    {
                        Vertex vertex = polygon.Vertices[j];
                        vertex.Position = newPositions[j];
                        vertex.UV       = newUV[j];
                    }

                    polygon.CalculatePlane();
                }
            }

            if (affectedPolygons.Count > 0)
            {
                for (int i = 0; i < affectedPolygons.Count; i++)
                {
                    affectedPolygons[i].ResetVertexNormals();
                }

                foreach (PrimitiveBrush brush in targetBrushes)
                {
                    brush.Invalidate(true);

                    brush.BreakTypeRelation();
                }
            }
        }
コード例 #5
0
        public override void OnInspectorGUI()
        {
//            DrawDefaultInspector();

            DrawBrushTypeField();

//			BrushOrder brushOrder = BrushTarget.GetBrushOrder();
//			string positionString = string.Join(",", brushOrder.Position.Select(item => item.ToString()).ToArray());
//            GUILayout.Label(positionString, EditorStyles.boldLabel);

//			List<BrushCache> intersections = BrushTarget.BrushCache.IntersectingVisualBrushCaches;
//
//			for (int i = 0; i < intersections.Count; i++)
//			{
//				GUILayout.Label(intersections[i].Mode.ToString(), EditorStyles.boldLabel);
//			}

            GUILayout.BeginHorizontal();

            GUI.SetNextControlName("rescaleTextbox");

            rescaleString = EditorGUILayout.TextField(rescaleString);

            bool keyboardEnter = Event.current.isKey &&
                                 Event.current.keyCode == KeyCode.Return &&
                                 Event.current.type == EventType.KeyUp &&
                                 GUI.GetNameOfFocusedControl() == "rescaleTextbox";

            if (GUILayout.Button("Rescale") || keyboardEnter)
            {
                // Try to parse a Vector3 scale from the input string
                Vector3 rescaleVector3;
                if (StringHelper.TryParseScale(rescaleString, out rescaleVector3))
                {
                    // None of the scale components can be zero
                    if (rescaleVector3.x != 0 && rescaleVector3.y != 0 && rescaleVector3.z != 0)
                    {
                        // Rescale all the brushes
                        Undo.RecordObjects(targets, "Rescale Polygons");
                        foreach (var thisBrush in targets)
                        {
                            BrushUtility.Rescale((PrimitiveBrush)thisBrush, rescaleVector3);
                        }
                    }
                }
            }

            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();

            GUI.SetNextControlName("resizeTextbox");

            resizeString = EditorGUILayout.TextField(resizeString);

            keyboardEnter = Event.current.isKey &&
                            Event.current.keyCode == KeyCode.Return &&
                            Event.current.type == EventType.KeyUp &&
                            GUI.GetNameOfFocusedControl() == "resizeTextbox";

            if (GUILayout.Button("Resize") || keyboardEnter)
            {
                // Try to parse a Vector3 scale from the input string
                Vector3 resizeVector3;
                if (StringHelper.TryParseScale(resizeString, out resizeVector3))
                {
                    // None of the size components can be zero
                    if (resizeVector3.x != 0 && resizeVector3.y != 0 && resizeVector3.z != 0)
                    {
                        // Rescale all the brushes so that the local bounds is the same size as the resize vector
                        Undo.RecordObjects(targets, "Resize Polygons");
                        PrimitiveBrush[] brushes = BrushTargets;
                        foreach (PrimitiveBrush brush in brushes)
                        {
                            BrushUtility.Resize(brush, resizeVector3);
                        }
                    }
                }
            }

            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            sourceMesh = EditorGUILayout.ObjectField(sourceMesh, typeof(Mesh), false) as Mesh;

            if (GUILayout.Button("Import"))
            {
                if (sourceMesh != null)
                {
                    Undo.RecordObjects(targets, "Import Polygons From Mesh");

                    Polygon[] polygons = BrushFactory.GeneratePolygonsFromMesh(sourceMesh).ToArray();
                    bool      convex   = GeometryHelper.IsBrushConvex(polygons);
                    if (!convex)
                    {
                        Debug.LogError("Concavities detected in imported mesh. This may result in issues during CSG, please change the source geometry so that it is convex");
                    }
                    foreach (var thisBrush in targets)
                    {
                        ((PrimitiveBrush)thisBrush).SetPolygons(polygons, true);
                    }
                }
            }

            GUILayout.EndHorizontal();

            List <PrimitiveBrush> orderedTargets = BrushTargets.ToList();

            orderedTargets.Sort((x, y) => x.transform.GetSiblingIndex().CompareTo(y.transform.GetSiblingIndex()));

            if (GUILayout.Button("Set As First"))
            {
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    // REVERSED
                    PrimitiveBrush thisBrush = orderedTargets[orderedTargets.Count - 1 - i];

                    Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                    thisBrush.transform.SetAsFirstSibling();
                }

                // Force all the brushes to recalculate their intersections and get ready for rebuilding
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    orderedTargets[i].RecalculateIntersections();
                    orderedTargets[i].BrushCache.SetUnbuilt();
                }
            }

            if (GUILayout.Button("Send Earlier"))
            {
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    PrimitiveBrush thisBrush = orderedTargets[i];

                    int siblingIndex = thisBrush.transform.GetSiblingIndex();
                    if (siblingIndex > 0)
                    {
                        Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                        siblingIndex--;
                        thisBrush.transform.SetSiblingIndex(siblingIndex);
                    }
                }

                // Force all the brushes to recalculate their intersections and get ready for rebuilding
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    orderedTargets[i].RecalculateIntersections();
                    orderedTargets[i].BrushCache.SetUnbuilt();
                }
            }

            if (GUILayout.Button("Send Later"))
            {
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    // REVERSED
                    PrimitiveBrush thisBrush = orderedTargets[orderedTargets.Count - 1 - i];

                    int siblingIndex = thisBrush.transform.GetSiblingIndex();
                    Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                    siblingIndex++;
                    thisBrush.transform.SetSiblingIndex(siblingIndex);
                }

                // Force all the brushes to recalculate their intersections and get ready for rebuilding
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    orderedTargets[i].RecalculateIntersections();
                    orderedTargets[i].BrushCache.SetUnbuilt();
                }
            }

            if (GUILayout.Button("Set As Last"))
            {
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    PrimitiveBrush thisBrush = orderedTargets[i];

                    Undo.SetTransformParent(thisBrush.transform, thisBrush.transform.parent, "Change Order");
                    thisBrush.transform.SetAsLastSibling();
                }

                // Force all the brushes to recalculate their intersections and get ready for rebuilding
                for (int i = 0; i < orderedTargets.Count; i++)
                {
                    orderedTargets[i].RecalculateIntersections();
                    orderedTargets[i].BrushCache.SetUnbuilt();
                }
            }

            serializedObject.ApplyModifiedProperties();



//            GUILayout.Label("UVs", EditorStyles.boldLabel);
//
//            if (GUILayout.Button("Flip XY"))
//            {
//                UVUtility.FlipUVsXY(thisBrush.Polygons);
//            }
//
//            GUILayout.BeginHorizontal();
//            if (GUILayout.Button("Flip X"))
//            {
//                UVUtility.FlipUVsX(thisBrush.Polygons);
//            }
//            if (GUILayout.Button("Flip Y"))
//            {
//                UVUtility.FlipUVsY(thisBrush.Polygons);
//            }
//            GUILayout.EndHorizontal();
//
//            GUILayout.BeginHorizontal();
//            if (GUILayout.Button("UVs x 2"))
//            {
//                UVUtility.ScaleUVs(thisBrush.Polygons, 2f);
//            }
//            if (GUILayout.Button("UVs / 2"))
//            {
//                UVUtility.ScaleUVs(thisBrush.Polygons, .5f);
//            }
//            GUILayout.EndHorizontal();
            // Ensure Edit Mode is on
//            csgModel.EditMode = true;
        }
コード例 #6
0
ファイル: CurvedStairBrush.cs プロジェクト: Ciziu/trancepcja
        public override void Invalidate(bool polygonsChanged)
        {
            base.Invalidate(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;    //
                // user is trying to scale up.                                //
                if (localBounds.extents.y > m_LastKnownExtents.y) //
                {                                                 //
                    numSteps          += 1;                       //
                    m_LastKnownExtents = localBounds.extents;     //
                    Invalidate(true);                             // recusion! <3                         //
                    return;                                       //
                }                                                 //
                // user is trying to scale down.                              //
                if (localBounds.extents.y < m_LastKnownExtents.y) //
                {                                                 //
                    numSteps -= 1;                                //
                    if (numSteps < 1)
                    {
                        numSteps = 1;                         //
                    }
                    m_LastKnownExtents = localBounds.extents; //
                    Invalidate(true);                         // recusion! <3                         //
                    return;                                   //
                }                                             //
            }                                                 //
            ////////////////////////////////////////////////////////////////////

            // local variables
            List <Vector3> vertexPositions = new List <Vector3>();
            Plane          plane;
            Vector3        rotateStep = new Vector3();
            Vector3        vertex = new Vector3(), newVertex = new Vector3();
            float          adjustment;
            int            innerStart, outerStart, bottomInnerStart, bottomOuterStart;

            // begin
            rotateStep.z = angleOfCurve / numSteps;

            if (counterClockwise)
            {
                rotateStep.z *= -1;
            }

            // generate the inner curve points.
            innerStart = vertexPositions.Count;
            vertex.x   = innerRadius;
            for (int x = 0; x < (numSteps + 1); x++)
            {
                if (x == 0)
                {
                    adjustment = addToFirstStep;
                }
                else
                {
                    adjustment = 0;
                }

                newVertex = Quaternion.Euler(rotateStep * x) * vertex;
                vertexPositions.Add(new Vector3(newVertex.x, vertex.z - adjustment, newVertex.y));
                vertex.z += stepHeight;
                vertexPositions.Add(new Vector3(newVertex.x, vertex.z, newVertex.y));
            }

            // generate the outer curve points.
            outerStart = vertexPositions.Count;
            vertex.x   = innerRadius + stepWidth;
            vertex.z   = 0;
            for (int x = 0; x < (numSteps + 1); x++)
            {
                if (x == 0)
                {
                    adjustment = addToFirstStep;
                }
                else
                {
                    adjustment = 0;
                }

                newVertex = Quaternion.Euler(rotateStep * x) * vertex;
                vertexPositions.Add(new Vector3(newVertex.x, vertex.z - adjustment, newVertex.y));
                vertex.z += stepHeight;
                vertexPositions.Add(new Vector3(newVertex.x, vertex.z, newVertex.y));
            }

            // generate the bottom inner curve points.
            bottomInnerStart = vertexPositions.Count;
            vertex.x         = innerRadius;
            vertex.z         = 0;
            for (int x = 0; x < (numSteps + 1); x++)
            {
                newVertex = Quaternion.Euler(rotateStep * x) * vertex;
                vertexPositions.Add(new Vector3(newVertex.x, vertex.z - addToFirstStep, newVertex.y));
            }

            // generate the bottom outer curve points.
            bottomOuterStart = vertexPositions.Count;
            vertex.x         = innerRadius + stepWidth;
            for (int x = 0; x < (numSteps + 1); x++)
            {
                newVertex = Quaternion.Euler(rotateStep * x) * vertex;
                vertexPositions.Add(new Vector3(newVertex.x, vertex.z - addToFirstStep, newVertex.y));
            }

            // vertex indices to easily flip faces for the counter clockwise mode.
            int index0 = 0;
            int index1 = 1;
            int index2 = 2;
            int index3 = 3;

            // flip faces if counter clockwise mode is enabled.
            if (counterClockwise)
            {
                index0 = 2;
                index1 = 1;
                index2 = 0;
                index3 = 3;
            }

            // we calculate the bounds of the output csg.
            Bounds csgBounds = new Bounds();

            // 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;

                // retrieve the polygons from the current cube brush.
                Polygon[] polygons = generatedBrushes[i].GetPolygons();

                // +-----------------------------------------------------+
                // | Cube Polygons                                       |
                // +--------+--------+--------+--------+--------+--------+
                // | Poly:0 | Poly:1 | Poly:2 | Poly:3 | Poly:4 | Poly:5 |
                // +-----------------------------------------------------+
                // | 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];

                // 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]);



                // retrieve the vertices of the front polygon.
                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];

                // 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;



                // retrieve the vertices of the left polygon.
                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];

                // 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;



                // retrieve the vertices of the right polygon.
                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];

                // 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;



                // retrieve the vertices of the bottom polygon.
                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];

                // 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]);



                // retrieve the vertices of the back polygon.
                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];

                // 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;

                generatedBrushes[i].Invalidate(true);
                csgBounds.Encapsulate(generatedBrushes[i].GetBounds());
            }

            // apply the generated csg bounds.
            localBounds         = csgBounds;
            m_LastKnownExtents  = localBounds.extents;
            m_LastKnownPosition = transform.localPosition;
        }
コード例 #7
0
        public void TranslateSelectedVertices(Vector3 worldDelta)
        {
            // So we know which polygons need to have their normals recalculated
            List <Polygon> affectedPolygons = new List <Polygon>();

            foreach (PrimitiveBrush brush in targetBrushes)
            {
                Polygon[] polygons   = brush.GetPolygons();
                Vector3   localDelta = brush.transform.InverseTransformDirection(worldDelta);

                for (int i = 0; i < polygons.Length; i++)
                {
                    Polygon polygon = polygons[i];

                    int vertexCount = polygon.Vertices.Length;

                    Vector3[] newPositions = new Vector3[vertexCount];
                    Vector2[] newUV        = new Vector2[vertexCount];

                    for (int j = 0; j < vertexCount; j++)
                    {
                        newPositions[j] = polygon.Vertices[j].Position;
                        newUV[j]        = polygon.Vertices[j].UV;
                    }

                    bool polygonAffected = false;

                    for (int j = 0; j < vertexCount; j++)
                    {
                        Vertex vertex = polygon.Vertices[j];
                        if (selectedVertices.ContainsKey(vertex))
                        {
                            Vector3 startPosition = startPositions[vertex];
                            Vector3 newPosition   = vertex.Position + localDelta;

                            Vector3 accumulatedDelta = newPosition - startPosition;

                            if (CurrentSettings.PositionSnappingEnabled)
                            {
                                float snapDistance = CurrentSettings.PositionSnapDistance;
                                //							newPosition = targetBrush.transform.TransformPoint(newPosition);
                                accumulatedDelta = MathHelper.RoundVector3(accumulatedDelta, snapDistance);
                                //							newPosition = targetBrush.transform.InverseTransformPoint(newPosition);
                            }

                            if (accumulatedDelta != Vector3.zero)
                            {
                                newPosition = startPosition + accumulatedDelta;

                                newPositions[j] = newPosition;

                                newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition);

                                polygonAffected = true;
                            }
                        }
                    }

                    if (polygonAffected)
                    {
                        affectedPolygons.Add(polygon);
                    }

                    // Apply all the changes to the polygon
                    for (int j = 0; j < vertexCount; j++)
                    {
                        Vertex vertex = polygon.Vertices[j];
                        vertex.Position = newPositions[j];
                        vertex.UV       = newUV[j];
                    }

                    polygon.CalculatePlane();
                }
            }

            if (affectedPolygons.Count > 0)
            {
                for (int i = 0; i < affectedPolygons.Count; i++)
                {
                    affectedPolygons[i].ResetVertexNormals();
                }

                foreach (PrimitiveBrush brush in targetBrushes)
                {
                    brush.Invalidate(true);

                    brush.BreakTypeRelation();
                }
            }
        }
コード例 #8
0
        /// <summary>
        /// Translates the specified vertices by a position delta (local to the brush) and updates the UVs
        /// </summary>
        /// <param name="brush">Brush from which the vertices belong.</param>
        /// <param name="specifiedVertices">Specified vertices to be translated.</param>
        /// <param name="localDelta">Local positional delta.</param>
        public static void TranslateSpecifiedVertices(Brush brush, List <Vertex> specifiedVertices, Vector3 localDelta)
        {
            Polygon.Vector3ComparerEpsilon positionComparer = new Polygon.Vector3ComparerEpsilon();

            // Cache the positions as the position of vertices will change while in the for loop
            List <Vector3> specifiedPositions = specifiedVertices.Select(item => item.Position).ToList();

            // So we know which polygons need to have their normals recalculated
            List <Polygon> affectedPolygons = new List <Polygon>();

            Polygon[] polygons = brush.GetPolygons();

            for (int i = 0; i < polygons.Length; i++)
            {
                Polygon polygon = polygons[i];

                int vertexCount = polygon.Vertices.Length;

                Vector3[] newPositions = new Vector3[vertexCount];
                Vector2[] newUV        = new Vector2[vertexCount];

                for (int j = 0; j < vertexCount; j++)
                {
                    newPositions[j] = polygon.Vertices[j].Position;
                    newUV[j]        = polygon.Vertices[j].UV;
                }

                bool polygonAffected = false;

                for (int j = 0; j < vertexCount; j++)
                {
                    Vertex vertex = polygon.Vertices[j];
                    if (specifiedPositions.Contains(vertex.Position, positionComparer))
                    {
                        Vector3 newPosition = vertex.Position + localDelta;

                        newPositions[j] = newPosition;

                        newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition);

                        polygonAffected = true;
                    }
                }

                if (polygonAffected)
                {
                    affectedPolygons.Add(polygon);
                }

                // Apply all the changes to the polygon
                for (int j = 0; j < vertexCount; j++)
                {
                    Vertex vertex = polygon.Vertices[j];
                    vertex.Position = newPositions[j];
                    vertex.UV       = newUV[j];
                }

                polygon.CalculatePlane();
            }

            if (affectedPolygons.Count > 0)
            {
                for (int i = 0; i < affectedPolygons.Count; i++)
                {
                    affectedPolygons[i].ResetVertexNormals();
                }
            }
        }
コード例 #9
0
        /// <summary>
        /// Tells the brush it has changed
        /// </summary>
        /// <param name="polygonsChanged">If set to <c>true</c> polygons will be recached.</param>
        public override void Invalidate(bool polygonsChanged)
        {
            if (!gameObject.activeInHierarchy)
            {
                return;
            }

            // Make sure there is a mesh filter on this object
            MeshFilter   meshFilter   = gameObject.AddOrGetComponent <MeshFilter>();
            MeshRenderer meshRenderer = gameObject.AddOrGetComponent <MeshRenderer>();

            // Used to use mesh colliders for ray collision, but not any more so clean them up
            MeshCollider[] meshColliders = GetComponents <MeshCollider>();

            if (meshColliders.Length > 0)
            {
                for (int i = 0; i < meshColliders.Length; i++)
                {
                    DestroyImmediate(meshColliders[i]);
                }
            }

            bool requireRegen = false;

            // If the cached ID hasn't been set or we mismatch
            if (cachedInstanceID == 0 ||
                gameObject.GetInstanceID() != cachedInstanceID)
            {
                requireRegen     = true;
                cachedInstanceID = gameObject.GetInstanceID();
            }


            Mesh renderMesh = meshFilter.sharedMesh;

            if (requireRegen)
            {
                renderMesh = new Mesh();
            }

            if (polygons != null)
            {
                List <int> polygonIndices;
                BrushFactory.GenerateMeshFromPolygons(polygons, ref renderMesh, out polygonIndices);
            }

            if (mode == CSGMode.Subtract)
            {
                MeshHelper.Invert(ref renderMesh);
            }
            // Displace the triangles for display along the normals very slightly (this is so we can overlay built
            // geometry with semi-transparent geometry and avoid depth fighting)
            MeshHelper.Displace(ref renderMesh, 0.001f);

            meshFilter.sharedMesh = renderMesh;

            meshRenderer.receiveShadows    = false;
            meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

            meshFilter.hideFlags   = HideFlags.NotEditable;          // | HideFlags.HideInInspector;
            meshRenderer.hideFlags = HideFlags.NotEditable;          // | HideFlags.HideInInspector;

#if UNITY_EDITOR
            Material material;
            if (IsNoCSG)
            {
                material = UnityEditor.AssetDatabase.LoadMainAssetAtPath(CSGModel.GetSabreCSGPath() + "Materials/NoCSG.mat") as Material;
            }
            else
            {
                material = UnityEditor.AssetDatabase.LoadMainAssetAtPath(CSGModel.GetSabreCSGPath() + "Materials/" + this.mode.ToString() + ".mat") as Material;
            }
            meshRenderer.sharedMaterial = material;
#endif
            isBrushConvex = GeometryHelper.IsBrushConvex(polygons);

            if (polygonsChanged)
            {
                RecalculateBrushCache();
            }

            UpdateVisibility();

            objectVersionSerialized++;
            objectVersionUnserialized = objectVersionSerialized;
        }
コード例 #10
0
ファイル: BrushChunk.cs プロジェクト: mryhep/AD_Git
        internal List <Polygon> ProvideSubtractChunks(List <BrushChunk> subtractChunks)
        {
            List <Polygon> damagedPolygons = new List <Polygon>();

            List <Polygon> addedPolygons = new List <Polygon>();

            // If this chunk knows about any split planes
//			if(splitPlanes.Count > 0)
            {
                // For each of the subtract chunks
                int subtractChunkCount = subtractChunks.Count;
                for (int chunkIndex = 0; chunkIndex < subtractChunkCount; chunkIndex++)
                {
                    int polygonCount = subtractChunks[chunkIndex].Polygons.Count;
                    // For each of the polygons within those subtract chunks
                    for (int j = 0; j < polygonCount; j++)
                    {
                        Polygon polygon = subtractChunks[chunkIndex].Polygons[j];

                        // Disregard any polygons that can't be displayed as final geometry
                        if (polygon.ExcludeFromFinal)
                        {
                            continue;
                        }

                        Plane polygonPlane = polygon.CachedPlaneTest;

                        // Determine if any of the split planes this chunk has been split against match any of those
                        // subtraction polygons
                        bool anyFound = false;


                        int   polygonsCount = polygons.Count;
                        Plane splitPlane;
                        for (int i = 0; i < polygonsCount; i++)
                        {
                            if (!polygons[i].ExcludeFromFinal)
                            {
                                continue;
                            }

                            splitPlane = polygons[i].CachedPlaneTest;

                            if (MathHelper.PlaneEqualsLooserWithFlip(polygonPlane, splitPlane))
                            {
                                anyFound = true;
                                break;
                            }
                        }

                        bool added = false;
                        if (anyFound)
                        {
                            // TODO: Is GetCenterPoint expensive?
                            Vector3 target = polygon.GetCenterPoint();
                            // If this brush chunk contains the subtraction polygon
                            // TODO: This is a heftly call a lot of the time, so see about optimising
                            if (GeometryHelper.PolyhedronContainsPointEpsilon3(this.polygons, target))
                            {
                                added = true;
                                // Duplicate the subtraction polygon
                                polygon = polygon.DeepCopy();


                                // Flip it, so that it can form outer geometry
                                polygon.Flip();
//
//
                                for (int i = 0; i < this.polygons.Count; i++)
                                {
                                    if (!this.polygons[i].ExcludeFromFinal && MathHelper.PlaneEqualsLooser(this.polygons[i].Plane, polygon.Plane))
                                    {
                                        if (!addedPolygons.Contains(this.polygons[i]))
                                        {
                                            //Debug.LogWarning("Removing duplicate from chunk " + this.uniqueID + " subtraction chunk " + subtractChunks[chunkIndex].UniqueID);
                                            polygons[i].ExcludeFromFinal = true;
                                        }
                                    }
                                }

                                // Add it to this brush chunk
                                polygons.Add(polygon);

                                addedPolygons.Add(polygon);
                            }
                        }

                        if (!added)
                        {
                            damagedPolygons.Add(polygon);
                        }
                    }
                }
            }

            return(damagedPolygons);
        }
コード例 #11
0
        protected static List <Brush> CalculateIntersectingBrushes(Brush sourceBrush, List <Brush> brushes, bool isCollisionPass)
        {
            // If the brush is not CSG it can't intersect any brushes!
            if (sourceBrush.IsNoCSG || sourceBrush.Mode == CSGMode.Volume)
            {
                return(new List <Brush>());
            }
            // Return empty lists if the pass is not relevant
            if (isCollisionPass)
            {
                if (!sourceBrush.hasCollision)
                {
                    return(new List <Brush>());
                }
            }
            else
            {
                if (!sourceBrush.isVisible)
                {
                    return(new List <Brush>());
                }
            }

            BrushCache sourceCache = sourceBrush.BrushCache;

            List <Brush> intersectingBrushes = new List <Brush>();

            Bounds targetBounds = sourceCache.Bounds;

            Polygon[] targetPolygons = sourceCache.Polygons;

            // Find the index of this brush
            int thisIndex = -1;

            for (int i = 0; i < brushes.Count; i++)
            {
                if (brushes[i] != null)
                {
                    BrushCache brushCache = brushes[i].BrushCache;
                    if (brushCache == sourceCache)
                    {
                        thisIndex = i;
                        break;
                    }
                }
            }

            // Go through the brushes before this one
            for (int i = thisIndex - 1; i >= 0; i--)
            {
                if (!Brush.IsInvalidForBuild(brushes[i]))
                {
                    // Skip any brushes not suitable for the pass
                    if (brushes[i].isNoCSG || brushes[i].mode == CSGMode.Volume)
                    {
                        // NoCSG and volume brushes skip the CSG calcs
                        continue;
                    }
                    else if (isCollisionPass && !brushes[i].HasCollision)
                    {
                        continue;
                    }
                    else if (!isCollisionPass && !brushes[i].IsVisible)
                    {
                        continue;
                    }

                    BrushCache brushCache = brushes[i].BrushCache;
                    if (brushCache.Bounds.IntersectsApproximate(targetBounds))
                    {
                        if (GeometryHelper.PolyhedraIntersect(brushCache.Polygons, targetPolygons))
                        {
                            intersectingBrushes.Add(brushes[i]);

                            // If the brush is contained entirely by a previous subtraction then it's impossible
                            // to intersect with any brushes before that subtraction
                            if (brushCache.Mode == CSGMode.Subtract)
                            {
                                if (GeometryHelper.PolyhedronContainsPolyhedron(brushCache.Polygons, targetPolygons))
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            intersectingBrushes.Reverse();

            List <BrushCache> activeSubtractions = new List <BrushCache>();

            // Go through the brushes after this one
            for (int i = thisIndex + 1; i < brushes.Count; i++)
            {
                if (!Brush.IsInvalidForBuild(brushes[i]))
                {
                    // Skip any brushes not suitable for the pass
                    if (brushes[i].isNoCSG || brushes[i].mode == CSGMode.Volume)
                    {
                        // NoCSG and volume brushes skip the CSG calcs
                        continue;
                    }
                    else if (isCollisionPass && !brushes[i].HasCollision)
                    {
                        // Collision pass and this brush has no collision so skip
                        continue;
                    }
                    else if (!isCollisionPass && !brushes[i].IsVisible)
                    {
                        // Visual pass and this brush isn't visible so skip
                        continue;
                    }

                    BrushCache brushCache = brushes[i].BrushCache;
                    if (brushCache.Bounds.IntersectsApproximate(targetBounds))
                    {
                        if (GeometryHelper.PolyhedraIntersect(brushCache.Polygons, targetPolygons))
                        {
                            bool containedByPreviousSubtraction = false;

                            for (int j = 0; j < activeSubtractions.Count; j++)
                            {
                                BrushCache subtractionCache = activeSubtractions[j];

                                if (subtractionCache.Bounds.IntersectsApproximate(brushCache.Bounds))
                                {
                                    if (GeometryHelper.PolyhedronContainsPolyhedron(subtractionCache.Polygons, brushCache.Polygons))
                                    {
                                        containedByPreviousSubtraction = true;
                                        break;
                                    }
                                }
                            }

                            if (!containedByPreviousSubtraction)
                            {
                                intersectingBrushes.Add(brushes[i]);

                                if (brushCache.Mode == CSGMode.Subtract)
                                {
                                    activeSubtractions.Add(brushCache);
                                }
                            }
                        }
                    }
                }
            }

            return(intersectingBrushes);
        }
コード例 #12
0
        public override void PerformWeld()
        {
            List <List <Vertex> > groupedVertices = new List <List <Vertex> >();

            VertexComparerTolerance comparer = new VertexComparerTolerance(tolerance);

            // Group the selected vertices into clusters
            for (int sourceVertexIndex = 0; sourceVertexIndex < sourceVertices.Count; sourceVertexIndex++)
            {
                Vertex sourceVertex = sourceVertices[sourceVertexIndex];

                bool added = false;

                for (int groupIndex = 0; groupIndex < groupedVertices.Count; groupIndex++)
                {
                    if (groupedVertices[groupIndex].Contains(sourceVertex, comparer))
                    {
                        groupedVertices[groupIndex].Add(sourceVertex);
                        added = true;
                        break;
                    }
                }

                if (!added)
                {
                    groupedVertices.Add(new List <Vertex>()
                    {
                        sourceVertex
                    });
                }
            }


            for (int groupIndex = 0; groupIndex < groupedVertices.Count; groupIndex++)
            {
                List <Vertex> vertices = groupedVertices[groupIndex];

                // Ignoring any groups that only contain one vertex
                if (vertices.Count > 1)
                {
                    // New position for the vertices is their center
                    Vector3 newPosition = Vector3.zero;
                    for (int vertexIndex = 0; vertexIndex < vertices.Count; vertexIndex++)
                    {
                        newPosition += vertices[vertexIndex].Position;
                    }
                    newPosition /= vertices.Count;

                    // Update all the selected vertices UVs
                    for (int vertexIndex = 0; vertexIndex < vertices.Count; vertexIndex++)
                    {
                        Polygon polygon = vertexPolygonMappings[vertices[vertexIndex]];
                        vertices[vertexIndex].UV = GeometryHelper.GetUVForPosition(polygon, newPosition);
                    }

                    // Update all the selected vertices to their new position
                    for (int vertexIndex = 0; vertexIndex < vertices.Count; vertexIndex++)
                    {
                        vertices[vertexIndex].Position = newPosition;
                    }
                }
            }
        }
コード例 #13
0
        void OnMouseAction3D(SceneView sceneView, Event e)
        {
            if (primaryTargetBrush == null || CameraPanInProgress || e.button != 0)
            {
                return;
            }

            Vector2 mousePosition = e.mousePosition;

            mousePosition = EditorHelper.ConvertMousePointPosition(mousePosition);

            Ray   ray          = Camera.current.ScreenPointToRay(mousePosition);
            float bestDistance = float.PositiveInfinity;

            Polygon bestPolygon = null;

            float testDistance;

            foreach (Brush brush in targetBrushes)
            {
                List <Polygon> polygons    = brush.GenerateTransformedPolygons().ToList();
                Polygon        testPolygon = GeometryHelper.RaycastPolygons(polygons, ray, out testDistance, 0.1f);
                if (testPolygon != null && testDistance < bestDistance)
                {
                    bestDistance = testDistance;
                    bestPolygon  = testPolygon;
                }
            }

            if (bestPolygon != null)
            {
//				VisualDebug.ClearAll();
//				VisualDebug.AddPolygon(hitPolygon, Color.red);
                Vector3 hitPoint = ray.GetPoint(bestDistance);

                if (e.type == EventType.MouseDown || e.type == EventType.MouseMove)
                {
                    if (e.type == EventType.MouseMove && planeEstablished)
                    {
                        return;
                    }

                    points[0]    = hitPoint;
                    displayPoint = true;

                    if (CurrentSettings.PositionSnappingEnabled)
                    {
                        float snapDistance = CurrentSettings.PositionSnapDistance;
                        points[0] = MathHelper.RoundVector3(points[0], snapDistance);
                    }

                    points[1] = points[0];
                    points[2] = points[0];

                    isFlipped = false;
                }
                else
                {
                    points[1] = hitPoint;

                    if (CurrentSettings.PositionSnappingEnabled)
                    {
                        float snapDistance = CurrentSettings.PositionSnapDistance;
                        points[1] = MathHelper.RoundVector3(points[1], snapDistance);
                    }
                    points[2] = points[0] - bestPolygon.Plane.normal;

                    if (e.type == EventType.MouseUp)
                    {
                        planeEstablished = true;

                        if (points[1] == points[0])
                        {
                            ResetTool();
                        }
                    }
                }
                SceneView.RepaintAll();
            }
        }
コード例 #14
0
        private void CreateBrush(List <Vector3> positions)
        {
            Polygon sourcePolygon = PolygonFactory.ConstructPolygon(positions, true);

            // Early out if it wasn't possible to create the polygon
            if (sourcePolygon == null)
            {
                return;
            }

            if (activePolygon != null)
            {
                for (int i = 0; i < sourcePolygon.Vertices.Length; i++)
                {
                    Vector2 newUV = GeometryHelper.GetUVForPosition(activePolygon, sourcePolygon.Vertices[i].Position);
                    sourcePolygon.Vertices[i].UV = newUV;
                }
            }

            Vector3 planeNormal = GetActivePlane().normal;

            //			Debug.Log(Vector3.Dot(sourcePolygon.Plane.normal, planeNormal));

            // Flip the polygon if the winding order is wrong
            if (Vector3.Dot(sourcePolygon.Plane.normal, planeNormal) < 0)
            {
                sourcePolygon.Flip();

                // Need to flip the UVs across the U (X) direction
                for (int i = 0; i < sourcePolygon.Vertices.Length; i++)
                {
                    Vector2 uv = sourcePolygon.Vertices[i].UV;
                    uv.x = 1 - uv.x;
                    sourcePolygon.Vertices[i].UV = uv;
                }
            }

            float   extrusionDistance = 1;
            Vector3 positionOffset    = Vector3.zero;

            if (selectingHeight)
            {
                extrusionDistance = prismHeight;
            }
            else
            {
                if (activePolygon != null && activeBrush != null)
                {
                    extrusionDistance = activeBrush.CalculateExtentsInAxis(planeNormal);
                }
                else
                {
                    Brush lastSelectedBrush = csgModel.LastSelectedBrush;
                    if (lastSelectedBrush != null)
                    {
                        Bounds lastSelectedBrushBounds = lastSelectedBrush.GetBoundsTransformed();

                        for (int i = 0; i < 3; i++)
                        {
                            if (!planeNormal[i].EqualsWithEpsilon(0))
                            {
                                if (lastSelectedBrushBounds.size[i] != 0)
                                {
                                    extrusionDistance = lastSelectedBrushBounds.size[i];

                                    if (planeNormal[i] > 0)
                                    {
                                        positionOffset[i] = lastSelectedBrushBounds.center[i] - lastSelectedBrushBounds.extents[i];
                                    }
                                    else
                                    {
                                        positionOffset[i] = lastSelectedBrushBounds.center[i] + lastSelectedBrushBounds.extents[i];
                                    }
                                }
                            }
                        }
                    }
                }

                // Subtractions should go through
                if (csgMode == CSGMode.Subtract)
                {
                    sourcePolygon.Flip();
                }
            }

            Quaternion rotation;

            Polygon[] polygons;
            SurfaceUtility.ExtrudePolygon(sourcePolygon, extrusionDistance, out polygons, out rotation);

            GameObject newObject = csgModel.CreateCustomBrush(polygons);

            PrimitiveBrush newBrush = newObject.GetComponent <PrimitiveBrush>();

            newObject.transform.rotation  = rotation;
            newObject.transform.position += positionOffset;

            if (activePolygon != null &&
                activePolygon.Material != csgModel.GetDefaultMaterial())
            {
                for (int i = 0; i < polygons.Length; i++)
                {
                    polygons[i].Material = activePolygon.Material;
                }
            }
            // Finally give the new brush the other set of polygons
            newBrush.SetPolygons(polygons, true);

            newBrush.Mode = csgMode;

            newBrush.ResetPivot();

            // Use this brush as the basis for drawing the next brush
            csgModel.SetLastSelectedBrush(newBrush);

            Undo.RegisterCreatedObjectUndo(newObject, "Draw Brush");
        }
コード例 #15
0
        private static void SplitAndRemove(LinkedList <BrushChunk> brushChunks, BrushCache removee, int[] polygonsRemoved)
        {
            Polygon[] polygons    = removee.Polygons;
            Plane[]   splitPlanes = removee.SplitPlanes;

            for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
            {
                Plane splitPlane = splitPlanes[polygonIndex];

                for (LinkedListNode <BrushChunk> current = brushChunks.First; current != null;)
                {
#if !NO_EARLYOUT
                    if (!current.Value.GetBounds().IntersectsApproximate(removee.Bounds))
                    {
                        current = current.Next;
                        continue;
                    }
#endif

                    BrushChunk chunkIn;
                    BrushChunk chunkOut;

                    if (current.Value.SplitByPlane(splitPlane, out chunkIn, out chunkOut))
                    {
                        // TODO: If chunkIn is fully inside polygons, delete it
                        current.Value = chunkOut;

                        if (!GeometryHelper.PolyhedronContainsPolyhedron(polygons, chunkIn.Polygons))
                        {
                            current = brushChunks.AddAfter(current, chunkIn);
                        }
                        else
                        {
                            for (int i = 0; i < chunkIn.Polygons.Count; i++)
                            {
                                if (chunkIn.Polygons[i].UniqueIndex != -1)
                                {
                                    int relativeIndex = chunkIn.Polygons[i].UniqueIndex - firstPolygonUID;
                                    MarkPolygonRemoved(relativeIndex, polygonsRemoved);
                                }
                            }
                        }

                        // Next iteration
                        current = current.Next;
                    }
                    else
                    {
                        LinkedListNode <BrushChunk> next = current.Next;
                        BrushChunk chunk = current.Value;
                        if (GeometryHelper.PolyhedronContainsPolyhedron(polygons, chunk.Polygons))
                        {
                            for (int i = 0; i < chunk.Polygons.Count; i++)
                            {
                                if (chunk.Polygons[i].UniqueIndex != -1)
                                {
                                    int relativeIndex = chunk.Polygons[i].UniqueIndex - firstPolygonUID;
                                    MarkPolygonRemoved(relativeIndex, polygonsRemoved);
                                }
                            }

                            brushChunks.Remove(current);
                        }
                        // Next iteration
                        current = next;
                    }
                }
            }
        }
コード例 #16
0
        public static void DisplacePolygons(Polygon[] polygons, float distance)
        {
            // Used for determining if two vertices are the same
            Polygon.VertexComparerEpsilon vertexComparer = new Polygon.VertexComparerEpsilon();
            // Used for determining if two positions or normals are the same
            Polygon.Vector3ComparerEpsilon vectorComparer = new Polygon.Vector3ComparerEpsilon();

            // Group overlapping positions and also track their normals
            List <List <Vertex> >  groupedVertices = new List <List <Vertex> >();
            List <List <Vector3> > groupedNormals  = new List <List <Vector3> >();

            // Maps back from a vertex to the polygon it came from, used for UV calculation
            Dictionary <Vertex, Polygon> vertexPolygonMappings = new Dictionary <Vertex, Polygon>();

            for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
            {
                Vertex[] vertices = polygons[polygonIndex].Vertices;

                // Group the selected vertices into clusters
                for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++)
                {
                    Vertex sourceVertex = vertices[vertexIndex];

                    vertexPolygonMappings[sourceVertex] = polygons[polygonIndex];

                    bool added = false;

                    for (int groupIndex = 0; groupIndex < groupedVertices.Count; groupIndex++)
                    {
                        if (groupedVertices[groupIndex].Contains(sourceVertex, vertexComparer))
                        {
                            groupedVertices[groupIndex].Add(sourceVertex);
                            // Add the normal of the polygon if it hasn't already been added (this prevents issues with two polygons that are coplanar)
                            if (!groupedNormals[groupIndex].Contains(polygons[polygonIndex].Plane.normal, vectorComparer))
                            {
                                groupedNormals[groupIndex].Add(polygons[polygonIndex].Plane.normal);
                            }
                            added = true;
                            break;
                        }
                    }

                    if (!added)
                    {
                        groupedVertices.Add(new List <Vertex>()
                        {
                            sourceVertex
                        });
                        groupedNormals.Add(new List <Vector3>()
                        {
                            polygons[polygonIndex].Plane.normal
                        });
                    }
                }
            }

            List <List <Vector3> > groupedPositions = new List <List <Vector3> >();
            List <List <Vector2> > groupedUV        = new List <List <Vector2> >();

            // Calculate the new positions and UVs, but don't assign them as they must be calculated in one go
            for (int i = 0; i < groupedVertices.Count; i++)
            {
                groupedPositions.Add(new List <Vector3>());
                groupedUV.Add(new List <Vector2>());

                for (int j = 0; j < groupedVertices[i].Count; j++)
                {
                    Vector3 position = groupedVertices[i][j].Position;
                    for (int k = 0; k < groupedNormals[i].Count; k++)
                    {
                        position += groupedNormals[i][k] * distance;
                    }
                    Polygon primaryPolygon = vertexPolygonMappings[groupedVertices[i][j]];

                    Vector2 uv = GeometryHelper.GetUVForPosition(primaryPolygon, position);
                    groupedPositions[i].Add(position);
                    groupedUV[i].Add(uv);
                }
            }

            // Apply the new positions and UVs now that they've all been calculated
            for (int i = 0; i < groupedVertices.Count; i++)
            {
                for (int j = 0; j < groupedVertices[i].Count; j++)
                {
                    Vertex vertex = groupedVertices[i][j];
                    vertex.Position = groupedPositions[i][j];
                    vertex.UV       = groupedUV[i][j];
                }
            }

            // Polygon planes have moved, so recalculate them
            for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
            {
                polygons[polygonIndex].CalculatePlane();
            }
        }
コード例 #17
0
        public override void OnInspectorGUI()
        {
            float drawableWidth = EditorGUIUtility.currentViewWidth;

            drawableWidth -= 42;             // Take some off for scroll bars and padding

            PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => ((PrimitiveBrush)item).BrushType).ToArray();

            PrimitiveBrushType?activeType = (selectedTypes.Length == 1) ? (PrimitiveBrushType?)selectedTypes[0] : null;

            using (new NamedVerticalScope("Type"))
            {
                GUILayout.BeginHorizontal();

                float areaWidth          = drawableWidth - 18;
                int   buttonWidth        = Mathf.RoundToInt(areaWidth / 5f);
                int   stretchButtonWidth = Mathf.RoundToInt(areaWidth - buttonWidth * 4); // To ensure a justified alignment one button must be stretched slightly
                int   buttonHeight       = 50;

                GUIStyle brushButtonStyle = new GUIStyle(GUI.skin.button);
                brushButtonStyle.imagePosition = ImagePosition.ImageAbove;
                brushButtonStyle.fontSize      = 10;

                GUIStyle labelStyle = new GUIStyle(GUI.skin.label);
                labelStyle.alignment = TextAnchor.LowerCenter;
                labelStyle.fontSize  = brushButtonStyle.fontSize;

                bool shortMode = (areaWidth < 260); // Whether certain words need to be abbreviated to fit in the box

                DrawBrushButton(PrimitiveBrushType.Cube, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
                DrawBrushButton(PrimitiveBrushType.Prism, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
                DrawBrushButton(PrimitiveBrushType.Cylinder, activeType, brushButtonStyle, labelStyle, stretchButtonWidth, buttonHeight, shortMode);
                DrawBrushButton(PrimitiveBrushType.Sphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
                DrawBrushButton(PrimitiveBrushType.IcoSphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);

                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();

                DrawBrushButton(PrimitiveBrushType.Cone, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);

                GUI.enabled = true; // Reset GUI enabled so that the next items aren't disabled
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();

                if (activeType.HasValue)
                {
                    GUILayout.Label("Active: " + selectedTypes[0]);
                }
                else
                {
                    GUILayout.Label("Active: Mixed");
                }

                if (activeType.HasValue)
                {
                    EditorGUIUtility.labelWidth = 60;
                    EditorGUIUtility.fieldWidth = 50;
                    EditorGUI.BeginChangeCheck();
                    if (activeType.Value == PrimitiveBrushType.Prism)
                    {
                        EditorGUILayout.PropertyField(prismSideCountProp, new GUIContent("Sides"));
                    }
                    else if (activeType.Value == PrimitiveBrushType.Cylinder)
                    {
                        EditorGUILayout.PropertyField(cylinderSideCountProp, new GUIContent("Sides"));
                    }
                    else if (activeType.Value == PrimitiveBrushType.Sphere)
                    {
                        EditorGUILayout.PropertyField(sphereSideCountProp, new GUIContent("Sides"));
                    }
                    else if (activeType.Value == PrimitiveBrushType.IcoSphere)
                    {
                        EditorGUILayout.PropertyField(icoSphereIterationCountProp, new GUIContent("Iterations"));
                    }
                    else if (activeType.Value == PrimitiveBrushType.Cone)
                    {
                        EditorGUILayout.PropertyField(coneSideCountProp, new GUIContent("Sides"));
                    }
                    if (EditorGUI.EndChangeCheck())
                    {
                        // One of the properties has changed
                        serializedObject.ApplyModifiedProperties();
                        ResetPolygonsKeepScale();
                    }
                }

                GUILayout.EndHorizontal();
            }

            using (new NamedVerticalScope("Size"))
            {
                if (GUILayout.Button(new GUIContent("Reset Bounds", "Resets the bounds of the brush to [2,2,2]")))
                {
                    ResetBounds();
                }

                GUILayout.BeginHorizontal();

                GUI.SetNextControlName("rescaleTextbox");

                scaleString = EditorGUILayout.TextField(scaleString);

                bool keyboardEnter = Event.current.isKey &&
                                     Event.current.keyCode == KeyCode.Return &&
                                     Event.current.type == EventType.KeyUp &&
                                     GUI.GetNameOfFocusedControl() == "rescaleTextbox";

                if (GUILayout.Button("Scale", GUILayout.MaxWidth(drawableWidth / 3f)) || keyboardEnter)
                {
                    // Try to parse a Vector3 scale from the input string
                    Vector3 scaleVector3;
                    if (StringHelper.TryParseScale(scaleString, out scaleVector3))
                    {
                        // None of the scale components can be zero
                        if (scaleVector3.x != 0 && scaleVector3.y != 0 && scaleVector3.z != 0)
                        {
                            // Rescale all the brushes
                            Undo.RecordObjects(targets, "Scale Polygons");
                            foreach (var thisBrush in targets)
                            {
                                BrushUtility.Scale((PrimitiveBrush)thisBrush, scaleVector3);
                            }
                        }
                    }
                }

                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();

                GUI.SetNextControlName("resizeTextbox");

                resizeString = EditorGUILayout.TextField(resizeString);

                keyboardEnter = Event.current.isKey &&
                                Event.current.keyCode == KeyCode.Return &&
                                Event.current.type == EventType.KeyUp &&
                                GUI.GetNameOfFocusedControl() == "resizeTextbox";

                if (GUILayout.Button("Resize", GUILayout.MaxWidth(drawableWidth / 3f)) || keyboardEnter)
                {
                    // Try to parse a Vector3 scale from the input string
                    Vector3 resizeVector3;
                    if (StringHelper.TryParseScale(resizeString, out resizeVector3))
                    {
                        // None of the size components can be zero
                        if (resizeVector3.x != 0 && resizeVector3.y != 0 && resizeVector3.z != 0)
                        {
                            // Rescale all the brushes so that the local bounds is the same size as the resize vector
                            Undo.RecordObjects(targets, "Resize Polygons");
                            PrimitiveBrush[] brushes = BrushTargets.Cast <PrimitiveBrush>().ToArray();
                            foreach (PrimitiveBrush brush in brushes)
                            {
                                BrushUtility.Resize(brush, resizeVector3);
                            }
                        }
                    }
                }

                GUILayout.EndHorizontal();
            }

            using (new NamedVerticalScope("Rotation"))
            {
                GUILayout.Label("Align up direction", EditorStyles.boldLabel);
                GUILayout.BeginHorizontal();
                if (GUILayout.Button("X"))
                {
                    AlignUpToAxis(new Vector3(1, 0, 0), false);
                }
                if (GUILayout.Button("Y"))
                {
                    AlignUpToAxis(new Vector3(0, 1, 0), false);
                }
                if (GUILayout.Button("Z"))
                {
                    AlignUpToAxis(new Vector3(0, 0, 1), false);
                }
                GUILayout.EndHorizontal();

                GUILayout.Label("Align up direction (keep positions)", EditorStyles.boldLabel);

                GUILayout.BeginHorizontal();
                if (GUILayout.Button("X"))
                {
                    AlignUpToAxis(new Vector3(1, 0, 0), true);
                }
                if (GUILayout.Button("Y"))
                {
                    AlignUpToAxis(new Vector3(0, 1, 0), true);
                }
                if (GUILayout.Button("Z"))
                {
                    AlignUpToAxis(new Vector3(0, 0, 1), true);
                }
                GUILayout.EndHorizontal();
            }

            using (new NamedVerticalScope("Misc"))
            {
                // Import Row
                GUILayout.BeginHorizontal();
                sourceMesh = EditorGUILayout.ObjectField(sourceMesh, typeof(Mesh), false) as Mesh;

                if (GUILayout.Button("Import", GUILayout.MaxWidth(drawableWidth / 3f)))
                {
                    if (sourceMesh != null)
                    {
                        Undo.RecordObjects(targets, "Import Polygons From Mesh");

                        Polygon[] polygons = BrushFactory.GeneratePolygonsFromMesh(sourceMesh).ToArray();
                        bool      convex   = GeometryHelper.IsBrushConvex(polygons);
                        if (!convex)
                        {
                            Debug.LogError("Concavities detected in imported mesh. This may result in issues during CSG, please change the source geometry so that it is convex");
                        }
                        foreach (var thisBrush in targets)
                        {
                            ((PrimitiveBrush)thisBrush).SetPolygons(polygons, true);
                        }
                    }
                }

                GUILayout.EndHorizontal();

                // Shell Row
                GUILayout.BeginHorizontal();

                if (shellDistance == 0)
                {
                    shellDistance = CurrentSettings.PositionSnapDistance;
                }

                shellDistance = EditorGUILayout.FloatField("Distance", shellDistance);

                if (GUILayout.Button("Shell", GUILayout.MaxWidth(drawableWidth / 3f)))
                {
                    List <GameObject> newSelection = new List <GameObject>();
                    foreach (var thisBrush in targets)
                    {
                        GameObject newObject = ((PrimitiveBrush)thisBrush).Duplicate();
                        Polygon[]  polygons  = newObject.GetComponent <PrimitiveBrush>().GetPolygons();
                        VertexUtility.DisplacePolygons(polygons, -shellDistance);
                        Bounds newBounds = newObject.GetComponent <PrimitiveBrush>().GetBounds();
                        // Verify the new geometry
                        if (GeometryHelper.IsBrushConvex(polygons) &&
                            newBounds.GetSmallestExtent() > 0)
                        {
                            Undo.RegisterCreatedObjectUndo(newObject, "Shell");
                            newSelection.Add(newObject);
                        }
                        else
                        {
                            // Produced a concave brush, delete it and pretend nothing happened
                            GameObject.DestroyImmediate(newObject);
                            Debug.LogWarning("Could not shell " + thisBrush.name + " as shelled geometry would not be valid. Try lowering the shell distance and attempt Shell again.");
                        }
                    }

                    if (newSelection.Count > 0)
                    {
                        Selection.objects = newSelection.ToArray();
                    }
                }

                GUILayout.EndHorizontal();

                // Split Intersecting Row
                if (GUILayout.Button("Split Intersecting Brushes"))
                {
                    // Chop up the intersecting brushes by the brush planes, ideally into as few new brushes as possible

                    PrimitiveBrush[] brushes = BrushTargets.Cast <PrimitiveBrush>().ToArray();

                    BrushUtility.SplitIntersecting(brushes);
                }


                //			BrushOrder brushOrder = BrushTarget.GetBrushOrder();
                //			string positionString = string.Join(",", brushOrder.Position.Select(item => item.ToString()).ToArray());
                //            GUILayout.Label(positionString, EditorStyles.boldLabel);

                //List<BrushCache> intersections = ((PrimitiveBrush)BrushTarget).BrushCache.IntersectingVisualBrushCaches;
                //GUILayout.Label("Intersecting brushes " + intersections.Count, EditorStyles.boldLabel);

                //for (int i = 0; i < intersections.Count; i++)
                //{
                //    GUILayout.Label(intersections[i].Mode.ToString(), EditorStyles.boldLabel);
                //}
            }

            base.OnInspectorGUI();
        }
コード例 #18
0
        public void TranslateSelectedVertices(Vector3 worldDelta)
        {
            foreach (PrimitiveBrush brush in targetBrushes)
            {
                bool anyAffected = false;

                Polygon[] polygons   = brush.GetPolygons();
                Vector3   localDelta = brush.transform.InverseTransformDirection(worldDelta);

                for (int i = 0; i < polygons.Length; i++)
                {
                    Polygon polygon = polygons[i];

                    polygon.CalculatePlane();
                    Vector3 previousPlaneNormal = polygons[i].Plane.normal;

                    int vertexCount = polygon.Vertices.Length;

                    Vector3[] newPositions = new Vector3[vertexCount];
                    Vector2[] newUV        = new Vector2[vertexCount];

                    for (int j = 0; j < vertexCount; j++)
                    {
                        newPositions[j] = polygon.Vertices[j].Position;
                        newUV[j]        = polygon.Vertices[j].UV;
                    }

                    bool polygonAffected = false;

                    for (int j = 0; j < vertexCount; j++)
                    {
                        Vertex vertex = polygon.Vertices[j];
                        if (selectedVertices.ContainsKey(vertex))
                        {
                            Vector3 startPosition = startPositions[vertex];
                            Vector3 newPosition   = vertex.Position + localDelta;

                            Vector3 accumulatedDelta = newPosition - startPosition;

                            if (CurrentSettings.PositionSnappingEnabled)
                            {
                                float snapDistance = CurrentSettings.PositionSnapDistance;
                                //							newPosition = targetBrush.transform.TransformPoint(newPosition);
                                accumulatedDelta = MathHelper.RoundVector3(accumulatedDelta, snapDistance);
                                //							newPosition = targetBrush.transform.InverseTransformPoint(newPosition);
                            }

                            if (accumulatedDelta != Vector3.zero)
                            {
                                newPosition = startPosition + accumulatedDelta;

                                newPositions[j] = newPosition;

                                newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition);

                                polygonAffected = true;
                                anyAffected     = true;
                            }
                        }
                    }

                    // Apply all the changes to the polygon
                    for (int j = 0; j < vertexCount; j++)
                    {
                        Vertex vertex = polygon.Vertices[j];
                        vertex.Position = newPositions[j];
                        vertex.UV       = newUV[j];
                    }

                    if (polygonAffected)
                    {
                        // Polygon geometry has changed, inform the polygon that it needs to recalculate its cached plane
                        polygons[i].CalculatePlane();

                        Vector3 newPlaneNormal = polygons[i].Plane.normal;

                        // Find the rotation from the original polygon plane to the new polygon plane
                        Quaternion normalRotation = Quaternion.FromToRotation(previousPlaneNormal, newPlaneNormal);

                        // Update the affected normals so they are rotated by the rotational difference of the polygon from translation
                        for (int j = 0; j < vertexCount; j++)
                        {
                            Vertex vertex = polygon.Vertices[j];
                            vertex.Normal = normalRotation * vertex.Normal;
                        }
                    }
                }

                if (anyAffected)                // If any polygons have changed
                {
                    // Mark the polygons and brush as having changed
                    brush.Invalidate(true);

                    // Assume that the brush no longer resembles it's base shape, this has false positives but that's not a big issue
                    brush.BreakTypeRelation();
                }
            }
        }