/// <summary> /// Performs a raycast against the mesh triangles and returns info /// about the closest hit or null if no triangle was hit by the ray. /// </summary> /// <param name="meshTransform"> /// The mesh transform which brings the mesh in the same space as the /// ray. /// </param> /// <remarks> /// This method will build the tree if it hasn't already been built. /// </remarks> public MeshRayHit RaycastClosest(Ray ray, Matrix4x4 meshTransform) { // Build the tree if it hasn't already been built if (!_isBuilt) { Build(); } // Work in mesh local space by transforming the ray by the inverse of // the mesh transform. It is faster to perform this transformation here // instead of transforming every possibly hit triangle by 'meshTransform'. Ray modelSpaceRay = ray.InverseTransform(meshTransform); // Get the list of tree nodes which are hit by the ray if (!_tree.RaycastAll(modelSpaceRay, _nodeHitBuffer)) { return(null); } // Store data in preparation for closest hit identification float t; float minT = float.MaxValue; MeshTriangle closestTriangle = null; bool foundTriangle = false; // Loop through each node hit foreach (var nodeHit in _nodeHitBuffer) { // Get the associated mesh triangle and check if the ray intersects it MeshTriangle meshTriangle = nodeHit.HitNode.Data; if (TriangleMath.Raycast(modelSpaceRay, out t, meshTriangle.Vertex0, meshTriangle.Vertex1, meshTriangle.Vertex2)) { if (Vector3.Dot(modelSpaceRay.direction, meshTriangle.Normal) < 0.0f) { // If the intersection offset is smaller than what we have so far, // it means we have a new closest hit. if (t < minT) { minT = t; closestTriangle = meshTriangle; foundTriangle = true; } } } } // If we found a triangle, we can return the mesh ray hit information if (foundTriangle) { // Convert the t value in world space. Do the same for the normal. Vector3 worldHit = meshTransform.MultiplyPoint(modelSpaceRay.GetPoint(minT)); minT = (ray.origin - worldHit).magnitude / ray.direction.magnitude; Vector3 transformedNormal = meshTransform.inverse.transpose.MultiplyVector(closestTriangle.Normal).normalized; // Return the hit instance return(new MeshRayHit(ray, closestTriangle.TriangleIndex, minT, transformedNormal)); } return(null); }
public bool RaycastAll(Ray ray, SceneRaycastPrecision raycastPresicion, List <GameObjectRayHit> hits) { hits.Clear(); if (!_objectTree.RaycastAll(ray, _nodeHitBuffer)) { return(false); } var boundsQConfig = new ObjectBounds.QueryConfig(); boundsQConfig.ObjectTypes = GameObjectTypeHelper.AllCombined; boundsQConfig.NoVolumeSize = Vector3Ex.FromValue(_nonMeshObjectSize); Vector3 camLook = RTFocusCamera.Get.Look; if (raycastPresicion == SceneRaycastPrecision.BestFit) { foreach (var nodeHit in _nodeHitBuffer) { GameObject sceneObject = nodeHit.HitNode.Data; if (sceneObject == null || !sceneObject.activeInHierarchy) { continue; } Renderer renderer = sceneObject.GetComponent <Renderer>(); if (renderer != null && !renderer.isVisible) { continue; } GameObjectType objectType = sceneObject.GetGameObjectType(); if (objectType == GameObjectType.Mesh) { GameObjectRayHit objectHit = RaycastMeshObject(ray, sceneObject); if (objectHit != null) { hits.Add(objectHit); } } else if (objectType == GameObjectType.Terrain) { TerrainCollider terrainCollider = sceneObject.GetComponent <TerrainCollider>(); if (terrainCollider != null) { RaycastHit hitInfo; if (terrainCollider.Raycast(ray, out hitInfo, float.MaxValue)) { hits.Add(new GameObjectRayHit(ray, hitInfo)); } } } else if (objectType == GameObjectType.Sprite) { GameObjectRayHit objectHit = RaycastSpriteObject(ray, sceneObject); if (objectHit != null) { hits.Add(objectHit); } } else { OBB worldOBB = ObjectBounds.CalcWorldOBB(sceneObject, boundsQConfig); if (worldOBB.IsValid) { float t; if (BoxMath.Raycast(ray, out t, worldOBB.Center, worldOBB.Size, worldOBB.Rotation)) { var faceDesc = BoxMath.GetFaceClosestToPoint(ray.GetPoint(t), worldOBB.Center, worldOBB.Size, worldOBB.Rotation, camLook); var hit = new GameObjectRayHit(ray, sceneObject, faceDesc.Plane.normal, t); hits.Add(hit); } } } } } else if (raycastPresicion == SceneRaycastPrecision.Box) { foreach (var nodeHit in _nodeHitBuffer) { GameObject sceneObject = nodeHit.HitNode.Data; if (sceneObject == null || !sceneObject.activeInHierarchy) { continue; } Renderer renderer = sceneObject.GetComponent <Renderer>(); if (renderer != null && !renderer.isVisible) { continue; } OBB worldOBB = ObjectBounds.CalcWorldOBB(sceneObject, boundsQConfig); if (worldOBB.IsValid) { float t; if (BoxMath.Raycast(ray, out t, worldOBB.Center, worldOBB.Size, worldOBB.Rotation)) { var faceDesc = BoxMath.GetFaceClosestToPoint(ray.GetPoint(t), worldOBB.Center, worldOBB.Size, worldOBB.Rotation, camLook); var hit = new GameObjectRayHit(ray, sceneObject, faceDesc.Plane.normal, t); hits.Add(hit); } } } } return(hits.Count != 0); }