2016-01-02 2 views
0

Я использую события касания с двумя пальцами, чтобы прижать-поворот-увеличить объект THREE.Mesh, используя кватернионы. Это мой первый раз, используя этот метод вращения, и из-за того, что я уверен, что какое-то свойство кватернионов, которое я не могу понять, вращение постепенно начинает дрожать повсюду, когда общее касание перетаскивает объект примерно на 90 °. Затем он постепенно возвращается к первоначальному плавному, хотя и заметно нелинейному, вращению, когда снова тянется назад под углом 90 °. (90 ° - это всего лишь предположение).Вращение кватерниона в трио.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. Функция загара замирает до бесконечности на 90 градусов. –

+0

Я беспокоился об этом. Однако atan2 не имеет никаких бесконечностей, только разрыв между положительным и отрицательным 180 °. –

ответ

0

Я получил его, сделав все повороты касания инкрементальными, а не относительными к 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

Смежные вопросы