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