private static bool StartToolDragging() { jumpedMousePosition += Event.current.delta; Event.current.Use(); if (ToolIsDragging) { UpdateDragVector(); return(false); } // We set ToolIsDragging to true to be able to tell the difference between dragging and clicking ToolIsDragging = true; // Find the intersecting surfaces startSurfaceReference = hoverSurfaceReference; var currentIntersection = hoverIntersection.Value.surfaceIntersection; selectedSurfaceReferences = ChiselSurfaceSelectionManager.Selection.ToArray(); // We need all the brushContainerAssets for all the surfaces we're moving, so that we can record them for an undo selectedBrushContainerAsset = ChiselSurfaceSelectionManager.SelectedBrushMeshes.ToArray(); // We copy all the original surface uvMatrices, so we always apply rotations and transformations relatively to the original // This makes it easier to recover from edge cases and makes it more accurate, floating point wise. selectedUVMatrices = new UVMatrix[selectedSurfaceReferences.Length]; for (int i = 0; i < selectedSurfaceReferences.Length; i++) { if (selectedSurfaceReferences[i].Polygon.surface == null) { selectedUVMatrices[i] = UVMatrix.identity; } else { selectedUVMatrices[i] = selectedSurfaceReferences[i].Polygon.surface.surfaceDescription.UV0; } } // Find the intersection point/plane in model space var nodeTransform = startSurfaceReference.node.hierarchyItem.Transform; var modelTransform = CSGNodeHierarchyManager.FindModelTransformOfTransform(nodeTransform); worldStartPosition = modelTransform.localToWorldMatrix.MultiplyPoint(hoverIntersection.Value.surfaceIntersection.worldIntersection); worldProjectionPlane = modelTransform.localToWorldMatrix.TransformPlane(hoverIntersection.Value.surfaceIntersection.worldPlane); worldIntersection = worldStartPosition; // TODO: we want to be able to determine delta movement over a plane. Ideally it would match the position of the cursor perfectly. // unfortunately when moving the cursor towards the horizon of the plane, relative to the camera, the delta movement // becomes too large or even infinity. Ideally we'd switch to a camera facing plane for these cases and determine movement in // a less perfect way that would still allow the user to move or rotate things in a reasonable way. // more accurate for small movements worldDragPlane = worldProjectionPlane; // TODO: (unfinished) prevents drag-plane from intersecting near plane (makes movement slow down to a singularity when further away from click position) //worldDragPlane = new Plane(Camera.current.transform.forward, worldStartPosition); // TODO: ideally we'd interpolate the behavior of the worldPlane between near and far behavior UpdateDragVector(); return(true); }
static void ResetSelection() { hoverIntersection = null; hoverSurfaceReference = null; selectedSurfaceReferences = null; selectedBrushMeshAsset = null; selectedUVMatrices = null; }
// TODO: put somewhere else public static ChiselWireframe GetSurfaceWireframe(SurfaceReference surface) { if (!surfaceOutlineCache.TryGetValue(surface, out ChiselWireframe wireframe)) { wireframe = ChiselWireframe.CreateWireframe(surface.TreeBrush, surface.surfaceID); surfaceOutlineCache[surface] = wireframe; } return(wireframe); }
static void SnapSurfaceVertices(UVSnapSettings snapSettings, SurfaceReference surfaceReference, Vector3 intersectionPoint, float preferenceFactor, ref Vector3 snappedPoint, ref float bestDist) { if (surfaceReference == null) { return; } if ((snapSettings & UVSnapSettings.GeometryVertices) == UVSnapSettings.None) { return; } var localToWorldSpace = surfaceReference.LocalToWorldSpace; var brushMesh = surfaceReference.BrushMesh; if (brushMesh == null) { return; } Debug.Assert(surfaceReference.surfaceIndex >= 0 && surfaceReference.surfaceIndex < brushMesh.polygons.Length); var polygon = brushMesh.polygons[surfaceReference.surfaceIndex]; var edges = brushMesh.halfEdges; var vertices = brushMesh.vertices; var firstEdge = polygon.firstEdge; var lastEdge = firstEdge + polygon.edgeCount; var bestDistSqr = float.PositiveInfinity; var bestVertex = snappedPoint; for (int e = firstEdge; e < lastEdge; e++) { var worldSpaceVertex = localToWorldSpace.MultiplyPoint(vertices[edges[e].vertexIndex]); var dist_sqr = (worldSpaceVertex - intersectionPoint).sqrMagnitude; if (dist_sqr < bestDistSqr) { bestDistSqr = dist_sqr; bestVertex = worldSpaceVertex; } } if (float.IsInfinity(bestDistSqr)) { return; } var closestVertexDistance = Mathf.Sqrt(bestDistSqr) * preferenceFactor; if (closestVertexDistance < bestDist) { bestDist = closestVertexDistance; snappedPoint = bestVertex; } }
static Vector3 SnapIntersection(Vector3 intersectionPoint, SurfaceReference surfaceReference, out bool haveWeSnapped) { if (surfaceReference == null) { haveWeSnapped = false; return(intersectionPoint); } // TODO: visualize what we're snapping against var bestDist = float.PositiveInfinity; var snappedPoint = intersectionPoint; var handleSize = HandleUtility.GetHandleSize(intersectionPoint); // When holding V we force to ONLY and ALWAYS snap against vertices var snapSettings = forceVertexSnapping ? UVSnapSettings.GeometryVertices : CurrentSnapSettings; // Snap to closest point on grid SnapGridIntersection(snapSettings, surfaceReference, intersectionPoint, 1.5f, ref snappedPoint, ref bestDist); // snap to vertices of surface that are closest to the intersection point SnapSurfaceVertices(snapSettings, surfaceReference, intersectionPoint, 1.0f, ref snappedPoint, ref bestDist); // snap to edges of surface that are closest to the intersection point SnapSurfaceEdges(snapSettings, surfaceReference, intersectionPoint, 2.0f, ref snappedPoint, ref bestDist); // TODO: snap to UV space bounds (and more?) var gridSnappingenabled = (CurrentSnapSettings & UVSnapSettings.GeometryGrid) != UVSnapSettings.None; var minSnapDistance = (forceVertexSnapping || gridSnappingenabled) ? float.PositiveInfinity : (handleSize * kMinSnapDistance); if (bestDist < minSnapDistance) { haveWeSnapped = true; intersectionPoint = snappedPoint; } else { haveWeSnapped = false; } return(intersectionPoint); }
public static SurfaceReference[] FindSurfaceReference(Vector2 position, bool selectAllSurfaces, out CSGTreeBrushIntersection intersection, out SurfaceReference surfaceReference) { intersection = CSGTreeBrushIntersection.None; surfaceReference = null; try { if (!PickFirstGameObject(position, out intersection)) { return(null); } var brush = intersection.brush; var node = CSGNodeHierarchyManager.FindCSGNodeByInstanceID(brush.UserID); if (!node) { return(null); } surfaceReference = node.FindSurfaceReference(brush, intersection.surfaceID); if (selectAllSurfaces) { return(node.GetAllSurfaceReferences(brush)); } if (surfaceReference == null) { return(null); } return(new SurfaceReference[] { surfaceReference }); } catch (Exception ex) { Debug.LogException(ex); return(null); } }
static bool UpdateHoverSurfaces(Vector2 mousePosition, Rect dragArea, SelectionType selectionType, bool clearHovering) { try { hoverIntersection = null; hoverSurfaceReference = null; bool modified = false; if (clearHovering || !InEditCameraMode) { if (hoverSurfaces.Count != 0) { hoverSurfaces.Clear(); modified = true; } } if (!dragArea.Contains(mousePosition)) { return(modified); } if (!InEditCameraMode) { return(modified); } CSGTreeBrushIntersection intersection; SurfaceReference surfaceReference; var foundSurfaces = CSGClickSelectionManager.FindSurfaceReference(mousePosition, false, out intersection, out surfaceReference); if (foundSurfaces == null) { modified = (hoverSurfaces != null) || modified; hoverIntersection = null; return(modified); } if (!float.IsInfinity(intersection.surfaceIntersection.distance)) { intersection.surfaceIntersection.worldIntersection = SnapIntersection(intersection.surfaceIntersection.worldIntersection, surfaceReference, out pointHasSnapped); } hoverIntersection = intersection; hoverSurfaceReference = surfaceReference; if (foundSurfaces.Length == hoverSurfaces.Count) { modified = !hoverSurfaces.ContainsAll(foundSurfaces) || modified; } else { modified = true; } if (foundSurfaces.Length > 0) { hoverSurfaces.AddRange(foundSurfaces); } return(modified); } finally { CSGSurfaceSelectionManager.SetHovering(selectionType, hoverSurfaces); } }
static void SnapSurfaceEdges(UVSnapSettings snapSettings, SurfaceReference surfaceReference, Vector3 intersectionPoint, float preferenceFactor, ref Vector3 snappedPoint, ref float bestDist) { if (surfaceReference == null) { return; } if ((snapSettings & UVSnapSettings.GeometryEdges) == UVSnapSettings.None) { return; } var localToWorldSpace = surfaceReference.LocalToWorldSpace; var subMesh = surfaceReference.SubMesh; Debug.Assert(surfaceReference.surfaceIndex >= 0 && surfaceReference.surfaceIndex < subMesh.Polygons.Length); var grid = UnitySceneExtensions.Grid.defaultGrid; var xAxis = grid.Right; var yAxis = grid.Up; var zAxis = grid.Forward; var intersectionPlane = surfaceReference.WorldPlane.Value; var snapAxis = Axis.X | Axis.Y | Axis.Z; if (Mathf.Abs(Vector3.Dot(xAxis, intersectionPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.X; } if (Mathf.Abs(Vector3.Dot(yAxis, intersectionPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.Y; } if (Mathf.Abs(Vector3.Dot(zAxis, intersectionPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.Z; } var polygons = subMesh.Polygons; var polygon = polygons[surfaceReference.surfaceIndex]; var edges = subMesh.HalfEdges; var vertices = subMesh.Vertices; var firstEdge = polygon.firstEdge; var lastEdge = firstEdge + polygon.edgeCount; for (int e = firstEdge; e < lastEdge; e++) { var twinIndex = edges[e].twinIndex; #if USE_MANAGED_CSG_IMPLEMENTATION var polygonIndex = edges[e].polygonIndex; #else // The managed CSG solution will have a polygon index stored for each half-edge // the native solution doesn't have this, however, so we need to find the polygon ourselves int polygonIndex = -1; for (int p = 0; p < polygons.Length; p++) { if (twinIndex < polygons[p].firstEdge) { continue; } if (twinIndex >= polygons[p].firstEdge + polygons[p].edgeCount) { continue; } polygonIndex = p; break; } if (polygonIndex == -1) { continue; } #endif var surfaceIndex = polygonIndex; // FIXME: throughout the code we're making assumptions about polygonIndices being the same as surfaceIndices, // this needs to be fixed var localPlane = subMesh.Orientations[surfaceIndex].localPlane; var worldPlane = localToWorldSpace.TransformPlane(localPlane); if ((CurrentSnapSettings & UVSnapSettings.GeometryGrid) != UVSnapSettings.None) { var edgeDirection = Vector3.Cross(intersectionPlane.normal, worldPlane.normal); var edgeSnapAxis = snapAxis; if (Mathf.Abs(Vector3.Dot(xAxis, edgeDirection)) >= kAlignmentEpsilon) { edgeSnapAxis &= ~Axis.X; } if (Mathf.Abs(Vector3.Dot(yAxis, edgeDirection)) >= kAlignmentEpsilon) { edgeSnapAxis &= ~Axis.Y; } if (Mathf.Abs(Vector3.Dot(zAxis, edgeDirection)) >= kAlignmentEpsilon) { edgeSnapAxis &= ~Axis.Z; } if (edgeSnapAxis == Axis.None) { continue; } float dist; var ray = new Ray(snappedPoint, xAxis); if ((edgeSnapAxis & Axis.X) != Axis.None && worldPlane.UnsignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } ray.direction = yAxis; if ((edgeSnapAxis & Axis.Y) != Axis.None && worldPlane.UnsignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } ray.direction = zAxis; if ((edgeSnapAxis & Axis.Z) != Axis.None && worldPlane.UnsignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } } else { var closestPoint = worldPlane.ClosestPointOnPlane(intersectionPoint); var dist = (closestPoint - intersectionPoint).magnitude * preferenceFactor; if (dist < bestDist) { bestDist = dist; snappedPoint = closestPoint; } } } }
static void SnapGridIntersection(UVSnapSettings snapSettings, SurfaceReference surfaceReference, Vector3 intersectionPoint, float preferenceFactor, ref Vector3 snappedPoint, ref float bestDist) { if ((snapSettings & UVSnapSettings.GeometryGrid) == UVSnapSettings.None) { return; } var grid = UnitySceneExtensions.Grid.defaultGrid; var gridSnappedPoint = Snapping.SnapPoint(intersectionPoint, grid); var worldPlane = surfaceReference.WorldPlane.Value; var xAxis = grid.Right; var yAxis = grid.Up; var zAxis = grid.Forward; var snapAxis = Axis.X | Axis.Y | Axis.Z; if (Mathf.Abs(Vector3.Dot(xAxis, worldPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.X; } if (Mathf.Abs(Vector3.Dot(yAxis, worldPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.Y; } if (Mathf.Abs(Vector3.Dot(zAxis, worldPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.Z; } if (Mathf.Abs(worldPlane.GetDistanceToPoint(gridSnappedPoint)) < kDistanceEpsilon) { bestDist = (gridSnappedPoint - intersectionPoint).magnitude * preferenceFactor; snappedPoint = gridSnappedPoint; } else { float dist; var ray = new Ray(gridSnappedPoint, xAxis); if ((snapAxis & Axis.X) != Axis.None && worldPlane.UnsignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } ray.direction = yAxis; if ((snapAxis & Axis.Y) != Axis.None && worldPlane.UnsignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } ray.direction = zAxis; if ((snapAxis & Axis.Z) != Axis.None && worldPlane.UnsignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } } }
static void SnapSurfaceEdges(UVSnapSettings snapSettings, SurfaceReference surfaceReference, Vector3 intersectionPoint, float preferenceFactor, ref Vector3 snappedPoint, ref float bestDist) { if (surfaceReference == null) { return; } if ((snapSettings & UVSnapSettings.GeometryEdges) == UVSnapSettings.None) { return; } var localToWorldSpace = surfaceReference.LocalToWorldSpace; var brushMesh = surfaceReference.BrushMesh; if (brushMesh == null) { return; } Debug.Assert(surfaceReference.surfaceIndex >= 0 && surfaceReference.surfaceIndex < brushMesh.polygons.Length); var grid = UnitySceneExtensions.Grid.defaultGrid; var xAxis = grid.Right; var yAxis = grid.Up; var zAxis = grid.Forward; var intersectionPlane = surfaceReference.WorldPlane.Value; var snapAxis = Axis.X | Axis.Y | Axis.Z; if (Mathf.Abs(Vector3.Dot(xAxis, intersectionPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.X; } if (Mathf.Abs(Vector3.Dot(yAxis, intersectionPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.Y; } if (Mathf.Abs(Vector3.Dot(zAxis, intersectionPlane.normal)) >= kAlignmentEpsilon) { snapAxis &= ~Axis.Z; } var polygons = brushMesh.polygons; var polygon = polygons[surfaceReference.surfaceIndex]; var halfEdges = brushMesh.halfEdges; var halfEdgePolygonIndices = brushMesh.halfEdgePolygonIndices; var vertices = brushMesh.vertices; var firstEdge = polygon.firstEdge; var lastEdge = firstEdge + polygon.edgeCount; for (int e = firstEdge; e < lastEdge; e++) { var twinIndex = halfEdges[e].twinIndex; var polygonIndex = halfEdgePolygonIndices[e]; var surfaceIndex = polygonIndex; // FIXME: throughout the code we're making assumptions about polygonIndices being the same as surfaceIndices, // this needs to be fixed var localPlaneVector = brushMesh.surfaces[surfaceIndex].localPlane; var localPlane = new Plane((Vector3)localPlaneVector, localPlaneVector.w); var worldPlane = localToWorldSpace.TransformPlane(localPlane); if ((CurrentSnapSettings & UVSnapSettings.GeometryGrid) != UVSnapSettings.None) { var edgeDirection = Vector3.Cross(intersectionPlane.normal, worldPlane.normal); var edgeSnapAxis = snapAxis; if (Mathf.Abs(Vector3.Dot(xAxis, edgeDirection)) >= kAlignmentEpsilon) { edgeSnapAxis &= ~Axis.X; } if (Mathf.Abs(Vector3.Dot(yAxis, edgeDirection)) >= kAlignmentEpsilon) { edgeSnapAxis &= ~Axis.Y; } if (Mathf.Abs(Vector3.Dot(zAxis, edgeDirection)) >= kAlignmentEpsilon) { edgeSnapAxis &= ~Axis.Z; } if (edgeSnapAxis == Axis.None) { continue; } float dist; var ray = new Ray(snappedPoint, xAxis); if ((edgeSnapAxis & Axis.X) != Axis.None && worldPlane.SignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } ray.direction = yAxis; if ((edgeSnapAxis & Axis.Y) != Axis.None && worldPlane.SignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } ray.direction = zAxis; if ((edgeSnapAxis & Axis.Z) != Axis.None && worldPlane.SignedRaycast(ray, out dist)) { var planePoint = ray.GetPoint(dist); var abs_dist = (planePoint - intersectionPoint).magnitude * preferenceFactor; if (abs_dist < bestDist) { bestDist = abs_dist; snappedPoint = planePoint; } } } else { var closestPoint = worldPlane.ClosestPointOnPlane(intersectionPoint); var dist = (closestPoint - intersectionPoint).magnitude * preferenceFactor; if (dist < bestDist) { bestDist = dist; snappedPoint = closestPoint; } } } }
public static bool IsSelected(SurfaceReference surface) { return(Data.selectedSurfaces.Contains(surface)); }
public static SurfaceReference[] FindSurfaceReferences(Vector2 position, bool selectAllSurfaces, out ChiselIntersection intersection, out SurfaceReference surfaceReference) { intersection = ChiselIntersection.None; surfaceReference = null; try { if (!PickFirstGameObject(position, out intersection)) { return(null); } var node = intersection.node; if (!node) { return(null); } var brush = intersection.brushIntersection.brush; surfaceReference = node.FindSurfaceReference(brush, intersection.brushIntersection.surfaceIndex); if (selectAllSurfaces) { return(node.GetAllSurfaceReferences(brush)); } if (surfaceReference == null) { return(null); } return(new SurfaceReference[] { surfaceReference }); } catch (Exception ex) { Debug.LogException(ex); return(null); } }
public SurfaceOutline(Transform transform, SurfaceReference surface) { this.transform = transform; this.surface = surface; }
static bool FindSurfaceReference(ChiselNode chiselNode, CSGTreeBrush brush, CSGTreeBrush findBrush, int surfaceID, out SurfaceReference surfaceReference) { surfaceReference = null; if (findBrush != brush) { return(false); } var brushMeshBlob = BrushMeshManager.GetBrushMeshBlob(findBrush.BrushMesh.BrushMeshID); if (!brushMeshBlob.IsCreated) { return(true); } ref var brushMesh = ref brushMeshBlob.Value;
static bool FindSurfaceReference(ChiselNode chiselNode, CSGTreeBranch branch, CSGTreeBrush findBrush, int surfaceID, out SurfaceReference surfaceReference) { surfaceReference = null; for (int i = 0; i < branch.Count; i++) { if (FindSurfaceReference(chiselNode, branch[i], findBrush, surfaceID, out surfaceReference)) { return(true); } } return(false); }
static bool FindSurfaceReference(ChiselNode chiselNode, CSGTreeNode node, CSGTreeBrush findBrush, int surfaceID, out SurfaceReference surfaceReference) { surfaceReference = null; switch (node.Type) { case CSGNodeType.Branch: return(FindSurfaceReference(chiselNode, (CSGTreeBranch)node, findBrush, surfaceID, out surfaceReference)); case CSGNodeType.Brush: return(FindSurfaceReference(chiselNode, (CSGTreeBrush)node, findBrush, surfaceID, out surfaceReference)); default: return(false); } }