Пытаюсь написать систему снарядов используя спецэффекты и новые нативки. SetSpecialEffectOrientation судя по всему использует углы эйлера, причем направлением "верх" для них является вовсе не верх а "лево" (вроде бы) относительно стандартной игровой камеры. Вот сижу и пытаюсь понять каким образом можно заставит эффект "смотреть" в определенную точку пространства. Обычно ориентация dummy юнита задается двумя углами: поворотом (в плоскости земли) и углом атаки, потому что эти параметры легко посчитать. Но я не могу понять как их конвертировать в yaw, pitch и roll. Надеюсь на помощь.
Приложил карту с моими попытками. Изначально модель стрелки направлена вниз чтобы не делать поправок на поворот. В чат вводить угол атаки (наклона к земле) в градусах. От 0 до 45 все более менее (хотя стрелка то обгоняет то отстает от эффекта по контуру), в при угле больше 45 градусов появляются странные движения.

Принятый ответ

Ах, я понял чего ты хочешь конкретно сейчас. Чтобы модель смотрела в камеру, словно на экране выбора персонажа? В таком случае ситуация немного упрощается.
Нам больше не нужно хранить текущую матрицу поворота эффекта, ведь мы не производим ее вращение, вместо этого мы высчитывает всю необходимую ориентацию из положения камеры и самого эффекта. Подход, в целом, остается прежним, но для поиска осей ориентации мы теперь будем использовать некоторый вспомогательный вектор, чье направление совпадает с направлением глобальной оси Z, и путем векторного произведения этого вспомогательного вектора на вектор оси X эффекта (получаемый как и прежде p2 - p1) мы получаем вектор ориентации эффекта Y, а векторное произведение X на Y нам даст ось Z эффекта.
Весь процесс можно разжевать на множество абзацев, но делать этого нет смысла. Я проще скину твою карту с новым вариантом.
Тем не менее, я вижу некоторое непонимание темы и потому считаю должным указать на некоторые ошибки в твоем прошлом коде
Код
    private function RotationMatrixToEuler takes MATRIX3 R returns VECTOR3
    
        local real x
        local real y
        local real z
        local real sy = SquareRoot(R.m11 * R.m11  +  R.m21 * R.m21)
     
        local boolean singular = sy < .000001 or sy == 0 // If
     
        if not singular then
        
            set x = Atan2(R.m32 , R.m33)
            set y = Atan2(-R.m31, sy)
            set z = Atan2(R.m21, R.m11)
        
        else
        
            set x = Atan2(-R.m23, R.m22)
            set y = Atan2(-R.m31, sy)
            set z = 0
            
        endif
        
        return VECTOR3.New_1(x, y, z)
    
    endfunction
Я не понимаю, как работает эта функция. В моем понимании, преобразование матрицы поворота в углы Эйлера происходит за счет обратного преобразования. То есть, чтобы преобразовать матрицу в Эйлер, необходимо знать как преобразуется Эйлер в матрицу, поскольку это обратные друг другу процессы. В новом примере я ее переписал.
Код
        set rotation.m11 = X.x
        set rotation.m12 = X.y
        set rotation.m13 = X.z
        
        set rotation.m21 = Y.x
        set rotation.m22 = Y.y
        set rotation.m23 = Y.z
        
        set rotation.m31 = Z.x
        set rotation.m32 = Z.y
        set rotation.m33 = Z.z
Здесь идет неверное назначение параметров матрицы. Дело в том, что матрица записывается следующим образом (из readme библиотеки Math)
m11 m12 m13
m21 m22 m23
m31 m32 m33
При этом, оси записываются слева направо, столбиками, то есть , ось X здесь, это m11, m21 и m31. Следовательно верной записью будет
Код
        set rotation.m11 = X.x
        set rotation.m21 = X.y
        set rotation.m31 = X.z
        
        set rotation.m12 = Y.x
        set rotation.m22 = Y.y
        set rotation.m32 = Y.z
        
        set rotation.m13 = Z.x
        set rotation.m23 = Z.y
        set rotation.m33 = Z.z
И самая ложная строка
        set rotation = MATRIX3.New_0()
Это создаст нулевую матрицу, то есть матрицу, все значения которой - нули. Базовая же матрица поворота, это единичная матрица, то есть матрица, чья главная диагональ состоит из единиц, а все остальное - нули.
Но это просто заметки, в текущей версии этих участков кода больше нет.
Загруженные файлы
`
ОЖИДАНИЕ РЕКЛАМЫ...
2
17
6 лет назад
2
SetSpecialEffectOrientation точно ейлера использует мож кватарионы скок там значений 3 или 4?
0
3
6 лет назад
Отредактирован Drynwhyl
0
ledoed:
SetSpecialEffectOrientation точно ейлера использует мож кватарионы скок там значений 3 или 4?
native BlzSetSpecialEffectOrientation takes effect whichEffect, real yaw, real pitch, real roll returns nothing
0
17
6 лет назад
Отредактирован ledoed
0
а значения какие они принимаю 0....1. (сильно измениться угол? )или 0....360. ?
0
24
6 лет назад
0
Углы могут быть как в градусах так и в радианах - что именно в этом случае надо проверять.
0
3
6 лет назад
0
ledoed:
а значения какие они принимаю 0....1. (сильно измениться угол? )или 0....360. ?
Значения принимают в радианах.
0
24
6 лет назад
0
Drynwhyl, стоп, а в чем проблема то? Одна из трех поворотных осей не особо нужна для системы снарядов, если не нужны извращения, остается понять какие две соответсвуют тем двум что высчитываются в классическом способе и возможно добавить смещения и/или инверсию, если ноль в другом месте.
0
17
6 лет назад
0
еси в поворотах те сложно используй вектора
чтобы вектора в радианы перевести над нормализовать
пример вектор(40,500,12)
длина = квадратный корень из(40^2+500^2+12^2)
нормализованый = вектор/длина - смори чтобы длина не была 0
0
3
6 лет назад
Отредактирован Drynwhyl
0
prog:
Drynwhyl, стоп, а в чем проблема то? Одна из трех поворотных осей не особо нужна для системы снарядов, если не нужны извращения, остается понять какие две соответсвуют тем двум что высчитываются в классическом способе и возможно добавить смещения и/или инверсию, если ноль в другом месте.
Проблема в том что в углах эйлера каждый из поворотов проходит вокруг нового положения объекта а не вокруг статичной оси.
ledoed:
еси в поворотах те сложно используй вектора
чтобы вектора в радианы перевести над нормализовать
пример вектор(40,500,12)
длина = квадратный корень из(40^2+500^2+12^2)
нормализованый = вектор/длина - смори чтобы длина не была 0
я использовал такой способ но в таком случае параметр roll который этот вектор не характеризует принимает странные значения и вращает объект совершенно непредсказуемым образом.
Прикрепил файл карты.
0
24
6 лет назад
0
Drynwhyl, стандартный тест делал?
  • выставить объект в нулевые значения осей
  • выставить рядом объект под управлением классической системы, тоже в нулевые значения осей
  • если ориентация объектов не совпадает, то добавлять оффсеты пока положение не совпадет (менять управляемый стандартной системой объект)
  • проанализировать полученные оффсеты и перенести их на новую систему так чтобы положение объекта совпало с нулем классической системы
Ну и, напоминаю, одна из осей у тебя будет константой, скорее всего нулем. Например, если вверх это к камере, то "не нужная" ось это roll.
0
17
6 лет назад
0
причем направлением "верх" для них является вовсе не верх а "лево"
Верх, лево, право, это относительные понятия, не существует единого стандарта. В Warcraft 3, "вверх", это положительное направление по оси Z.
Вращение объекта в пространстве задается углами Эйлера, матрицей поворота или кватернионами.
Нативка
native BlzSetSpecialEffectOrientation takes effect whichEffect, real yaw, real pitch, real roll returns nothing
Задает вращение углами Эйлера, где
  • yaw - вращение вокруг оси X
  • pitch - вращение вокруг оси Y
  • roll - вращение вокруг оси Z
Порядок вращения объекта углами Эйлера сильно влияет на конечный результат и Warcraft использует порядок, установленный в атрибутах упомянутой выше нативки, то есть X-Y-Z.
Для работы с эффектами тебе в любом случае придется использовать матрицы поворота, преобразуя их в углы Эйлера.
каким образом можно заставит эффект "смотреть" в определенную точку пространства
Для этого тебе нужно построить матрицу поворота исходя из текущего положения эффекта и точки наблюдения.
Скажем что p1, это точка положения эффекта, а p2, это точка наблюдения. Операцией (p2 - p1) ты получишь вектор, назовем его v1, направленный из точки p1 к точке p2. Нормализуя полученный вектор v1 ты получишь ось X матрицы поворота искомой ориентации твоего эффекта. Исходя из текущей ориентации объекта, тебе нужно получить вектор оси X текущей матрицы поворта. Векторное произведение вектора v1 и текущей оси X объекта тебе даст вектор v2, перпендикулярный вектору v1. Нормализуя вектор v2 ты получишь ось Z новой матрицы поворта. Векторное произведение векторов v1 и v2 даст вектор v3, перпендикулярный им обоим, а именно - ось Y матрицы поворота искомой ориентации. Таким образом вектора v1, v2 и v3 станут матрицей поворта искомой ориентации.
Остается преобразовать полученную матрицу в углы Эйлера и передать полученные значения эффекту.
0
17
6 лет назад
Отредактирован ledoed
0
ты поход в шарнирный замок попадаешь
попробуй потестить углы до 90 градусов
карту проверить не смогу вара последнего нет
а черт не то говорю, 360 градусов в радианах 6,28
0
3
6 лет назад
0
prog:
Drynwhyl, стандартный тест делал?
  • выставить объект в нулевые значения осей
  • выставить рядом объект под управлением классической системы, тоже в нулевые значения осей
  • если ориентация объектов не совпадает, то добавлять оффсеты пока положение не совпадет (менять управляемый стандартной системой объект)
  • проанализировать полученные оффсеты и перенести их на новую систему так чтобы положение объекта совпало с нулем классической системы
Ну и, напоминаю, одна из осей у тебя будет константой, скорее всего нулем. Например, если вверх это к камере, то "не нужная" ось это roll.
Да, я это делал. И в результате этих тестов я понял что для этой функции все 3 угла не будут константами. На скриншоте пример. Там юнит повернут на угол 315 (он же -45) с углом атаки 56. Но yaw pitch и roll крайне странные. Из этих тестов я сделал вывод что каждый угол влияет на все остальные углы.
Загруженные файлы
0
17
6 лет назад
0
врядли в эйлерах там зависимость от угла по плоскости есть , зато в кватарионах там каждый угол зависит от силы вектора другого
0
3
6 лет назад
Отредактирован Drynwhyl
0
GetLocalPlayer:
Спасибо, сейчас буду пробовать.
Вот что у меня получилось:
GIF
Как видно все почти работает, однако изменение угла атаки камеры заставляет эффект отклонится в противоположную сторону. Так же при определенном угле эффект резко переворачивает "на спину". Кроме того я не могу понять как мне изменить угол под которым эффект смотрит в камеру (Умножить v1 на матрицу поворота?)
Код
library Test initializer Init


    globals
    
        private camerasetup array cameras
        
        private effect e
        
        private real ex = .0
        private real ey = .0
        private real ez = 150.
        
        private MATRIX3 rotation 
    
    endglobals
    
    
    private function RotationMatrixToEuler takes MATRIX3 R returns VECTOR3
    
        local real x
        local real y
        local real z
        local real sy = SquareRoot(R.m11 * R.m11  +  R.m21 * R.m21)
     
        local boolean singular = sy < .000001 or sy == 0 // If
     
        if not singular then
        
            set x = Atan2(R.m32 , R.m33)
            set y = Atan2(-R.m31, sy)
            set z = Atan2(R.m21, R.m11)
        
        else
        
            set x = Atan2(-R.m23, R.m22)
            set y = Atan2(-R.m31, sy)
            set z = 0
            
        endif
        
        return VECTOR3.New_1(x, y, z)
    
    endfunction
    
    
    private function UpdateEffectOrientation takes nothing returns nothing
    
        local VECTOR3 euler
    
        local VECTOR3 v1
        local VECTOR3 v2
        local VECTOR3 v3
        
        local VECTOR3 X
        local VECTOR3 Y
        local VECTOR3 Z
        
        local VECTOR3 cX = VECTOR3.New_1(0, 0, 1)
        
        set v1 = VECTOR3.New_1(              /*
            */ GetCameraEyePositionX() - ex, /*
            */ GetCameraEyePositionY() - ey, /*
            */ GetCameraEyePositionZ() - ez)    
            
        set v2 = Vec3Cross(VECTOR3.New_0(), v1, cX)
        set v3 = Vec3Cross(VECTOR3.New_0(), v1, v2)
            
        set X = Vec3Normalize(VECTOR3.New_0(), v1)
        set Z = Vec3Normalize(VECTOR3.New_0(), v2)
        set Y = Vec3Normalize(VECTOR3.New_0(), v3)
        
        set rotation.m11 = X.x
        set rotation.m12 = X.y
        set rotation.m13 = X.z
        
        set rotation.m21 = Y.x
        set rotation.m22 = Y.y
        set rotation.m23 = Y.z
        
        set rotation.m31 = Z.x
        set rotation.m32 = Z.y
        set rotation.m33 = Z.z
        
        set euler = RotationMatrixToEuler(rotation)
        
        call BlzSetSpecialEffectOrientation(e, euler.x, euler.y, euler.z)
    
        call v1.destroy()
        call v2.destroy()
        call v3.destroy()
        
        call cX.destroy()
    
        call X.destroy()
        call Y.destroy()
        call Z.destroy()
        
    endfunction
    
    private function SetCamera takes nothing returns nothing
    
        call CameraSetupApplyForceDuration(cameras[S2I(GetEventPlayerChatString())], false, .5)
        
    endfunction
    
    
    private function Init takes nothing returns nothing
    
        local trigger t = CreateTrigger()    
        
        call TriggerRegisterPlayerChatEvent(t, Player(0), "", false)
        call TriggerAddAction(t, function SetCamera)        
        
        set t = null

        set cameras[1] = gg_cam_Camera_001
        set cameras[2] = gg_cam_Camera_002
        set cameras[3] = gg_cam_Camera_003
        set cameras[4] = gg_cam_Camera_004

        set cameras[5] = gg_cam_Camera_005
        set cameras[6] = gg_cam_Camera_006
        set cameras[7] = gg_cam_Camera_007
        set cameras[8] = gg_cam_Camera_008
        
        set rotation = MATRIX3.New_0()
        
        set e = AddSpecialEffect("units\\human\\Footman\\Footman.mdl", .0, .0)
        call BlzSetSpecialEffectPosition(e, ex, ey, ez)
        
        call TimerStart(CreateTimer(), .03125, true, function UpdateEffectOrientation)

    endfunction
    
    
endlibrary
Загруженные файлы
0
17
6 лет назад
Отредактирован GetLocalPlayer
0
Ах, я понял чего ты хочешь конкретно сейчас. Чтобы модель смотрела в камеру, словно на экране выбора персонажа? В таком случае ситуация немного упрощается.
Нам больше не нужно хранить текущую матрицу поворота эффекта, ведь мы не производим ее вращение, вместо этого мы высчитывает всю необходимую ориентацию из положения камеры и самого эффекта. Подход, в целом, остается прежним, но для поиска осей ориентации мы теперь будем использовать некоторый вспомогательный вектор, чье направление совпадает с направлением глобальной оси Z, и путем векторного произведения этого вспомогательного вектора на вектор оси X эффекта (получаемый как и прежде p2 - p1) мы получаем вектор ориентации эффекта Y, а векторное произведение X на Y нам даст ось Z эффекта.
Весь процесс можно разжевать на множество абзацев, но делать этого нет смысла. Я проще скину твою карту с новым вариантом.
Тем не менее, я вижу некоторое непонимание темы и потому считаю должным указать на некоторые ошибки в твоем прошлом коде
Код
    private function RotationMatrixToEuler takes MATRIX3 R returns VECTOR3
    
        local real x
        local real y
        local real z
        local real sy = SquareRoot(R.m11 * R.m11  +  R.m21 * R.m21)
     
        local boolean singular = sy < .000001 or sy == 0 // If
     
        if not singular then
        
            set x = Atan2(R.m32 , R.m33)
            set y = Atan2(-R.m31, sy)
            set z = Atan2(R.m21, R.m11)
        
        else
        
            set x = Atan2(-R.m23, R.m22)
            set y = Atan2(-R.m31, sy)
            set z = 0
            
        endif
        
        return VECTOR3.New_1(x, y, z)
    
    endfunction
Я не понимаю, как работает эта функция. В моем понимании, преобразование матрицы поворота в углы Эйлера происходит за счет обратного преобразования. То есть, чтобы преобразовать матрицу в Эйлер, необходимо знать как преобразуется Эйлер в матрицу, поскольку это обратные друг другу процессы. В новом примере я ее переписал.
Код
        set rotation.m11 = X.x
        set rotation.m12 = X.y
        set rotation.m13 = X.z
        
        set rotation.m21 = Y.x
        set rotation.m22 = Y.y
        set rotation.m23 = Y.z
        
        set rotation.m31 = Z.x
        set rotation.m32 = Z.y
        set rotation.m33 = Z.z
Здесь идет неверное назначение параметров матрицы. Дело в том, что матрица записывается следующим образом (из readme библиотеки Math)
m11 m12 m13
m21 m22 m23
m31 m32 m33
При этом, оси записываются слева направо, столбиками, то есть , ось X здесь, это m11, m21 и m31. Следовательно верной записью будет
Код
        set rotation.m11 = X.x
        set rotation.m21 = X.y
        set rotation.m31 = X.z
        
        set rotation.m12 = Y.x
        set rotation.m22 = Y.y
        set rotation.m32 = Y.z
        
        set rotation.m13 = Z.x
        set rotation.m23 = Z.y
        set rotation.m33 = Z.z
И самая ложная строка
        set rotation = MATRIX3.New_0()
Это создаст нулевую матрицу, то есть матрицу, все значения которой - нули. Базовая же матрица поворота, это единичная матрица, то есть матрица, чья главная диагональ состоит из единиц, а все остальное - нули.
Но это просто заметки, в текущей версии этих участков кода больше нет.
Загруженные файлы
Принятый ответ
Чтобы оставить комментарий, пожалуйста, войдите на сайт.