public CSGNode(CSGNodeType branchOperator) { this.NodeType = branchOperator; this.Left = null; this.Right = null; this.Translation = new Vector3(0, 0, 0); this.LocalTranslation = new Vector3(0, 0, 0); }
public static void UpdateChildTransformations(CSGNode node) { if (node.NodeType == CSGNodeType.Brush) return; UpdateChildTransformations(node.Left, node.Translation); UpdateChildTransformations(node.Right, node.Translation); }
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 void UpdateChildTransformations(CSGNode node) { if (node.NodeType == CSGNodeType.Brush) { return; } UpdateChildTransformations(node.Left, node.Translation); UpdateChildTransformations(node.Right, node.Translation); }
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 CSGNode(CSGNodeType branchOperator, CSGNode left, CSGNode right) { this.NodeType = branchOperator; this.Left = left; this.Right = right; this.Translation = new Vector3(0, 0, 0); this.LocalTranslation = new Vector3(0, 0, 0); left.Parent = this; right.Parent = this; }
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; } }
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; }
private static void UpdateMesh(CSGNode node, CSGNode root) { //if (node.Left != null) UpdateMesh(node.Left, node); //if (node.Right != null) UpdateMesh(node.Right, node); var processedNode = node; var processedMesh = node.cachedMesh; 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; } }
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))); } }
public static IEnumerable<CSGNode> FindChildBrushes(CSGNode node, Microsoft.Xna.Framework.Matrix transformation) { node.WorldTransformation = node.Transformation.Transform * transformation; if (node.NodeType != CSGNodeType.Brush) { foreach (var brush in FindChildBrushes(node.Left, node.WorldTransformation)) yield return brush; foreach (var brush in FindChildBrushes(node.Right, node.WorldTransformation)) yield return brush; yield break; } else yield return node; }
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); } } }
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); } }
//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; // } //} #endregion #region FindChildBrushes public static IEnumerable <CSGNode> FindChildBrushes(CSGNode node, Microsoft.Xna.Framework.Matrix transformation) { node.WorldTransformation = node.Transformation.Transform * transformation; if (node.NodeType != CSGNodeType.Brush) { foreach (var brush in FindChildBrushes(node.Left, node.WorldTransformation)) { yield return(brush); } foreach (var brush in FindChildBrushes(node.Right, node.WorldTransformation)) { yield return(brush); } yield break; } else { yield return(node); } }
// Create meshes for a given number of nodes and perform CSG on these. #region ProcessCSGNodes private static void ProcessNode(CSGNode node, CSGNode root) { if (node.cachedMesh == null) { if (node.NodeType != CSGNodeType.Brush) { ProcessNode(node.Left, node); ProcessNode(node.Right, node); node.cachedMesh = CSGMesh.Combine(node.Translation, node.Left, node.Right); node.Bounds.Clear(); node.Bounds.Add(node.Left.Bounds.Translated(Vector3.Subtract(node.Left.Translation, node.Translation))); node.Bounds.Add(node.Right.Bounds.Translated(Vector3.Subtract(node.Right.Translation, node.Translation))); UpdateMesh(node, root); } else { node.cachedMesh = CSGMesh.CreateFromPlanes(node.Generator.GetPlanes(node.WorldTransformation)); node.Bounds.Set(node.cachedMesh.Bounds); } } }
static void FindParentNode(Transform opTransform, out CSGNode parentNode, out Transform parentTransform) { var iterator = opTransform.parent; while (iterator) { var currentNodeObj = iterator.GetComponent(TypeConstants.CSGNodeType); if (currentNodeObj) { var operationNode = currentNodeObj as CSGOperation; if (!operationNode || !operationNode.PassThrough) { parentNode = currentNodeObj as CSGNode; parentTransform = iterator; return; } } iterator = iterator.parent; } parentTransform = null; parentNode = null; }
public void Init(CSGNode node, Int32 nodeID) { Transform = node.transform; TransformID = node.transform.GetInstanceID(); NodeID = nodeID; }
public CSGNode(string id, CSGNodeType branchOperator, CSGNode left, CSGNode right) { this.NodeType = branchOperator; this.Left = left; this.Right = right; }
void CreateVisualObjects() { CleanUp(); //Debug.Log("CreateVisualObjects"); //prevForceGrid = Grid.ForceGrid; prevForcedGridCenter = Grid.ForcedGridCenter; prevForcedGridRotation = Grid.ForcedGridRotation; sourceSurfaceAlignment = PrefabSourceAlignment.AlignedFront; destinationSurfaceAlignment = PrefabDestinationAlignment.AlignToSurface; visualDragGameObject = new List <GameObject>(); foreach (var obj in dragGameObjects) { CSGNode node = obj.GetComponent <CSGNode>(); if (!node) { continue; } sourceSurfaceAlignment = node.PrefabSourceAlignment; destinationSurfaceAlignment = node.PrefabDestinationAlignment; GameObject copy; if (node && node.PrefabBehaviour == PrefabInstantiateBehaviour.Copy) { copy = GameObject.Instantiate <GameObject>(obj); } else { copy = PrefabUtility.InstantiatePrefab(obj) as GameObject; } if (!copy) { continue; } copy.name = obj.name; visualDragGameObject.Add(copy); } var model = SelectionUtility.LastUsedModel; if (model) { var parent = model.transform; int counter = 0; foreach (var obj in visualDragGameObject) { if (!obj) { continue; } if (obj.activeSelf) { obj.transform.SetParent(parent, false); obj.transform.SetSiblingIndex(hoverSiblingIndex + counter); counter++; } } } }
public static void Init(this HierarchyItem self, CSGNode node, Int32 nodeID) { self.Transform = node.transform; self.TransformID = node.transform.GetInstanceID(); self.NodeID = nodeID; }
// Categorize the given inputPolygons as being inside/outside or (reverse-)aligned // with the shape that is defined by the current brush or csg-branch. // When an inputPolygon crosses the node, it is split into pieces and every individual // piece is then categorized. #region Categorize public static void Categorize(CSGNode processedNode, CSGMesh processedMesh, CSGNode categorizationNode, List <Polygon> inputPolygons, List <Polygon> inside, List <Polygon> aligned, List <Polygon> revAligned, List <Polygon> outside) { // When you go deep enough in the tree it's possible that all categories point to the same // destination. So we detect that and potentially avoid a lot of wasted work. if (inside == revAligned && inside == aligned && inside == outside) { inside.AddRange(inputPolygons); return; } Restart: if (processedNode == categorizationNode) { // When the currently processed node is the same node as we categorize against, then // we know that all our polygons are visible and we set their default category // (usually aligned, unless it's an instancing node in which case it's precalculated) foreach (var polygon in inputPolygons) { switch (polygon.Category) { case PolygonCategory.Aligned: aligned.Add(polygon); break; case PolygonCategory.ReverseAligned: revAligned.Add(polygon); break; case PolygonCategory.Inside: inside.Add(polygon); break; case PolygonCategory.Outside: outside.Add(polygon); break; } // When brushes overlap and they share the same surface area we only want to keep // the polygons of the last brush in the tree, and skip all others. // At this point in the tree we know that this polygon belongs to this brush, so // we set it to visible. If the polygon is found to share the surface area with another // brush further on in the tree it'll be set to invisible again in mesh.Intersect. polygon.Visible = true; } return; } var leftNode = categorizationNode.Left; var rightNode = categorizationNode.Right; switch (categorizationNode.NodeType) { case CSGNodeType.Brush: { processedMesh.Intersect(categorizationNode.Bounds, categorizationNode.Generator.GetPlanes(categorizationNode.WorldTransformation), categorizationNode.Translation, processedNode.Translation, inputPolygons, inside, aligned, revAligned, outside); break; } case CSGNodeType.Addition: { // ( A || B) var relativeLeftTrans = Vector3.Subtract(processedNode.Translation, leftNode.Translation); var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation); if (AABB.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds)) { if (AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { // When our polygons lie outside the bounds of both the left and the right node, then // all the polygons can be categorized as being 'outside' outside.AddRange(inputPolygons); } else { //Categorize(processedNode, mesh, right, // inputPolygons, // inside, aligned, revAligned, outside); categorizationNode = rightNode; goto Restart; } } else if (AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { //Categorize(processedNode, left, mesh, // inputPolygons, // inside, aligned, revAligned, outside); categorizationNode = leftNode; goto Restart; } else { LogicalOr(processedNode, processedMesh, categorizationNode, inputPolygons, inside, aligned, revAligned, outside, false, false); } break; } case CSGNodeType.Common: { // !(!A || !B) var relativeLeftTrans = Vector3.Subtract(processedNode.Translation, leftNode.Translation); var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation); if (AABB.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds) || AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { // When our polygons lie outside the bounds of both the left and the right node, then // all the polygons can be categorized as being 'outside' outside.AddRange(inputPolygons); } else { LogicalOr(processedNode, processedMesh, categorizationNode, inputPolygons, outside, revAligned, aligned, inside, true, true); } break; } case CSGNodeType.Subtraction: { // !(!A || B) var relativeLeftTrans = Vector3.Subtract(processedNode.Translation, leftNode.Translation); var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation); if (AABB.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds)) { // When our polygons lie outside the bounds of both the left node, then // all the polygons can be categorized as being 'outside' outside.AddRange(inputPolygons); } else if (AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { categorizationNode = leftNode; goto Restart; } else { LogicalOr(processedNode, processedMesh, categorizationNode, inputPolygons, outside, revAligned, aligned, inside, true, false); } break; } } }
// We cache our base meshes here 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 (node.cachedMesh == null) { // 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, root.Transformation.Transform); var brushMeshes = ProcessCSGNodes(node, childNodes); mesh = CSGMesh.Combine(node.Translation, brushMeshes); } else { mesh = CSGMesh.CreateFromPlanes(node.Generator.GetPlanes(node.WorldTransformation)); } // Cache the mesh node.cachedMesh = mesh; } else { mesh = node.cachedMesh; } // 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); } //ProcessNode(root, root); //Parallel.ForEach(nodes, ProcessNode); //CSGUtility.UpdateBounds(root); //UpdateMesh(root, root); //Parallel.ForEach(meshes, updateDelegate); //*/ return(meshes); }
private static void UpdateMesh(CSGNode node, CSGNode root) { //if (node.Left != null) UpdateMesh(node.Left, node); //if (node.Right != null) UpdateMesh(node.Right, node); var processedNode = node; var processedMesh = node.cachedMesh; 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; }
private static void ProcessNode(CSGNode node, CSGNode root) { if (node.cachedMesh == null) { if (node.NodeType != CSGNodeType.Brush) { ProcessNode(node.Left, node); ProcessNode(node.Right, node); node.cachedMesh = CSGMesh.Combine(node.Translation, node.Left, node.Right); node.Bounds.Clear(); node.Bounds.Add(node.Left.Bounds.Translated(Vector3.Subtract(node.Left.Translation, node.Translation))); node.Bounds.Add(node.Right.Bounds.Translated(Vector3.Subtract(node.Right.Translation, node.Translation))); UpdateMesh(node, root); } else { node.cachedMesh = CSGMesh.CreateFromPlanes(node.Generator.GetPlanes(node.WorldTransformation)); node.Bounds.Set(node.cachedMesh.Bounds); } } }
// We cache our base meshes here 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 (node.cachedMesh == null) { // 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, root.Transformation.Transform); var brushMeshes = ProcessCSGNodes(node, childNodes); mesh = CSGMesh.Combine(node.Translation, brushMeshes); } else mesh = CSGMesh.CreateFromPlanes(node.Generator.GetPlanes(node.WorldTransformation)); // Cache the mesh node.cachedMesh = mesh; } else mesh = node.cachedMesh; // 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); //ProcessNode(root, root); //Parallel.ForEach(nodes, ProcessNode); //CSGUtility.UpdateBounds(root); //UpdateMesh(root, root); //Parallel.ForEach(meshes, updateDelegate); //*/ return meshes; }
public static void Categorize(CSGNode processedNode, CSGMesh processedMesh, CSGNode categorizationNode, List<Polygon> inputPolygons, List<Polygon> inside, List<Polygon> aligned, List<Polygon> revAligned, List<Polygon> outside) { // When you go deep enough in the tree it's possible that all categories point to the same // destination. So we detect that and potentially avoid a lot of wasted work. if (inside == revAligned && inside == aligned && inside == outside) { inside.AddRange(inputPolygons); return; } Restart: if (processedNode == categorizationNode) { // When the currently processed node is the same node as we categorize against, then // we know that all our polygons are visible and we set their default category // (usually aligned, unless it's an instancing node in which case it's precalculated) foreach (var polygon in inputPolygons) { switch (polygon.Category) { case PolygonCategory.Aligned: aligned.Add(polygon); break; case PolygonCategory.ReverseAligned: revAligned.Add(polygon); break; case PolygonCategory.Inside: inside.Add(polygon); break; case PolygonCategory.Outside: outside.Add(polygon); break; } // When brushes overlap and they share the same surface area we only want to keep // the polygons of the last brush in the tree, and skip all others. // At this point in the tree we know that this polygon belongs to this brush, so // we set it to visible. If the polygon is found to share the surface area with another // brush further on in the tree it'll be set to invisible again in mesh.Intersect. polygon.Visible = true; } return; } var leftNode = categorizationNode.Left; var rightNode = categorizationNode.Right; switch (categorizationNode.NodeType) { case CSGNodeType.Brush: { processedMesh.Intersect( categorizationNode.Bounds, categorizationNode.Generator.GetPlanes(categorizationNode.WorldTransformation), categorizationNode.Translation, processedNode.Translation, inputPolygons, inside, aligned, revAligned, outside); break; } case CSGNodeType.Addition: { // ( A || B) var relativeLeftTrans = Vector3.Subtract(processedNode.Translation, leftNode.Translation); var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation); if (AABB.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds)) { if (AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { // When our polygons lie outside the bounds of both the left and the right node, then // all the polygons can be categorized as being 'outside' outside.AddRange(inputPolygons); } else { //Categorize(processedNode, mesh, right, // inputPolygons, // inside, aligned, revAligned, outside); categorizationNode = rightNode; goto Restart; } } else if (AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { //Categorize(processedNode, left, mesh, // inputPolygons, // inside, aligned, revAligned, outside); categorizationNode = leftNode; goto Restart; } else { LogicalOr(processedNode, processedMesh, categorizationNode, inputPolygons, inside, aligned, revAligned, outside, false, false); } break; } case CSGNodeType.Common: { // !(!A || !B) var relativeLeftTrans = Vector3.Subtract(processedNode.Translation, leftNode.Translation); var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation); if (AABB.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds) || AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { // When our polygons lie outside the bounds of both the left and the right node, then // all the polygons can be categorized as being 'outside' outside.AddRange(inputPolygons); } else { LogicalOr(processedNode, processedMesh, categorizationNode, inputPolygons, outside, revAligned, aligned, inside, true, true); } break; } case CSGNodeType.Subtraction: { // !(!A || B) var relativeLeftTrans = Vector3.Subtract(processedNode.Translation, leftNode.Translation); var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation); if (AABB.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds)) { // When our polygons lie outside the bounds of both the left node, then // all the polygons can be categorized as being 'outside' outside.AddRange(inputPolygons); } else if (AABB.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds)) { categorizationNode = leftNode; goto Restart; } else { LogicalOr(processedNode, processedMesh, categorizationNode, inputPolygons, outside, revAligned, aligned, inside, true, false); } break; } } }
void CreateVisualObjects(bool inSceneView = false) { CleanUp(); prevForcedGridCenter = RealtimeCSG.CSGGrid.ForcedGridCenter; prevForcedGridRotation = RealtimeCSG.CSGGrid.ForcedGridRotation; sourceSurfaceAlignment = PrefabSourceAlignment.AlignedFront; destinationSurfaceAlignment = PrefabDestinationAlignment.AlignToSurface; visualDragGameObject = new List <GameObject>(); var foundTransforms = new List <Transform>(); foreach (var obj in dragGameObjects) { foundTransforms.AddRange(obj.GetComponentsInChildren <Transform>()); CSGNode node = obj.GetComponent <CSGNode>(); if (!node) { continue; } sourceSurfaceAlignment = node.PrefabSourceAlignment; destinationSurfaceAlignment = node.PrefabDestinationAlignment; bool createCopyInsteadOfInstance = node && node.PrefabBehaviour == PrefabInstantiateBehaviour.Copy; GameObject copy = CSGPrefabUtility.Instantiate(obj, createCopyInsteadOfInstance); if (!copy) { continue; } copy.name = obj.name; visualDragGameObject.Add(copy); } ignoreTransforms = new HashSet <Transform>(foundTransforms); if (inSceneView) { var model = SelectionUtility.LastUsedModel; if (model && !containsModel) { var parent = model.transform; int counter = 0; foreach (var obj in visualDragGameObject) { if (!obj) { continue; } if (obj.activeSelf) { obj.transform.SetParent(parent, false); obj.transform.SetSiblingIndex(hoverSiblingIndex + counter); counter++; } } } } else { var parent = hoverParent; int counter = 0; foreach (var obj in visualDragGameObject) { if (!obj) { continue; } if (obj.activeSelf) { obj.transform.SetParent(parent, false); obj.transform.SetSiblingIndex(hoverSiblingIndex + counter); counter++; } } } }
// 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); } } }
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 CSGNode(string id, CSGNodeType branchOperator) { this.NodeType = branchOperator; this.Left = null; this.Right = null; }