Esempio n. 1
0
        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.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 ((snapAxis & 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 ((snapAxis & 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;
                    }
                }
            }
        }
        public Vector3 SnapExtents1D(Vector3 currentPosition)
        {
            this.snapResult = SnapResult1D.None;

            var boundsActive = Snapping.BoundsSnappingActive;
            var pivotActive  = Snapping.PivotSnappingActive;

            // Get custom snapping positions along the ray
            var haveCustomSnapping = Snapping.GetCustomSnappingPoints(this.slideOffset + slidePosition, this.slideDirection, 0, s_CustomSnapPoints);

            if (!boundsActive && !pivotActive && !haveCustomSnapping)
            {
                return(currentPosition);
            }


            const float kMinPointSnap = 0.25f;
            float       minPointSnap  = !(boundsActive || pivotActive) ? kMinPointSnap : float.PositiveInfinity;


            // Offset to snapped position relative to the unsnapped position
            // (used to determine which snap value is closest to unsnapped position)
            // Smallest value is used
            float snappedOffsetDistance    = float.PositiveInfinity;
            float snappedOffsetAbsDistance = float.PositiveInfinity;


            var deltaToOrigin    = currentPosition - this.slideOrigin;
            var distanceToOrigin = SnappingUtility.WorldPointToDistance(deltaToOrigin, this.slideDirection);

            var quantized_min_extents = float.PositiveInfinity;
            var quantized_max_extents = float.PositiveInfinity;
            var snappedExtents        = Extents1D.empty;

            if (boundsActive)
            {
                (float abs_distance, float snappedOffset, float quantized_min, float quantized_max) = Snapping.SnapBounds(this.slideExtents + distanceToOrigin, this.snappingStep);
                quantized_min_extents = quantized_min;
                quantized_max_extents = quantized_max;
                snappedExtents.min    = this.slideExtents.min + distanceToOrigin + Mathf.Abs(quantized_min_extents);
                snappedExtents.max    = this.slideExtents.min + distanceToOrigin + Mathf.Abs(quantized_max_extents);
                if (snappedOffsetAbsDistance > abs_distance)
                {
                    snappedOffsetAbsDistance = abs_distance; snappedOffsetDistance = snappedOffset;
                }
            }

            var quantized_pivot = float.PositiveInfinity;

            if (pivotActive)
            {
                (float abs_distance, float snappedOffset, float quantized) = Snapping.SnapPoint(this.startOffset + distanceToOrigin, this.snappingStep);
                quantized_pivot = quantized;
                if (snappedOffsetAbsDistance > abs_distance)
                {
                    snappedOffsetAbsDistance = abs_distance; snappedOffsetDistance = snappedOffset;
                }
            }

            if (haveCustomSnapping)
            {
                (float abs_distance, float snappedOffset) = Snapping.SnapCustom(s_CustomSnapPoints, this.startOffset + distanceToOrigin, this.slideDirection, minPointSnap, s_CustomDistances);
                if (snappedOffsetAbsDistance > abs_distance)
                {
                    snappedOffsetAbsDistance = abs_distance; snappedOffsetDistance = snappedOffset;
                }
            }

            // If we didn't actually snap, just return the actual unsnapped position
            if (float.IsInfinity(snappedOffsetDistance))
            {
                return(currentPosition);
            }

            // Snap against drag start position
            if (Mathf.Abs(snappedOffsetDistance) > Mathf.Abs(distanceToOrigin))
            {
                snappedOffsetDistance = distanceToOrigin;
            }

            var quantizedDistance = SnappingUtility.Quantize(snappedOffsetDistance);

            // Figure out what kind of snapping visualization to show, this needs to be done afterwards since
            // while we're snapping each type of snap can override the next one.
            // Yet at the same time it's possible to snap with multiple snap-types at the same time.

            if (boundsActive)
            {
                if (quantizedDistance == quantized_min_extents)
                {
                    this.snapResult |= SnapResult1D.Min;
                }
                if (quantizedDistance == quantized_max_extents)
                {
                    this.snapResult |= SnapResult1D.Max;
                }

                min = this.slideOrigin + SnappingUtility.DistanceToWorldPoint(snappedExtents.min, this.slideDirection);
                max = this.slideOrigin + SnappingUtility.DistanceToWorldPoint(snappedExtents.max, this.slideDirection);
            }

            if (pivotActive)
            {
                if (quantizedDistance == quantized_pivot)
                {
                    this.snapResult |= SnapResult1D.Pivot;
                }
            }

            if (haveCustomSnapping)
            {
                Snapping.SendCustomSnappedEvents(quantizedDistance, s_CustomDistances, 0);
            }


            // Calculate the new position based on the snapped offset
            var newOffset       = distanceToOrigin - snappedOffsetDistance;
            var snappedDistance = SnappingUtility.DistanceToWorldPoint(newOffset, this.slideDirection);
            var snappedPosition = (snappedDistance + this.slideOrigin);

            return(snappedPosition);
        }
Esempio n. 3
0
        static void FindClosestSnapPointsToPlane(GameObject[] selection, Vector3 startWorldPoint, Vector3 currentWorldPoint, Grid worldSlideGrid, float maxSnapDistance, List <SurfaceSnap> allSurfaceSnapEvents, List <EdgeSnap> allEdgeSnapEvents, List <VertexSnap> allVertexSnapEvents)
        {
            if (selection == null || selection.Length == 0)
            {
                return;
            }

            if (allSurfaceSnapEvents == null &&
                allEdgeSnapEvents == null &&
                allVertexSnapEvents == null)
            {
                return;
            }

            var worldSlidePlane = worldSlideGrid.PlaneXZ;

            var gridSnapping = Snapping.GridSnappingActive;

            if (gridSnapping)
            {
                var vectorX = worldSlideGrid.Right * maxSnapDistance;
                var vectorZ = worldSlideGrid.Forward * maxSnapDistance;

                var snappedWorldPoint = Snapping.SnapPoint(currentWorldPoint, worldSlideGrid);
                FindSnapPointsAlongRay(selection, snappedWorldPoint, vectorX, allSurfaceSnapEvents, allEdgeSnapEvents, allVertexSnapEvents);
                FindSnapPointsAlongRay(selection, snappedWorldPoint, vectorZ, allSurfaceSnapEvents, allEdgeSnapEvents, allVertexSnapEvents);

                snappedWorldPoint = Snapping.SnapPoint(startWorldPoint, worldSlideGrid);
                FindSnapPointsAlongRay(selection, snappedWorldPoint, vectorX, allSurfaceSnapEvents, allEdgeSnapEvents, allVertexSnapEvents);
                FindSnapPointsAlongRay(selection, snappedWorldPoint, vectorZ, allSurfaceSnapEvents, allEdgeSnapEvents, allVertexSnapEvents);
            }


            worldSlidePlane = new Plane(worldSlidePlane.normal, currentWorldPoint);

            s_SelectedBrushes.Clear();
            foreach (var go in selection)
            {
                if (!go)
                {
                    continue;
                }

                var node = go.GetComponent <ChiselNode>();
                if (!node)
                {
                    continue;
                }

                s_SelectedNodes.Clear();
                node.CollectCSGTreeNodes(s_SelectedNodes);
                foreach (var child in s_SelectedNodes)
                {
                    if (!child.Valid || child.Type != CSGNodeType.Brush)
                    {
                        continue;
                    }
                    s_SelectedBrushes.Add((CSGTreeBrush)child);
                }
            }

            if (s_SelectedBrushes.Count == 0)
            {
                return;
            }

            var snapDistanceSqr = maxSnapDistance * maxSnapDistance;

            EdgeSnap[] foundEdges = new EdgeSnap[2];
            int        foundEdgeCount;

            foreach (var csgBrush in s_SelectedBrushes)
            {
                var csgTree   = csgBrush.Tree;
                var brushMesh = BrushMeshManager.GetBrushMesh(csgBrush.BrushMesh);
                var polygons  = brushMesh.polygons;
                var halfEdges = brushMesh.halfEdges;
                var vertices  = brushMesh.vertices;
                var planes    = brushMesh.planes;
                var halfEdgePolygonIndices = brushMesh.halfEdgePolygonIndices;

                // TODO: store this information with brush
                var model       = ChiselNodeHierarchyManager.FindChiselNodeByInstanceID(csgTree.UserID) as ChiselModel;
                var worldToNode = csgBrush.TreeToNodeSpaceMatrix * model.hierarchyItem.WorldToLocalMatrix;
                var nodeToWorld = model.hierarchyItem.LocalToWorldMatrix * csgBrush.NodeToTreeSpaceMatrix;

                var brushPoint = worldToNode.MultiplyPoint(currentWorldPoint);
                var brushPlane = worldToNode.TransformPlane(worldSlidePlane);

                if (allVertexSnapEvents != null)
                {
                    if (gridSnapping)
                    {
                        for (int i = 0; i < vertices.Length; i++)
                        {
                            var vertex = vertices[i];
                            var dist0  = brushPlane.GetDistanceToPoint(vertex);
                            if (math.abs(dist0) > snapDistanceSqr)
                            {
                                continue;
                            }
                            allVertexSnapEvents.Add(new VertexSnap
                            {
                                brush        = csgBrush,
                                vertexIndex  = i,
                                intersection = nodeToWorld.MultiplyPoint(vertex)
                            });
                        }
                    }
                    else
                    {
                        for (int i = 0; i < vertices.Length; i++)
                        {
                            var vertex = vertices[i];
                            if (math.lengthsq(vertex - (float3)brushPoint) > snapDistanceSqr)
                            {
                                continue;
                            }
                            var dist0 = brushPlane.GetDistanceToPoint(vertex);
                            if (math.abs(dist0) > snapDistanceSqr)
                            {
                                continue;
                            }
                            allVertexSnapEvents.Add(new VertexSnap
                            {
                                brush        = csgBrush,
                                vertexIndex  = i,
                                intersection = nodeToWorld.MultiplyPoint(vertex)
                            });
                        }
                    }
                }


                if (allSurfaceSnapEvents == null &&
                    allEdgeSnapEvents == null)
                {
                    continue;
                }


                for (int surfaceIndex = 0; surfaceIndex < polygons.Length; surfaceIndex++)
                {
                    var polygon   = polygons[surfaceIndex];
                    var firstEdge = polygon.firstEdge;
                    var lastEdge  = firstEdge + polygon.edgeCount;

                    // TODO: If point is ON plane, ignore. We don't want to "snap" to every point on that surface b/c then we won't be snapping at all

                    foundEdgeCount = 0;
                    for (int e0 = lastEdge - 1, e1 = firstEdge; e1 < lastEdge; e0 = e1, e1++)
                    {
                        var i0 = halfEdges[e0].vertexIndex;
                        var i1 = halfEdges[e1].vertexIndex;

                        var vertex0 = vertices[i0];
                        var vertex1 = vertices[i1];

                        var distance0 = brushPlane.GetDistanceToPoint(vertex0);
                        var distance1 = brushPlane.GetDistanceToPoint(vertex1);

                        // Edge is plane aligned
                        if (math.abs(distance0) < kPlaneDistanceEpsilon &&
                            math.abs(distance1) < kPlaneDistanceEpsilon)
                        {
                            if (i0 < i1 && // skip duplicate edges
                                allEdgeSnapEvents != null)
                            {
                                if (gridSnapping)
                                {
                                }
                                else
                                {
                                    if (ClosestPointToLine(brushPoint, vertex0, vertex1, out Vector3 newVertex))
                                    {
                                        allEdgeSnapEvents.Add(new EdgeSnap
                                        {
                                            brush         = csgBrush,
                                            surfaceIndex0 = surfaceIndex,
                                            surfaceIndex1 = halfEdgePolygonIndices[halfEdges[e1].twinIndex],
                                            vertexIndex0  = i0,
                                            vertexIndex1  = i1,
                                            intersection  = nodeToWorld.MultiplyPoint(newVertex),
                                            from          = nodeToWorld.MultiplyPoint(vertex0),
                                            to            = nodeToWorld.MultiplyPoint(vertex1)
                                        });
                                    }
                                }
                            }
                            continue;
                        }

                        {
                            if ((distance0 < -snapDistanceSqr && distance1 < -snapDistanceSqr) ||
                                (distance0 > snapDistanceSqr && distance1 > snapDistanceSqr))
                            {
                                continue;
                            }

                            // TODO: Find intersection between plane and edge
                            var vector = vertex0 - vertex1;
                            var length = distance0 - distance1;
                            var delta  = distance0 / length;

                            if (float.IsNaN(delta) || float.IsInfinity(delta))
                            {
                                continue;
                            }

                            var newVertex = (Vector3)(vertex0 - (vector * delta));
                            var distanceN = brushPlane.GetDistanceToPoint(newVertex);

                            if ((distanceN <= distance0 && distanceN <= distance1) ||
                                (distanceN >= distance0 && distanceN >= distance1))
                            {
                                continue;
                            }

                            if ((newVertex - brushPoint).sqrMagnitude > snapDistanceSqr)
                            {
                                continue;
                            }

                            foundEdges[foundEdgeCount] = new EdgeSnap
                            {
                                brush         = csgBrush,
                                surfaceIndex0 = surfaceIndex,
                                surfaceIndex1 = halfEdgePolygonIndices[halfEdges[e1].twinIndex],
                                vertexIndex0  = i0,
                                vertexIndex1  = i1,
                                intersection  = nodeToWorld.MultiplyPoint(newVertex),
                                from          = nodeToWorld.MultiplyPoint(vertex0),
                                to            = nodeToWorld.MultiplyPoint(vertex1)
                            };
                            if (i0 < i1 && // skip duplicate edges
                                allEdgeSnapEvents != null)
                            {
                                allEdgeSnapEvents.Add(foundEdges[foundEdgeCount]);
                            }

                            foundEdgeCount++;
                            if (foundEdgeCount == 2)
                            {
                                break;
                            }
                        }
                    }

                    if (allSurfaceSnapEvents != null && foundEdgeCount > 0 && !gridSnapping)
                    {
                        if (foundEdgeCount == 2)
                        {
                            var plane      = planes[surfaceIndex];
                            var unityPlane = new Plane(plane.xyz, plane.w);

                            var vertex0 = foundEdges[0].intersection;
                            var vertex1 = foundEdges[1].intersection;

                            if (ClosestPointToLine(currentWorldPoint, vertex0, vertex1, out Vector3 closestWorldPoint))
                            {
                                allSurfaceSnapEvents.Add(new SurfaceSnap
                                {
                                    brush        = csgBrush,
                                    surfaceIndex = surfaceIndex,
                                    intersection = closestWorldPoint,
                                    normal       = nodeToWorld.MultiplyVector(unityPlane.normal),
                                });
                            }
                        }
                    }
                }
            }
        }
        public Vector3 SnapExtents3D(Extents3D extentsInGridSpace, Vector3 worldCurrentPosition, Vector3 worldStartPosition, Grid worldSlideGrid, out SnapResult3D snapResult, Axes enabledAxes = Axes.XYZ, bool ignoreStartPoint = false)
        {
            s_CustomSnapPoints.Clear();
            // TODO: have a method that handles multiple dimensions at the same time
            var haveCustomSnapping = Snapping.GetCustomSnappingPoints(worldStartPosition, worldCurrentPosition, worldSlideGrid, 0, s_CustomSnapPoints);

            var boundsActive = Snapping.BoundsSnappingActive;
            var pivotActive  = Snapping.PivotSnappingActive;

            snapResult = SnapResult3D.None;
            if (!boundsActive && !pivotActive && !haveCustomSnapping)
            {
                return(worldCurrentPosition);
            }

            const float kMinPointSnap = 0.25f;
            float       minPointSnap  = !(boundsActive || pivotActive) ? kMinPointSnap : float.PositiveInfinity;


            var offsetInWorldSpace = worldCurrentPosition - worldStartPosition;
            var offsetInGridSpace  = _worldToGridSpace.MultiplyVector(offsetInWorldSpace);
            var pivotInGridSpace   = _worldToGridSpace.MultiplyVector(worldCurrentPosition - Center);

            // Snap our extents in grid space
            var movedExtentsInGridspace = extentsInGridSpace + offsetInGridSpace;


            var snappedOffset    = Vector3.zero;
            var absSnappedOffset = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);

            var enabledAxisLookup = new[] { (enabledAxes & Axes.X) > 0, (enabledAxes & Axes.Y) > 0, (enabledAxes & Axes.Z) > 0 };

            var quantized_pivot       = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
            var quantized_min_extents = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
            var quantized_max_extents = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);

            for (int i = 0; i < 3; i++)
            {
                if (!enabledAxisLookup[i])
                {
                    continue;
                }

                if (pivotActive)
                {
                    (float abs_pivot_offset, float snappedPivot, float quantized_offset) = Snapping.SnapPoint(pivotInGridSpace[i], _spacing[i]);
                    quantized_pivot[i] = quantized_offset;
                    if (absSnappedOffset[i] > abs_pivot_offset)
                    {
                        absSnappedOffset[i] = abs_pivot_offset; snappedOffset[i] = snappedPivot;
                    }
                }

                if (boundsActive)
                {
                    (float abs_bounds_distance, float snappedBoundsOffset, float quantized_min, float quantized_max) = Snapping.SnapBounds(movedExtentsInGridspace[i], _spacing[i]);
                    quantized_min_extents[i] = quantized_min;
                    quantized_max_extents[i] = quantized_max;

                    if (absSnappedOffset[i] > abs_bounds_distance)
                    {
                        absSnappedOffset[i] = abs_bounds_distance; snappedOffset[i] = snappedBoundsOffset;
                    }
                }
            }

            if (haveCustomSnapping)
            {
                (Vector3 abs_distance, Vector3 snappedCustomOffset) = Snapping.SnapCustom(s_CustomSnapPoints, pivotInGridSpace, enabledAxes, minPointSnap, s_CustomDistances);
                if (absSnappedOffset.sqrMagnitude > abs_distance.sqrMagnitude)
                {
                    absSnappedOffset = abs_distance; snappedOffset = snappedCustomOffset;
                }
            }

            // Snap against drag start position
            if (!ignoreStartPoint)
            {
                if (Mathf.Abs(snappedOffset.x) > Mathf.Abs(offsetInGridSpace.x))
                {
                    offsetInGridSpace.x = snappedOffset.x = 0;
                }
                if (Mathf.Abs(snappedOffset.y) > Mathf.Abs(offsetInGridSpace.y))
                {
                    offsetInGridSpace.y = snappedOffset.y = 0;
                }
                if (Mathf.Abs(snappedOffset.z) > Mathf.Abs(offsetInGridSpace.z))
                {
                    offsetInGridSpace.z = snappedOffset.z = 0;
                }
            }

            var quantizedOffset = new Vector3(SnappingUtility.Quantize(snappedOffset.x),
                                              SnappingUtility.Quantize(snappedOffset.y),
                                              SnappingUtility.Quantize(snappedOffset.z));

            // Figure out what kind of snapping visualization to show, this needs to be done afterwards since
            // while we're snapping each type of snap can override the next one.
            // Yet at the same time it's possible to snap with multiple snap-types at the same time.

            if (boundsActive)
            {
                if (quantized_min_extents.x == quantizedOffset.x)
                {
                    snapResult |= SnapResult3D.MinX;
                }
                if (quantized_max_extents.x == quantizedOffset.x)
                {
                    snapResult |= SnapResult3D.MaxX;
                }

                if (quantized_min_extents.y == quantizedOffset.y)
                {
                    snapResult |= SnapResult3D.MinY;
                }
                if (quantized_max_extents.y == quantizedOffset.y)
                {
                    snapResult |= SnapResult3D.MaxY;
                }

                if (quantized_min_extents.z == quantizedOffset.z)
                {
                    snapResult |= SnapResult3D.MinZ;
                }
                if (quantized_max_extents.z == quantizedOffset.z)
                {
                    snapResult |= SnapResult3D.MaxZ;
                }
            }

            if (pivotActive)
            {
                if (quantized_pivot.x == quantizedOffset.x)
                {
                    snapResult |= SnapResult3D.PivotX;
                }
                if (quantized_pivot.y == quantizedOffset.y)
                {
                    snapResult |= SnapResult3D.PivotY;
                }
                if (quantized_pivot.z == quantizedOffset.z)
                {
                    snapResult |= SnapResult3D.PivotZ;
                }
            }

            if (haveCustomSnapping)
            {
                Snapping.SendCustomSnappedEvents(quantizedOffset, s_CustomDistances, 0);
            }

            if (absSnappedOffset.x == 0 &&
                absSnappedOffset.y == 0 &&
                absSnappedOffset.z == 0)
            {
                return(worldStartPosition);
            }

            var snappedOffsetInWorldSpace   = _gridToWorldSpace.MultiplyVector(offsetInGridSpace - snappedOffset);
            var snappedPositionInWorldSpace = (worldStartPosition + snappedOffsetInWorldSpace);

            //Debug.Log($"{(float3)snappedOffsetInWorldSpace} {(float3)snappedOffset} {(float3)snappedPositionInWorldSpace}");

            return(snappedPositionInWorldSpace);
        }