/// <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 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)); }
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); } }