Beispiel #1
0
        public bool ObjectMeshIntersectsAnyMesh(GameObject queryMeshObject, TransformMatrix worldMatrix, List <GameObject> ignoreObjects)
        {
            if (ignoreObjects == null)
            {
                ignoreObjects = new List <GameObject>();
            }

            Octave3DMesh octave3DMesh = queryMeshObject.GetOctave3DMesh();

            if (octave3DMesh == null)
            {
                return(false);
            }

            return(_gameObjectSphereTree.ObjectMeshIntersectsAnyMesh(octave3DMesh, worldMatrix, new HashSet <GameObject>(ignoreObjects)));
        }
        public void SetRotation(Quaternion rotation)
        {
            Quaternion relativeRotation = _rotation.GetRelativeRotation(rotation);

            _growDirection  = relativeRotation * _growDirection;
            _rotation       = rotation;
            _rotationMatrix = new TransformMatrix(Vector3.zero, _rotation, Vector3.one);

            foreach (ObjectPlacementBox box in _boxes)
            {
                Vector3 newBoxCenter = box.Center - _basePosition;
                newBoxCenter  = relativeRotation * newBoxCenter;
                newBoxCenter += _basePosition;

                box.Center   = newBoxCenter;
                box.Rotation = _rotation;
            }
        }
        private void CalculateInitialMinMaxPoints(out Vector3 minPoint, out Vector3 maxPoint)
        {
            // We will sort the points along the X axis, but we have to take the polygon's coordinate
            // system into account because if we use the global X axis, we will run into trouble when
            // all points of the polygon reside on the YZ plane (i.e. same X coordinate).
            Vector3 polyLocalRight, polyLocalLook;

            if (_polygonNormal.IsAlignedWith(Vector3.up))
            {
                polyLocalRight = Vector3.right;
                polyLocalLook  = Vector3.Cross(polyLocalRight, Vector3.up);
                polyLocalLook.Normalize();
            }
            else
            {
                polyLocalRight = Vector3.Cross(_polygonNormal, Vector3.up);
                polyLocalRight.Normalize();
                polyLocalLook = Vector3.Cross(polyLocalRight, _polygonNormal);
                polyLocalLook.Normalize();
            }

            Quaternion      polyRotation    = Quaternion.LookRotation(polyLocalLook, _polygonNormal);
            TransformMatrix transformMatrix = new TransformMatrix(Vector3.zero, polyRotation, Vector3.one);

            // Note: We will work in polygon local space and transform the points to world space after we are done.
            minPoint = transformMatrix.MultiplyPointInverse(_polygonPointsOnSamePlane[0]);
            maxPoint = transformMatrix.MultiplyPointInverse(_polygonPointsOnSamePlane[0]);
            for (int ptIndex = 0; ptIndex < _polygonPointsOnSamePlane.Count; ++ptIndex)
            {
                Vector3 point = transformMatrix.MultiplyPointInverse(_polygonPointsOnSamePlane[ptIndex]);
                if (point.x < minPoint.x)
                {
                    minPoint = point;
                }
                if (point.x > maxPoint.x)
                {
                    maxPoint = point;
                }
            }

            minPoint = transformMatrix.MultiplyPoint(minPoint);
            maxPoint = transformMatrix.MultiplyPoint(maxPoint);
        }
Beispiel #4
0
        public List <Triangle3DIntersectInfo> GetIntersectingTriangles(TransformMatrix thisTransform, Octave3DMesh otherMesh, TransformMatrix otherTransform)
        {
            OrientedBox thisOOBB = new OrientedBox(ModelAABB);

            thisOOBB.Transform(thisTransform);
            OrientedBox otherOOBB = new OrientedBox(otherMesh.ModelAABB);

            otherOOBB.Transform(otherTransform);

            if (thisOOBB.Intersects(otherOOBB))
            {
                List <Triangle3D> thisTriangles = GetOverlappedTriangles(otherOOBB, thisTransform);

                var output = new List <Triangle3DIntersectInfo>(50);
                Triangle3DIntersectInfo intersectInfo;
                for (int thisTriIndex = 0; thisTriIndex < thisTriangles.Count; ++thisTriIndex)
                {
                    Triangle3D thisTri = thisTriangles[thisTriIndex];

                    OrientedBox       thisTriOOBB    = new OrientedBox(thisTri.GetEncapsulatingBox());
                    List <Triangle3D> otherTriangles = otherMesh.GetOverlappedTriangles(thisTriOOBB, otherTransform);

                    for (int otherTriIndex = 0; otherTriIndex < otherTriangles.Count; ++otherTriIndex)
                    {
                        Triangle3D otherTri = otherTriangles[otherTriIndex];
                        if (thisTri.IntersectsTriangle(otherTri, out intersectInfo))
                        {
                            output.Add(intersectInfo);
                        }
                    }
                }

                return(output);
            }
            else
            {
                return(new List <Triangle3DIntersectInfo>());
            }
        }
Beispiel #5
0
        public Box Transform(TransformMatrix transformMatrix)
        {
            Vector3 rightAxis = transformMatrix.GetNormalizedRightAxisNoScaleSign();
            Vector3 upAxis    = transformMatrix.GetNormalizedUpAxisNoScaleSign();
            Vector3 lookAxis  = transformMatrix.GetNormalizedLookAxisNoScaleSign();
            Vector3 scale     = transformMatrix.Scale;

            Vector3 newCenter = transformMatrix.MultiplyPoint(Center);

            Vector3 boxExtents      = Extents;
            Vector3 newExtentsRight = rightAxis * boxExtents.x * scale.x;
            Vector3 newExtentsUp    = upAxis * boxExtents.y * scale.y;
            Vector3 newExtentsLook  = lookAxis * boxExtents.z * scale.z;

            float newExtentsX = Mathf.Abs(newExtentsRight.x) + Mathf.Abs(newExtentsUp.x) + Mathf.Abs(newExtentsLook.x);
            float newExtentsY = Mathf.Abs(newExtentsRight.y) + Mathf.Abs(newExtentsUp.y) + Mathf.Abs(newExtentsLook.y);
            float newExtentsZ = Mathf.Abs(newExtentsRight.z) + Mathf.Abs(newExtentsUp.z) + Mathf.Abs(newExtentsLook.z);

            Vector3 newSize = new Vector3(newExtentsX, newExtentsY, newExtentsZ) * 2.0f;

            return(new Box(newCenter, newSize));
        }
Beispiel #6
0
        public List <Triangle3D> GetOverlappedWorldTriangles(OrientedBox box, TransformMatrix meshTransformMatrix)
        {
            // If the tree was not yet build, we need to build it because we need
            // the triangle information in order to perform the raycast.
            if (!_wasBuilt)
            {
                Build();
            }

            // Work in mesh model space because the tree data exists in model space
            OrientedBox meshSpaceBox     = new OrientedBox(box);
            Matrix4x4   inverseTransform = meshTransformMatrix.ToMatrix4x4x.inverse;

            meshSpaceBox.Transform(inverseTransform);

            List <SphereTreeNode <MeshSphereTreeTriangle> > overlappedNodes = _sphereTree.OverlapBox(meshSpaceBox);

            if (overlappedNodes.Count == 0)
            {
                return(new List <Triangle3D>());
            }

            Box queryBox = Box.FromPoints(meshSpaceBox.GetCenterAndCornerPoints());
            var overlappedWorldTriangles = new List <Triangle3D>(50);

            foreach (var node in overlappedNodes)
            {
                int triangleIndex             = node.Data.TriangleIndex;
                MeshTriangleInfo triangleInfo = _octave3DMesh.GetMeshTriangleInfo(triangleIndex);

                if (triangleInfo.ModelSpaceTriangle.IntersectsBox(queryBox))
                {
                    triangleInfo.ModelSpaceTriangle.TransformPoints(meshTransformMatrix);
                    overlappedWorldTriangles.Add(triangleInfo.ModelSpaceTriangle);
                }
            }

            return(overlappedWorldTriangles);
        }
Beispiel #7
0
        public static MirroredRotation MirrorMatrixRotation(Plane mirrorPlane, TransformMatrix rotationMatrix)
        {
            Vector3 axesScale = rotationMatrix.Scale;
            Vector3 rightAxis = rotationMatrix.GetNormalizedRightAxisNoScaleSign();
            Vector3 upAxis    = rotationMatrix.GetNormalizedUpAxisNoScaleSign();
            Vector3 lookAxis  = rotationMatrix.GetNormalizedLookAxisNoScaleSign();

            rightAxis = MirrorNormal(mirrorPlane, rightAxis);
            upAxis    = MirrorNormal(mirrorPlane, upAxis);
            lookAxis  = MirrorNormal(mirrorPlane, lookAxis);

            Quaternion newRotation     = Quaternion.LookRotation(lookAxis, upAxis);
            Vector3    resultRightAxis = Vector3.Cross(upAxis, lookAxis);

            bool pointsInSameDirection = resultRightAxis.IsPointingInSameGeneralDirection(rightAxis);

            if (!pointsInSameDirection)
            {
                axesScale[0] *= -1.0f;
            }

            return(new MirroredRotation(newRotation, axesScale));
        }
Beispiel #8
0
        public bool Raycast(Ray ray, out float t)
        {
            TransformMatrix transformMatrix = TransformMatrix;
            Ray             modelSpaceRay   = ray.InverseTransform(transformMatrix.ToMatrix4x4x);

            float modelSpaceT;

            if (_modelSpaceBox.Raycast(modelSpaceRay, out modelSpaceT))
            {
                // Note: The intersection offset (i.e. T value) we have calculated so far is expressed in the box model space.
                //       We have to calculate the intersection point in world space and use that to calculate the world space
                //       T value which we will store in the output parameter.
                Vector3 modelSpaceIntersectionPoint = modelSpaceRay.GetPoint(modelSpaceT);
                Vector3 worldIntersectionPoint      = transformMatrix.MultiplyPoint(modelSpaceIntersectionPoint);

                t = (ray.origin - worldIntersectionPoint).magnitude;
                return(true);
            }
            else
            {
                t = 0.0f;
                return(false);
            }
        }
 public static Vector3 GetLocalVector(CoordinateSystemAxis axis, TransformMatrix transformMatrix)
 {
     if (axis == CoordinateSystemAxis.PositiveRight)
     {
         return(transformMatrix.GetNormalizedRightAxis());
     }
     if (axis == CoordinateSystemAxis.NegativeRight)
     {
         return(-transformMatrix.GetNormalizedRightAxis());
     }
     if (axis == CoordinateSystemAxis.PositiveUp)
     {
         return(transformMatrix.GetNormalizedUpAxis());
     }
     if (axis == CoordinateSystemAxis.NegativeUp)
     {
         return(-transformMatrix.GetNormalizedUpAxis());
     }
     if (axis == CoordinateSystemAxis.PositiveLook)
     {
         return(transformMatrix.GetNormalizedLookAxis());
     }
     return(-transformMatrix.GetNormalizedLookAxis());
 }
Beispiel #10
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);
        }
Beispiel #11
0
        private MatrixObjectBoxPair CalculateMatrixObjectBoxPair(int brushElementIndex, ObjectSurfaceData objectSurfaceData)
        {
            // Store the needed data for easy access
            DecorPaintObjectPlacementBrushElement brushElement = _allValidBrushElements[brushElementIndex];
            OrientedBox objectBox = new OrientedBox(_brushElementsPrefabOrientedBoxes[brushElementIndex]);

            // Establish the object's scale. Randomize the scale if necessary. Otherwise, just use the brush element's scale.
            Vector3 xyzObjectBoxScale = _brushElementsPrefabWorldScaleValues[brushElementIndex];

            if (brushElement.ScaleRandomizationSettings.RandomizeScale)
            {
                // Generate a random scale factor and apply it to the current scale
                ObjectUniformScaleRandomizationSettings uniformScaleRandomizationSettings = brushElement.ScaleRandomizationSettings.UniformScaleRandomizationSettings;
                xyzObjectBoxScale *= UnityEngine.Random.Range(uniformScaleRandomizationSettings.MinScale, uniformScaleRandomizationSettings.MaxScale);;
            }
            else
            {
                xyzObjectBoxScale *= brushElement.Scale;
            }

            // Apply the scale that we have calculated
            objectBox.Scale = xyzObjectBoxScale;

            // Now we will calculate the rotation. First we will calculate a quaternion which allow us to take the
            // specified rotation offset into account. The quaternion rotates around the surface normal by an angle
            // of 'RotationOffset' degrees.
            Quaternion rotationOffset = brushElement.AlignToSurface ? Quaternion.AngleAxis(brushElement.RotationOffsetInDegrees, objectSurfaceData.SurfaceNormal) : Quaternion.identity;

            // If we need to align to stroke, we have some more work to do. Otherwise, we will just set the
            // rotation to be the same as the prefab's rotation, but offset by 'rotationOffset'.
            if (brushElement.AlignToStroke)
            {
                // First calculate the rotation without any offset. This is the rotation of the prefab plus the rotation which
                // must be applied for alignment.
                Quaternion rotationWithoutOffset = _rotationToApplyForStrokeAlignment * _elementToCurrentPrefabRotation[brushElement];

                // The rotation of the box must be set to the rotation which we just calculated (which ensures proper alingment) plus
                // the rotation offset.
                objectBox.Rotation = rotationOffset * rotationWithoutOffset;

                // Store the rotation inside the dictionary if an entry doesn't already exist
                if (!_elementToNewPrefabRotation.ContainsKey(brushElement))
                {
                    _elementToNewPrefabRotation.Add(brushElement, rotationWithoutOffset);
                }
            }
            else
            {
                objectBox.Rotation = rotationOffset * brushElement.Prefab.UnityPrefab.transform.rotation;
            }

            // Adjust the rotation of the object so that its axis is aligned with the placement surface if necessary
            if (brushElement.AlignToSurface)
            {
                AdjustObjectBoxRotationOnSurface(objectBox, objectSurfaceData, brushElement);
            }

            // The final step is to rotate the object by a random amount around the placement surface
            if (brushElement.RotationRandomizationMode != BrushElementRotationRandomizationMode.None)
            {
                Vector3 rotationAxis = objectSurfaceData.SurfaceNormal;
                if (brushElement.RotationRandomizationMode == BrushElementRotationRandomizationMode.X)
                {
                    rotationAxis = Vector3.right;
                }
                else if (brushElement.RotationRandomizationMode == BrushElementRotationRandomizationMode.Y)
                {
                    rotationAxis = Vector3.up;
                }
                else if (brushElement.RotationRandomizationMode == BrushElementRotationRandomizationMode.Z)
                {
                    rotationAxis = Vector3.forward;
                }

                if (!brushElement.AlignToStroke)
                {
                    // When stroke alignment is not required, we will generate a random rotation angle and apply it
                    // to the current box rotation.
                    float      randomRotationAngle = UnityEngine.Random.Range(0.0f, 360.0f);
                    Quaternion additionalRotation  = Quaternion.AngleAxis(randomRotationAngle, rotationAxis);
                    objectBox.Rotation = additionalRotation * objectBox.Rotation;
                }
                else
                {
                    // When both rotation randomization and stroke alingment are turned on, we will proudce a small
                    // random rotation offset to randomize the alingmnet a little bit.
                    float      randomRotationAngle = UnityEngine.Random.Range(0.0f, 25.0f);
                    Quaternion additionalRotation  = Quaternion.AngleAxis(randomRotationAngle, rotationAxis);
                    objectBox.Rotation = additionalRotation * objectBox.Rotation;
                }
            }

            // Place the object on the surface
            AdjustObjectBoxCenterToSitOnSurface(objectBox, objectSurfaceData, brushElement);

            // Construct the object matrix and return the object/marix pair
            TransformMatrix objectMatrix = new TransformMatrix(ObjectPositionCalculator.CalculateObjectHierarchyPosition(brushElement.Prefab,
                                                                                                                         objectBox.Center, xyzObjectBoxScale, objectBox.Rotation), objectBox.Rotation, xyzObjectBoxScale);

            return(new MatrixObjectBoxPair(objectMatrix, objectBox));
        }
Beispiel #12
0
 public void RenderGizmosDebug(TransformMatrix meshTransformMatrix)
 {
     _meshSphereTree.RenderGizmosDebug(meshTransformMatrix);
 }
Beispiel #13
0
 public MatrixObjectBoxPair(TransformMatrix objectMatrix, OrientedBox objectBox)
 {
     _objectMatrix = objectMatrix;
     _objectBox    = new OrientedBox(objectBox);
 }
Beispiel #14
0
        public bool IntersectsMesh(TransformMatrix thisTransform, Octave3DMesh otherMesh, TransformMatrix otherTransform)
        {
            OrientedBox thisOOBB = new OrientedBox(ModelAABB);

            thisOOBB.Transform(thisTransform);
            OrientedBox otherOOBB = new OrientedBox(otherMesh.ModelAABB);

            otherOOBB.Transform(otherTransform);

            if (!thisOOBB.Intersects(otherOOBB))
            {
                return(false);
            }

            List <Triangle3D> thisTriangles = GetOverlappedTriangles(otherOOBB, thisTransform);

            for (int thisTriIndex = 0; thisTriIndex < thisTriangles.Count; ++thisTriIndex)
            {
                Triangle3D        thisTri        = thisTriangles[thisTriIndex];
                OrientedBox       thisTriOOBB    = new OrientedBox(thisTri.GetEncapsulatingBox());
                List <Triangle3D> otherTriangles = otherMesh.GetOverlappedTriangles(thisTriOOBB, otherTransform);
                for (int otherTriIndex = 0; otherTriIndex < otherTriangles.Count; ++otherTriIndex)
                {
                    Triangle3D otherTri = otherTriangles[otherTriIndex];
                    if (thisTri.IntersectsTriangle(otherTri))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
 /// <summary>
 /// This method can be called to render any necessary gizmos for debugging purposes.
 /// </summary>
 public void RenderGizmosDebug()
 {
     _sphereTree.RenderGizmosDebug(TransformMatrix.GetIdentity());
 }
Beispiel #16
0
 /// <summary>
 /// Renders the tree gizmos in the scene view for debugging purposes.
 /// </summary>
 /// <param name="meshTransformMatrix">
 /// The transform matrix fo the mesh which is associated with the tree.
 /// </param>
 public void RenderGizmosDebug(TransformMatrix meshTransformMatrix)
 {
     // ToDO: Set scale to 1?
     _sphereTree.RenderGizmosDebug(meshTransformMatrix);
 }
Beispiel #17
0
        public MeshRayHit(Ray ray, float hitEnter, Octave3DMesh hitMesh, int hitTriangleIndex, Vector3 hitPoint, Vector3 hitNormal, TransformMatrix meshTransformMatrix)
        {
            _ray              = ray;
            _hitEnter         = hitEnter;
            _hitMesh          = hitMesh;
            _hitCollider      = new Octave3DMeshCollider(_hitMesh, meshTransformMatrix);
            _hitTraingleIndex = hitTriangleIndex;
            _hitPoint         = hitPoint;

            _hitNormal = hitNormal;
            _hitNormal.Normalize();
        }
 public TransformMatrix(TransformMatrix source)
 {
     _translation = source._translation;
     _rotation    = source._rotation;
     _scale       = source._scale;
 }
 public void Transform(TransformMatrix transformMatrix)
 {
     _transformMatrix = transformMatrix * _transformMatrix;
     RecalculateLocalAxes();
 }
Beispiel #20
0
 private bool DoesBoxPassObjectIntersectionTest(OrientedBox objectBox, GameObject prefab, TransformMatrix objectMatrix)
 {
     if (_allowObjectIntersection)
     {
         return(true);
     }
     return(!Octave3DScene.Get().BoxIntersectsAnyObjectBoxes(objectBox, new List <GameObject> {
         DecorPaintObjectPlacement.Get().DecorPaintSurfaceObject
     }, true));
 }
Beispiel #21
0
        public static Quaternion CalculateRotationQuaternionForAxisAlignment(Quaternion currentRotation, CoordinateSystemAxis alignmentAxis, Vector3 destinationAxis)
        {
            TransformMatrix rotationMatrix = TransformMatrix.GetIdentity();

            rotationMatrix.Rotation = currentRotation;

            Vector3 axisToAlign             = CoordinateSystemAxes.GetLocalVector(alignmentAxis, rotationMatrix);
            bool    isAlignmentAxisNegative = CoordinateSystemAxes.IsNegativeAxis(alignmentAxis);

            // Already aligned?
            bool pointsInSameDirection;
            bool isAlignedWithDestinationAxis = destinationAxis.IsAlignedWith(axisToAlign, out pointsInSameDirection);

            if (isAlignedWithDestinationAxis && pointsInSameDirection)
            {
                return(currentRotation);
            }

            if (!isAlignedWithDestinationAxis)
            {
                Vector3 rotationAxis = Vector3.Cross(axisToAlign, destinationAxis);
                rotationAxis.Normalize();
                return(Quaternion.AngleAxis(axisToAlign.AngleWith(destinationAxis), rotationAxis) * currentRotation);
            }
            else
            {
                // If this point is reached, it means the axis is aligned with the destination axis but points in the opposite
                // direction. In this case we can not use the cross product, so we will have to regenerate the axes.
                Vector3 newRightAxis, newUpAxis, newLookAxis;
                if (alignmentAxis == CoordinateSystemAxis.PositiveRight || alignmentAxis == CoordinateSystemAxis.NegativeRight)
                {
                    newRightAxis = destinationAxis;
                    if (isAlignmentAxisNegative)
                    {
                        newRightAxis *= -1.0f;
                    }

                    bool worldUpPointsSameDirAsRightAxis;
                    if (newRightAxis.IsAlignedWith(Vector3.up, out worldUpPointsSameDirAsRightAxis))
                    {
                        newLookAxis = worldUpPointsSameDirAsRightAxis ? Vector3.forward : Vector3.back;
                    }
                    else
                    {
                        newLookAxis = Vector3.Cross(newRightAxis, Vector3.up);
                    }

                    if (isAlignmentAxisNegative)
                    {
                        newLookAxis *= -1.0f;
                    }
                    newUpAxis = Vector3.Cross(newLookAxis, newRightAxis);
                }
                if (alignmentAxis == CoordinateSystemAxis.PositiveUp || alignmentAxis == CoordinateSystemAxis.NegativeUp)
                {
                    newUpAxis = destinationAxis;
                    if (isAlignmentAxisNegative)
                    {
                        newUpAxis *= -1.0f;
                    }

                    bool worldUpPointsSameDirAsUpAxis;
                    if (newUpAxis.IsAlignedWith(Vector3.up, out worldUpPointsSameDirAsUpAxis))
                    {
                        newLookAxis = worldUpPointsSameDirAsUpAxis ? Vector3.forward : Vector3.back;
                    }
                    else
                    {
                        newLookAxis = Vector3.Cross(newUpAxis, Vector3.up);
                    }

                    if (isAlignmentAxisNegative)
                    {
                        newLookAxis *= -1.0f;
                    }
                }
                else
                {
                    newLookAxis = destinationAxis;
                    if (isAlignmentAxisNegative)
                    {
                        newLookAxis *= -1.0f;
                    }

                    bool worldUpPointsSameDirAsLookAxis;
                    if (newLookAxis.IsAlignedWith(Vector3.up, out worldUpPointsSameDirAsLookAxis))
                    {
                        newUpAxis = worldUpPointsSameDirAsLookAxis ? Vector3.forward : Vector3.back;
                    }
                    else
                    {
                        newUpAxis = Vector3.Cross(newLookAxis, Vector3.up);
                    }

                    if (isAlignmentAxisNegative)
                    {
                        newUpAxis *= -1.0f;
                    }
                }

                // Normalize the axes which were calculated
                newUpAxis.Normalize();
                newLookAxis.Normalize();

                // Now use the axes to calculate the rotation quaternion
                return(Quaternion.LookRotation(newLookAxis, newUpAxis));
            }
        }
Beispiel #22
0
 public void SetTransformMatrix(TransformMatrix transformMatrix)
 {
     _renderableCoordinateSystem.SetTransformMatrix(transformMatrix);
 }
 public CoordinateSystem(TransformMatrix transformMatrix)
 {
     SetTransformMatrix(transformMatrix);
 }
Beispiel #24
0
 public MeshRayHit Raycast(Ray ray, TransformMatrix meshTransformMatrix)
 {
     //meshTransformMatrix.Scale = meshTransformMatrix.Scale.GetVectorWithPositiveComponents();
     return(_meshSphereTree.Raycast(ray, meshTransformMatrix));
 }
Beispiel #25
0
        /// <summary>
        /// Returns the world space vertices overlapped by the specified box.
        /// </summary>
        public List <Vector3> GetOverlappedWorldVerts(OrientedBox box, TransformMatrix meshTransformMatrix)
        {
            // If the tree was not yet build, we need to build it because we need
            // the triangle information in order to perform the raycast.
            if (!_wasBuilt)
            {
                Build();
            }

            // Work in mesh model space because the tree data exists in model space
            OrientedBox meshSpaceOOBB    = new OrientedBox(box);
            Matrix4x4   inverseTransform = meshTransformMatrix.ToMatrix4x4x.inverse;

            meshSpaceOOBB.Transform(inverseTransform);

            // Used to avoid duplicate indices since there can be triangles which share the same vertices
            // and we don't want to return the same vertex multiple times.
            HashSet <int> usedIndices = new HashSet <int>();

            // Retrieve the nodes overlapped by the specified box
            List <SphereTreeNode <MeshSphereTreeTriangle> > overlappedNodes = _sphereTree.OverlapBox(meshSpaceOOBB);

            if (overlappedNodes.Count == 0)
            {
                return(new List <Vector3>());
            }

            // Loop through all nodes
            var overlappedWorldVerts = new List <Vector3>(50);

            foreach (var node in overlappedNodes)
            {
                // Get the triangle associated with the node
                int triangleIndex = node.Data.TriangleIndex;

                MeshTriangleInfo triangleInfo   = _octave3DMesh.GetMeshTriangleInfo(triangleIndex);
                List <Vector3>   trianglePoints = triangleInfo.ModelSpaceTriangle.GetPoints();

                for (int ptIndex = 0; ptIndex < trianglePoints.Count; ++ptIndex)
                {
                    if (usedIndices.Contains(triangleInfo.VertIndices[ptIndex]))
                    {
                        continue;
                    }

                    Vector3 point = trianglePoints[ptIndex];
                    if (meshSpaceOOBB.ContainsPoint(point))
                    {
                        overlappedWorldVerts.Add(meshTransformMatrix.MultiplyPoint(point));
                        usedIndices.Add(triangleInfo.VertIndices[ptIndex]);
                    }
                }

                /*Triangle3D modelSpaceTriangle = _octave3DMesh.GetTriangle(triangleIndex);
                 *
                 * // Now check which of the triangle points resides inside the box
                 * List<Vector3> trianglePoints = modelSpaceTriangle.GetPoints();
                 * foreach(var pt in trianglePoints)
                 * {
                 *  // When a point resides inside the box, we will transform it in world space and add it to the final point list
                 *  if (box.ContainsPoint(pt)) overlappedWorldVerts.Add(meshTransformMatrix.MultiplyPoint(pt));
                 * }*/
            }

            return(overlappedWorldVerts);
        }
Beispiel #26
0
 public List <Vector3> GetOverlappedWorldVerts(Box worldOverlapBox, TransformMatrix meshTransformMatrix)
 {
     return(_meshSphereTree.GetOverlappedWorldVerts(worldOverlapBox.ToOrientedBox(), meshTransformMatrix));
 }
Beispiel #27
0
        /// <summary>
        /// Performs a ray cast against the mesh tree and returns an instance of the 'MeshRayHit'
        /// class which holds information about the ray hit. The method returns the hit which is
        /// closest to the ray origin. If no triangle was hit, the method returns null.
        /// </summary>
        public MeshRayHit Raycast(Ray ray, TransformMatrix meshTransformMatrix)
        {
            // If the tree was not yet build, we need to build it because we need
            // the triangle information in order to perform the raycast.
            if (!_wasBuilt)
            {
                Build();
            }

            // When the sphere tree is constructed it is constructed in the mesh local space (i.e. it takes
            // no position/rotation/scale into account). This is required because a mesh can be shared by
            // lots of different objects each with its own transform data. This is why we need the mes matrix
            // parameter. It allows us to transform the ray in the mesh local space and perform our tests there.
            Ray meshLocalSpaceRay = ray.InverseTransform(meshTransformMatrix.ToMatrix4x4x);

            // First collect all terminal nodes which are intersected by this ray. If no nodes
            // are intersected, we will return null.
            List <SphereTreeNodeRayHit <MeshSphereTreeTriangle> > nodeRayHits = _sphereTree.RaycastAll(meshLocalSpaceRay);

            if (nodeRayHits.Count == 0)
            {
                return(null);
            }

            // We now have to loop thorugh all intersected nodes and find the triangle whose
            // intersection point is closest to the ray origin.
            float      minT                   = float.MaxValue;
            Triangle3D closestTriangle        = null;
            int        indexOfClosestTriangle = -1;
            Vector3    closestHitPoint        = Vector3.zero;

            foreach (var nodeRayHit in nodeRayHits)
            {
                // Retrieve the data associated with the node and construct the mesh triangle instance
                MeshSphereTreeTriangle sphereTreeTriangle = nodeRayHit.HitNode.Data;
                Triangle3D             meshTriangle       = _octave3DMesh.GetTriangle(sphereTreeTriangle.TriangleIndex);

                // Check if the ray intersects the trianlge which resides in the node
                float hitEnter;
                if (meshTriangle.Raycast(meshLocalSpaceRay, out hitEnter))
                {
                    // The trianlge is intersected by the ray, but we also have to ensure that the
                    // intersection point is closer than what we have found so far. If it is, we
                    // store all relevant information.
                    if (hitEnter < minT)
                    {
                        minT                   = hitEnter;
                        closestTriangle        = meshTriangle;
                        indexOfClosestTriangle = sphereTreeTriangle.TriangleIndex;
                        closestHitPoint        = meshLocalSpaceRay.GetPoint(hitEnter);
                    }
                }
            }

            // If we found the closest triangle, we can construct the ray hit instance and return it.
            // Otherwise we return null. This can happen when the ray intersects the triangle node
            // spheres, but the triangles themselves.
            if (closestTriangle != null)
            {
                // We have worked in mesh local space up until this point, but we want to return the
                // hit info in world space, so we have to transform the hit data accordingly.
                closestHitPoint = meshTransformMatrix.MultiplyPoint(closestHitPoint);
                minT            = (ray.origin - closestHitPoint).magnitude;
                Vector3 worldNormal = meshTransformMatrix.MultiplyVector(closestTriangle.Normal);

                return(new MeshRayHit(ray, minT, _octave3DMesh, indexOfClosestTriangle, closestHitPoint, worldNormal, meshTransformMatrix));
            }
            else
            {
                return(null);
            }
        }
Beispiel #28
0
 public List <Vector3> GetOverlappedWorldVerts(OrientedBox box, TransformMatrix meshTransformMatrix)
 {
     return(_meshSphereTree.GetOverlappedWorldVerts(box, meshTransformMatrix));
 }
Beispiel #29
0
 public void Transform(TransformMatrix transformMatrix)
 {
     _coordinateSystem.Transform(transformMatrix);
 }
Beispiel #30
0
 public List <Triangle3D> GetOverlappedTriangles(OrientedBox box, TransformMatrix meshTransformMatrix)
 {
     return(_meshSphereTree.GetOverlappedWorldTriangles(box, meshTransformMatrix));
 }