public PlaneIntersection(CSGTreeBrushIntersection brushIntersection, CSGNode node, CSGModel model) { this.point = brushIntersection.surfaceIntersection.worldIntersection; this.plane = brushIntersection.surfaceIntersection.worldPlane; this.node = node; this.model = model; }
// Recursively remove all polygons in `polygons` that are inside this BSP // tree. public List <CSGPolygon> clipPolygons(List <CSGPolygon> polygons) { if (!validPlane) { return(new List <CSGPolygon>(polygons)); } List <CSGPolygon> front = new List <CSGPolygon>(); List <CSGPolygon> back = new List <CSGPolygon>(); for (int i = 0; i < polygons.Count; i++) { plane.splitPolygon(polygons[i], front, back, front, back); } if (this.front != null) { front = this.front.clipPolygons(front); } if (this.back != null) { back = this.back.clipPolygons(back); } else { back = new List <CSGPolygon>(); } front.AddRange(back); return(front); }
public static bool IsPartOfDefaultModel(UnityEngine.Object[] targetObjects) { if (targetObjects == null) { return(false); } for (int i = 0; i < targetObjects.Length; i++) { CSGNode node = targetObjects[i] as CSGNode; if (Equals(node, null)) { var gameObject = targetObjects[i] as GameObject; if (gameObject) { node = gameObject.GetComponent <CSGNode>(); } } if (node) { if (CSGGeneratedComponentManager.IsDefaultModel(node.hierarchyItem.Model)) { return(true); } } } return(false); }
public static CSGNode GetTopMostGroupForNode(CSGNode node) { if (!node) { return(null); } var topSelected = node; var parent = node.transform.parent; while (parent) { var model = parent.GetComponent <CSGModel>(); if (model) { break; } var parentOp = parent.GetComponent <CSGOperation>(); if (parentOp && parentOp.HandleAsOne && !parentOp.PassThrough) { topSelected = parentOp; } parent = parent.transform.parent; } return(topSelected); }
public override CSGNode ToCSGNode(CSGNode parent, HashSet <CSGNode> animateNodes, bool ignoreAnimate) { var branch = new CSGNode(this.ID, Operator); branch.Parent = parent; branch.LocalTranslation = this.Translation; if (parent != null) { branch.Translation = Vector3.Add(parent.Translation, branch.LocalTranslation); } else { branch.Translation = branch.LocalTranslation; } branch.Left = Left.ToCSGNode(branch, animateNodes, Animate || ignoreAnimate); branch.Right = Right.ToCSGNode(branch, animateNodes, Animate || ignoreAnimate); if (Animate && !ignoreAnimate) { animateNodes.Add(branch); } return(branch); }
private static CSGOperation GetGroupOperationForNode(CSGNode node) { if (!node) { return(null); } var parent = node.transform.parent; while (parent) { var model = parent.GetComponent <CSGModel>(); if (model) { return(null); } var parentOp = parent.GetComponent <CSGOperation>(); if (parentOp && //!parentOp.PassThrough && parentOp.HandleAsOne) { return(parentOp); } parent = parent.transform.parent; } return(null); }
public SurfaceReference(CSGNode node, CSGBrushMeshAsset brushMeshAsset, int subNodeIndex, int subMeshIndex, int surfaceIndex, int surfaceID) { this.node = node; this.brushMeshAsset = brushMeshAsset; this.subNodeIndex = subNodeIndex; this.subMeshIndex = subMeshIndex; this.surfaceIndex = surfaceIndex; this.surfaceID = surfaceID; }
public static void UpdateChildTransformations(CSGNode node) { if (node.NodeType == CSGNodeType.Brush) { return; } UpdateChildTransformations(node.Left, node.Translation); UpdateChildTransformations(node.Right, node.Translation); }
public static IChiselNodeDetails GetNodeDetails(CSGNode node) { IChiselNodeDetails someInterface; if (nodeDetailsLookup.TryGetValue(node.GetType(), out someInterface)) { return(someInterface); } return(null); }
public static GUIContent GetHierarchyIcon(CSGNode node) { IChiselNodeDetails someInterface; if (nodeDetailsLookup.TryGetValue(node.GetType(), out someInterface)) { return(someInterface.GetHierarchyIconForGenericNode(node)); } return(null); }
public static void UpdateChildTransformations(CSGNode node, Vector3 parentTranslation) { node.Translation = Vector3.Add(parentTranslation, node.LocalTranslation); if (node.NodeType == CSGNodeType.Brush) { return; } UpdateChildTransformations(node.Left, node.Translation); UpdateChildTransformations(node.Right, node.Translation); }
public static IRaycastGeometry <float3> Pipe(float radius = 1, float thickness = 0, string plane = "xy", float3?lowerBound = null, float3?upperBound = null) { if (thickness == 0) { return(Cylinder(radius, plane)); } var cylinder1 = new CSGNode(Cylinder(radius, plane, lowerBound, upperBound)); var cylinder2 = new CSGNode(Cylinder(radius + thickness, plane, lowerBound, upperBound)); return(cylinder1 | cylinder2); }
public static void UpdateBounds(CSGNode node) { if (node.NodeType != CSGNodeType.Brush) { var leftNode = node.Left; var rightNode = node.Right; UpdateBounds(leftNode); UpdateBounds(rightNode); node.Bounds.Clear(); node.Bounds.Add(leftNode.Bounds.Translated(Vector3.Subtract(leftNode.Translation, node.Translation))); node.Bounds.Add(rightNode.Bounds.Translated(Vector3.Subtract(rightNode.Translation, node.Translation))); } }
// Remove all polygons in this BSP tree that are inside the other BSP tree // `bsp`. public void clipTo(CSGNode bsp) { this.polygons = bsp.clipPolygons(this.polygons); if (this.front != null) { this.front.clipTo(bsp); } if (this.back != null) { this.back.clipTo(bsp); } }
// Return a new CSG solid representing space both this solid and in the // solid `csg`. Neither this solid nor the solid `csg` are modified. // // A.intersect(B) // // +-------+ // | | // | A | // | +--+----+ = +--+ // +----+--+ | +--+ // | B | // | | // +-------+ // public CSG intersect(CSG csg) { CSGNode a = new CSGNode(this.clone().polygons); CSGNode b = new CSGNode(csg.clone().polygons); a.invert(); b.clipTo(a); b.invert(); a.clipTo(b); b.clipTo(a); a.addPolygons(b.allPolygons()); a.invert(); return(CSG.fromPolygons(a.allPolygons())); }
internal static void OnHierarchyWindowItemGUI(CSGNode node, Rect selectionRect) { // TODO: implement material drag & drop support on hierarchy items if (Event.current.type != EventType.Repaint) { return; } var icon = ChiselNodeDetailsManager.GetHierarchyIcon(node); if (icon != null) { RenderIcon(selectionRect, icon); } }
internal static void Unregister(CSGNode node) { if (!registeredNodeLookup.Remove(node)) { return; } var model = node as CSGModel; if (!ReferenceEquals(model, null)) { componentGenerator.Unregister(model); sharedUnityMeshes.Unregister(model); registeredModels.Remove(model); } }
public static IEnumerable <CSGNode> FindChildNodes(CSGNode node) { yield return(node); if (node.NodeType != CSGNodeType.Brush) { foreach (var child in FindChildNodes(node.Left)) { yield return(child); } foreach (var child in FindChildNodes(node.Right)) { yield return(child); } } }
// Return a new CSG solid representing space in either this solid or in the // solid `csg`. Neither this solid nor the solid `csg` are modified. // // A.union(B) // // +-------+ +-------+ // | | | | // | A | | | // | +--+----+ = | +----+ // +----+--+ | +----+ | // | B | | | // | | | | // +-------+ +-------+ // public CSG union(CSG csg) { CSGNode a = new CSGNode(this.clone().polygons); CSGNode b = new CSGNode(csg.clone().polygons); a.clipTo(b); b.clipTo(a); b.invert(); b.clipTo(a); b.invert(); List <CSGPolygon> result = a.allPolygons(); result.AddRange(b.allPolygons()); return(CSG.fromPolygons(result)); }
public static IEnumerable <CSGNode> FindChildBrushes(CSGNode node) { if (node.NodeType != CSGNodeType.Brush) { foreach (var brush in FindChildBrushes(node.Left)) { yield return(brush); } foreach (var brush in FindChildBrushes(node.Right)) { yield return(brush); } yield break; } else { yield return(node); } }
// Build a BSP tree out of `polygons`. When called on an existing tree, the // new polygons are filtered down to the bottom of the tree and become new // nodes there. Each set of polygons is partitioned using the first polygon // (no heuristic is used to pick a good split). public void addPolygons(List <CSGPolygon> polygons) { if (polygons.Count == 0) { return; } if (!validPlane) { this.plane = polygons[0].plane; validPlane = true; } List <CSGPolygon> front = new List <CSGPolygon>(); List <CSGPolygon> back = new List <CSGPolygon>(); for (int i = 0; i < polygons.Count; i++) { this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back); } if (front.Count > 0) { if (this.front == null) { this.front = new CSGNode(); } this.front.addPolygons(front); } if (back.Count > 0) { if (this.back == null) { this.back = new CSGNode(); } this.back.addPolygons(back); } }
public override CSGNode ToCSGNode(CSGNode parent, HashSet <CSGNode> animateNodes, bool ignoreAnimate) { var leaf = new CSGNode(this.ID, Planes); leaf.Parent = parent; leaf.LocalTranslation = this.Translation; if (parent != null) { leaf.Translation = Vector3.Add(parent.Translation, leaf.LocalTranslation); } else { leaf.Translation = leaf.LocalTranslation; } if (Animate && !ignoreAnimate) { animateNodes.Add(leaf); } return(leaf); }
public CSGNode clone() { CSGNode node = new CSGNode(); node.plane = plane; node.validPlane = validPlane; if (front != null) { node.front = front.clone(); } if (back != null) { node.back = back.clone(); } for (int i = 0; i < polygons.Count; i++) { node.polygons.Add(polygons[i].clone()); } return(node); }
// Convert solid space to empty space and empty space to solid space. public void invert() { for (int i = 0; i < polygons.Count; i++) { polygons[i].flip(); } plane.flip(); if (front != null) { front.invert(); } if (back != null) { back.invert(); } CSGNode temp = front; front = back; back = temp; }
GUIContent IChiselNodeDetails.GetHierarchyIconForGenericNode(CSGNode node) { return(GetHierarchyIcon((T)node)); }
internal static GameObject PickNodeOrGameObject(Camera camera, Vector2 pickposition, int layers, ref GameObject[] ignore, ref GameObject[] filter, out CSGModel model, out CSGNode node, out CSGTreeBrushIntersection intersection) { TryNextSelection: intersection = new CSGTreeBrushIntersection { surfaceID = -1, brushUserID = -1 }; model = null; node = null; Material sharedMaterial; var gameObject = PickModel(camera, pickposition, layers, ref ignore, ref filter, out model, out sharedMaterial); if (object.Equals(gameObject, null)) { return(null); } if (model) { int filterLayerParameter0 = (sharedMaterial) ? sharedMaterial.GetInstanceID() : 0; { var worldRay = camera.ScreenPointToRay(pickposition); var worldRayStart = worldRay.origin; var worldRayVector = (worldRay.direction * (camera.farClipPlane - camera.nearClipPlane)); var worldRayEnd = worldRayStart + worldRayVector; CSGTreeBrushIntersection tempIntersection; if (CSGSceneQuery.FindFirstWorldIntersection(model, worldRayStart, worldRayEnd, filterLayerParameter0, layers, ignore, filter, out tempIntersection)) { var clickedBrush = tempIntersection.brush; node = CSGNodeHierarchyManager.FindCSGNodeByInstanceID(clickedBrush.UserID); if (node) { if (ignore != null && ignore.Contains(node.gameObject)) { node = null; return(null); } intersection = tempIntersection; return(node.gameObject); } else { node = null; } } } if (ignore == null) { return(null); } ArrayUtility.Add(ref ignore, gameObject); goto TryNextSelection; } if (object.Equals(gameObject, null)) { return(null); } if (ignore != null && ignore.Contains(gameObject)) { return(null); } return(gameObject); }
// Logical OR set operation on polygons // // Table showing final output from combination of categorization of left and right node // // | right node // | inside aligned r-aligned outside // -----------------+------------------------------------------ // left inside | I I I I // node aligned | I A I A // r-aligned | I I R R // outside | I A R O // // I = inside A = aligned // O = outside R = reverse aligned // #region LogicalOr static void LogicalOr(CSGNode processedNode, CSGMesh processedMesh, CSGNode categorizationNode, List <Polygon> inputPolygons, List <Polygon> inside, List <Polygon> aligned, List <Polygon> revAligned, List <Polygon> outside, bool inverseLeft, bool inverseRight) { var leftNode = categorizationNode.Left; var rightNode = categorizationNode.Right; var defaultCapacity = inputPolygons.Count / 2; // ... Allocations are ridiculously cheap in .NET, there is a garbage collection penalty however. // CSG can be performed without temporary buffers and recursion by using flags, // which would increase performance and scalability (garbage collection interfers with parallelization). // It makes the code a lot harder to read however. var leftAligned = new List <Polygon>(defaultCapacity); var leftRevAligned = new List <Polygon>(defaultCapacity); var leftOutside = new List <Polygon>(defaultCapacity); //var leftInside = new List<Polygon>(defaultCapacity); // everything that's inside the left node // is always part of the inside category // First categorize polygons in left path ... if (inverseLeft) { Categorize(processedNode, processedMesh, leftNode, inputPolygons, leftOutside, leftRevAligned, leftAligned, inside); } else { Categorize(processedNode, processedMesh, leftNode, inputPolygons, inside, leftAligned, leftRevAligned, leftOutside); } // ... Then categorize the polygons in the right path // Note that no single polygon will go into more than one of the Categorize methods below if (inverseRight) { if (leftAligned.Count > 0) { if (inside == aligned) { inside.AddRange(leftAligned); } else { Categorize(processedNode, processedMesh, rightNode, leftAligned, aligned, inside, aligned, inside); } } if (leftRevAligned.Count > 0) { if (inside == revAligned) { inside.AddRange(leftRevAligned); } else { Categorize(processedNode, processedMesh, rightNode, leftRevAligned, revAligned, revAligned, inside, inside); } } if (leftOutside.Count > 0) { Categorize(processedNode, processedMesh, rightNode, leftOutside, outside, revAligned, aligned, inside); } } else { if (leftAligned.Count > 0) { if (inside == aligned) { inside.AddRange(leftAligned); } else { Categorize(processedNode, processedMesh, rightNode, leftAligned, inside, aligned, inside, aligned); } } if (leftRevAligned.Count > 0) { if (inside == revAligned) { inside.AddRange(leftRevAligned); } else { Categorize(processedNode, processedMesh, rightNode, leftRevAligned, inside, inside, revAligned, revAligned); } } if (leftOutside.Count > 0) { Categorize(processedNode, processedMesh, rightNode, leftOutside, inside, aligned, revAligned, outside); } } }
public SelectedNode(CSGNode node, Transform transform) { this.node = node; this.transform = transform; }
public static ConcurrentDictionary <CSGNode, CSGMesh> ProcessCSGNodes(CSGNode root, IEnumerable <CSGNode> nodes) { var meshes = new ConcurrentDictionary <CSGNode, CSGMesh>(); var buildMesh = (Action <CSGNode>) delegate(CSGNode node) { CSGMesh mesh; if (!cachedBaseMeshes.TryGetValue(node, out mesh)) { // If the node we're performing csg on is a brush, we simply create the geometry from the planes // If the node is a more complicated node, we perform csg on it's child nodes and combine the // meshes that are created. // Note that right now we cache brushes per node, but we can improve on this by caching on // node type instead. Since lots of nodes will have the same geometry in real life and only // need to be created once. It won't help much in runtime performance considering they're // cached anyway, but it'll save on memory usage. if (node.NodeType != CSGNodeType.Brush) { var childNodes = CSGUtility.FindChildBrushes(node); var brushMeshes = ProcessCSGNodes(node, childNodes); mesh = CSGMesh.Combine(node.Translation, brushMeshes); } else { mesh = CSGMesh.CreateFromPlanes(node.Planes); } // Cache the mesh cachedBaseMeshes[node] = mesh; } // Clone the cached mesh so we can perform CSG on it. var clonedMesh = mesh.Clone(); node.Bounds.Set(clonedMesh.Bounds); meshes[node] = clonedMesh; }; var updateDelegate = (Action <KeyValuePair <CSGNode, CSGMesh> >) delegate(KeyValuePair <CSGNode, CSGMesh> item) { var processedNode = item.Key; var processedMesh = item.Value; var inputPolygons = processedMesh.Polygons; var insidePolygons = new List <Polygon>(inputPolygons.Count); var outsidePolygons = new List <Polygon>(inputPolygons.Count); var alignedPolygons = new List <Polygon>(inputPolygons.Count); var reversedPolygons = new List <Polygon>(inputPolygons.Count); CSGCategorization.Categorize(processedNode, processedMesh, root, inputPolygons, // these are the polygons that are categorized insidePolygons, alignedPolygons, reversedPolygons, outsidePolygons ); // Flag all non aligned polygons as being invisible, and store their categorizations // so we can use it if we instance this mesh. foreach (var polygon in insidePolygons) { polygon.Category = PolygonCategory.Inside; polygon.Visible = false; } foreach (var polygon in outsidePolygons) { polygon.Category = PolygonCategory.Outside; polygon.Visible = false; } foreach (var polygon in alignedPolygons) { polygon.Category = PolygonCategory.Aligned; } foreach (var polygon in reversedPolygons) { polygon.Category = PolygonCategory.ReverseAligned; } }; // // Here we run build the meshes and perform csg on them either in serial or parallel // /* * foreach (var node in nodes) * buildMesh(node); * CSGUtility.UpdateBounds(root); * foreach (var item in meshes) * updateDelegate(item); * /*/ Parallel.ForEach(nodes, buildMesh); CSGUtility.UpdateBounds(root); Parallel.ForEach(meshes, updateDelegate); //*/ return(meshes); }
public abstract CSGNode ToCSGNode(CSGNode parent, HashSet <CSGNode> animateNodes, bool ignoreAnimate);