예제 #1
0
        public bool ExtensionVectorIntersects(ObjectPlacementBoxStackSegment querySegment)
        {
            if (NumberOfStacks == 0 || querySegment.NumberOfStacks == 0)
            {
                return(false);
            }

            if (_extensionDirection.IsPerpendicularTo(querySegment.ExtensionDirection))
            {
                Plane   segmentBasePlane = GetBasePlane();
                Vector3 thisSegmentStart = segmentBasePlane.ProjectPoint(FirstStackBasePosition);
                Vector3 thisSegmentEnd   = segmentBasePlane.ProjectPoint(LastStackBasePosition);
                if (NumberOfStacks == 1)
                {
                    thisSegmentEnd = thisSegmentStart + _extensionDirection * 0.5f * GetBoxSizeAlongNormalizedDirection(_extensionDirection);
                }

                Vector3 querySegmentStart = segmentBasePlane.ProjectPoint(querySegment.FirstStackBasePosition);
                Vector3 querySegmentEnd   = segmentBasePlane.ProjectPoint(querySegment.LastStackBasePosition);
                if (querySegment.NumberOfStacks == 1)
                {
                    querySegmentEnd = querySegmentStart + querySegment.ExtensionDirection * 0.5f * querySegment.GetBoxSizeAlongNormalizedDirection(querySegment.ExtensionDirection);
                }

                Segment3D thisSegment3D  = new Segment3D(thisSegmentStart, thisSegmentEnd);
                Segment3D querySegment3D = new Segment3D(querySegmentStart, querySegmentEnd);

                return(thisSegment3D.IntersectsWith(querySegment3D));
            }

            return(false);
        }
예제 #2
0
        public static Plane GetPolygonEdgePlane(Segment3D polyEdge, Vector3 polyNormal)
        {
            Vector3 planeNormal = Vector3.Cross(polyEdge.Direction, polyNormal);

            planeNormal.Normalize();

            return(new Plane(planeNormal, polyEdge.StartPoint));
        }
예제 #3
0
        private Plane GetSegmentPlane(Segment3D segment)
        {
            Vector3 segmentPlaneNormal = Vector3.Cross(segment.Direction, _polygonNormal);

            segmentPlaneNormal.Normalize();

            return(new Plane(segmentPlaneNormal, segment.StartPoint));
        }
예제 #4
0
        public Plane GetSegmentPlane(int segmentIndex)
        {
            Segment3D segment = GetSegment(segmentIndex);

            Vector3 segmentPlaneNormal = Vector3.Cross(segment.Direction, _plane.normal);

            segmentPlaneNormal.Normalize();
            return(new Plane(segmentPlaneNormal, segment.StartPoint));
        }
예제 #5
0
            public static Plane CalculateSegmentPlaneNormal(ObjectNode firstNode, ObjectNode secondNode)
            {
                Segment3D segment            = CalculateSegmentBetweenNodes(firstNode, secondNode);
                Vector3   segmentPlaneNormal = Vector3.Cross(segment.Direction, firstNode.ObjectSurfaceData.SurfaceNormal);

                segmentPlaneNormal.Normalize();

                return(new Plane(segmentPlaneNormal, segment.StartPoint));
            }
예제 #6
0
        public bool IntersectsWith(Segment3D segment, out float t1, out float t2)
        {
            t1 = t2 = 0.0f;

            if (_length == 0.0f || segment.Length == 0.0f)
            {
                return(false);
            }

            // If the segments are parallel, they don't intersect
            if (segment.NormalizedDirection.IsAlignedWith(_normalizedDirection))
            {
                return(false);
            }

            // Check if the 2 segments are coplanar
            Vector3 segmentPlaneNormal = Vector3.Cross(segment.NormalizedDirection, _normalizedDirection);
            Vector3 fromThisSegmentStartToOtherSegmentStart = segment.StartPoint - _startPoint;

            if (!fromThisSegmentStartToOtherSegmentStart.IsPerpendicularTo(segmentPlaneNormal))
            {
                return(false);
            }

            // Build a plane which slices thorough the segment
            Vector3 slicingPlaneNormal = Vector3.Cross(segmentPlaneNormal, _normalizedDirection);

            slicingPlaneNormal.Normalize();
            Plane slicingPlane = new Plane(slicingPlaneNormal, _startPoint);

            // Check if the query segment intersects the plane
            float t;
            Ray3D querySegmentRay = new Ray3D(segment.StartPoint, segment.Direction);

            if (querySegmentRay.IntersectsPlane(slicingPlane, out t))
            {
                // The segment intersect the plane, but we have to ensure that the intersection point lies somewehre along 'this' segment.
                Vector3 intersectionPoint = querySegmentRay.Origin + t * querySegmentRay.Direction;

                Vector3 toIntersectionPoint = intersectionPoint - _startPoint;
                if (Vector3.Dot(toIntersectionPoint, _normalizedDirection) < 0.0f)
                {
                    return(false);
                }
                if (toIntersectionPoint.sqrMagnitude > _sqrLength)
                {
                    return(false);
                }

                t1 = toIntersectionPoint.magnitude / _length;
                t2 = t;

                return(true);
            }

            return(false);
        }
        public Plane GetBoundarySegmentPlane(int segmentIndex)
        {
            Segment3D boundarySegment = GetBoundarySegment(segmentIndex);

            Vector3 centerPivotPoint = GetPointByIndex(IndexOfPointInCenter);
            Vector3 centerPivotPointProjectedOnSegment = centerPivotPoint.CalculateProjectionPointOnSegment(boundarySegment.StartPoint, boundarySegment.EndPoint);
            Vector3 planeNormal = centerPivotPointProjectedOnSegment - centerPivotPoint;

            planeNormal.Normalize();

            return(new Plane(planeNormal, boundarySegment.StartPoint));
        }
예제 #8
0
        public List <Segment3D> Calculate()
        {
            Vector3Extensions.EliminateDuplicatePoints(_polygonPointsOnSamePlane);
            if (_polygonPointsOnSamePlane.Count < 3)
            {
                return(new List <Segment3D>());
            }

            List <Vector3> workingPointList = new List <Vector3>(_polygonPointsOnSamePlane);

            Vector3 minPoint, maxPoint;

            CalculateInitialMinMaxPoints(out minPoint, out maxPoint);
            workingPointList.RemoveAll(item => item == minPoint || item == maxPoint);

            List <Segment3D> allHullSegments       = new List <Segment3D>();
            Segment3D        minMaxSegment         = new Segment3D(minPoint, maxPoint);
            Plane            minMaxSegmentPlane    = GetSegmentPlane(minMaxSegment);
            bool             furthestPointIsBehind = false;
            int indexOfPointFurthestFromPlane      = minMaxSegmentPlane.GetIndexOfFurthestPointInFront(workingPointList);

            if (indexOfPointFurthestFromPlane < 0)
            {
                indexOfPointFurthestFromPlane = minMaxSegmentPlane.GetIndexOfFurthestPointBehind(workingPointList);
                furthestPointIsBehind         = true;
            }
            if (indexOfPointFurthestFromPlane >= 0)
            {
                Vector3 furthestPointFromPlane = workingPointList[indexOfPointFurthestFromPlane];
                workingPointList.RemoveAt(indexOfPointFurthestFromPlane);

                List <Segment3D> hullSegments = new List <Segment3D>();
                if (furthestPointIsBehind)
                {
                    hullSegments.Add(new Segment3D(minMaxSegment.EndPoint, furthestPointFromPlane));
                    hullSegments.Add(new Segment3D(furthestPointFromPlane, minMaxSegment.StartPoint));
                    hullSegments.Add(minMaxSegment);
                    QuickHull(workingPointList, hullSegments);
                }
                else
                {
                    hullSegments.Add(new Segment3D(furthestPointFromPlane, minMaxSegment.EndPoint));
                    hullSegments.Add(new Segment3D(minMaxSegment.StartPoint, furthestPointFromPlane));
                    hullSegments.Add(new Segment3D(minMaxSegment.EndPoint, minMaxSegment.StartPoint));      // Reversed min-max segment
                    QuickHull(workingPointList, hullSegments);
                }

                allHullSegments.AddRange(hullSegments);
            }

            return(allHullSegments);
        }
예제 #9
0
        private bool IsPointInsideHull(List <Segment3D> currentHullSegments, Vector3 point)
        {
            for (int segmentIndex = 0; segmentIndex < currentHullSegments.Count; ++segmentIndex)
            {
                Segment3D currentSegment = currentHullSegments[segmentIndex];
                if (!IsPointBehindSegmentPlane(currentSegment, point))
                {
                    return(false);
                }
            }

            return(true);
        }
예제 #10
0
        private void QuickHull(List <Vector3> workingPoints, List <Segment3D> hullSegments)
        {
            // First, remove all points which lie inside the hull
            for (int pointIndex = 0; pointIndex < workingPoints.Count;)
            {
                Vector3 point = workingPoints[pointIndex];
                if (IsPointInsideHull(hullSegments, point))
                {
                    workingPoints.RemoveAt(pointIndex);
                }
                else
                {
                    ++pointIndex;
                }
            }

            // Loop thorugh each existing hull segment and create new segments as necessary
            var newSegments = new List <Segment3D>();

            for (int segmentIndex = 0; segmentIndex < hullSegments.Count;)
            {
                Segment3D currentSegment      = hullSegments[segmentIndex];
                Plane     currentSegmentPlane = GetSegmentPlane(currentSegment);

                // Expand the hull by creating new segments if necessary
                int indexOfFurthestPointInFront = currentSegmentPlane.GetIndexOfFurthestPointInFront(workingPoints);
                if (indexOfFurthestPointInFront >= 0)
                {
                    Vector3 furthestPointInFront = workingPoints[indexOfFurthestPointInFront];
                    hullSegments.RemoveAt(segmentIndex);

                    Segment3D firstSegment  = new Segment3D(currentSegment.StartPoint, furthestPointInFront);
                    Segment3D secondSegment = new Segment3D(furthestPointInFront, currentSegment.EndPoint);

                    newSegments.Add(firstSegment);
                    newSegments.Add(secondSegment);

                    workingPoints.RemoveAt(indexOfFurthestPointInFront);
                }
                else
                {
                    ++segmentIndex;
                }
            }

            if (newSegments.Count != 0)
            {
                hullSegments.AddRange(newSegments);
                QuickHull(workingPoints, hullSegments);
            }
        }
예제 #11
0
        private void CalculateIndexOfStrokeAlignmentSegmentPlane()
        {
            int indexOfPointClosestToStrokeSurfacePickPoint = _pivotPointsOfLastPlacedHierarchy.GetIndexOfPointClosestToPoint(_strokeSurface.MouseCursorPickPoint);

            if (indexOfPointClosestToStrokeSurfacePickPoint != ProjectedBoxFacePivotPoints.IndexOfPointInCenter)
            {
                Vector3 pivotPointClosestToStrokeSurfacePickPoint = _pivotPointsOfLastPlacedHierarchy.GetPointByIndex(indexOfPointClosestToStrokeSurfacePickPoint);

                // Now identify the segment whose center point is closest to the calculated pivot point
                List <Segment3D> segments = _pivotPointsOfLastPlacedHierarchy.GetAllBoundarySegments();
                int   indexOfBestSegment  = -1;
                float minDistance         = float.MaxValue;
                for (int segmentIndex = 0; segmentIndex < segments.Count; ++segmentIndex)
                {
                    Segment3D segment         = segments[segmentIndex];
                    Vector3   segmentMidPoint = segment.GetPoint(0.5f);

                    float distanceFromPivotPoint = (segmentMidPoint - pivotPointClosestToStrokeSurfacePickPoint).magnitude;
                    if (distanceFromPivotPoint < minDistance)
                    {
                        minDistance        = distanceFromPivotPoint;
                        indexOfBestSegment = segmentIndex;
                    }
                }

                _indexOfStrokeAlignmentSegmentPlane = indexOfBestSegment;
            }
            else
            {
                Vector3      fromCenterToStrokeSurfacePickPoint = _strokeSurface.MouseCursorPickPoint - _pivotPointsOfLastPlacedHierarchy.CenterPoint;
                List <Plane> pivotPointBoundarySegmentPlanes    = _pivotPointsOfLastPlacedHierarchy.GetAllBoundarySegmentPlanes();
                _indexOfStrokeAlignmentSegmentPlane = PlaneExtensions.GetIndexOfPlaneWhoseNormalIsMostAlignedWithDir(pivotPointBoundarySegmentPlanes, fromCenterToStrokeSurfacePickPoint);
            }

            /* Left here for reference. This is the way in which it was initially done.
             * if(indexOfPointClosestToStrokeSurfacePickPoint != ProjectedBoxFacePivotPoints.IndexOfPointInCenter)
             * {
             *  Vector3 pivotPointClosestToStrokeSurfacePickPoint = _pivotPointsOfLastPlacedHierarchy.GetPointByIndex(indexOfPointClosestToStrokeSurfacePickPoint);
             *  Vector3 fromCenterPivotToCalculatedClosestPoint = pivotPointClosestToStrokeSurfacePickPoint - _pivotPointsOfLastPlacedHierarchy.CenterPoint;
             *
             *  pivotPointBoundarySegmentPlanes = _pivotPointsOfLastPlacedHierarchy.GetAllBoundarySegmentPlanes();
             *  _indexOfStrokeAlignmentSegmentPlane = PlaneExtensions.GetIndexOfPlaneWhoseNormalIsMostAlignedWithDir(pivotPointBoundarySegmentPlanes, fromCenterPivotToCalculatedClosestPoint);
             * }
             * else
             * {
             *  fromClosestPointToStrokeSurfacePickPoint = _strokeSurface.MouseCursorPickPoint - _pivotPointsOfLastPlacedHierarchy.CenterPoint;
             *  pivotPointBoundarySegmentPlanes = _pivotPointsOfLastPlacedHierarchy.GetAllBoundarySegmentPlanes();
             *  _indexOfStrokeAlignmentSegmentPlane = PlaneExtensions.GetIndexOfPlaneWhoseNormalIsMostAlignedWithDir(pivotPointBoundarySegmentPlanes, fromCenterPivotToStrokeSurfacePickPoint);
             * }
             */
        }
예제 #12
0
        public bool IntersectsRay(Ray3D ray, out float t)
        {
            Plane   circlePlane  = Plane;
            Vector3 circleCenter = Center;
            float   scaledRadius = ScaledRadius;

            // Note: 'IntersectsPlane' not enough. Also check for containment????
            if (ray.IntersectsPlane(circlePlane, out t))
            {
                return(true);
            }
            else
            if (ray.Direction.IsPerpendicularTo(circlePlane.normal))
            {
                // Project the circle center onto the ray direction segment. If the distance between the circle center
                // and the projected center is <= the circle's radius, the ray might intersect the circle. Otherwise,
                // the ray can not possibly intersect the circle.
                Segment3D segment = new Segment3D(ray);
                Vector3   centerProjectedOnRayDir     = circleCenter.CalculateProjectionPointOnSegment(segment.StartPoint, segment.EndPoint);
                Vector3   fromCenterToProjectedCenter = centerProjectedOnRayDir - circleCenter;
                float     triAdjSideLength1           = fromCenterToProjectedCenter.magnitude;
                if (triAdjSideLength1 > scaledRadius)
                {
                    return(false);
                }

                // At this point it is possible that the ray might intersect the circle. Calcluate how much
                // we have to move from the center projection to a point on the circle along the reverse of
                // the ray direction vector. We will store this amount in 'triAdjSideLength2'.
                float triAdjSideLength2 = Mathf.Sqrt(scaledRadius * scaledRadius - triAdjSideLength1 * triAdjSideLength1);

                // Now check if moving from the projected center along the reverse ray direction by an amount equal to
                // 'triAdjSideLength2', we end up on a point which resides on the ray direction segment.
                Vector3 normalizedRayDirection = ray.Direction;
                normalizedRayDirection.Normalize();
                Vector3 targetPoint = centerProjectedOnRayDir - triAdjSideLength2 * normalizedRayDirection;
                if (targetPoint.IsOnSegment(segment.StartPoint, segment.EndPoint))
                {
                    // The point sits on the segment, which means that the ray intersects the circle.
                    // Now we need to calculate the intersection offset.
                    t = (targetPoint - ray.Origin).magnitude / ray.Direction.magnitude;
                    return(true);
                }
            }

            return(false);
        }
예제 #13
0
        public List <Plane> GetBoundarySegmentPlanesFacingOutward()
        {
            List <Segment3D> boundarySegments = GetBoundarySegments();
            List <Plane>     segmentPlanes    = new List <Plane>(boundarySegments.Count);
            Plane            quadPlane        = Plane;

            for (int segmentIndex = 0; segmentIndex < boundarySegments.Count; ++segmentIndex)
            {
                Segment3D segment     = boundarySegments[segmentIndex];
                Vector3   planeNormal = Vector3.Cross(segment.Direction, quadPlane.normal);
                planeNormal.Normalize();

                segmentPlanes.Add(new Plane(planeNormal, segment.StartPoint));
            }

            return(segmentPlanes);
        }
        private void CalculateArea()
        {
            List <Segment3D> boundarySegments = GetAllBoundarySegments();

            for (int segmentIndex = 0; segmentIndex < boundarySegments.Count; ++segmentIndex)
            {
                Segment3D currentSegment = boundarySegments[segmentIndex];
                for (int perpSegmentIndex = segmentIndex + 1; perpSegmentIndex < boundarySegments.Count; ++perpSegmentIndex)
                {
                    Segment3D perpSegment = boundarySegments[perpSegmentIndex];
                    if (perpSegment.IsPerpendicualrTo(currentSegment))
                    {
                        Vector3 cross = Vector3.Cross(perpSegment.Direction, currentSegment.Direction);
                        _area = cross.magnitude;
                        return;
                    }
                }
            }
        }
예제 #15
0
        private bool IsPointBehindSegmentPlane(Segment3D segment, Vector3 point)
        {
            Plane segmentPlane = GetSegmentPlane(segment);

            return(segmentPlane.IsPointBehind(point));
        }
예제 #16
0
        public static void RenderOrientedBoxCornerEdges(OrientedBox orientedBox, float cornerEdgeLengthPercentage, Color color)
        {
            cornerEdgeLengthPercentage = Mathf.Clamp(cornerEdgeLengthPercentage, 0.0f, 1.0f);
            List <Vector3> boxCornerPoints = orientedBox.GetCenterAndCornerPoints();

            GizmosColor.Push(color);

            // Render the corner edges along the top edge of the box's front face
            Segment3D segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontTopLeft], boxCornerPoints[(int)BoxPoint.FrontTopRight]);
            float     edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;

            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the bottom edge of the box's front face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontBottomLeft], boxCornerPoints[(int)BoxPoint.FrontBottomRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the left edge of the box's front face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontTopLeft], boxCornerPoints[(int)BoxPoint.FrontBottomLeft]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the right edge of the box's front face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontTopRight], boxCornerPoints[(int)BoxPoint.FrontBottomRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the top edge of the box's back face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.BackTopLeft], boxCornerPoints[(int)BoxPoint.BackTopRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the bottom edge of the box's back face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.BackBottomLeft], boxCornerPoints[(int)BoxPoint.BackBottomRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the left edge of the box's back face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.BackTopLeft], boxCornerPoints[(int)BoxPoint.BackBottomLeft]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the right edge of the box's back face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.BackTopRight], boxCornerPoints[(int)BoxPoint.BackBottomRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the left edge of the box's top face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontTopLeft], boxCornerPoints[(int)BoxPoint.BackTopRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the right edge of the box's top face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontTopRight], boxCornerPoints[(int)BoxPoint.BackTopLeft]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the left edge of the box's bottom face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontBottomLeft], boxCornerPoints[(int)BoxPoint.BackBottomRight]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            // Render the corner edges along the right edge of the box's bottom face
            segment    = new Segment3D(boxCornerPoints[(int)BoxPoint.FrontBottomRight], boxCornerPoints[(int)BoxPoint.BackBottomLeft]);
            edgeLength = segment.HalfLength * cornerEdgeLengthPercentage;
            if (edgeLength > 0.0f)
            {
                Gizmos.DrawLine(segment.StartPoint, segment.StartPoint + segment.NormalizedDirection * edgeLength);
                Gizmos.DrawLine(segment.EndPoint, segment.EndPoint - segment.NormalizedDirection * edgeLength);
            }

            GizmosColor.Pop();
        }
예제 #17
0
 public bool IsPerpendicualrTo(Segment3D segment)
 {
     return(Direction.IsPerpendicularTo(segment.Direction));
 }
예제 #18
0
        public List <ObjectPlacementData> Calculate(Quaternion rotationToApplyForStrokeAlignment)
        {
            if (!ValidateCalculationRequirements())
            {
                return(new List <ObjectPlacementData>());
            }

            _objectNodeNetwork.Clear();
            _rotationToApplyForStrokeAlignment = rotationToApplyForStrokeAlignment;
            CreateSurfaceColliderProjector();
            _elementToNewPrefabRotation.Clear();
            _allowObjectIntersection = ObjectPlacementSettings.Get().ObjectIntersectionSettings.AllowIntersectionForDecorPaintBrushModeDrag;

            int currentObjectIndex           = 0;
            var objectPlacementDataInstances = new List <ObjectPlacementData>(_workingBrush.MaxNumberOfObjects);

            while (currentObjectIndex < _workingBrush.MaxNumberOfObjects)
            {
                DecorPaintObjectPlacementBrushElement brushElement = _brushElementSpawnChanceTable.PickEntity(UnityEngine.Random.Range(0.0f, 1.0f));
                int brushElementIndex = _allValidBrushElements.FindIndex(item => item == brushElement);
                ++currentObjectIndex;

                // No object nodes were created yet?
                if (_objectNodeNetwork.NumberOfNodes == 0)
                {
                    // Create the first node at a random position inside the brush circle
                    Vector3             randomPositionInsideCircle = _workingBrushCircle.GetRandomPointInside();
                    ObjectSurfaceData   objectSurfaceData          = CalculateObjectSurfaceData(randomPositionInsideCircle);
                    MatrixObjectBoxPair matrixObjectBoxPair        = CalculateMatrixObjectBoxPair(brushElementIndex, objectSurfaceData);

                    // We need to know if the normal of the surface on which the object resides lies within the desired slope range
                    bool passesSlopeTest = DoesObjectSurfacePassSlopeTest(objectSurfaceData, brushElement);

                    // Note: Even if the slope test is not passed, we will still create an object node. The reason for this is that
                    //       we want to have some kind of continuity in the algorithm. Imagine that the brush circle is large and is
                    //       divided by a large terrain mountain which sits in the middle. If the object generation starts on one side
                    //       of the mountain, the algorithm might never get a chance to go over the other side if the slope condition
                    //       is not satisifed. We want to spread objects as much as possible so even though this object will not be
                    //       placed in the scene, we will still add it to the node network.
                    _objectNodeNetwork.AddNodeToEnd(matrixObjectBoxPair.ObjectBox, objectSurfaceData);
                    if (passesSlopeTest && DoesBoxPassObjectIntersectionTest(matrixObjectBoxPair.ObjectBox, brushElement.Prefab.UnityPrefab, matrixObjectBoxPair.ObjectMatrix))
                    {
                        objectPlacementDataInstances.Add(new ObjectPlacementData(matrixObjectBoxPair.ObjectMatrix, brushElement.Prefab, brushElement.MustEmbedInSurface));
                    }
                }
                else
                {
                    // Are there any node segments available?
                    if (_objectNodeNetwork.NumberOfSegments != 0)
                    {
                        // The first step is to generate a random node index and store references to that node and its immediate neighbour
                        _objectNodeNetwork.RemoveAllNodesWhichGenerateConcavities(_workingBrushCircle.Plane);
                        int        randomNodeIndex = _objectNodeNetwork.GetRandomNodeIndex();
                        ObjectNode firstNode       = _objectNodeNetwork.GetNodeByIndex(randomNodeIndex);
                        ObjectNode secondNode      = _objectNodeNetwork.GetNodeByIndex(randomNodeIndex + 1);

                        // Calculate the plane of the segment which unites the 2 nodes. We will also store the
                        // actual segment and the middle point on the segment. We will use this middle point to
                        // generate the initial object position.
                        Segment3D   nodeSegment     = ObjectNodeNetwork.CalculateSegmentBetweenNodes(firstNode, secondNode);
                        Vector3     segmentMidPoint = nodeSegment.GetPoint(0.5f);
                        Plane       segmentPlane    = ObjectNodeNetwork.CalculateSegmentPlaneNormal(firstNode, secondNode);
                        OrientedBox firstNodeBox    = firstNode.ObjectBox;
                        OrientedBox secondNodeBox   = secondNode.ObjectBox;

                        // Calculate the furthest point in front of the plane using the corner points of the
                        // 2 nodes. The idea is to move the new object as much as possible from the bulk of
                        // objects that have already been generated.
                        Vector3        furthestPointFromPlane;
                        List <Vector3> nodeCornerPoints = firstNodeBox.GetCornerPoints();
                        nodeCornerPoints.AddRange(secondNodeBox.GetCornerPoints());
                        if (!segmentPlane.GetFurthestPointInFront(nodeCornerPoints, out furthestPointFromPlane))
                        {
                            continue;
                        }

                        // Use the calculated furthest point from plane and the the existing plane normal to calculate the
                        // pivot plane. The new object will reside at some distance away from this plane.
                        Plane pivotPlane = new Plane(segmentPlane.normal, furthestPointFromPlane);

                        // Calculate the new object transform data. We will use the segment's mid point to generate the
                        // initial object position.
                        ObjectSurfaceData   objectSurfaceData   = CalculateObjectSurfaceData(segmentMidPoint);
                        MatrixObjectBoxPair matrixObjectBoxPair = CalculateMatrixObjectBoxPair(brushElementIndex, objectSurfaceData);
                        OrientedBox         objectBox           = matrixObjectBoxPair.ObjectBox;

                        // Identify the objects's furthest point behind the plane
                        Vector3        objectBoxPivotPoint;
                        List <Vector3> objectBoxCornerPoints = objectBox.GetCornerPoints();
                        if (!pivotPlane.GetFurthestPointBehind(objectBoxCornerPoints, out objectBoxPivotPoint))
                        {
                            continue;
                        }

                        // Use the furthest point to move the object in front of the plane and take the distance between objects into account
                        Vector3 fromPivotPointToCenter = objectBox.Center - objectBoxPivotPoint;
                        Vector3 projectedPivotPoint    = pivotPlane.ProjectPoint(objectBoxPivotPoint);
                        objectBox.Center = projectedPivotPoint + fromPivotPointToCenter + pivotPlane.normal * _workingBrush.DistanceBetweenObjects;

                        // Generate the object surface data
                        objectSurfaceData = CalculateObjectSurfaceData(objectBox.Center);
                        bool passesSlopeTest = DoesObjectSurfacePassSlopeTest(objectSurfaceData, brushElement);

                        // Now we need to adjust the orientation and center of the box. If the calculated center
                        // lies outside the brush circle, we will ignore this node.
                        AdjustObjectBoxRotationOnSurface(objectBox, objectSurfaceData, brushElement);
                        AdjustObjectBoxCenterToSitOnSurface(objectBox, objectSurfaceData, brushElement);
                        if (!_workingBrushCircle.ContainsPoint(_workingBrushCircle.Plane.ProjectPoint(objectBox.Center)))
                        {
                            continue;
                        }

                        // Recalculate the object matrix using the new box data
                        TransformMatrix objectMatrix = matrixObjectBoxPair.ObjectMatrix;
                        objectMatrix.Rotation    = objectBox.Rotation;
                        objectMatrix.Translation = ObjectPositionCalculator.CalculateObjectHierarchyPosition(brushElement.Prefab, objectBox.Center, objectMatrix.Scale, objectBox.Rotation);

                        // We have been modifying the matrix and box data independently so we will ensure that the box uses the latest data
                        OrientedBox finalBox = new OrientedBox(objectBox);
                        finalBox.SetTransformMatrix(objectMatrix);

                        // If the slope test passed, we will calculate an object placement data instance. Otherwise, we will just insert a new node.
                        if (passesSlopeTest && DoesBoxPassObjectIntersectionTest(finalBox, brushElement.Prefab.UnityPrefab, objectMatrix))
                        {
                            objectPlacementDataInstances.Add(new ObjectPlacementData(objectMatrix, brushElement.Prefab, brushElement.MustEmbedInSurface));
                        }
                        _objectNodeNetwork.InsertAfterNode(objectBox, objectSurfaceData, randomNodeIndex);
                    }
                    else
                    {
                        // When there are no segments available it means we have only one node. We will use this node to generate
                        // a new one at some distance away from it. First we will store some data that we will need during the entire
                        // procedure.
                        ObjectNode  pivotNode = _objectNodeNetwork.GetFirstNode();
                        Vector3     pivotNodeSurfaceTangent = pivotNode.ObjectSurfaceData.GetSurfaceTangentVector();
                        OrientedBox pivotNodeObjectBox      = pivotNode.ObjectBox;

                        // We will place the new node at some distance away from the first node's face which points
                        // along the calculated tangent vector. We will call this the pivot face.
                        BoxFace pivotBoxFace   = pivotNodeObjectBox.GetBoxFaceMostAlignedWithNormal(pivotNodeSurfaceTangent);
                        Plane   pivotFacePlane = pivotNodeObjectBox.GetBoxFacePlane(pivotBoxFace);

                        // Generate the data for the new node in the same position as the first node.
                        // Note: Although the same position is used, the rotation and scale will differ and they will
                        //       be established by 'CalculateMatrixObjectBoxPair'.
                        MatrixObjectBoxPair matrixObjectBoxPair = CalculateMatrixObjectBoxPair(brushElementIndex, pivotNode.ObjectSurfaceData);
                        OrientedBox         objectBox           = matrixObjectBoxPair.ObjectBox;

                        // At this point we have to start moving the generated object box to its new positino along the
                        // tangent vector. We will do this by calculating the furthest box point which lies behind the
                        // pivot plane and then move the box so that this point resides on that plane. We will call this
                        // the pivot point.
                        // Note: We will perform a safety check to see if this point could not be calculated and use the
                        //       closest point in front if necessary. However, this check should not be necessary. Because
                        //       we are placing te object box in the center of the previous box, we can be usre that there
                        //       will always be a point which lies behind the pivot plane.
                        Vector3        objectBoxPivotPoint;
                        List <Vector3> objectBoxCornerPoints = objectBox.GetCornerPoints();
                        if (!pivotFacePlane.GetFurthestPointBehind(objectBoxCornerPoints, out objectBoxPivotPoint) &&
                            !pivotFacePlane.GetClosestPointInFront(objectBoxCornerPoints, out objectBoxPivotPoint))
                        {
                            continue;
                        }

                        // Project the pivot point onto the pivot plane. We will also store a vector which goes from the
                        // original pivot point to the box center. This will allow us to maintain the relationship between
                        // the projected pivot point and the box center so that the center can be adjusted accordingly.
                        Vector3 fromPivotPointToCenter = objectBox.Center - objectBoxPivotPoint;
                        Vector3 projectedPivotPoint    = pivotFacePlane.ProjectPoint(objectBoxPivotPoint);

                        // Adjust the center using the projected pivot point and also take the distance between objects into account
                        objectBox.Center = projectedPivotPoint + fromPivotPointToCenter + pivotNodeSurfaceTangent * _workingBrush.DistanceBetweenObjects;

                        // Generate the object surface data at the current box position.
                        // Note: This is the step which can actually cause objects to intersect a little bit. The surface data is
                        //       calculated by projecting along the brush circle plane normal. If we are placing objects on a terrain
                        //       and the center of the circle lies somewhere at the base of the terrain where the normal points straight
                        //       up, but the center of the box resides somewhere on a clif, the new center might move the box closer
                        //       or even further away from the pivot node. This however, should not be a problem especially if the distance
                        //       between objects is not 0.
                        ObjectSurfaceData objectSurfaceData = CalculateObjectSurfaceData(objectBox.Center);
                        bool passesSlopeTest = DoesObjectSurfacePassSlopeTest(objectSurfaceData, brushElement);

                        // Now we need to adjust the orientation and center of the box. If the calculated center
                        // lies outside the brush circle, we will ignore this node.
                        AdjustObjectBoxRotationOnSurface(objectBox, objectSurfaceData, brushElement);
                        AdjustObjectBoxCenterToSitOnSurface(objectBox, objectSurfaceData, brushElement);
                        if (!_workingBrushCircle.ContainsPoint(_workingBrushCircle.Plane.ProjectPoint(objectBox.Center)))
                        {
                            continue;
                        }

                        // Recalculate the object matrix using the new box data
                        TransformMatrix objectMatrix = matrixObjectBoxPair.ObjectMatrix;
                        objectMatrix.Rotation    = objectBox.Rotation;
                        objectMatrix.Translation = ObjectPositionCalculator.CalculateObjectHierarchyPosition(brushElement.Prefab, objectBox.Center, objectMatrix.Scale, objectBox.Rotation);

                        // We have been modifying the matrix and box data independently so we will ensure that the box uses the latest data
                        OrientedBox finalBox = new OrientedBox(objectBox);
                        finalBox.SetTransformMatrix(objectMatrix);

                        // If the slope test passed, we will calculate an object placement data instance. Otherwise, we will just insert a new node.
                        if (passesSlopeTest && DoesBoxPassObjectIntersectionTest(finalBox, brushElement.Prefab.UnityPrefab, objectMatrix))
                        {
                            objectPlacementDataInstances.Add(new ObjectPlacementData(objectMatrix, brushElement.Prefab, brushElement.MustEmbedInSurface));
                        }
                        _objectNodeNetwork.InsertAfterNode(objectBox, objectSurfaceData, 0);
                    }
                }
            }

            // Adjust the prefab rotations for the next time the function is called
            if (_elementToNewPrefabRotation.Count != 0)
            {
                foreach (var prefabRotationPair in _elementToNewPrefabRotation)
                {
                    DecorPaintObjectPlacementBrushElement brushElement = prefabRotationPair.Key;
                    if (_elementToCurrentPrefabRotation.ContainsKey(brushElement))
                    {
                        _elementToCurrentPrefabRotation[brushElement] = prefabRotationPair.Value;
                    }
                }
            }

            return(objectPlacementDataInstances);
        }
예제 #19
0
        public bool IntersectsWith(Segment3D segment)
        {
            float t1, t2;

            return(IntersectsWith(segment, out t1, out t2));
        }