| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "Handles/BsHandleSliderDisc.h"
- #include "Handles/BsHandleManager.h"
- #include "Handles/BsHandleSliderManager.h"
- #include "Math/BsRay.h"
- #include "Math/BsVector3.h"
- #include "Math/BsQuaternion.h"
- #include "Components/BsCCamera.h"
- namespace bs
- {
- const float HandleSliderDisc::TORUS_RADIUS = 0.1f;
- HandleSliderDisc::HandleSliderDisc(const Vector3& normal, float radius, bool fixedScale, UINT64 layer)
- : HandleSlider(fixedScale, layer), mNormal(normal), mRadius(radius), mHasCutoffPlane(false), mDirection(BsZero)
- , mStartPosition(BsZero), mDelta(0.0f)
- {
- mCollider = Torus(normal, radius, TORUS_RADIUS);
- HandleSliderManager& sliderManager = HandleManager::instance().getSliderManager();
- sliderManager._registerSlider(this);
- }
- HandleSliderDisc::~HandleSliderDisc()
- {
- HandleSliderManager& sliderManager = HandleManager::instance().getSliderManager();
- sliderManager._unregisterSlider(this);
- }
- void HandleSliderDisc::setCutoffPlane(Degree angle, bool enabled)
- {
- mHasCutoffPlane = enabled;
- if (mHasCutoffPlane)
- {
- Vector3 up = mNormal;
- Quaternion alignWithStart = Quaternion(-Vector3::UNIT_Y, angle);
- Quaternion alignWithUp = Quaternion::getRotationFromTo(Vector3::UNIT_Y, up);
- Vector3 right = alignWithUp.rotate(alignWithStart.rotate(Vector3::UNIT_X));
- right.normalize();
- Vector3 planeNormal = right.cross(up);
- mCutoffPlane = Plane(planeNormal, 0.0f);
- }
- }
- bool HandleSliderDisc::intersects(const Vector2I& screenPos, const Ray& ray, float& t) const
- {
- Ray localRay = ray;
- localRay.transform(getTransformInv());
- auto intersect = mCollider.intersects(localRay);
- if (intersect.first)
- {
- t = intersect.second;
- if (mHasCutoffPlane)
- {
- auto cutoffIntersect = mCutoffPlane.intersects(localRay);
- if (cutoffIntersect.first && cutoffIntersect.second < t)
- return false;
- }
- return true;
- }
- return false;
- }
- Vector3 HandleSliderDisc::calculateClosestPointOnArc(const Ray& inputRay, const Vector3& center, const Vector3& up,
- float radius, Degree startAngle, Degree angleAmount)
- {
- Vector3 arcBasis[3];
- arcBasis[1] = up;
- arcBasis[1].orthogonalComplement(arcBasis[2], arcBasis[0]);
- Matrix4 worldToPlane = Matrix4::IDENTITY;
- worldToPlane.setColumn(0, (Vector4)arcBasis[0]);
- worldToPlane.setColumn(1, (Vector4)arcBasis[1]);
- worldToPlane.setColumn(2, (Vector4)arcBasis[2]);
- worldToPlane.setColumn(3, (Vector4)worldToPlane.multiplyAffine(-center));
- worldToPlane[3][3] = 1;
- Plane plane(up, (-center).dot(up));
- Vector3 pointOnPlane;
- auto intersectResult = plane.intersects(inputRay);
- if (intersectResult.first)
- pointOnPlane = inputRay.getPoint(intersectResult.second);
- else
- pointOnPlane = Vector3::ZERO;
- pointOnPlane = worldToPlane.multiplyAffine(pointOnPlane);
- Vector2 pointOnPlane2D(pointOnPlane.x, pointOnPlane.z); // y always 0
- Vector2 closestPoint2D;
- float dist = pointOnPlane2D.length();
- if (dist > 0.0f)
- closestPoint2D = mRadius * (pointOnPlane2D / dist);
- else
- closestPoint2D = Vector2(mRadius, 0);
- Vector2 negClosestPoint2D = -closestPoint2D;
- Radian angle = Math::atan2(negClosestPoint2D.y, negClosestPoint2D.x) + Radian(Math::PI);
- float angleRad = angle.valueRadians();
- float angleAmountRad = Math::clamp(angleAmount.valueRadians(), 0.0f, Math::PI * 2);
- float startAngleRad = startAngle.wrap().valueRadians();
- float endAngleRad = startAngleRad + angleAmountRad;
- float clampedAngle = angleRad;
- if (endAngleRad <= Math::PI * 2)
- {
- clampedAngle = Math::clamp(angleRad, startAngleRad, endAngleRad);
- }
- else
- {
- if (angleRad >= startAngleRad)
- clampedAngle = Math::clamp(angleRad, startAngleRad, Math::PI * 2);
- else
- {
- endAngleRad -= Math::PI * 2;
- if (angleRad > endAngleRad)
- {
- if ((startAngleRad - angleRad) > (angleRad - endAngleRad))
- clampedAngle = endAngleRad;
- else
- clampedAngle = startAngleRad;
- }
- else
- clampedAngle = angleRad;
- }
- }
- Vector3 clampedAnglePoint;
- clampedAnglePoint.x = Math::cos(clampedAngle) * radius;
- clampedAnglePoint.y = 0.0f;
- clampedAnglePoint.z = Math::sin(clampedAngle) * radius;
- return worldToPlane.inverseAffine().multiplyAffine(clampedAnglePoint);
- }
- Degree HandleSliderDisc::pointOnCircleToAngle(Vector3 up, Vector3 point)
- {
- Quaternion rot = Quaternion::getRotationFromTo(up, Vector3::UNIT_Y);
- Matrix4 worldToPlane = Matrix4::TRS(Vector3::ZERO, rot, Vector3::ONE);
- point = worldToPlane.multiplyAffine(point);
- Vector3 negPoint = -point;
- return Radian(Math::atan2(negPoint.z, negPoint.x) + Radian(Math::PI));
- }
- void HandleSliderDisc::activate(const SPtr<Camera>& camera, const Vector2I& pointerPos)
- {
- Ray localRay = camera->screenPointToRay(pointerPos);
- localRay.transformAffine(getTransformInv());
- mStartPosition = calculateClosestPointOnArc(localRay, Vector3::ZERO, mNormal, mRadius, Degree(0.0f), Degree(360.0f));
- mStartAngle = pointOnCircleToAngle(mNormal, mStartPosition);
- mStartPosition = getTransform().multiplyAffine(mStartPosition);
- Vector3 worldNormal = getTransform().multiplyDirection(mNormal);
- worldNormal.normalize();
- Vector3 toStart = mStartPosition - getPosition();
- mDirection = worldNormal.cross(toStart);
- mDirection.normalize();
- }
- void HandleSliderDisc::handleInput(const SPtr<Camera>& camera, const Vector2I& inputDelta)
- {
- assert(getState() == State::Active);
- mCurrentPointerPos += inputDelta;
- mDelta = calcDelta(camera, mStartPosition, mDirection, mStartPointerPos, mCurrentPointerPos) * Math::RAD2DEG;
- }
- }
|