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); }
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>()); } }
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)); }
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); }
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)); }
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()); }
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); }
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)); }
public void RenderGizmosDebug(TransformMatrix meshTransformMatrix) { _meshSphereTree.RenderGizmosDebug(meshTransformMatrix); }
public MatrixObjectBoxPair(TransformMatrix objectMatrix, OrientedBox objectBox) { _objectMatrix = objectMatrix; _objectBox = new OrientedBox(objectBox); }
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()); }
/// <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); }
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(); }
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)); }
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)); } }
public void SetTransformMatrix(TransformMatrix transformMatrix) { _renderableCoordinateSystem.SetTransformMatrix(transformMatrix); }
public CoordinateSystem(TransformMatrix transformMatrix) { SetTransformMatrix(transformMatrix); }
public MeshRayHit Raycast(Ray ray, TransformMatrix meshTransformMatrix) { //meshTransformMatrix.Scale = meshTransformMatrix.Scale.GetVectorWithPositiveComponents(); return(_meshSphereTree.Raycast(ray, meshTransformMatrix)); }
/// <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); }
public List <Vector3> GetOverlappedWorldVerts(Box worldOverlapBox, TransformMatrix meshTransformMatrix) { return(_meshSphereTree.GetOverlappedWorldVerts(worldOverlapBox.ToOrientedBox(), meshTransformMatrix)); }
/// <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); } }
public List <Vector3> GetOverlappedWorldVerts(OrientedBox box, TransformMatrix meshTransformMatrix) { return(_meshSphereTree.GetOverlappedWorldVerts(box, meshTransformMatrix)); }
public void Transform(TransformMatrix transformMatrix) { _coordinateSystem.Transform(transformMatrix); }
public List <Triangle3D> GetOverlappedTriangles(OrientedBox box, TransformMatrix meshTransformMatrix) { return(_meshSphereTree.GetOverlappedWorldTriangles(box, meshTransformMatrix)); }