BsHandleSliderDisc.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #include "BsHandleSliderDisc.h"
  2. #include "BsHandleManager.h"
  3. #include "BsHandleSliderManager.h"
  4. #include "BsRay.h"
  5. #include "BsVector3.h"
  6. #include "BsQuaternion.h"
  7. #include "BsCamera.h"
  8. namespace BansheeEngine
  9. {
  10. const float HandleSliderDisc::TORUS_RADIUS = 0.5f;
  11. HandleSliderDisc::HandleSliderDisc(const Vector3& normal, float radius, bool fixedScale)
  12. :HandleSlider(fixedScale), mRadius(radius), mDelta(0.0f)
  13. {
  14. Vector3 x, z;
  15. mNormal.orthogonalComplement(x, z);
  16. mTorusRotation = (Matrix4)Matrix3(x, mNormal, z); // Our Torus class doesn't allow us to specify a normal so we embed it here
  17. mCollider = Torus(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::updateCachedTransform() const
  27. {
  28. if (mFixedScale)
  29. mTransform.setTRS(mPosition, mRotation, mScale * mDistanceScale);
  30. else
  31. mTransform.setTRS(mPosition, mRotation, mScale);
  32. mTransform = mTransform * mTorusRotation;
  33. mTransformInv = mTransform.inverseAffine();
  34. mTransformDirty = false;
  35. }
  36. bool HandleSliderDisc::intersects(const Ray& ray, float& t) const
  37. {
  38. Ray localRay = ray;
  39. localRay.transform(getTransformInv());
  40. auto intersect = mCollider.intersects(localRay);
  41. if (intersect.first)
  42. {
  43. t = intersect.second;
  44. return true;
  45. }
  46. return false;
  47. }
  48. Vector3 HandleSliderDisc::calculateClosestPointOnArc(const Ray& inputRay, const Vector3& center, const Vector3& up,
  49. float radius, Degree startAngle, Degree angleAmount)
  50. {
  51. Vector3 arcBasis[3];
  52. arcBasis[1] = up;
  53. arcBasis[1].orthogonalComplement(arcBasis[2], arcBasis[0]);
  54. Matrix4 worldToPlane = Matrix4::IDENTITY;
  55. worldToPlane.setColumn(0, (Vector4)arcBasis[0]);
  56. worldToPlane.setColumn(1, (Vector4)arcBasis[1]);
  57. worldToPlane.setColumn(2, (Vector4)arcBasis[2]);
  58. worldToPlane.setColumn(3, (Vector4)worldToPlane.multiplyAffine(-center));
  59. worldToPlane[3][3] = 1;
  60. Plane plane(up, (-center).dot(up));
  61. Vector3 pointOnPlane;
  62. auto intersectResult = plane.intersects(inputRay);
  63. float t = 0.0f;
  64. if (intersectResult.second)
  65. pointOnPlane = inputRay.getPoint(intersectResult.first);
  66. else
  67. pointOnPlane = Vector3::ZERO;
  68. pointOnPlane = worldToPlane.multiplyAffine(pointOnPlane);
  69. Vector2 pointOnPlane2D(pointOnPlane.x, pointOnPlane.z); // y always 0
  70. Vector2 closestPoint2D;
  71. float dist = pointOnPlane2D.length();
  72. if (dist > 0.0f)
  73. closestPoint2D = mRadius * (pointOnPlane2D / dist);
  74. else
  75. closestPoint2D = Vector2(mRadius, 0);
  76. Radian angle = Math::atan2(-closestPoint2D.y, -closestPoint2D.x) + Math::PI;
  77. float angleRad = angle.valueRadians();
  78. float startAngleRad = startAngle.wrap().valueRadians();
  79. float endAngleRad = (startAngle + angleAmount).wrap().valueRadians();
  80. float clampedAngle;
  81. if (startAngleRad <= endAngleRad)
  82. clampedAngle = Math::clamp(angleRad, startAngleRad, endAngleRad);
  83. else
  84. {
  85. if ((angleRad < startAngleRad) && (angleRad > endAngleRad))
  86. {
  87. if ((startAngleRad - angleRad) > (angleRad - endAngleRad))
  88. clampedAngle = endAngleRad;
  89. else
  90. clampedAngle = startAngleRad;
  91. }
  92. else
  93. clampedAngle = angleRad;
  94. }
  95. Vector3 clampedAnglePoint;
  96. clampedAnglePoint.x = Math::cos(clampedAngle) * radius;
  97. clampedAnglePoint.z = Math::sin(clampedAngle) * radius;
  98. return worldToPlane.inverseAffine().multiplyAffine(clampedAnglePoint);
  99. }
  100. Degree HandleSliderDisc::pointOnCircleToAngle(Vector3 up, Vector3 point)
  101. {
  102. Vector3 arcBasis[3];
  103. arcBasis[1] = up;
  104. arcBasis[1].orthogonalComplement(arcBasis[2], arcBasis[0]);
  105. Matrix4 worldToPlane = Matrix4::IDENTITY;
  106. worldToPlane.setColumn(0, (Vector4)arcBasis[0]);
  107. worldToPlane.setColumn(1, (Vector4)arcBasis[1]);
  108. worldToPlane.setColumn(2, (Vector4)arcBasis[2]);
  109. point = worldToPlane.multiplyAffine(point);
  110. return Radian(Math::atan2(-point.z, -point.x) + Math::PI);
  111. }
  112. void HandleSliderDisc::activate(const CameraHandlerPtr& camera, const Vector2I& pointerPos)
  113. {
  114. Ray localRay = camera->screenPointToRay(pointerPos);
  115. localRay.transformAffine(getTransformInv());
  116. Quaternion camLocalRotation = camera->getRotation() * getRotation().inverse();
  117. Vector3 startDir = camLocalRotation.zAxis().cross(mNormal);
  118. Degree startAngle = pointOnCircleToAngle(mNormal, startDir);
  119. mStartPosition = calculateClosestPointOnArc(localRay, Vector3::ZERO, mNormal, mRadius, startAngle, Degree(180.0f));
  120. mStartPosition = getTransform().multiplyAffine(mStartPosition);
  121. mDirection = mNormal.cross(mStartPosition - getPosition());
  122. mDirection.normalize();
  123. }
  124. void HandleSliderDisc::handleInput(const CameraHandlerPtr& camera, const Vector2I& inputDelta)
  125. {
  126. assert(getState() == State::Active);
  127. mCurrentPointerPos += inputDelta;
  128. mDelta = calcDelta(camera, mStartPosition, mDirection, mStartPointerPos, mCurrentPointerPos);
  129. }
  130. }