CubismRaycaster.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /**
  2. * Copyright(c) Live2D Inc. All rights reserved.
  3. *
  4. * Use of this source code is governed by the Live2D Open Software license
  5. * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
  6. */
  7. using Live2D.Cubism.Core;
  8. using Live2D.Cubism.Rendering;
  9. using System.Collections.Generic;
  10. using UnityEngine;
  11. namespace Live2D.Cubism.Framework.Raycasting
  12. {
  13. /// <summary>
  14. /// Allows casting rays against <see cref="CubismRaycastable"/>s.
  15. /// </summary>
  16. public sealed class CubismRaycaster : MonoBehaviour
  17. {
  18. /// <summary>
  19. /// <see cref="CubismRenderer"/>s with <see cref="CubismRaycastable"/>s attached.
  20. /// </summary>
  21. private CubismRenderer[] Raycastables { get; set; }
  22. /// <summary>
  23. /// <see cref="CubismRaycastablePrecision"/>s with <see cref="CubismRaycastable"/>s attached.??????????????
  24. /// </summary>
  25. private CubismRaycastablePrecision[] RaycastablePrecisions { get; set; }
  26. /// <summary>
  27. /// Refreshes the controller. Call this method after adding and/or removing <see cref="CubismRaycastable"/>.
  28. /// </summary>
  29. private void Refresh()
  30. {
  31. var candidates = this
  32. .FindCubismModel()
  33. .Drawables;
  34. // Find raycastable drawables.
  35. var raycastables = new List<CubismRenderer>();
  36. var raycastablePrecisions = new List<CubismRaycastablePrecision>();
  37. for (var i = 0; i < candidates.Length; i++)
  38. {
  39. // Skip non-raycastables.
  40. if (candidates[i].GetComponent<CubismRaycastable>() == null)
  41. {
  42. continue;
  43. }
  44. raycastables.Add(candidates[i].GetComponent<CubismRenderer>());
  45. raycastablePrecisions.Add(candidates[i].GetComponent<CubismRaycastable>().Precision);
  46. }
  47. // Cache raycastables.
  48. Raycastables = raycastables.ToArray();
  49. RaycastablePrecisions = raycastablePrecisions.ToArray();
  50. }
  51. #region Unity Event Handling
  52. /// <summary>
  53. /// Called by Unity. Makes sure cache is initialized.
  54. /// </summary>
  55. private void Start()
  56. {
  57. // Initialize cache.
  58. Refresh();
  59. }
  60. #endregion
  61. /// <summary>
  62. /// Casts a ray.
  63. /// </summary>
  64. /// <param name="origin">The origin of the ray.</param>
  65. /// <param name="direction">The direction of the ray.</param>
  66. /// <param name="result">The result of the cast.</param>
  67. /// <param name="maximumDistance">[Optional] The maximum distance of the ray.</param>
  68. /// <returns><see langword="true"/> in case of a hit; <see langword="false"/> otherwise.</returns>
  69. /// <returns>The numbers of drawables had hit</returns>
  70. public int Raycast(Vector3 origin, Vector3 direction, CubismRaycastHit[] result, float maximumDistance = Mathf.Infinity)
  71. {
  72. return Raycast(new Ray(origin, direction), result, maximumDistance);
  73. }
  74. /// <summary>
  75. /// Casts a ray.
  76. /// </summary>
  77. /// <param name="ray"></param>
  78. /// <param name="result">The result of the cast.</param>
  79. /// <param name="maximumDistance">[Optional] The maximum distance of the ray.</param>
  80. /// <returns><see langword="true"/> in case of a hit; <see langword="false"/> otherwise.</returns>
  81. /// <returns>The numbers of drawables had hit</returns>
  82. public int Raycast(Ray ray, CubismRaycastHit[] result, float maximumDistance = Mathf.Infinity)
  83. {
  84. // Cast ray against model plane.
  85. var intersectionInWorldSpace = ray.origin + ray.direction * (ray.direction.z / ray.origin.z);
  86. var intersectionInLocalSpace = transform.InverseTransformPoint(intersectionInWorldSpace);
  87. intersectionInLocalSpace.z = 0;
  88. var distance = intersectionInWorldSpace.magnitude;
  89. // Return non-hits.
  90. if (distance > maximumDistance)
  91. {
  92. return 0;
  93. }
  94. // Cast against each raycastable.
  95. var hitCount = 0;
  96. for (var i = 0; i < Raycastables.Length; i++)
  97. {
  98. var raycastable = Raycastables[i];
  99. var raycastablePrecision = RaycastablePrecisions[i];
  100. // Skip inactive raycastables.
  101. if (!raycastable.MeshRenderer.enabled)
  102. {
  103. continue;
  104. }
  105. var bounds = raycastable.Mesh.bounds;
  106. // Skip non hits (bounding box)
  107. if (!bounds.Contains(intersectionInLocalSpace))
  108. {
  109. continue;
  110. }
  111. // Do detailed hit-detection against mesh if requested.
  112. if (raycastablePrecision == CubismRaycastablePrecision.Triangles)
  113. {
  114. if (!ContainsInTriangles(raycastable.Mesh, intersectionInLocalSpace))
  115. {
  116. continue;
  117. }
  118. }
  119. result[hitCount].Drawable = raycastable.GetComponent<CubismDrawable>();
  120. result[hitCount].Distance = distance;
  121. result[hitCount].LocalPosition = intersectionInLocalSpace;
  122. result[hitCount].WorldPosition = intersectionInWorldSpace;
  123. ++hitCount;
  124. // Exit if result buffer is full.
  125. if (hitCount == result.Length)
  126. {
  127. break;
  128. }
  129. }
  130. return hitCount;
  131. }
  132. /// <summary>
  133. /// Check the point is inside polygons.
  134. /// </summary>
  135. /// <param name="mesh"></param>
  136. /// <param name="inputPosition"></param>
  137. /// <returns></returns>
  138. private bool ContainsInTriangles(Mesh mesh, Vector3 inputPosition)
  139. {
  140. for (var i = 0; i < mesh.triangles.Length; i+=3)
  141. {
  142. var vertexPositionA = mesh.vertices[mesh.triangles[i]];
  143. var vertexPositionB = mesh.vertices[mesh.triangles[i + 1]];
  144. var vertexPositionC = mesh.vertices[mesh.triangles[i + 2]];
  145. var crossProduct1 =
  146. (vertexPositionB.x - vertexPositionA.x) * (inputPosition.y - vertexPositionB.y) -
  147. (vertexPositionB.y - vertexPositionA.y) * (inputPosition.x - vertexPositionB.x);
  148. var crossProduct2 =
  149. (vertexPositionC.x - vertexPositionB.x) * (inputPosition.y - vertexPositionC.y) -
  150. (vertexPositionC.y - vertexPositionB.y) * (inputPosition.x - vertexPositionC.x);
  151. var crossProduct3 =
  152. (vertexPositionA.x - vertexPositionC.x) * (inputPosition.y - vertexPositionA.y) -
  153. (vertexPositionA.y - vertexPositionC.y) * (inputPosition.x - vertexPositionA.x);
  154. if ((crossProduct1 > 0 && crossProduct2 > 0 && crossProduct3 > 0) ||
  155. (crossProduct1 < 0 && crossProduct2 < 0 && crossProduct3 < 0))
  156. {
  157. return true;
  158. }
  159. }
  160. return false;
  161. }
  162. }
  163. }