2016-01-02 32 views
0

我正在使用雙指觸摸事件來使用四元數捏 - 旋轉 - 縮放一個對象THREE.Mesh。這是我第一次使用這種旋轉方法,並且由於我確定了一些我似乎無法理解的四元數屬性,當總觸摸阻力將對象旋轉大約90度時,旋轉逐漸開始在整個地方抖動°。然後,當再次拖回到90°以下時,它會逐漸恢復到原始平滑,儘管明顯呈非線性旋轉。 (90°只是一個猜測)。四分位數旋轉在three.js旋轉超過約90°時會走路

我完全難住了。下面是我使用旋轉對象obj代碼:

// global state 
var 
     // keeps track of original object scale 
    pinchScale = new THREE.Vector3(), 

     // current first touch 
    touch1 = new THREE.Vector2(), 

     // current second touch 
    touch2 = new THREE.Vector2(), 

     // first touch at touch start 
    touch1OnHold = new THREE.Vector2(), 

     // second touch at touch start 
    touch2OnHold = new THREE.Vector2(), 

     // keeps track of total held rotation at last fired touchmove event 
    angularHoldPrev = new THREE.Quaternion(); 

    ⋮ 

    // key-value pairs inside an addEventListener utility 
touchstart: function (event) { 
    event.preventDefault(); 
    if (event.touches.length === 1) { 
    … 
    } else if (event.touches.length === 2) { 
    touch1OnHold.set(event.touches[0].pageX, event.touches[0].pageY); 
    touch2OnHold.set(event.touches[1].pageX, event.touches[1].pageY); 
    angularHoldPrev.set(0, 0, 0, 1) 
    } 
}, 
touchmove: function (event) { 
    event.preventDefault(); 
    if (event.touches.length === 1) { 
    … 
    } else if (event.touches.length === 2) { 
    touch1.set(event.touches[0].pageX, event.touches[0].pageY); 
    touch2.set(event.touches[1].pageX, event.touches[1].pageY); 
    var 
      // get touch spread at present event firing, and at the start of current hold 
     touchDiff = touch2.clone().sub(touch1), 
     touchDiffOnHold = touch2OnHold.clone().sub(touch1OnHold), 

      // camera is on z-axis; get this axis regardless of obj orientation 
     axis1 = new THREE.Vector3(0, 0, 1).applyQuaternion(obj.quaternion.clone().inverse()), 

      // get a touch rotation around this axis 
     rot1 = new THREE.Quaternion().setFromAxisAngle(axis1, (Math.atan2(touchDiffOnHold.y, touchDiffOnHold.x) - Math.atan2(touchDiff.y, touchDiff.x))).normalize(), 

      // get touch barycentre at present event firing, and at the start of current hold 
     touchCentre = touch1.clone().add(touch2).multiplyScalar(.5), 
     touchCentreOnHold = touch1OnHold.clone().add(touch2OnHold).multiplyScalar(.5), 

      // get axis of touch barycentre movement on the xy plane, regardless of obj orientation 
     axis2 = new THREE.Vector3(touchCentre.y - touchCentreOnHold.y, touchCentre.x - touchCentreOnHold.x, 0).applyQuaternion(obj.quaternion.clone().inverse()), 

      // get a rotation proportional to magnitude of touch movement 
     rot2 = new THREE.Quaternion().setFromAxisAngle(axis2, axis2.length() * rotationSensitivity).normalize(), 

      // combine the two rotations 
     rot = rot1.multiply(rot2); 

     // undo last rotation if not the empty quaternion 
    if (!angularHoldPrev.equals(new THREE.Quaternion())) obj.quaternion.multiply(angularHoldPrev.inverse()); 

     // perform the currently calculated rotation 
    obj.quaternion.multiply(rot); 

     // save this rotation for next event firing 
    angularHoldPrev.copy(rot); 

     // resize object according to change in touch spread 
    obj.scale.copy(pinchScale.clone().multiplyScalar(touchDiff.length()/touchDiffOnHold.length())) 
    } 
}, 
touchend: function (event) { 
    event.preventDefault(); 

    // reset original object scale 
    pinchScale.copy(obj.scale) 
} 

任何線索我怎麼能維護所有雙觸摸輸入比例旋轉將不勝感激。乾杯。

+0

快速猜測沒有深深看可能是你ATAN2。 tan函數以90度拉到無限遠處。 –

+0

我很擔心。然而atan2沒有任何的無窮大,只有正面和負面180°之間的不連續性。 –

回答

0

我通過使所有touchmove旋轉增量而不是相對於touchstart來實現它,並且它實際上清理了一些東西。 rotV是我爲動畫循環提供的一個角速度變量,當您移動它時,可以爲對象提供不錯的慣性踢。 (感謝@WestLangley的優化建議)

var touch1 = new THREE.Vector2(), 
    touch2 = new THREE.Vector2(), 
    touch1Prev = new THREE.Vector2(), 
    touch2Prev = new THREE.Vector2(), 
    rotV = new THREE.Quaternion(), 

    touchDiff = new THREE.Vector2(), 
    touchDiffPrev = new THREE.Vector2(), 
    touchCentre = new THREE.Vector2(), 
    touchCentrePrev = new THREE.Vector2(), 
    axis1 = new THREE.Vector3(), 
    axis2 = new THREE.Vector3(), 
    rot1 = new THREE.Quaternion(), 
    rot2 = new THREE.Quaternion(), 
    adjq = new THREE.Quaternion(); 

⋮ 

touchstart: function (event) { 
    event.preventDefault(); 
    if (event.touches.length === 1) { 
    … 
    } else if (event.touches.length === 2) { 
    touch1Prev.set(event.touches[0].pageX, event.touches[0].pageY); 
    touch2Prev.set(event.touches[1].pageX, event.touches[1].pageY) 
    } 
}, 
touchmove: function (event) { 
    event.preventDefault(); 
    adjq.copy(obj.quaternion).inverse(); 
    if (event.touches.length === 1) { 
    … 
    } else if (event.touches.length === 2) { 
    touch1.set(event.touches[0].pageX, event.touches[0].pageY); 
    touch2.set(event.touches[1].pageX, event.touches[1].pageY); 

    touchDiff.copy(touch2).sub(touch1); 
    touchDiffPrev.copy(touch2Prev).sub(touch1Prev); 
    axis1.set(0, 0, 1).applyQuaternion(adjq); 
    rot1.setFromAxisAngle(axis1, (Math.atan2(touchDiffPrev.y, touchDiffPrev.x) - Math.atan2(touchDiff.y, touchDiff.x))).normalize(); 

    touchCentre.copy(touch1).add(touch2).multiplyScalar(.5); 
    touchCentrePrev.copy(touch1Prev).add(touch2Prev).multiplyScalar(.5); 
    axis2.set(touchCentre.y - touchCentrePrev.y, touchCentre.x - touchCentrePrev.x, 0).applyQuaternion(adjq); 
    rot2.setFromAxisAngle(axis2, axis2.length() * rotationSensitivity * 10).normalize(); 

    obj.quaternion.multiply(rot1.multiply(rot2)); 
    rotV.multiply(rot1.slerp(adjq.set(0, 0, 0, 1), .9)); 
    obj.scale.multiplyScalar(touchDiff.length()/touchDiffPrev.length()); 

    touch1Prev.copy(touch1); 
    touch2Prev.copy(touch2) 
    } 
}, 
touchend: function (event) { 
    event.preventDefault() 
} 

我還是希望我能知道,雖然造成了原來的問題:P

+0

避免在緊密循環中調用'new'和'clone()'。創建一個實例並重用它。每個'touchmove'創建多少個實例? – WestLangley

+0

感謝@WestLangley,我已經更新了我的答案。另外我注意到你實際上在three.js中寫了很多四元數碼。你有沒有看到我忽視的任何耀眼的東西? –

+0

我可以回答關於three.js的問題,但我無法對您的代碼發表評論。抱歉。 :) – WestLangley