/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //---------------------------------------------------------- // tapCamera.cpp // Camera control with tap // //---------------------------------------------------------- #include #include "tapCamera.h" const float TRANSFORM_FACTOR = 15.f; const float TRANSFORM_FACTORZ = 10.f; const float MOMENTUM_FACTOR_DECREASE = 0.85f; const float MOMENTUM_FACTOR_DECREASE_SHIFT = 0.9f; const float MOMENTUM_FACTOR = 0.8f; const float MOMENTUM_FACTOR_THRESHOLD = 0.001f; //---------------------------------------------------------- // Ctor //---------------------------------------------------------- tapCamera::tapCamera(): _bDragging(false), _bPinching( false ), _bMomentum(false), _fBallRadius( 0.75f ), _fPinchStartDistanceSQ( 0.f ), _fRotation( 0.f ), _fRotationStart( 0.f ), _fRotationNow( 0.f ), _fMomentumSteps( 0.f ), _fFlipZ( 0.f ) { //Init offset initParameters(); _vFlip = vec2( 1.f, -1.f ); _fFlipZ = -1.f; _vPinchTransformFactor = vec3( 1.f, 1.f, 1.f ); _vBallCenter = vec2( 0, 0 ); _vBallNow = vec2( 0, 0 ); _vBallDown = vec2( 0, 0 ); _vecPinchStart = vec2( 0, 0 ); _vecPinchStartCenter = vec2( 0, 0 ); _vFlip = vec2( 0, 0 ); } void tapCamera::initParameters() { //Init parameters _vecOffset = vec3(); _vecOffsetNow = vec3(); _qBallRot = quaternion(); _qBallNow = quaternion(); _qBallNow.toMatrix(_mRotation); _fRotation = 0.f; _vDragDelta = vec2(); _vecOffsetDelta = vec3(); _bMomentum = false; } //---------------------------------------------------------- // Dtor //---------------------------------------------------------- tapCamera::~tapCamera() { } void tapCamera::update() { if( _bMomentum ) { float fMomenttumSteps = _fMomentumSteps; //Momentum rotation vec2 v = _vDragDelta; beginDrag(vec2() ); //NOTE:This call reset _VDragDelta drag(v * _vFlip); //Momentum shift _vecOffset += _vecOffsetDelta; ballUpdate(); endDrag(); //Decrease deltas _vDragDelta = v * MOMENTUM_FACTOR_DECREASE; _vecOffsetDelta = _vecOffsetDelta * MOMENTUM_FACTOR_DECREASE_SHIFT; //Count steps _fMomentumSteps = fMomenttumSteps * MOMENTUM_FACTOR_DECREASE; if( _fMomentumSteps < MOMENTUM_FACTOR_THRESHOLD ) { _bMomentum = false; } } else { _vDragDelta *= MOMENTUM_FACTOR; _vecOffsetDelta = _vecOffsetDelta * MOMENTUM_FACTOR; ballUpdate(); } vec3 vec = _vecOffset + _vecOffsetNow; vec3 vecTmp(TRANSFORM_FACTOR, -TRANSFORM_FACTOR, TRANSFORM_FACTORZ); vec *= vecTmp * _vPinchTransformFactor; _mTransform = mat4::translation(vec); } mat4& tapCamera::getRotationMatrix() { return _mRotation; } mat4& tapCamera::getTransformMatrix() { return _mTransform; } void tapCamera::reset(const bool bAnimate) { initParameters(); update(); } //---------------------------------------------------------- //Drag control //---------------------------------------------------------- void tapCamera::beginDrag(const vec2& v) { if( _bDragging ) endDrag(); if( _bPinching ) endPinch(); vec2 vec = v * _vFlip; _vBallNow = vec; _vBallDown = _vBallNow; _bDragging = true; _bMomentum = false; _vLastInput = vec; _vDragDelta = vec2(); } void tapCamera::endDrag() { int i; _qBallDown = _qBallNow; _qBallRot = quaternion(); _bDragging = false; _bMomentum = true; _fMomentumSteps = 1.0f; } void tapCamera::drag(const vec2& v ) { if( !_bDragging ) return; vec2 vec = v * _vFlip; _vBallNow = vec; _vDragDelta = _vDragDelta * MOMENTUM_FACTOR + (vec - _vLastInput); _vLastInput = vec; } //---------------------------------------------------------- //Pinch controll //---------------------------------------------------------- void tapCamera::beginPinch(const vec2& v1, const vec2& v2) { if( _bDragging ) endDrag(); if( _bPinching ) endPinch(); beginDrag( vec2() ); _vecPinchStartCenter = (v1 + v2) / 2.f; vec2 vec = v1 - v2; float fXDiff; float fYDiff; vec.value( fXDiff, fYDiff ); _fPinchStartDistanceSQ = fXDiff*fXDiff + fYDiff*fYDiff; _fRotationStart = atan2f( fYDiff, fXDiff ); _fRotationNow = 0; _bPinching = true; _bMomentum = false; //Init momentum factors _vecOffsetDelta = vec3(); } void tapCamera::endPinch() { _bPinching = false; _bMomentum = true; _fMomentumSteps = 1.f; _vecOffset += _vecOffsetNow; _fRotation += _fRotationNow; _vecOffsetNow = vec3(); _fRotationNow = 0; endDrag(); } void tapCamera::pinch(const vec2& v1, const vec2& v2) { if( !_bPinching ) return; //Update momentum factor _vecOffsetLast = _vecOffsetNow; float fXDiff, fYDiff; vec2 vec = v1 - v2; vec.value(fXDiff, fYDiff); float fDistanceSQ = fXDiff * fXDiff + fYDiff * fYDiff; float f = _fPinchStartDistanceSQ / fDistanceSQ; if( f < 1.f) f = -1.f / f + 1.0f; else f = f - 1.f; if( isnan(f) ) f = 0.f; vec = (v1 + v2) / 2.f - _vecPinchStartCenter; _vecOffsetNow = vec3( vec, _fFlipZ * f ); //Update momentum factor _vecOffsetDelta = _vecOffsetDelta * MOMENTUM_FACTOR + (_vecOffsetNow - _vecOffsetLast); // //Update ration quaternion float fRotation = atan2f( fYDiff, fXDiff ); _fRotationNow = fRotation - _fRotationStart; //Trackball rotation _qBallRot = quaternion( 0.f, 0.f, sinf(-_fRotationNow*0.5f), cosf(-_fRotationNow*0.5f) ); } //---------------------------------------------------------- //Trackball controll //---------------------------------------------------------- void tapCamera::ballUpdate() { if (_bDragging) { vec3 vFrom = pointOnSphere(_vBallDown); vec3 vTo = pointOnSphere(_vBallNow); vec3 vec = vFrom.cross(vTo); float w = vFrom.dot( vTo ); quaternion qDrag = quaternion(vec, w); qDrag = qDrag * _qBallDown; _qBallNow = _qBallRot * qDrag; } _qBallNow.toMatrix(_mRotation); } vec3 tapCamera::pointOnSphere(vec2& point) { vec3 ballMouse; float mag; vec2 vec = (point - _vBallCenter) / _fBallRadius; mag = vec.dot( vec ); if (mag > 1.f) { float scale = 1.f / sqrtf(mag); vec *= scale; ballMouse = vec3( vec, 0.f ); } else { ballMouse = vec3( vec, sqrtf(1.f - mag) ); } return (ballMouse); }