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> /// Performs a raycast and returns a list of hits for all objects whose meshes /// are intersected by the specified ray. /// </summary> public List <GameObjectRayHit> RaycastAllMesh(Ray ray) { // First, we will gather the objects whos boxes are intersected by the ray. If // no such objects exist, we will return an empty list. List <GameObjectRayHit> allBoxHits = RaycastAllBox(ray); if (allBoxHits.Count == 0) { return(new List <GameObjectRayHit>()); } // Now we will loop through all these objects and identify the ones whose meshes // are hit by the ray. var allMeshObjectHits = new List <GameObjectRayHit>(allBoxHits.Count); Octave3DMeshDatabase octave3DMeshDatabase = Octave3DMeshDatabase.Get(); foreach (var boxHit in allBoxHits) { // Store the object for easy access GameObject hitObject = boxHit.HitObject; if (hitObject == null) { continue; } if (!hitObject.activeInHierarchy) { continue; } Renderer renderer = hitObject.GetComponent <Renderer>(); if (!renderer.enabled) { continue; } // Store the object's mesh. If the object doesn't have a mesh, we will ignore it Mesh objectMesh = hitObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh == null) { continue; } // Check if the ray intersects the mesh Octave3DMesh octave3DMesh = octave3DMeshDatabase.GetOctave3DMesh(objectMesh); MeshRayHit meshRayHit = octave3DMesh.Raycast(ray, hitObject.transform.GetWorldMatrix()); // If the mesh was hit by the ray, we will store the hit info inside the output array if (meshRayHit != null) { allMeshObjectHits.Add(new GameObjectRayHit(ray, hitObject, null, meshRayHit, null, null)); } } return(allMeshObjectHits); }
public bool ObjectMeshIntersectsAnyMesh(Octave3DMesh octave3DQueryMesh, TransformMatrix worldMatrix, HashSet <GameObject> ignoreObjects) { if (ignoreObjects == null) { ignoreObjects = new HashSet <GameObject>(); } OrientedBox queryMeshOOBB = octave3DQueryMesh.GetOOBB(worldMatrix); if (queryMeshOOBB.IsInvalid()) { return(false); } List <SphereTreeNode <GameObject> > allOverlappedNodes = _sphereTree.OverlapBox(queryMeshOOBB); if (allOverlappedNodes.Count == 0) { return(false); } var overlappedObjects = new List <GameObject>(); foreach (SphereTreeNode <GameObject> node in allOverlappedNodes) { GameObject gameObject = node.Data; if (gameObject == null) { continue; } if (!gameObject.activeSelf) { continue; } if (ignoreObjects.Contains(gameObject)) { continue; } Mesh objectMesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh != null) { Octave3DMesh octave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(objectMesh); if (octave3DMesh != null) { if (octave3DQueryMesh.IntersectsMesh(worldMatrix, octave3DMesh, gameObject.transform.GetWorldMatrix())) { return(true); } } } } return(false); }
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 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 static Object2ObjectBoxSnapData Create(GameObject meshObject) { if (meshObject == null) { return(null); } Mesh objectMesh = meshObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh == null) { return(null); } Renderer renderer = meshObject.GetRenderer(); if (renderer == null || !renderer.enabled) { return(null); } Octave3DMesh octaveMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(objectMesh); if (octaveMesh == null) { return(null); } List <Box> modelVertOverlapBoxes = BuildModelVertOverlapBoxes(octaveMesh); var snapBoxIDs = Object2ObjectBoxSnapData.GetAllSnapBoxIDs(); var modelSnapBoxes = new List <Box>(snapBoxIDs.Length); Box modelMeshBox = octaveMesh.ModelAABB; BoxFace[] meshBoxFaces = Object2ObjectBoxSnapData.GetBoxFaceToSnapBoxIDMap(); foreach (var snapBox in snapBoxIDs) { Box overlapBox = modelVertOverlapBoxes[(int)snapBox]; List <Vector3> overlappedVerts = octaveMesh.GetOverlappedModelVerts(overlapBox); Plane meshFacePlane = modelMeshBox.GetBoxFacePlane(meshBoxFaces[(int)snapBox]); overlappedVerts = meshFacePlane.ProjectAllPoints(overlappedVerts); modelSnapBoxes.Add(Box.FromPoints(overlappedVerts)); } return(new Object2ObjectBoxSnapData(meshObject, modelSnapBoxes)); }
private static List <Box> BuildModelVertOverlapBoxes(Octave3DMesh octaveMesh) { const float overlapAmount = 0.2f; float halfOverlapAmount = overlapAmount * 0.5f; Box modelMeshBox = octaveMesh.ModelAABB; Vector3 meshBoxSize = modelMeshBox.Size; BoxFace[] meshBoxFaces = Object2ObjectBoxSnapData.GetBoxFaceToSnapBoxIDMap(); // Must have 1 to 1 mapping with Object2ObjectBoxSnapData.SnapBox enum const float sizeEps = 0.001f; Vector3[] overlapBoxSizes = new Vector3[] { // Left and right new Vector3(overlapAmount, meshBoxSize.y + sizeEps, meshBoxSize.z + sizeEps), new Vector3(overlapAmount, meshBoxSize.y + sizeEps, meshBoxSize.z + sizeEps), // Bottom and top new Vector3(meshBoxSize.x + sizeEps, overlapAmount, meshBoxSize.z + sizeEps), new Vector3(meshBoxSize.x + sizeEps, overlapAmount, meshBoxSize.z + sizeEps), // Back and front new Vector3(meshBoxSize.x + sizeEps, meshBoxSize.y + sizeEps, overlapAmount), new Vector3(meshBoxSize.x + sizeEps, meshBoxSize.y + sizeEps, overlapAmount), }; var overlapBoxes = new List <Box>(); for (int boxFaceIndex = 0; boxFaceIndex < meshBoxFaces.Length; ++boxFaceIndex) { BoxFace meshBoxFace = meshBoxFaces[boxFaceIndex]; Vector3 faceCenter = modelMeshBox.GetBoxFaceCenter(meshBoxFace); Vector3 faceNormal = modelMeshBox.GetBoxFacePlane(meshBoxFace).normal; Vector3 overlapCenter = faceCenter - faceNormal * halfOverlapAmount; overlapBoxes.Add(new Box(overlapCenter, overlapBoxSizes[boxFaceIndex])); } return(overlapBoxes); }
/// <summary> /// Constructor. /// </summary> /// <param name="octave3DMesh"> /// The mesh associated with the tree. /// </param> public MeshSphereTree(Octave3DMesh octave3DMesh) { _octave3DMesh = octave3DMesh; _sphereTree = new SphereTree <MeshSphereTreeTriangle>(2); }
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 void SnapHierarchyToNearbyObjects(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints) { Vector3 chosenSnapDestination = Vector3.zero, chosenSnapPivot = Vector3.zero; float minDistance = float.MaxValue; float snapEpsilon = Settings.ObjectToObjectSnapEpsilon; // Snapping will only work if there is at least one mesh object in the hierarchy or at least one sprite renderer // with a valid sprite. So we will first get that out of the way. If this condition is not met, we will just return. List <GameObject> meshObjectsInHierarchy = hierarchyRoot.GetHierarchyObjectsWithMesh(); List <GameObject> spriteObjectsInHierarchy = hierarchyRoot.GetHierarchyObjectsWithSprites(); if (meshObjectsInHierarchy.Count == 0 && spriteObjectsInHierarchy.Count == 0) { return; } // When snapping, we will need to collect the nearby snap destination points (e.g. vertices or box corner points). // In order to do this we will perform an overlap test using the hierarchy's world box. The size of the box is // increased by 'snapEpsilon' on all axes to account for the snap espilon. Box hierarchyWorldBox = hierarchyRoot.GetHierarchyWorldBox(); if (!hierarchyWorldBox.IsValid()) { return; } Box hierarchyQueryBox = hierarchyWorldBox; hierarchyQueryBox.Size = hierarchyQueryBox.Size + Vector3.one * snapEpsilon; // Acquire the nearby objects that contain the possible snap destination points List <GameObject> nearbyObjects = Octave3DScene.Get().OverlapBox(hierarchyQueryBox); if (nearbyObjects.Count == 0) { return; } // If the user chose vertex snapping, we will only continue if we have at least one mesh in our hierarchy. // Otherwise we will resort to box snapping. if (Settings.ObjectToObjectSnapMode == ObjectToObjectSnapMode.Vertex && meshObjectsInHierarchy.Count != 0) { foreach (GameObject gameObject in nearbyObjects) { Box objectWorldBox = Box.GetInvalid(); // We will first attempt to retrieve the mesh's world box Mesh objectMesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); bool objectHasMesh = objectMesh != null; Octave3DMesh objectOctave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(objectMesh); if (objectOctave3DMesh == null) { objectHasMesh = false; } if (objectHasMesh) { objectWorldBox = gameObject.GetMeshWorldBox(); } // If the world box is still invalid, we will acquire the object's sprite world box. if (objectWorldBox.IsInvalid()) { if (gameObject.HasSpriteRendererWithSprite()) { objectWorldBox = gameObject.GetNonMeshWorldBox(); } } // If at this point the box is still invalid, it most likely means that the object doesn't have // a mesh or a sprite attached to it, so we will go on to the next iteration of the loop. if (objectWorldBox.IsInvalid()) { continue; } Box objectQueryBox = objectWorldBox; objectQueryBox.Size += Vector3.one * snapEpsilon; // At this point, we have a valid world box, but we need to check if it comes from a mesh or from // a sprite. If it's from a mesh, we will perform the vertex-to-vertex snap. Otherwise, we will // snap the vertices of all meshes in our hierarchy to the corner points of the sprite. if (objectHasMesh) { // Detect the object vertices which are overlapped by the hierarchy box List <Vector3> objectOverlappedVerts = objectOctave3DMesh.GetOverlappedWorldVerts(hierarchyQueryBox, gameObject.transform.GetWorldMatrix()); // Loop through all meshes in the hierarchy foreach (var meshObjInHierarchy in meshObjectsInHierarchy) { Mesh hierarchyMesh = meshObjInHierarchy.GetMeshFromFilterOrSkinnedMeshRenderer(); if (hierarchyMesh == null) { continue; } Octave3DMesh hierarchyOctave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(hierarchyMesh); if (hierarchyOctave3DMesh == null) { continue; } // Detect all vertices inside the mesh which are overlapped by the object's box (inverse operation of what we performed in the // beginning). The idea is to collect all source vertices that could pottentially intersect with each other. List <Vector3> hierarchyMeshOverlappedVerts = hierarchyOctave3DMesh.GetOverlappedWorldVerts(objectQueryBox, meshObjInHierarchy.transform.GetWorldMatrix()); foreach (Vector3 hierarchyVertex in hierarchyMeshOverlappedVerts) { foreach (Vector3 objectVertex in objectOverlappedVerts) { float distance = (hierarchyVertex - objectVertex).magnitude; if (distance < minDistance) { minDistance = distance; chosenSnapDestination = objectVertex; chosenSnapPivot = hierarchyVertex; } } } } } else { List <Vector3> spriteWorldCornerPoints = objectWorldBox.GetCornerPoints(); foreach (var meshObjInHierarchy in meshObjectsInHierarchy) { Mesh hierarchyMesh = meshObjInHierarchy.GetMeshFromFilterOrSkinnedMeshRenderer(); if (hierarchyMesh == null) { continue; } Octave3DMesh hierarchyOctave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(hierarchyMesh); if (hierarchyOctave3DMesh == null) { continue; } List <Vector3> hierarchyMeshOverlappedVerts = hierarchyOctave3DMesh.GetOverlappedWorldVerts(objectQueryBox, meshObjInHierarchy.transform.GetWorldMatrix()); foreach (Vector3 hierarchyVertex in hierarchyMeshOverlappedVerts) { foreach (Vector3 spriteCornerPt in spriteWorldCornerPoints) { float distance = (hierarchyVertex - spriteCornerPt).magnitude; if (distance < minDistance) { minDistance = distance; chosenSnapDestination = spriteCornerPt; chosenSnapPivot = hierarchyVertex; } } } } } } } else { List <Vector3> hierarchyBoxCornerPoints = hierarchyWorldBox.GetCornerPoints(); foreach (GameObject gameObject in nearbyObjects) { Box objectWorldBox = Box.GetInvalid(); Mesh objectMesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh != null) { objectWorldBox = gameObject.GetMeshWorldBox(); } if (objectWorldBox.IsInvalid() && gameObject.HasSpriteRendererWithSprite()) { objectWorldBox = gameObject.GetNonMeshWorldBox(); } if (objectWorldBox.IsInvalid()) { continue; } List <Vector3> worldBoxCornerPoints = objectWorldBox.GetCornerPoints(); foreach (Vector3 hierarchyBoxPt in hierarchyBoxCornerPoints) { foreach (Vector3 objectMeshBoxPt in worldBoxCornerPoints) { float distance = (hierarchyBoxPt - objectMeshBoxPt).magnitude; if (distance < minDistance) { minDistance = distance; chosenSnapDestination = objectMeshBoxPt; chosenSnapPivot = hierarchyBoxPt; } } } } } if (minDistance < snapEpsilon) { SnapObjectHierarchyPosition(hierarchyRoot, chosenSnapPivot, chosenSnapDestination, projectedBoxFacePivotPoints, 0.0f); } }
public void UpdateForMouseMovement() { if (!_isActive) { return; } if (MouseButtonStates.Instance.IsMouseButtonDown(MouseButton.Left)) { _state = ObjectVertexSnapSessionState.SnapToDestination; } else { _state = ObjectVertexSnapSessionState.SelectSourceVertex; } if (_state == ObjectVertexSnapSessionState.SelectSourceVertex) { if (_sourceObjects == null || _sourceObjects.Count == 0) { _objectMask.ObjectCollectionMask.UnmaskAll(); MouseCursorRayHit cursorRayHit = GetCursorRayHit(); if (cursorRayHit.WasAnObjectHit) { GameObjectRayHit objectRayHit = cursorRayHit.ClosestObjectRayHit; MeshRayHit meshRayHit = objectRayHit.ObjectMeshHit; if (meshRayHit != null) { Octave3DMesh octaveMesh = meshRayHit.HitMesh; Triangle3D sourceTriangle = octaveMesh.GetTriangle(meshRayHit.HitTriangleIndex); sourceTriangle.TransformPoints(objectRayHit.HitObject.transform.localToWorldMatrix); _sourceVertex = sourceTriangle.GetPointClosestToPoint(meshRayHit.HitPoint); _sourceObject = objectRayHit.HitObject; _objectMask.ObjectCollectionMask.Mask(_sourceObject.transform.root.gameObject.GetAllChildrenIncludingSelf()); } else { SpriteRenderer spriteRenderer = objectRayHit.HitObject.GetComponent <SpriteRenderer>(); if (spriteRenderer != null) { _sourceObject = objectRayHit.HitObject; _sourceVertex = Vector3Extensions.GetClosestPointToPoint(objectRayHit.ObjectBoxHit.HitBox.GetCenterAndCornerPoints(), objectRayHit.HitPoint); _objectMask.ObjectCollectionMask.Mask(_sourceObject.transform.root.gameObject.GetAllChildrenIncludingSelf()); } } } } else { MouseCursorRayHit cursorRayHit = GetCursorRayHit(); if (cursorRayHit.WasAnObjectHit) { GameObjectRayHit objectRayHit = cursorRayHit.ClosestObjectRayHit; MeshRayHit meshRayHit = objectRayHit.ObjectMeshHit; if (meshRayHit != null) { Octave3DMesh octaveMesh = meshRayHit.HitMesh; Triangle3D sourceTriangle = octaveMesh.GetTriangle(meshRayHit.HitTriangleIndex); sourceTriangle.TransformPoints(objectRayHit.HitObject.transform.localToWorldMatrix); _sourceVertex = sourceTriangle.GetPointClosestToPoint(meshRayHit.HitPoint); } else { SpriteRenderer spriteRenderer = objectRayHit.HitObject.GetComponent <SpriteRenderer>(); if (spriteRenderer != null) { _sourceVertex = Vector3Extensions.GetClosestPointToPoint(objectRayHit.ObjectBoxHit.HitBox.GetCenterAndCornerPoints(), objectRayHit.HitPoint); } } } foreach (var parent in _sourceParents) { _objectMask.ObjectCollectionMask.Mask(parent.GetAllChildrenIncludingSelf()); } } } else { MouseCursorRayHit cursorRayHit = GetCursorRayHit(); if (cursorRayHit.WasAnythingHit) { bool useGridCellHit = false; if (!cursorRayHit.WasAnObjectHit) { useGridCellHit = true; } else if (cursorRayHit.WasAnObjectHit && cursorRayHit.WasACellHit) { float gridCellHitEnter = cursorRayHit.GridCellRayHit.HitEnter; float objectHitEnter = cursorRayHit.ClosestObjectRayHit.HitEnter; if (gridCellHitEnter < Mathf.Max(0.0f, (objectHitEnter - 1e-3f))) { useGridCellHit = true; } } if (useGridCellHit) { XZGridCell hitCell = cursorRayHit.GridCellRayHit.HitCell; XZOrientedQuad3D cellQuad = hitCell.Quad; _destinationObject = null; _destinationGridCell = hitCell; _snapPosition = cellQuad.GetPointClosestToPoint(cursorRayHit.GridCellRayHit.HitPoint, true); SnapToDestination(); } else { GameObjectRayHit objectRayHit = cursorRayHit.ClosestObjectRayHit; MeshRayHit meshRayHit = objectRayHit.ObjectMeshHit; if (meshRayHit != null) { _destinationObject = objectRayHit.HitObject; Triangle3D destinationTriangle = meshRayHit.HitMesh.GetTriangle(meshRayHit.HitTriangleIndex); destinationTriangle.TransformPoints(_destinationObject.transform.localToWorldMatrix); _destinationGridCell = null; _snapPosition = destinationTriangle.GetPointClosestToPoint(meshRayHit.HitPoint); SnapToDestination(); } else { SpriteRenderer spriteRenderer = objectRayHit.HitObject.GetComponent <SpriteRenderer>(); if (spriteRenderer != null) { _destinationGridCell = null; _destinationObject = objectRayHit.HitObject; _snapPosition = Vector3Extensions.GetClosestPointToPoint(objectRayHit.ObjectBoxHit.HitBox.GetCenterAndCornerPoints(), objectRayHit.HitPoint); SnapToDestination(); } } } } } }
public Octave3DMeshCollider(Octave3DMesh mesh, TransformMatrix meshTransform) { _mesh = mesh; _meshTransform = meshTransform; }