/// <summary> /// Scales the brush by a local Vector3 scale from its pivot /// </summary> /// <param name="brush">The brush to be rescaled</param> /// <param name="rescaleValue">Local scale to apply</param> public static void Scale(PrimitiveBrush brush, Vector3 scaleValue) { Polygon[] polygons = brush.GetPolygons(); for (int i = 0; i < polygons.Length; i++) { Polygon polygon = polygons[i]; polygons[i].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; } for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; Vector3 newPosition = vertex.Position.Multiply(scaleValue); newPositions[j] = newPosition; newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition); } // 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 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); // Rotate all the vertex normals by the new rotation for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; vertex.Normal = normalRotation * vertex.Normal; } } #if UNITY_EDITOR EditorHelper.SetDirty(brush); #endif brush.Invalidate(true); }
public static void InsertEdgeLoop(PrimitiveBrush brush, Plane localClipPlane) { // Clip the polygons against the plane List <Polygon> polygonsFront; List <Polygon> polygonsBack; if (PolygonFactory.SplitPolygonsByPlane(new List <Polygon>(brush.GetPolygons()), localClipPlane, true, out polygonsFront, out polygonsBack)) { List <Polygon> allPolygons = new List <Polygon>(); // Concat back into one list allPolygons.AddRange(polygonsFront); allPolygons.AddRange(polygonsBack); // Remove the inserted polygons for (int i = 0; i < allPolygons.Count; i++) { if (allPolygons[i].ExcludeFromFinal) { allPolygons.RemoveAt(i); i--; } } // Update the brush with the new polygons brush.SetPolygons(allPolygons.ToArray(), true); } }
/// <summary> /// Rebuilds the volume, creating or deleting the volume component and applying new settings. /// </summary> internal void RebuildVolume() { // volumes can only be primitive brushes at the moment. if (GetType() != typeof(PrimitiveBrush)) { return; } PrimitiveBrush self = (PrimitiveBrush)this; // remove volumes from brushes that are no longer volumes: if (Mode != CSGMode.Volume && Volume != null) { // set volume handle to null. Volume = null; // delete any built volume. Transform volume1 = transform.Find(Constants.GameObjectVolumeComponentIdentifier); if (volume1 != null) { GameObject.DestroyImmediate(volume1.gameObject); } } // generate all of the volume brushes: if (Mode == CSGMode.Volume && Volume != null) { // remove any existing built volume: Transform volume2 = transform.Find(Constants.GameObjectVolumeComponentIdentifier); if (volume2 != null) { GameObject.DestroyImmediate(volume2.gameObject); } // create the game object with convex mesh collider: Mesh mesh = new Mesh(); BrushFactory.GenerateMeshFromPolygonsFast(self.GetPolygons(), ref mesh, 0.0f); GameObject gameObject = CreateVolumeMeshCollider(mesh); gameObject.transform.position = transform.position; gameObject.transform.rotation = transform.rotation; // execute custom volume generation code: Volume.OnCreateVolume(gameObject); } }
/// <summary> /// Applies a clip or split operation to a brush by supplying a plane local to the brush. Clipping allows you to cut away and discard parts of the brush, while splitting allows you to split a brush in two. /// </summary> /// <returns>If keepBothSides is <c>true</c>, this returns the second brush if one was created.</returns> /// <param name="brush">Brush to clip/split</param> /// <param name="localPlane">Local plane to clip/split against</param> /// <param name="keepBothSides">If set to <c>true</c> split the brush and keep both sides.</param> /// <param name="resetPivots">If set to <c>true</c> make sure the pivot clipped brush and any new brush are at their center.</param> public static GameObject ApplyClipPlane(PrimitiveBrush brush, Plane localPlane, bool keepBothSides, bool resetPivots = true) { // Clip the polygons against the plane List <Polygon> polygonsFront; List <Polygon> polygonsBack; List <Polygon> polygonList = new List <Polygon>(brush.GetPolygons()); if (PolygonFactory.SplitPolygonsByPlane(polygonList, localPlane, false, out polygonsFront, out polygonsBack)) { // Update the brush with the new polygons brush.SetPolygons(polygonsFront.ToArray(), true); GameObject newObject = null; // If they have decided to split instead of clip, create a second brush with the other side if (keepBothSides) { newObject = brush.Duplicate(); // Finally give the new brush the other set of polygons newObject.GetComponent <PrimitiveBrush>().SetPolygons(polygonsBack.ToArray(), true); newObject.transform.SetSiblingIndex(brush.transform.GetSiblingIndex()); // Reset the new brush's pivot if (resetPivots) { newObject.GetComponent <PrimitiveBrush>().ResetPivot(); } } // Can't reset the first brush pivot until after the second brush is made, otherwise the second // brush is effectively translated twice, ending up in the wrong position if (resetPivots) { brush.ResetPivot(); } return(newObject); } else { return(null); } }