Difference between revisions of "Basic Concept"
Line 153: | Line 153: | ||
==== Hierarchy Example ==== | ==== Hierarchy Example ==== | ||
Возьмём реальные астрономические цифры и построим упрощённую модель солнечной системы. Так как используются космические расстояния, обозреть всю систему не представляется возможным (хотя ничего не мешает попробовать сделать модель более наглядной). В данном случае, модель взята не для визуализации, а для примера возможностей иерархии и вложенности трансформов. | Возьмём реальные астрономические цифры и построим упрощённую модель солнечной системы. Так как используются космические расстояния, обозреть всю систему не представляется возможным (хотя ничего не мешает попробовать сделать модель более наглядной). В данном случае, модель взята не для визуализации, а для примера возможностей иерархии и вложенности трансформов. | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | #include "W4Framework.h" | ||
+ | |||
+ | W4_USE_UNSTRICT_INTERFACE | ||
+ | |||
+ | class Planet | ||
+ | { | ||
+ | public: | ||
+ | //Немного данных о небесном теле: | ||
+ | // - имя | ||
+ | // - цвет | ||
+ | // - радиус (км) | ||
+ | // - радиус орбиты (км) | ||
+ | // - период оборота вокруг главного тела (дней) | ||
+ | // - угол отклонения орбиты относительно плоскости обращения вокруг главного тела | ||
+ | // - наклон оси | ||
+ | // - список спутников | ||
+ | Planet(const std::string& name, const color& planetColor, float planetRadius, float planetOrbit, float siderealPeriod, float eclipseAngle, float axisTilt, const std::vector<Planet>& moons) | ||
+ | : m_color(planetColor), | ||
+ | m_radius(planetRadius), | ||
+ | m_orbit(planetOrbit), | ||
+ | m_siderealPeriod(siderealPeriod), | ||
+ | m_eclipseAngle(eclipseAngle), | ||
+ | m_axisTilt(axisTilt), | ||
+ | m_moons(moons) | ||
+ | {} | ||
+ | color m_color; | ||
+ | float m_radius; | ||
+ | float m_orbit; | ||
+ | float m_siderealPeriod; | ||
+ | float m_eclipseAngle; | ||
+ | float m_axisTilt; | ||
+ | std::vector<Planet> m_moons; | ||
+ | }; | ||
+ | |||
+ | struct SolarDemo : public IGame | ||
+ | { | ||
+ | public: | ||
+ | void onStart() override | ||
+ | { | ||
+ | //на старте создаём солнечную систему | ||
+ | createSolarSystem(); | ||
+ | } | ||
+ | |||
+ | void onMove(const event::Touch::Move& evt) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | void onUpdate(float dt) override | ||
+ | { | ||
+ | for(auto& rotation: m_rotations) | ||
+ | { | ||
+ | //вращаем тела вокруг их главных тел со скоростью 0.5 дня в секунду | ||
+ | rotation.first->rotateLocal(rotation.second * (dt / 2)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private: | ||
+ | |||
+ | void createSolarSystem() | ||
+ | { | ||
+ | // вот она - Солнечная система! | ||
+ | const Planet solarSystem("Sun", color::yellow, 695500.f, 0.f, 345.39f, 0.f, 0.f, { | ||
+ | {"Mercury", color::white, 2439.7f, 57909050.f, .241f, 7.01f, .0352f, {}}, | ||
+ | {"Venus", color::magenta, 6051.8f, 108208000.f, .615, 3.39f, 177.36f, {}}, | ||
+ | {"Earth", color::green, 6378.f, 149598261.f, 1.f, 0.f, 23.44f, { | ||
+ | {"Moon", color::gray, 1737.1f, 384399.f, 1.f, 0.f, 0.f, {}} | ||
+ | }}, | ||
+ | {"Mars", color::red, 3393.5f, 227939100.f, 1.88f, 1.85f, 25.19f, { | ||
+ | {"Phobos", color::gray, 11.2667f, 9377.2f, .317f, 1.093f, 0.f, {}}, | ||
+ | {"Demos", color::gray, 10.f, 23458.f, 1.2625f, 1.85f , 0.f, {}} | ||
+ | }}, | ||
+ | {"Jupiter", color(1.f, .5f, 0.f, 1.f), 71400.f, 778547200.f, 11.86f, 1.31f, 3.13f, { | ||
+ | {"Callisto", color::gray, 2410.3f, 1882709.f, 16.69f, .205f, 0.f, {}}, | ||
+ | {"Europa", color::gray, 1560.8f, 671034.f, 3.55f, .471f, 0.f, {}}, | ||
+ | {"Ganymede", color::gray, 2634.1f, 1070412.f, 7.15f, .204f, 0.f, {}}, | ||
+ | {"Io", color::gray, 1818.f, 421700.f, 1.77f, .05f, 0.f, {}} | ||
+ | }}, | ||
+ | {"Saturn", (color::yellow + color::white) / 2, 60000.f, 1433449370.f, 29.46f, 2.49f, 26.73f, { | ||
+ | {"Dione", color::gray, 560.f, 377415.f, 2.74f, .028f, 0.f, {}}, | ||
+ | {"Enceladus", color::gray, 251.4f, 238042.f, 1.370218f, 0.f, 0.f, {}}, | ||
+ | {"Tethys", color::gray, 530.f, 294672.f, 1.887802f, 1.091f, 0.f, {}}, | ||
+ | {"Titan", color::gray, 2575.f, 1221865.f, 15.945f, .306f, 0.f, {}} | ||
+ | }}, | ||
+ | {"Uranus", color::cyan, 25600.f, 2876679082.f, 84.01f, .77f, 97.77f, { | ||
+ | {"Ariel", color::gray, 578.9f, 191020.f, 2.520379f, .26f, 0.f, {}}, | ||
+ | {"Oberon", color::gray, 761.4f, 583520.f, 13.463239f, .058f, 0.f, {}}, | ||
+ | {"Titania", color::gray, 588.9f, 435910.f, 8.705872f, .34f , 0.f, {}}, | ||
+ | {"Umbriel", color::gray, 584.7f, 266300.f, 4.144177f, .205f, 0.f, {}} | ||
+ | }}, | ||
+ | {"Neptune", color::blue, 24300.f, 4503443661.f, 164.79f, 1.77f, 28.32f, { | ||
+ | {"Triton", color::gray, 1353.4, 4503443661.f, 164.79f, 1.77f, 0.f, {}} | ||
+ | }} | ||
+ | }); | ||
+ | |||
+ | //Создаём Солнце | ||
+ | auto sun = Mesh::create::sphere(solarSystem.m_radius, 10, 10); | ||
+ | //Добавляем его в корневой узел | ||
+ | Render::getRoot()->addChild(sun); | ||
+ | |||
+ | //Устанавливаем цвет для базового материала | ||
+ | sun->getMaterialInst()->setParam("baseColor", solarSystem.m_color); | ||
+ | //Создаём планеты | ||
+ | createMoons(sun, solarSystem); | ||
+ | //Устанавливаем масштаб. Увы, расстояния такие, что с реальными расстояниями без лупы видно только Солнце... Но это пример. | ||
+ | sun->setLocalScale({1.e-7f, 1.e-7f, 1.e-7f}); | ||
+ | } | ||
+ | void createMoons(cref<Node> planetNode, const Planet& planet) | ||
+ | { | ||
+ | //для каждого спутника | ||
+ | for(const auto& moon: planet.m_moons) | ||
+ | { | ||
+ | //для иллюстрации вложенности - создадим узел - орбиту, при вращении которого вокруг центра тело будет передвигаться по орбите | ||
+ | auto orbitContainer = make::sptr<Node>(); | ||
+ | //отклоняем орбиту от плоскости вращения главного тела | ||
+ | orbitContainer->setLocalRotation({(planet.m_axisTilt + moon.m_eclipseAngle) * DEG2RAD, 0, 0}); | ||
+ | //создаём тело | ||
+ | auto moonNode = Mesh::create::sphere(moon.m_radius, 10, 10); | ||
+ | //добавляем спутник как "ребёнка" орбиты | ||
+ | orbitContainer->addChild(moonNode); | ||
+ | //устанавливаем позицию телав на орбите | ||
+ | moonNode->setLocalTranslation({moon.m_orbit, 0, 0}); | ||
+ | moonNode->getMaterialInst()->setParam("baseColor", moon.m_color); | ||
+ | //добавляем орбиту как "ребёнка" главного тела. Особое внимание на параметр false - он обозначает, | ||
+ | //что при чайлдинге сохраняются локальные трансформы. Таким образом, орбита окажется в той же позиции, | ||
+ | //что и планета и наклонена относительно его плоскости обращения | ||
+ | planetNode->addChild(orbitContainer, false); | ||
+ | //рекурсивно создаём спутники | ||
+ | createMoons(moonNode, moon); | ||
+ | |||
+ | //добавляем в список орбиты и кватернионы для их поворота в локальном пространстве (вычисляем из углов эйлера) | ||
+ | m_rotations.emplace_back(orbitContainer, Rotator(0, 0, 1 / moon.m_siderealPeriod)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | std::vector<std::pair<sptr<Node>, Rotator>> m_rotations; | ||
+ | }; | ||
+ | |||
+ | W4_RUN(SolarDemo) | ||
+ | </syntaxhighlight> | ||
=== VisibleNode === | === VisibleNode === |
Revision as of 08:06, 17 June 2020
Contents
Scope
В статье описаны основные решения, используемые в W4 Game Engine, и их API.
Coordinate System
Используется left-handed координатная система и вращение производится против часовой стрелки, если смотреть в направлении оси.
Render Tree
Отображаемый на экране результат и зависимость трансформаций объектов друг от друга определяются деревом рендеринга, то есть набором объектов рендеринга. Особенности дерева рендеринга W4 Game Engine:
- Минимальное дерево рендеринга состоит только из корневого узла.
- У каждого узла может быть ноль или более подузлов — «детей», но при этом у узла не может быть более одного “родителя“ (самый верхний узел - “корневой“ - родителя не имеет).
- Для того чтобы объект отобразился на экране и/или пересчитал свои данные, необходимо чтобы он находился в дереве.
- Иерархия встроенных типов узлов показана на схеме.
Примечания по использованию:
- При рисовании в несколько проходов, у каждого прохода создаётся своё дерево со своим корневым узлом.
- У каждого узла можно узнать локальные трансформации (то есть трансформации относительно “родителя“) и мировые, а также задать их.
- При задании мировых координат узла пересчитываются его локальные координаты, при задании локальных - мировые.
- У узла, не имеющего родителя трансформации совпадают.
- Можно временно отключить узел, при этом отключаются и все его “потомки“.
Интерфейсы узлов (классов) описаны далее.
Node
Node - базовый класс любого узла (в том числе RootNode).
Метод | Описание |
---|---|
w4::sptr<render::RootNode> getRoot() const
|
Возвращает корневой узел дерева или nullptr при её отсутствии. |
w4::sptr<Node> clone() const
|
Создаёт копию узла, но не имеющую “родителя“ |
w4::sptr<Node> getParent() const
|
Возвращает “родительский“ узел или nullptr при её отсутствии. |
void setEnabled(bool)
|
Включает/выключает узел и его “потомков“ |
void addChild(w4::cref<Node>, bool preserveTransorm = true)
void addChild(const std::string&, w4::cref<Node>, bool preserveTransorm = true)
|
Добавляет “детей“ к текущему узлу. Возможно задание имени для “ребёнка“.
Параметр "preserveTransform" определяет локальные(false) или глобальные(true) трансформации, которые сохраняются у “ребёнка“. Например, если дополнить код слева строками: childNode->setTranslation({1, 0, 0});
parentNode->addChild(childNode);
то узел childNode будет находиться в мировой позиции {1, 0, 0}, а если использовать следующие строки: childNode->setTranslation({1, 0, 0});
parentNode->addChild(childNode, false);
то узел childNode будет сдвинут на 1 по оси X относительно родителя. |
void removeChild(w4::cref<Node>)
void removeChild(const std::string&)
void removeChild(const std::list<w4::sptr<Node>>&)
|
Отвязка “детей“ от узла по указателю, имени и списку. |
void traversal(const Callback&)
void traversal(const PredicateC & predicate, const Callback&)
template<typename T> void traversalTyped(const std::function<void(w4::cref<T>)>&);
|
Выполнение функтора начиная с текущего узла и вниз по иерархии.
Callback имеет сигнатуру void(Node&) PredicateC - bool(w4::core::Node&) Последний метод вызывается только для узлов, чей тип унаследован от T |
void setWorldRotation(math::Rotator::cref, math::vec3::cref worldPt)
|
Установка поворота относительно точки в мировых координатах. |
void rotateAroundPoint(const math::Rotator& rotator, const math::vec3& worldPt)
|
Вращение вокруг точки в мировых координатах. |
template<typename T> w4::sptr<T> as()
template<typename T> w4::sptr<const T> as() const
template<typename T> T* asRaw()
template<typename T> const T* asRaw() const
|
Приведение типа узла (прим. указываемый тип не проверяется) |
template<typename T> bool derived_from() const
|
Проверка, является ли класс узла потомком заданного класса |
template<typename T> w4::sptr<T> getChild(const std::string&)
|
Получение ”ребёнка” по имени с приведением типа (прим. указываемый тип не проверяется). |
std::list<w4::sptr<Node>> getAllChildren() const
|
Получение списка всех узлов, находящихся ниже по иерархии (“дети“, “дети детей“…). |
std::list<w4::sptr<Node>> findChildren(std::string const&) const
|
Поиск “ребёнка“ по имени |
std::list<w4::sptr<Node>> findChildrenRecursive(std::string const&) const
|
Поиск вниз по иерархии по имени |
math::vec3::cref getWorldTranslation() const
void setWorldTranslation(math::vec3::cref)
math::vec3::cref getLocalTranslation() const
void setLocalTranslation(math::vec3::cref)
|
Получение и установка позиции в мировых или локальных координатах |
math::vec3::cref getWorldScale() const
void setWorldScale(math::vec3::cref)
math::vec3::cref getLocalScale() const
void setLocalScale(math::vec3::cref)
|
Получение и установка масштаба в мировых или локальных координатах |
math::vec3 getWorldUp() const
math::vec3 getWorldForward() const
math::vec3 getWorldRight() const
|
Получение нормализованных векторов в направлениях вверх, вперёд и вправо пространства модели в мировых координатах |
math::Transform::cref getWorldTransform() const
void setWorldTransform(math::Transform::cref)
math::Transform::cref getLocalTransform() const
void setLocalTransform(math::Transform::cref)
|
Получение и установка трансформации в мировых или локальных координатах. Трансформация - сочетание позиции, поворота и масштаба |
math::Rotator::cref getWorldRotation() const
void setWorldRotation(math::Rotator::cref)
math::Rotator::cref getLocalRotation() const
void setLocalRotation(math::Rotator::cref)
void rotateLocal(const math::Rotator&rotator)
void rotateWorld(const math::Rotator&rotator)
|
Получение и установка поворота в мировых или локальных координатах. Методы rotateWorld/Local добавляют поворот к текущим значениям |
const std::unordered_set<w4::sptr<Node>>& getChildren() const
|
Получение списка “детей“ |
bool isEnabled() const
|
Возвращает, включен ли узел |
bool hasParent() const
|
Возвращает имеет ли узел “родителя“ |
Hierarchy Example
Возьмём реальные астрономические цифры и построим упрощённую модель солнечной системы. Так как используются космические расстояния, обозреть всю систему не представляется возможным (хотя ничего не мешает попробовать сделать модель более наглядной). В данном случае, модель взята не для визуализации, а для примера возможностей иерархии и вложенности трансформов.
#include "W4Framework.h"
W4_USE_UNSTRICT_INTERFACE
class Planet
{
public:
//Немного данных о небесном теле:
// - имя
// - цвет
// - радиус (км)
// - радиус орбиты (км)
// - период оборота вокруг главного тела (дней)
// - угол отклонения орбиты относительно плоскости обращения вокруг главного тела
// - наклон оси
// - список спутников
Planet(const std::string& name, const color& planetColor, float planetRadius, float planetOrbit, float siderealPeriod, float eclipseAngle, float axisTilt, const std::vector<Planet>& moons)
: m_color(planetColor),
m_radius(planetRadius),
m_orbit(planetOrbit),
m_siderealPeriod(siderealPeriod),
m_eclipseAngle(eclipseAngle),
m_axisTilt(axisTilt),
m_moons(moons)
{}
color m_color;
float m_radius;
float m_orbit;
float m_siderealPeriod;
float m_eclipseAngle;
float m_axisTilt;
std::vector<Planet> m_moons;
};
struct SolarDemo : public IGame
{
public:
void onStart() override
{
//на старте создаём солнечную систему
createSolarSystem();
}
void onMove(const event::Touch::Move& evt)
{
}
void onUpdate(float dt) override
{
for(auto& rotation: m_rotations)
{
//вращаем тела вокруг их главных тел со скоростью 0.5 дня в секунду
rotation.first->rotateLocal(rotation.second * (dt / 2));
}
}
private:
void createSolarSystem()
{
// вот она - Солнечная система!
const Planet solarSystem("Sun", color::yellow, 695500.f, 0.f, 345.39f, 0.f, 0.f, {
{"Mercury", color::white, 2439.7f, 57909050.f, .241f, 7.01f, .0352f, {}},
{"Venus", color::magenta, 6051.8f, 108208000.f, .615, 3.39f, 177.36f, {}},
{"Earth", color::green, 6378.f, 149598261.f, 1.f, 0.f, 23.44f, {
{"Moon", color::gray, 1737.1f, 384399.f, 1.f, 0.f, 0.f, {}}
}},
{"Mars", color::red, 3393.5f, 227939100.f, 1.88f, 1.85f, 25.19f, {
{"Phobos", color::gray, 11.2667f, 9377.2f, .317f, 1.093f, 0.f, {}},
{"Demos", color::gray, 10.f, 23458.f, 1.2625f, 1.85f , 0.f, {}}
}},
{"Jupiter", color(1.f, .5f, 0.f, 1.f), 71400.f, 778547200.f, 11.86f, 1.31f, 3.13f, {
{"Callisto", color::gray, 2410.3f, 1882709.f, 16.69f, .205f, 0.f, {}},
{"Europa", color::gray, 1560.8f, 671034.f, 3.55f, .471f, 0.f, {}},
{"Ganymede", color::gray, 2634.1f, 1070412.f, 7.15f, .204f, 0.f, {}},
{"Io", color::gray, 1818.f, 421700.f, 1.77f, .05f, 0.f, {}}
}},
{"Saturn", (color::yellow + color::white) / 2, 60000.f, 1433449370.f, 29.46f, 2.49f, 26.73f, {
{"Dione", color::gray, 560.f, 377415.f, 2.74f, .028f, 0.f, {}},
{"Enceladus", color::gray, 251.4f, 238042.f, 1.370218f, 0.f, 0.f, {}},
{"Tethys", color::gray, 530.f, 294672.f, 1.887802f, 1.091f, 0.f, {}},
{"Titan", color::gray, 2575.f, 1221865.f, 15.945f, .306f, 0.f, {}}
}},
{"Uranus", color::cyan, 25600.f, 2876679082.f, 84.01f, .77f, 97.77f, {
{"Ariel", color::gray, 578.9f, 191020.f, 2.520379f, .26f, 0.f, {}},
{"Oberon", color::gray, 761.4f, 583520.f, 13.463239f, .058f, 0.f, {}},
{"Titania", color::gray, 588.9f, 435910.f, 8.705872f, .34f , 0.f, {}},
{"Umbriel", color::gray, 584.7f, 266300.f, 4.144177f, .205f, 0.f, {}}
}},
{"Neptune", color::blue, 24300.f, 4503443661.f, 164.79f, 1.77f, 28.32f, {
{"Triton", color::gray, 1353.4, 4503443661.f, 164.79f, 1.77f, 0.f, {}}
}}
});
//Создаём Солнце
auto sun = Mesh::create::sphere(solarSystem.m_radius, 10, 10);
//Добавляем его в корневой узел
Render::getRoot()->addChild(sun);
//Устанавливаем цвет для базового материала
sun->getMaterialInst()->setParam("baseColor", solarSystem.m_color);
//Создаём планеты
createMoons(sun, solarSystem);
//Устанавливаем масштаб. Увы, расстояния такие, что с реальными расстояниями без лупы видно только Солнце... Но это пример.
sun->setLocalScale({1.e-7f, 1.e-7f, 1.e-7f});
}
void createMoons(cref<Node> planetNode, const Planet& planet)
{
//для каждого спутника
for(const auto& moon: planet.m_moons)
{
//для иллюстрации вложенности - создадим узел - орбиту, при вращении которого вокруг центра тело будет передвигаться по орбите
auto orbitContainer = make::sptr<Node>();
//отклоняем орбиту от плоскости вращения главного тела
orbitContainer->setLocalRotation({(planet.m_axisTilt + moon.m_eclipseAngle) * DEG2RAD, 0, 0});
//создаём тело
auto moonNode = Mesh::create::sphere(moon.m_radius, 10, 10);
//добавляем спутник как "ребёнка" орбиты
orbitContainer->addChild(moonNode);
//устанавливаем позицию телав на орбите
moonNode->setLocalTranslation({moon.m_orbit, 0, 0});
moonNode->getMaterialInst()->setParam("baseColor", moon.m_color);
//добавляем орбиту как "ребёнка" главного тела. Особое внимание на параметр false - он обозначает,
//что при чайлдинге сохраняются локальные трансформы. Таким образом, орбита окажется в той же позиции,
//что и планета и наклонена относительно его плоскости обращения
planetNode->addChild(orbitContainer, false);
//рекурсивно создаём спутники
createMoons(moonNode, moon);
//добавляем в список орбиты и кватернионы для их поворота в локальном пространстве (вычисляем из углов эйлера)
m_rotations.emplace_back(orbitContainer, Rotator(0, 0, 1 / moon.m_siderealPeriod));
}
}
std::vector<std::pair<sptr<Node>, Rotator>> m_rotations;
};
W4_RUN(SolarDemo)