Применить вращение вокруг оси, заданной касательной точкой

У меня есть объект, отображаемый с использованием OpenGL ES на iPad. Модель определяется вершинами, нормалями и индексами для вершин. Происхождение модели – 0,0,0. Используя UIGestureRecognizer, я могу обнаруживать различные жесты – двуглавый проведите по горизонтали для вращения вокруг y, вертикально для вращения вокруг x. Двусторонний поворотный жест для вращения вокруг y. Панорамирование для перемещения модели вокруг. Жест для увеличения / масштабирования. Я хочу, чтобы зритель мог манипулировать моделью, чтобы увидеть (например) обратную сторону модели или всего этого сразу.

Основная стратегия взята из учебника Рэя Вендерлиха, но я переписал это в Swift.

Я понимаю, что кватернионы – это вектор и угол. Векторы up , right и front представляют три оси:

 front = GLKVector3Make(0.0, 0.0, 1.0) right = GLKVector3Make(1.0, 0.0, 0.0) up = GLKVector3Make(0.0, 1.0, 0.0) 

поэтому кватернионные яблоки вращаются вокруг каждой из трех осей (хотя только один из dx , dy , dz имеет значение, определяемое распознающим жестом).

 func rotate(rotation : GLKVector3, multiplier : Float) { let dx = rotation.x - rotationStart.x let dy = rotation.y - rotationStart.y let dz = rotation.z - rotationStart.z rotationStart = GLKVector3Make(rotation.x, rotation.y, rotation.z) rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dx * multiplier, up), rotationEnd) rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dy * multiplier, right), rotationEnd) rotationEnd = GLKQuaternionMultiply((GLKQuaternionMakeWithAngleAndVector3Axis(-dz, front)), rotationEnd) state = .Rotation } 

Для рисования используется modelViewMatrix, рассчитанная по следующей функции:

 func modelViewMatrix() -> GLKMatrix4 { var modelViewMatrix = GLKMatrix4Identity // translation and zoom modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, translationEnd.x, translationEnd.y, -initialDepth); // rotation let quaternionMatrix = GLKMatrix4MakeWithQuaternion(rotationEnd) modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, quaternionMatrix) // scale modelViewMatrix = GLKMatrix4Scale(modelViewMatrix, scaleEnd, scaleEnd, scaleEnd); // rotation return modelViewMatrix } 

И в основном это работает. Однако все относительно происхождения. Если модель повернута, то ось вращения всегда является осью, проходящей через начало координат – если увеличить масштаб, глядя на конец модели в сторону от начала координат и затем вращаясь, модель может быстро отскочить из вида. Если модель масштабируется, то начало координат всегда является фиксированной точкой, когда модель становится все больше или меньше – если исходное значение находится за пределами экрана, а масштаб уменьшается, модель может исчезнуть из вида, когда она рухнет в направлении начала координат …

Что должно произойти, так это то, что независимо от текущего вида модель вращается или масштабируется относительно текущего вида. Для вращения вокруг оси y, которая будет означать определение оси y, вокруг которой происходит вращение, проходящего вертикально через середину текущего вида. Для операции масштабирования неподвижная точка модели будет находиться в центре экрана, при этом модель сжимается к ней или растет с этой точки.

Я знаю, что в 2D решение всегда должно переводить в начало координат, применять поворот, а затем применять инверсию первого перевода. Я не понимаю, почему это должно быть разным в 3D, но я не могу найти какой-либо пример, делающий это с кватернионами только с матрицами. Я попытался применить перевод и его инверсию вокруг вращения, но ничего не повлияло.

Поэтому я попытался сделать это в функции rotate:

 let xTranslation : Float = 300.0 let yTranslation : Float = 300.0 let translation = GLKMatrix4Translate(GLKMatrix4Identity, xTranslation, yTranslation, -initialDepth); rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithMatrix4(translation) , rotationEnd) rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dx * multiplier, up), rotationEnd) rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dy * multiplier, right), rotationEnd) rotationEnd = GLKQuaternionMultiply((GLKQuaternionMakeWithAngleAndVector3Axis(-dz, front)), rotationEnd) // inverse translation let inverseTranslation = GLKMatrix4Translate(GLKMatrix4Identity, -xTranslation, -yTranslation, -initialDepth); rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithMatrix4(inverseTranslation) , rotationEnd) 

Перевод составляет 300 300, но никакого эффекта вообще нет, он все еще поворачивается вокруг, где я знаю происхождение. Я долго искал образец кода и не нашел.

ModelViewMatrix применяется в update () с помощью:

 effect?.transform.modelviewMatrix = modelViewMatrix 

Я мог бы также обмануть, скорректировав все значения в модели, чтобы 0,0,0 попадало в центральную точку, но это все равно было бы фиксированным происхождением и было бы незначительно лучше.

One Solution collect form web for “Применить вращение вокруг оси, заданной касательной точкой”

Проблема в последней сделанной вами операции, вы должны поменять inverseTranslation на rotationEnd :

 rotationEnd = GLKQuaternionMultiply(rotationEnd, GLKQuaternionMakeWithMatrix4(inverseTranslation)) 

И я думаю, что частичное вращение (dx, dy, dz) должно следовать тому же правилу.

На самом деле, если вы хотите изменить опорный элемент, так это то, как должно выполняться умножение матрицы:

 modelMatrix = translationMatrix * rotationMatrix * inverse(translationMatrix) 

и результат в однородных координатах будет рассчитываться следующим образом:

 newPoint = translationMatrix * rotationMatrix * inverse(translationMatrix) * v4(x,y,z,1) 

пример

Это пример 2D-теста, который можно запустить на игровой площадке.

введите описание изображения здесь

 let v4 = GLKVector4Make(1, 0, 0, 1) // Point A let T = GLKMatrix4Translate(GLKMatrix4Identity, 1, 2, 0); let rot = GLKMatrix4MakeWithQuaternion(GLKQuaternionMakeWithAngleAndVector3Axis(Float(M_PI)*0.5, GLKVector3Make(0, 0, 1))) //rotate by PI/2 around the z axis. let invT = GLKMatrix4Translate(GLKMatrix4Identity, -1, -2, 0); let partModelMat = GLKMatrix4Multiply(T, rot) let modelMat = GLKMatrix4Multiply(partModelMat, invT) //The parameters were swapped in your code //and the result would the rot matrix, since T*invT will be identity var v4r = GLKMatrix4MultiplyVector4(modelMat, v4) //ModelMatrix multiplication with pointA print(v4r.v) //(3,2,0,1) //Step by step multiplication using the relation described above v4r = GLKMatrix4MultiplyVector4(invT, v4) v4r = GLKMatrix4MultiplyVector4(rot, v4r) v4r = GLKMatrix4MultiplyVector4(T, v4r) print(v4r.v) //(3,2,0,1) 

Что касается шкалы, если я правильно понимаю, что вы хотите, я бы рекомендовал сделать это, как это делается здесь: https://gamedev.stackexchange.com/questions/61473/combining-rotation-scaling-around-a-pivot- с-перевод-в-а-матрица

  • Как изменить длительность вибрации в iOS / Swift?
  • iOS импортирует файлы в мое приложение
  • Ускоренная скорость SKSpriteNode при столкновении
  • Образец или демо SWIFT-код с использованием FLAnimatedImage
  • Как определить, когда высота клавиатуры обновляется?
  • Как ограничить область перетаскивания для изображений
  • Интерактивная анимация между контроллерами контейнеров Swift 3
  • Размер живых просмотров в Swift Playground
  • Swift: введите общий тип в один и тот же общий тип, но с подклассом связанного типа
  • Импорт заголовков C в Swift
  • Использование неразрешенного идентификатора «GMSPlacesClient» в xcode 7.3.1
  • Swift Как получить доступ к свойствам или методам из переменной?
  • PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.