BsHandleSliderDisc.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Handles/BsHandleSliderDisc.h"
  4. #include "Handles/BsHandleManager.h"
  5. #include "Handles/BsHandleSliderManager.h"
  6. #include "Math/BsRay.h"
  7. #include "Math/BsVector3.h"
  8. #include "Math/BsQuaternion.h"
  9. #include "Components/BsCCamera.h"
  10. namespace bs
  11. {
  12. const float HandleSliderDisc::TORUS_RADIUS = 0.1f;
  13. HandleSliderDisc::HandleSliderDisc(const Vector3& normal, float radius, bool fixedScale, UINT64 layer)
  14. : HandleSlider(fixedScale, layer), mNormal(normal), mRadius(radius), mHasCutoffPlane(false), mDirection(BsZero)
  15. , mStartPosition(BsZero), mDelta(0.0f)
  16. {
  17. mCollider = Torus(normal, radius, TORUS_RADIUS);
  18. HandleSliderManager& sliderManager = HandleManager::instance().getSliderManager();
  19. sliderManager._registerSlider(this);
  20. }
  21. HandleSliderDisc::~HandleSliderDisc()
  22. {
  23. HandleSliderManager& sliderManager = HandleManager::instance().getSliderManager();
  24. sliderManager._unregisterSlider(this);
  25. }
  26. void HandleSliderDisc::setCutoffPlane(Degree angle, bool enabled)
  27. {
  28. mHasCutoffPlane = enabled;
  29. if (mHasCutoffPlane)
  30. {
  31. Vector3 up = mNormal;
  32. Quaternion alignWithStart = Quaternion(-Vector3::UNIT_Y, angle);
  33. Quaternion alignWithUp = Quaternion::getRotationFromTo(Vector3::UNIT_Y, up);
  34. Vector3 right = alignWithUp.rotate(alignWithStart.rotate(Vector3::UNIT_X));
  35. right.normalize();
  36. Vector3 planeNormal = right.cross(up);
  37. mCutoffPlane = Plane(planeNormal, 0.0f);
  38. }
  39. }
  40. bool HandleSliderDisc::intersects(const Vector2I& screenPos, const Ray& ray, float& t) const
  41. {
  42. Ray localRay = ray;
  43. localRay.transform(getTransformInv());
  44. auto intersect = mCollider.intersects(localRay);
  45. if (intersect.first)
  46. {
  47. t = intersect.second;
  48. if (mHasCutoffPlane)
  49. {
  50. auto cutoffIntersect = mCutoffPlane.intersects(localRay);
  51. if (cutoffIntersect.first && cutoffIntersect.second < t)
  52. return false;
  53. }
  54. return true;
  55. }
  56. return false;
  57. }
  58. Vector3 HandleSliderDisc::calculateClosestPointOnArc(const Ray& inputRay, const Vector3& center, const Vector3& up,
  59. float radius, Degree startAngle, Degree angleAmount)
  60. {
  61. Vector3 arcBasis[3];
  62. arcBasis[1] = up;
  63. arcBasis[1].orthogonalComplement(arcBasis[2], arcBasis[0]);
  64. Matrix4 worldToPlane = Matrix4::IDENTITY;
  65. worldToPlane.setColumn(0, (Vector4)arcBasis[0]);
  66. worldToPlane.setColumn(1, (Vector4)arcBasis[1]);
  67. worldToPlane.setColumn(2, (Vector4)arcBasis[2]);
  68. worldToPlane.setColumn(3, (Vector4)worldToPlane.multiplyAffine(-center));
  69. worldToPlane[3][3] = 1;
  70. Plane plane(up, (-center).dot(up));
  71. Vector3 pointOnPlane;
  72. auto intersectResult = plane.intersects(inputRay);
  73. if (intersectResult.first)
  74. pointOnPlane = inputRay.getPoint(intersectResult.second);
  75. else
  76. pointOnPlane = Vector3::ZERO;
  77. pointOnPlane = worldToPlane.multiplyAffine(pointOnPlane);
  78. Vector2 pointOnPlane2D(pointOnPlane.x, pointOnPlane.z); // y always 0
  79. Vector2 closestPoint2D;
  80. float dist = pointOnPlane2D.length();
  81. if (dist > 0.0f)
  82. closestPoint2D = mRadius * (pointOnPlane2D / dist);
  83. else
  84. closestPoint2D = Vector2(mRadius, 0);
  85. Vector2 negClosestPoint2D = -closestPoint2D;
  86. Radian angle = Math::atan2(negClosestPoint2D.y, negClosestPoint2D.x) + Radian(Math::PI);
  87. float angleRad = angle.valueRadians();
  88. float angleAmountRad = Math::clamp(angleAmount.valueRadians(), 0.0f, Math::PI * 2);
  89. float startAngleRad = startAngle.wrap().valueRadians();
  90. float endAngleRad = startAngleRad + angleAmountRad;
  91. float clampedAngle = angleRad;
  92. if (endAngleRad <= Math::PI * 2)
  93. {
  94. clampedAngle = Math::clamp(angleRad, startAngleRad, endAngleRad);
  95. }
  96. else
  97. {
  98. if (angleRad >= startAngleRad)
  99. clampedAngle = Math::clamp(angleRad, startAngleRad, Math::PI * 2);
  100. else
  101. {
  102. endAngleRad -= Math::PI * 2;
  103. if (angleRad > endAngleRad)
  104. {
  105. if ((startAngleRad - angleRad) > (angleRad - endAngleRad))
  106. clampedAngle = endAngleRad;
  107. else
  108. clampedAngle = startAngleRad;
  109. }
  110. else
  111. clampedAngle = angleRad;
  112. }
  113. }
  114. Vector3 clampedAnglePoint;
  115. clampedAnglePoint.x = Math::cos(clampedAngle) * radius;
  116. clampedAnglePoint.y = 0.0f;
  117. clampedAnglePoint.z = Math::sin(clampedAngle) * radius;
  118. return worldToPlane.inverseAffine().multiplyAffine(clampedAnglePoint);
  119. }
  120. Degree HandleSliderDisc::pointOnCircleToAngle(Vector3 up, Vector3 point)
  121. {
  122. Quaternion rot = Quaternion::getRotationFromTo(up, Vector3::UNIT_Y);
  123. Matrix4 worldToPlane = Matrix4::TRS(Vector3::ZERO, rot, Vector3::ONE);
  124. point = worldToPlane.multiplyAffine(point);
  125. Vector3 negPoint = -point;
  126. return Radian(Math::atan2(negPoint.z, negPoint.x) + Radian(Math::PI));
  127. }
  128. void HandleSliderDisc::activate(const SPtr<Camera>& camera, const Vector2I& pointerPos)
  129. {
  130. Ray localRay = camera->screenPointToRay(pointerPos);
  131. localRay.transformAffine(getTransformInv());
  132. mStartPosition = calculateClosestPointOnArc(localRay, Vector3::ZERO, mNormal, mRadius, Degree(0.0f), Degree(360.0f));
  133. mStartAngle = pointOnCircleToAngle(mNormal, mStartPosition);
  134. mStartPosition = getTransform().multiplyAffine(mStartPosition);
  135. Vector3 worldNormal = getTransform().multiplyDirection(mNormal);
  136. worldNormal.normalize();
  137. Vector3 toStart = mStartPosition - getPosition();
  138. mDirection = worldNormal.cross(toStart);
  139. mDirection.normalize();
  140. }
  141. void HandleSliderDisc::handleInput(const SPtr<Camera>& camera, const Vector2I& inputDelta)
  142. {
  143. assert(getState() == State::Active);
  144. mCurrentPointerPos += inputDelta;
  145. mDelta = calcDelta(camera, mStartPosition, mDirection, mStartPointerPos, mCurrentPointerPos) * Math::RAD2DEG;
  146. }
  147. }