Перемещение объекта в 3-д пространстве

PR9INICHEK

Переводчик DZR
Ур
8
 
Всем привет :)

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

Сейчас столкнулся с тем, что нужно понимать, как работать с векторами, матрицами, кватернионами и т.п, чтобы перемещать, крутить, наклонять и делать другие интересные вещи.

Буду писать тогда свои мысли, делится кодом и всем-всем-всем, чтобы было проще понять и возможно получить совет от более знающих дело людей

Как-то так ;)


gaq1316an2v31.png
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Навыка создавать интерфейсы, как на скрине, у меня пока что нет, поэтому начал с малого.

Разобрался, как создавать вещи в зданиях на плоскостях.
Это достаточно просто и информацию об этом здесь не буду размещать, так как её и так много в интернете.

Добавив возможность появляться топорикам на пеньках - https://steamcommunity.com/sharedfiles/filedetails/?id=2571138211, опытные ребята в комментариях посоветовали разобраться, как работает создание вещей под разными углами(а именно запчасти на разрушенных машинах).

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

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

PR9INICHEK

Переводчик DZR
Ур
8
 
Разбираться начал вот с чего:

Взял данные по одной разбитой машине(wreck) и по одной части у неё:
C++:
<group name="Land_Wreck_sed02_aban1_yellow" pos="4769.067871 475.491882 15035.115234" rpy="-2.592932 -1.635105 49.184288" a="40.815704" />

<proxy type="Sedan_02_Door_1_2_YellowRust" pos="0.125 -0.129 -0.870" rpy="0.0 200.0 0.0"/>

В оффлайн миссии подкрутил настройки таким образом, чтобы эта дверь появлялась всегда у этой машины, чтобы узнать её конечные результаты

Получились вот такие данные:
C++:
<group name="Land_Wreck_sed02_aban1_yellow" pos="4769.067871 475.491882 15035.115234" rpy="-2.592932 -1.635105 49.184288" a="40.815704" />

Position - <4769.067871, 475.491882, 15035.115234>

Orientation - <49.184288, -1.635105, -2.592932>

Direction - <0.756508, -0.028534, 0.653362>

C++:
<proxy type="Sedan_02_Door_1_2_YellowRust" pos="0.125 -0.129 -0.870" rpy="0.0 200.0 0.0"/>

Position - <4769.730957, 475.398682, 15034.538086>

Orientation - <-110.815697, -0.000000, 0.000000>

Direction - <-0.934728, -0.000000, -0.355363>

То есть у меня есть координаты машины и вектор её ориентации в пространстве
Данные по двери из файла с конфигурацией и конечный результат

Теперь нужно понять, какие происходили вычисления, чтобы получить то же самое
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Я появился у этой машины и начал через Workbench и Script Editor экспериментировать с векторами
Первым делом мне нужно было найти два объекта и узнать данные по ним, которые отражены выше

Для этого написал простой скрипт, который использует встроенную функцию по поиску 3д объектов рядом с игроком:

C++:
/*
    Поиск объектов в заданном радиусе и вывод информации о них
*/

// Создаём массив из объектов, куда будем сохранять информацию
/*
    array<Object> objects = new array<Object>;
    vector vPosForSearch = GetGame().GetPlayer().GetPosition();
    float vRadiusForSearch = 2;

    GetGame().GetObjectsAtPosition3D( vPosForSearch, vRadiusForSearch, objects, NULL );

    foreach(Object element : objects)
    {
        Print( element );
        Print( element.GetType() );
        Print( element.GetPosition() );
        Print( element.GetOrientation() );
    };
/*

Заодно научился пользовать функцией foreach для перебора массива :cool:

Дополнительно узнал, что можно в выводе разделять блоки информации пустой строкой, передавая в функцию Print() символ переноса строки:
C++:
Print( "\n" );

Может кому-то пригодится ;)
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Далее захотел перенести машину на новые координаты, используя координаты смещения и сбросить её ориентацию в пространстве, чтобы она была "0 0 0"
По идее такие вычисления и происходят при создании вещи(объекта).
Он создаётся в координатах "0 0 0", затем его перемещают на нужные координаты и при необходимости меняют ориентацию в пространстве(крутят, наклоняют, смещают относительно одной оси)

Перемещение:
Как я понял, оно достаточно просто реализуется.
К координатам объекта, которые хранятся в одномерном массиве с тремя элементами(vector) прибавляют координаты смещения
То есть нулевой элемент одного массива складывают с нулевым элементом другого и так по порядку.

Я сделал таким образом и дополнительно через матрицы, так как в одной из статей в интернете прочитал, что часто используют для этого матрицы, чтобы сэкономить на вычислениях, когда объект надо переместить и изменить ориентацию, а там как раз используются вычисления с помощью матрицы.
Ну и плюс, чтобы попрактиковаться в них и понять, как происходят такие вычисления.

Изначальную позицию машины сохранил в переменной vCarPos
C-like:
vector vCarPos = carObject.GetPosition();
// <4769.07, 475.492, 15035.1>

Смещение записал в отдельную переменную vOffsetCarPos
C-like:
vector vOffsetCarPos = "1.0 0.5 1.0";

Далее создал единичную матрицу, так как именно с ней происходят необходимые вычисления для перемещения объекта
В игре для этого есть отдельная функция, поэтому ей и воспользовался:
C-like:
Math3D.MatrixIdentity4()

На вход она принимает двумерный массив 3 х 4, поэтому нужно было его объявить.
Получился такой код:
C-like:
vector vMatrixIdentity[4];
Math3D.MatrixIdentity4( vMatrixIdentity );
Print( vMatrixIdentity );
/*
    <1, 0, 0>,
    <0, 1, 0>,
    <0, 0, 1>,
    <0, 0, 0>
*/

Сразу вывел результат на экран, чтобы понимать, с чем имею дело :)

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

При таком положении дел, я просто добавил данные по смещению в последнюю строчку:
C-like:
vMatrixIdentity[3][0] = vOffsetCarPos[0];
vMatrixIdentity[3][1] = vOffsetCarPos[1];
vMatrixIdentity[3][2] = vOffsetCarPos[2];

и получилось:
C-like:
Print( vMatrixIdentity );
/*
    <1, 0, 0>,
    <0, 1, 0>,
    <0, 0, 1>,
    <1, 0.5, 1>
*/

И перемножил координаты автомобиля(вектор) на матрицу перемещения
C-like:
vector vCarPosTransform = vCarPos.Multiply4(vMatrixIdentity);
Print(vCarPosTransform);

Получился ожидаемый результат:
C-like:
<4770.07, 475.992, 15036.1>
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Изменение ориентации в пространстве:
Из информации в интернете усвоил, что чтобы всё прошло хорошо и без ошибок используются кватернионы.
В DayZ тоже есть функции для работы с ними, поэтому решил их и задействовать.

Записал текущую ориентацию в отдельную переменную vCarOrientation:
C-like:
vector vCarOrientation = carObject.GetOrientation();
Print( vCarOrientation );
// <49.1843,-1.63511,-2.59293>

Далее создал специальную матрицу вращения(rotation matrix) с помощью функции RotationMatrixFromAngles() и сохранил в переменную vMatrixRotation:
C-like:
vector vMatrixRotation[3];
vCarOrientation.RotationMatrixFromAngles( vMatrixRotation );
Print( vMatrixRotation );
/*
    <0.653936, 0.0452213, -0.755197>,
    <-0.00799703, 0.998569, 0.0528698>,
    <0.756508, -0.0285341, 0.653362>
*/

Как будет время, надо бы узнать, какие происходят вычисления внутри функции RotationMatrixFromAngles()

Затем эту матрицу превратил в кватернион(одномерный массив из четырёх элементов):
C-like:
float fCarQuaternion[4];
Math3D.MatrixToQuat( vMatrixRotation, fCarQuaternion );
Print( fCarQuaternion );
// 0.0223858, 0.415714, 0.0146349, 0.909102

И превратил обратно в углы(вектор)
C-like:
vector vAnglesCar = Math3D.QuatToAngles( fCarQuaternion );
Print( vAnglesCar );
// <49.1843, -1.63511, -2.59293>

То есть получились изначальные данные.

Судя по всему, как раз так и проходят вычисления и их и нужно использовать
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Сброс ориентации у автомобиля на 0 0 0:
Чтобы воплотить задуманное, записал новую ориентацию в vOffsetCarOrientation:
C-like:
vector vOffsetCarOrientation = "0.0 0.0 0.0";

и далее её преобразовал в матрицу вращения vOffsetMatrixRotation:
C-like:
vector vOffsetMatrixRotation[3];
vOffsetCarOrientation.RotationMatrixFromAngles( vOffsetMatrixRotation );
Print( vOffsetMatrixRotation );
/*
    <1, -0, 0>,
    <0, 1, -0>,
    <0, 0, 1>
*/

Матрицу вращения в кватернион fOffsetCarQuaternion:
C-like:
float fOffsetCarQuaternion[4];
Math3D.MatrixToQuat( vOffsetMatrixRotation, fOffsetCarQuaternion );
Print( fOffsetCarQuaternion );
// -0, 0, -0, 1

И перемножил два кватерниона, fCarQuaternion из предыдущего поста и fOffsetCarQuaternion, результат записал в новый кватернион fMultiplyCarQuaternion
C-like:
float fMultiplyCarQuaternion[4];
Math3D.QuatMultiply( fMultiplyCarQuaternion, fCarQuaternion, fOffsetCarQuaternion );
Print( fMultiplyCarQuaternion );
// -0, 0, -0, 0.909102

По итогу, преобразовал fMultiplyCarQuaternion обратно в углы, чтобы передать функции SetOrientation()
C-like:
vector vMultiplyAnglesCar = Math3D.QuatToAngles( fMultiplyCarQuaternion );
Print( vMultiplyAnglesCar );
// <0, 0, 0>

Вот так, если ничего не напутал, происходит вычисление нового местоположения объекта
 
Спасибо за такую крутую тему! Очень полезно.
Как сделать так, чтобы объект нельзя было бы поставить на другой динамический объект? Например, если стоит машина, ты поставил на неё что-то, а игрок уехал. Что будет? Объект зависнет в воздухе. Как запретить ставить на такие объекты?
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Спасибо за такую крутую тему! Очень полезно.
Как сделать так, чтобы объект нельзя было бы поставить на другой динамический объект? Например, если стоит машина, ты поставил на неё что-то, а игрок уехал. Что будет? Объект зависнет в воздухе. Как запретить ставить на такие объекты?
Хороший вопрос :)

Пока что в этом не разбирался, так как не было потребности.

Если что, можно будет в рамках отдельной темы ;)
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Для перемещения использовал матрицу и описал это в посте, так как судя по всему перемещение именно через неё происходит, ведь если из финальных координат просто вычитать координаты смещения двери, то не получатся координаты машины:
Position - <4769.730957, 475.398682, 15034.538086>
-
Position - <4769.067871, 475.491882, 15035.115234>
=
<0.663086, -0.0932, -0.577148>

pos="0.125 -0.129 -0.870"
(Смещение двери из файла)
 

PR9INICHEK

Переводчик DZR
Ур
8
 
Пришла недавно идея, что нужно попробовать реализовать именно повороты объекта относительно другого через квартенионы и может тогда основная проблема и будет решена
 
Пряня, наш квантовый механик :drinks:
 
Сверху