| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 | /** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */using Live2D.Cubism.Core;using Live2D.Cubism.Rendering;using System.Collections.Generic;using UnityEngine;namespace Live2D.Cubism.Framework.Raycasting{    /// <summary>    /// Allows casting rays against <see cref="CubismRaycastable"/>s.    /// </summary>    public sealed class CubismRaycaster : MonoBehaviour    {        /// <summary>        /// <see cref="CubismRenderer"/>s with <see cref="CubismRaycastable"/>s attached.        /// </summary>        private CubismRenderer[] Raycastables { get; set; }        /// <summary>        /// <see cref="CubismRaycastablePrecision"/>s with <see cref="CubismRaycastable"/>s attached.??????????????        /// </summary>        private CubismRaycastablePrecision[] RaycastablePrecisions { get; set; }        /// <summary>        /// Refreshes the controller. Call this method after adding and/or removing <see cref="CubismRaycastable"/>.        /// </summary>        private void Refresh()        {            var candidates = this                .FindCubismModel()                .Drawables;            // Find raycastable drawables.            var raycastables = new List<CubismRenderer>();            var raycastablePrecisions = new List<CubismRaycastablePrecision>();            for (var i = 0; i < candidates.Length; i++)            {                // Skip non-raycastables.                if (candidates[i].GetComponent<CubismRaycastable>() == null)                {                    continue;                }                raycastables.Add(candidates[i].GetComponent<CubismRenderer>());                raycastablePrecisions.Add(candidates[i].GetComponent<CubismRaycastable>().Precision);            }            // Cache raycastables.            Raycastables = raycastables.ToArray();            RaycastablePrecisions = raycastablePrecisions.ToArray();        }        #region Unity Event Handling        /// <summary>        /// Called by Unity. Makes sure cache is initialized.        /// </summary>        private void Start()        {            // Initialize cache.            Refresh();        }        #endregion        /// <summary>        /// Casts a ray.        /// </summary>        /// <param name="origin">The origin of the ray.</param>        /// <param name="direction">The direction of the ray.</param>        /// <param name="result">The result of the cast.</param>        /// <param name="maximumDistance">[Optional] The maximum distance of the ray.</param>        /// <returns><see langword="true"/> in case of a hit; <see langword="false"/> otherwise.</returns>        /// <returns>The numbers of drawables had hit</returns>        public int Raycast(Vector3 origin, Vector3 direction, CubismRaycastHit[] result, float maximumDistance = Mathf.Infinity)        {            return Raycast(new Ray(origin, direction), result, maximumDistance);        }        /// <summary>        /// Casts a ray.        /// </summary>        /// <param name="ray"></param>        /// <param name="result">The result of the cast.</param>        /// <param name="maximumDistance">[Optional] The maximum distance of the ray.</param>        /// <returns><see langword="true"/> in case of a hit; <see langword="false"/> otherwise.</returns>        /// <returns>The numbers of drawables had hit</returns>        public int Raycast(Ray ray, CubismRaycastHit[] result, float maximumDistance = Mathf.Infinity)        {            // Cast ray against model plane.            var intersectionInWorldSpace = ray.origin + ray.direction * (ray.direction.z / ray.origin.z);            var intersectionInLocalSpace = transform.InverseTransformPoint(intersectionInWorldSpace);            intersectionInLocalSpace.z = 0;            var distance = intersectionInWorldSpace.magnitude;            // Return non-hits.            if (distance > maximumDistance)            {                return 0;            }            // Cast against each raycastable.            var hitCount = 0;            for (var i = 0; i < Raycastables.Length; i++)            {                var raycastable = Raycastables[i];                var raycastablePrecision = RaycastablePrecisions[i];                // Skip inactive raycastables.                if (!raycastable.MeshRenderer.enabled)                {                    continue;                }                var bounds = raycastable.Mesh.bounds;                // Skip non hits (bounding box)                if (!bounds.Contains(intersectionInLocalSpace))                {                    continue;                }                // Do detailed hit-detection against mesh if requested.                if (raycastablePrecision == CubismRaycastablePrecision.Triangles)                {                    if (!ContainsInTriangles(raycastable.Mesh, intersectionInLocalSpace))                    {                        continue;                    }                }                result[hitCount].Drawable = raycastable.GetComponent<CubismDrawable>();                result[hitCount].Distance = distance;                result[hitCount].LocalPosition = intersectionInLocalSpace;                result[hitCount].WorldPosition = intersectionInWorldSpace;                ++hitCount;                // Exit if result buffer is full.                if (hitCount == result.Length)                {                    break;                }            }            return hitCount;        }        /// <summary>        /// Check the point is inside polygons.        /// </summary>        /// <param name="mesh"></param>        /// <param name="inputPosition"></param>        /// <returns></returns>        private bool ContainsInTriangles(Mesh mesh, Vector3 inputPosition)        {            for (var i = 0; i < mesh.triangles.Length; i+=3)            {                var vertexPositionA = mesh.vertices[mesh.triangles[i]];                var vertexPositionB = mesh.vertices[mesh.triangles[i + 1]];                var vertexPositionC = mesh.vertices[mesh.triangles[i + 2]];                var crossProduct1 =                    (vertexPositionB.x - vertexPositionA.x) * (inputPosition.y - vertexPositionB.y) -                    (vertexPositionB.y - vertexPositionA.y) * (inputPosition.x - vertexPositionB.x);                var crossProduct2 =                    (vertexPositionC.x - vertexPositionB.x) * (inputPosition.y - vertexPositionC.y) -                    (vertexPositionC.y - vertexPositionB.y) * (inputPosition.x - vertexPositionC.x);                var crossProduct3 =                    (vertexPositionA.x - vertexPositionC.x) * (inputPosition.y - vertexPositionA.y) -                    (vertexPositionA.y - vertexPositionC.y) * (inputPosition.x - vertexPositionA.x);                if ((crossProduct1 > 0 && crossProduct2 > 0 && crossProduct3 > 0) ||                    (crossProduct1 < 0 && crossProduct2 < 0 && crossProduct3 < 0))                {                    return true;                }            }            return false;        }    }}
 |