Example #1
0
        /// <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);
        }