Difference between revisions of "Basic Concept"
(46 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
+ | <languages /> | ||
== Scope == | == Scope == | ||
− | + | <translate> | |
− | В статье описаны основные решения, используемые в W4 Game Engine | + | <!--T:1--> |
+ | В статье описаны основные решения, используемые в W4 Game Engine: | ||
+ | * Координатная истема | ||
+ | * Нодовая система | ||
+ | * API основных классов | ||
+ | </translate> | ||
== Coordinate System == | == Coordinate System == | ||
− | [[File:Concept 01.png|thumb|Координатная система]] Используется [https://en.wikipedia.org/wiki/Cartesian_coordinate_system#In_three_dimensions left-handed координатная система] и вращение производится против часовой стрелки, если смотреть в направлении оси. | + | [[File:Concept 01.png|thumb|<translate><!--T:2--> Координатная система</translate>]] <translate><!--T:3--> Используется [https://en.wikipedia.org/wiki/Cartesian_coordinate_system#In_three_dimensions left-handed координатная система] и вращение производится против часовой стрелки, если смотреть в направлении оси.</translate> |
== Render Tree == | == Render Tree == | ||
+ | <translate><!--T:4--> | ||
Отображаемый на экране результат и зависимость трансформаций объектов друг от друга определяются деревом рендеринга, то есть набором объектов рендеринга. Особенности дерева рендеринга W4 Game Engine: | Отображаемый на экране результат и зависимость трансформаций объектов друг от друга определяются деревом рендеринга, то есть набором объектов рендеринга. Особенности дерева рендеринга W4 Game Engine: | ||
* Минимальное дерево рендеринга состоит только из корневого узла. | * Минимальное дерево рендеринга состоит только из корневого узла. | ||
* У каждого узла может быть ноль или более подузлов — «детей», но при этом у узла не может быть более одного “родителя“ (самый верхний узел - “корневой“ - родителя не имеет). | * У каждого узла может быть ноль или более подузлов — «детей», но при этом у узла не может быть более одного “родителя“ (самый верхний узел - “корневой“ - родителя не имеет). | ||
* Для того чтобы объект отобразился на экране и/или пересчитал свои данные, необходимо чтобы он находился в дереве. | * Для того чтобы объект отобразился на экране и/или пересчитал свои данные, необходимо чтобы он находился в дереве. | ||
− | * Иерархия встроенных типов узлов показана на схеме. | + | * Иерархия встроенных типов узлов показана на схеме.</translate> |
− | [[File:Concept_02.png]] | + | [[File:Concept_02.png|<translate><!--T:5--> Иерархия типов</translate>|500 px]] |
+ | <translate><!--T:6--> | ||
+ | Назначение и интерфейсы узлов (классов) описаны в следующих параграфах. | ||
+ | |||
+ | <!--T:7--> | ||
Примечания по использованию: | Примечания по использованию: | ||
* При рисовании в несколько проходов, у каждого прохода создаётся своё дерево со своим корневым узлом. | * При рисовании в несколько проходов, у каждого прохода создаётся своё дерево со своим корневым узлом. | ||
Line 21: | Line 32: | ||
* У узла, не имеющего родителя трансформации совпадают. | * У узла, не имеющего родителя трансформации совпадают. | ||
* Можно временно отключить узел, при этом отключаются и все его “потомки“. | * Можно временно отключить узел, при этом отключаются и все его “потомки“. | ||
− | + | </translate> | |
− | |||
=== Node === | === Node === | ||
− | Node - базовый класс любого узла (в том числе RootNode). | + | <translate><!--T:8--> Node - базовый класс любого узла (в том числе RootNode).</translate> |
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
− | ! | + | ! Method !! Description |
|- | |- | ||
− | | <syntaxhighlight lang="c++">w4::sptr<render::RootNode> getRoot() const</syntaxhighlight> || Возвращает корневой узел дерева или nullptr при её отсутствии | + | | <syntaxhighlight lang="c++">w4::sptr<render::RootNode> getRoot() const</syntaxhighlight> || <translate><!--T:9--> Возвращает корневой узел дерева или nullptr при её отсутствии</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">w4::sptr<Node> clone() const</syntaxhighlight> || Создаёт копию узла, но не имеющую “родителя“ | + | | <syntaxhighlight lang="c++">w4::sptr<Node> clone() const</syntaxhighlight> || <translate><!--T:10--> Создаёт копию узла, но не имеющую “родителя“</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">w4::sptr<Node> getParent() const</syntaxhighlight> || Возвращает “родительский“ узел или nullptr при | + | | <syntaxhighlight lang="c++">w4::sptr<Node> getParent() const</syntaxhighlight> || <translate><!--T:11--> Возвращает “родительский“ узел или nullptr при его отсутствии</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">void setEnabled(bool)</syntaxhighlight> || Включает/выключает узел и его “потомков“ | + | | <syntaxhighlight lang="c++">void setEnabled(bool)</syntaxhighlight> || <translate><!--T:12--> Включает/выключает узел и его “потомков“</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">void addChild(w4::cref<Node>, bool preserveTransorm = true) | | <syntaxhighlight lang="c++">void addChild(w4::cref<Node>, bool preserveTransorm = true) | ||
void addChild(const std::string&, w4::cref<Node>, bool preserveTransorm = true) | void addChild(const std::string&, w4::cref<Node>, bool preserveTransorm = true) | ||
− | </syntaxhighlight> || Добавляет “детей“ к текущему узлу. Возможно задание имени для “ребёнка“. | + | </syntaxhighlight> || <translate><!--T:13--> |
+ | Добавляет “детей“ к текущему узлу. Возможно задание имени для “ребёнка“. | ||
'''Параметр "preserveTransform" определяет локальные(false) или глобальные(true) трансформации, которые сохраняются у “ребёнка“.''' | '''Параметр "preserveTransform" определяет локальные(false) или глобальные(true) трансформации, которые сохраняются у “ребёнка“.''' | ||
− | Например, если дополнить код слева строками: | + | <!--T:14--> |
+ | Например, если дополнить код слева строками:</translate> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
− | childNode-> | + | childNode->setLocalTranslation({1, 0, 0}); |
parentNode->addChild(childNode); | parentNode->addChild(childNode); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | то узел childNode будет находиться в мировой позиции {1, 0, 0}, | + | <translate><!--T:15--> |
+ | то узел childNode будет находиться в мировой позиции {1, 0, 0}, | ||
− | а если использовать следующие строки: | + | <!--T:16--> |
+ | а если использовать следующие строки:</translate> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
− | childNode-> | + | childNode->setLocalTranslation({1, 0, 0}); |
parentNode->addChild(childNode, false); | parentNode->addChild(childNode, false); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | то узел childNode будет сдвинут на 1 по оси X относительно родителя. | + | <translate><!--T:17--> то узел childNode будет сдвинут на 1 по оси X относительно родителя.</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">void removeChild(w4::cref<Node>) | | <syntaxhighlight lang="c++">void removeChild(w4::cref<Node>) | ||
Line 62: | Line 76: | ||
void removeChild(const std::string&) | void removeChild(const std::string&) | ||
− | void removeChild(const std::list<w4::sptr<Node>>&)</syntaxhighlight> || Отвязка “детей“ от узла по указателю, имени и списку. | + | void removeChild(const std::list<w4::sptr<Node>>&)</syntaxhighlight> || <translate><!--T:18--> Отвязка “детей“ от узла по указателю, имени и списку.</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">void traversal(const Callback&) | | <syntaxhighlight lang="c++">void traversal(const Callback&) | ||
− | + | void traversal(const PredicateC & predicate, const Callback&) | |
− | template<typename T> void traversalTyped(const std::function<void(w4::cref<T>)>&);</syntaxhighlight> || Выполнение функтора начиная с текущего узла и вниз по иерархии. | + | template<typename T> void traversalTyped(const std::function<void(w4::cref<T>)>&);</syntaxhighlight> || <translate><!--T:19--> Выполнение функтора начиная с текущего узла и вниз по иерархии. </translate> |
− | Callback имеет сигнатуру '''void(Node&) PredicateC - bool(w4::core::Node&)''' | + | <translate><!--T:20--> Callback имеет сигнатуру</translate> '''void(Node&) PredicateC - bool(w4::core::Node&)''' |
− | Последний метод | + | <translate><!--T:21--> Последний метод вызывает функтор только для тех узлов, чей тип унаследован от T</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">void setWorldRotation(math::Rotator::cref, math::vec3::cref worldPt)</syntaxhighlight> || Установка поворота относительно точки в мировых координатах | + | | <syntaxhighlight lang="c++">void setWorldRotation(math::Rotator::cref, math::vec3::cref worldPt)</syntaxhighlight> || <translate><!--T:22--> Установка поворота относительно точки в мировых координатах</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">void rotateAroundPoint(const math::Rotator& rotator, const math::vec3& worldPt)</syntaxhighlight> || Вращение вокруг точки в мировых координатах | + | | <syntaxhighlight lang="c++">void rotateAroundPoint(const math::Rotator& rotator, const math::vec3& worldPt)</syntaxhighlight> || <translate><!--T:23--> Вращение вокруг точки в мировых координатах</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">template<typename T> w4::sptr<T> as() | | <syntaxhighlight lang="c++">template<typename T> w4::sptr<T> as() | ||
Line 84: | Line 98: | ||
template<typename T> T* asRaw() | template<typename T> T* asRaw() | ||
− | template<typename T> const T* asRaw() const</syntaxhighlight> || Приведение типа узла (прим. указываемый тип не проверяется) | + | template<typename T> const T* asRaw() const</syntaxhighlight> || <translate><!--T:24--> Приведение типа узла (прим. указываемый тип не проверяется)</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">template<typename T> bool derived_from() const</syntaxhighlight> || Проверка, является ли класс узла потомком заданного класса | + | | <syntaxhighlight lang="c++">template<typename T> bool derived_from() const</syntaxhighlight> || <translate><!--T:25--> Проверка, является ли класс узла потомком заданного класса</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">template<typename T> w4::sptr<T> getChild(const std::string&)</syntaxhighlight> || Получение ”ребёнка” по имени с приведением типа (прим. указываемый тип не проверяется) | + | | <syntaxhighlight lang="c++">template<typename T> w4::sptr<T> getChild(const std::string&)</syntaxhighlight> || <translate><!--T:26--> Получение ”ребёнка” по имени с приведением типа (прим. указываемый тип не проверяется)</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">std::list<w4::sptr<Node>> getAllChildren() const</syntaxhighlight> || Получение списка всех узлов, находящихся ниже по иерархии (“дети“, “дети детей“…) | + | | <syntaxhighlight lang="c++">std::list<w4::sptr<Node>> getAllChildren() const</syntaxhighlight> || <translate><!--T:27--> Получение списка всех узлов, находящихся ниже по иерархии (“дети“, “дети детей“…)</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">std::list<w4::sptr<Node>> findChildren(std::string const&) const</syntaxhighlight> || Поиск “ребёнка“ по имени | + | | <syntaxhighlight lang="c++">std::list<w4::sptr<Node>> findChildren(std::string const&) const</syntaxhighlight> || <translate><!--T:28--> Поиск “ребёнка“ по имени</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">std::list<w4::sptr<Node>> findChildrenRecursive(std::string const&) const</syntaxhighlight> || Поиск вниз по иерархии по имени | + | | <syntaxhighlight lang="c++">std::list<w4::sptr<Node>> findChildrenRecursive(std::string const&) const</syntaxhighlight> || <translate><!--T:29--> Поиск вниз по иерархии по имени</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">math::vec3::cref getWorldTranslation() const | | <syntaxhighlight lang="c++">math::vec3::cref getWorldTranslation() const | ||
Line 104: | Line 118: | ||
void setLocalTranslation(math::vec3::cref) | void setLocalTranslation(math::vec3::cref) | ||
− | </syntaxhighlight> || Получение и установка позиции в мировых или локальных координатах | + | </syntaxhighlight> || <translate><!--T:30--> Получение и установка позиции в мировых или локальных координатах</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">math::vec3::cref getWorldScale() const | | <syntaxhighlight lang="c++">math::vec3::cref getWorldScale() const | ||
Line 114: | Line 128: | ||
void setLocalScale(math::vec3::cref) | void setLocalScale(math::vec3::cref) | ||
− | </syntaxhighlight> || Получение и установка масштаба в мировых или локальных координатах | + | </syntaxhighlight> || <translate><!--T:31--> Получение и установка масштаба в мировых или локальных координатах</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">math::vec3 getWorldUp() const | | <syntaxhighlight lang="c++">math::vec3 getWorldUp() const | ||
Line 120: | Line 134: | ||
math::vec3 getWorldForward() const | math::vec3 getWorldForward() const | ||
− | math::vec3 getWorldRight() const</syntaxhighlight> || Получение нормализованных векторов в направлениях вверх, вперёд и вправо пространства модели в мировых координатах | + | math::vec3 getWorldRight() const</syntaxhighlight> || <translate><!--T:32--> Получение нормализованных векторов в направлениях вверх, вперёд и вправо пространства модели в мировых координатах</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">math::Transform::cref getWorldTransform() const | | <syntaxhighlight lang="c++">math::Transform::cref getWorldTransform() const | ||
Line 129: | Line 143: | ||
void setLocalTransform(math::Transform::cref) | void setLocalTransform(math::Transform::cref) | ||
− | </syntaxhighlight> || Получение и установка трансформации в мировых или локальных координатах. Трансформация - сочетание позиции, поворота и масштаба | + | </syntaxhighlight> || <translate><!--T:33--> Получение и установка трансформации в мировых или локальных координатах. Трансформация - сочетание позиции, поворота и масштаба</translate> |
|- | |- | ||
| <syntaxhighlight lang="c++">math::Rotator::cref getWorldRotation() const | | <syntaxhighlight lang="c++">math::Rotator::cref getWorldRotation() const | ||
Line 142: | Line 156: | ||
void rotateWorld(const math::Rotator&rotator) | void rotateWorld(const math::Rotator&rotator) | ||
− | </syntaxhighlight> || Получение и установка поворота в мировых или локальных координатах. Методы rotateWorld/Local добавляют поворот к текущим значениям | + | </syntaxhighlight> || <translate><!--T:34--> Получение и установка поворота в мировых или локальных координатах. Методы rotateWorld/Local добавляют поворот к текущим значениям</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">const std::unordered_set<w4::sptr<Node>>& getChildren() const</syntaxhighlight> || Получение списка “детей“ | + | | <syntaxhighlight lang="c++">const std::unordered_set<w4::sptr<Node>>& getChildren() const</syntaxhighlight> || <translate><!--T:35--> Получение списка “детей“</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">bool isEnabled() const</syntaxhighlight> || Возвращает, включен ли узел | + | | <syntaxhighlight lang="c++">bool isEnabled() const</syntaxhighlight> || <translate><!--T:36--> Возвращает, включен ли узел</translate> |
|- | |- | ||
− | | <syntaxhighlight lang="c++">bool hasParent() const</syntaxhighlight> || Возвращает имеет ли узел “родителя“ | + | | <syntaxhighlight lang="c++">bool hasParent() const</syntaxhighlight> || <translate><!--T:37--> Возвращает имеет ли узел “родителя“</translate> |
|- | |- | ||
|} | |} | ||
==== Hierarchy Example ==== | ==== Hierarchy Example ==== | ||
− | Возьмём реальные астрономические цифры и построим упрощённую модель солнечной системы. Так как используются космические расстояния, обозреть всю систему не представляется возможным (хотя ничего не мешает попробовать сделать модель более наглядной). В данном случае, модель взята не для визуализации, а для примера возможностей иерархии и вложенности трансформов. | + | <translate><!--T:38--> Возьмём реальные астрономические цифры и построим упрощённую модель солнечной системы. Так как используются космические расстояния, обозреть всю систему не представляется возможным (хотя ничего не мешает попробовать сделать модель более наглядной). В данном случае, модель взята не для визуализации, а для примера возможностей иерархии и вложенности трансформов.</translate> |
+ | <syntaxhighlight lang="c++"> | ||
+ | #include "W4Framework.h" | ||
+ | |||
+ | W4_USE_UNSTRICT_INTERFACE | ||
+ | |||
+ | class Planet | ||
+ | { | ||
+ | public: | ||
+ | <translate><!--T:39--> | ||
+ | //Немного данных о небесном теле: | ||
+ | // - имя | ||
+ | // - цвет | ||
+ | // - радиус (км) | ||
+ | // - радиус орбиты (км) | ||
+ | // - период оборота вокруг главного тела (дней) | ||
+ | // - угол отклонения орбиты относительно плоскости обращения вокруг главного тела | ||
+ | // - наклон оси | ||
+ | // - список спутников</translate> | ||
+ | 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 | ||
+ | { | ||
+ | //<translate><!--T:40--> на старте создаём солнечную систему</translate> | ||
+ | createSolarSystem(); | ||
+ | } | ||
+ | |||
+ | void onMove(const event::Touch::Move& evt) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | void onUpdate(float dt) override | ||
+ | { | ||
+ | for(auto& rotation: m_rotations) | ||
+ | { | ||
+ | //<translate><!--T:41--> вращаем тела вокруг их главных тел со скоростью 0.5 дня в секунду</translate> | ||
+ | rotation.first->rotateLocal(rotation.second * (dt / 2)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private: | ||
+ | |||
+ | void createSolarSystem() | ||
+ | { | ||
+ | // <translate><!--T:42--> вот она - Солнечная система!</translate> | ||
+ | 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, {}} | ||
+ | }} | ||
+ | }); | ||
+ | |||
+ | //<translate><!--T:43--> Создаём Солнце</translate> | ||
+ | auto sun = Mesh::create::sphere(solarSystem.m_radius, 10, 10); | ||
+ | //<translate><!--T:44--> Добавляем его в корневой узел</translate> | ||
+ | Render::getRoot()->addChild(sun); | ||
+ | |||
+ | //<translate><!--T:45--> Устанавливаем цвет для базового материала</translate> | ||
+ | sun->getMaterialInst()->setParam("baseColor", solarSystem.m_color); | ||
+ | //<translate><!--T:46--> Создаём планеты</translate> | ||
+ | createMoons(sun, solarSystem); | ||
+ | //<translate><!--T:47--> Устанавливаем масштаб. Увы, расстояния такие, что с реальными расстояниями без лупы видно только Солнце... Но это пример.</translate> | ||
+ | sun->setLocalScale({1.e-7f, 1.e-7f, 1.e-7f}); | ||
+ | } | ||
+ | void createMoons(cref<Node> planetNode, const Planet& planet) | ||
+ | { | ||
+ | //<translate><!--T:48--> для каждого спутника</translate> | ||
+ | for(const auto& moon: planet.m_moons) | ||
+ | { | ||
+ | //<translate><!--T:49--> для иллюстрации вложенности - создадим узел - орбиту, при вращении которого вокруг центра тело будет передвигаться по орбите</translate> | ||
+ | auto orbitContainer = make::sptr<Node>(); | ||
+ | //<translate><!--T:50--> отклоняем орбиту от плоскости вращения главного тела</translate> | ||
+ | orbitContainer->setLocalRotation({(planet.m_axisTilt + moon.m_eclipseAngle) * DEG2RAD, 0, 0}); | ||
+ | //<translate><!--T:51--> создаём тело</translate> | ||
+ | auto moonNode = Mesh::create::sphere(moon.m_radius, 10, 10); | ||
+ | //<translate><!--T:52--> добавляем спутник как "ребёнка" орбиты</translate> | ||
+ | orbitContainer->addChild(moonNode); | ||
+ | //<translate><!--T:53--> устанавливаем позицию тела на орбите</translate> | ||
+ | moonNode->setLocalTranslation({moon.m_orbit, 0, 0}); | ||
+ | moonNode->getMaterialInst()->setParam("baseColor", moon.m_color); | ||
+ | //<translate><!--T:54--> | ||
+ | добавляем орбиту как "ребёнка" главного тела. Особое внимание на параметр false - он обозначает, | ||
+ | //что при чайлдинге сохраняются локальные трансформы. Таким образом, орбита окажется в той же позиции, | ||
+ | //что и планета и наклонена относительно его плоскости обращения</translate> | ||
+ | planetNode->addChild(orbitContainer, false); | ||
+ | //<translate><!--T:55--> рекурсивно создаём спутники</translate> | ||
+ | createMoons(moonNode, moon); | ||
+ | |||
+ | //<translate><!--T:56--> добавляем в список орбиты и кватернионы для их поворота в локальном пространстве (вычисляем из углов эйлера)</translate> | ||
+ | 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 === | ||
+ | <translate><!--T:57--> | ||
+ | Базовый класс для отображаемого узла. | ||
+ | <!--T:58--> | ||
+ | Особенности: | ||
+ | * Все отображаемые узлы содержат вертексный буфер и состоят из одного и более элемента surface (который содержит индексный буфер). | ||
+ | * Каждому surface можно назначить свой материал. | ||
+ | Все отображаемые узлы имеют следующие дополнительные методы:</translate> | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">const MaterialInstPtr& getMaterialInst(const std::string& surfaceName) const</syntaxhighlight> || <translate><!--T:59--> Возвращает материал указанного surface</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">const MaterialInstPtr& getMaterialInst() const</syntaxhighlight> || <translate><!--T:60--> Получает материал, установленный для всех surface. Если материалы surface отличаются - выдаёт ошибку в лог и возвращает какой-то материал.</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setMaterialInst(const MaterialInstPtr & m)</syntaxhighlight> || <translate><!--T:61--> Устанавливает материал для всех surface</translate> | ||
+ | |} | ||
==== Mesh ==== | ==== Mesh ==== | ||
+ | <translate><!--T:62--> | ||
+ | Объект, отображающий геометрию. | ||
+ | |||
+ | <!--T:63--> | ||
+ | Объекты типа Mesh в движке можно создать тремя путями:</translate> | ||
+ | 1. <translate><!--T:64--> Загрузить из ресурсов (см. также [http://wiki.w4-dev.ciliz.com/index.php/Mesh_Converter Mesh Converter]):</translate> | ||
+ | <syntaxhighlight lang="c++">teapot = Asset::load(Path("resources/teapot", "teapot.asset"))->getRoot()->getChild<Mesh>("Utah Teapot Quads");</syntaxhighlight> | ||
+ | 2. <translate><!--T:65--> Вызвать встроенный генератор:</translate> | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | auto sphere = Mesh::create::sphere<translate><!--T:66--> (радиус, количество параллелей, количество меридианов)</translate>; | ||
+ | auto box = Mesh::create::box({width, height, depth}); //<translate><!--T:67--> box - параллелепипед, у которого каждая грань - surface</translate> | ||
+ | auto cube = Mesh::create::cube({width, height, depth}); | ||
+ | auto mappedCube = Mesh::create::mappedCube({width, height, depth}); //<translate><!--T:68--> параллелепипед с uv координатами под развёртку</translate> | ||
+ | auto plane1 = Mesh::create::plane({width, height}, scaleUv); //<translate><!--T:69--> плоскость по осям XY. scaleUv = false(по умолчанию) - UV от 0 до 1, true - от 0 до ширины/высоты</translate> | ||
+ | auto plane2 = Mesh::create::plane({left, down}, {right, up}, scaleUv; //<translate><!--T:70--> то же самое</translate> | ||
+ | auto cylinder = Mesh::create::cylinder<translate><!--T:71--> (высота, радиус, количество секторов)</translate>; | ||
+ | auto cone = Mesh::create::cone(<translate><!--T:72--> верхний радиус, нижний радиус, высота, количество секторов, количество сегментов по вертикали</translate>); | ||
+ | auto torus = Mesh::create::torus(<translate><!--T:73--> радиус кольца, радиус "трубы", количество сегментов кольца, количество сегментов "трубы", угол дуги кольца(2*PI для замкнутого тора)</translate>); | ||
+ | auto skybox = Mesh::create::skybox(); //<translate><!--T:74--> Скайбокс. Единичный куб со сторонами, отображаемыми изнутри.</translate> | ||
+ | </syntaxhighlight> | ||
+ | 3. <translate><!--T:75--> Создать из кода:</translate> | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | auto indicesBuf = make::sptr<UserIndicesBuffer>("Floor indices"); | ||
+ | auto verticesBuf = make::sptr<UserVerticesBuffer<Mesh::VertexFormat>>("floor_" + std::to_string(width) + "x" + std::to_string(height) + "-" + std::to_string(offset)); | ||
+ | indicesBuf->setNeedExport(false); | ||
+ | verticesBuf->setNeedExport(false); | ||
+ | |||
+ | resources::MeshVertexDataBuilder vdb(verticesBuf, indicesBuf); | ||
+ | //<translate><!--T:76--> позиция, UV, нормаль</translate> | ||
+ | vdb.addVertex({-width / 2 + offset, 0, height / 2}, {offset/width, 0.f}, {0, 1, 0}); | ||
+ | vdb.addVertex({width / 2 - offset, 0, height / 2}, {1.f - offset/width, 0.f}, {0, 1, 0}); | ||
+ | vdb.addVertex({-width / 2, 0, height / 2 - offset}, {0.f, offset/height}, {0, 1, 0}); | ||
+ | vdb.addVertex({width / 2, 0, height / 2 - offset}, {1.f, offset/height}, {0, 1, 0}); | ||
+ | |||
+ | vdb.addVertex({width / 2, 0, -height / 2 + offset}, {0.f, 1.f - offset/height}, {0, 1, 0}); | ||
+ | vdb.addVertex({-width / 2, 0, -height / 2 + offset}, {1.f, 1.f - offset/height}, {0, 1, 0}); | ||
+ | vdb.addVertex({width / 2 - offset, 0, -height / 2}, {offset/width, 1.f}, {0, 1, 0}); | ||
+ | vdb.addVertex({-width / 2 + offset, 0, -height / 2}, {1.f - offset/width, 1.f}, {0, 1, 0}); | ||
+ | |||
+ | vdb.addIndices({2, 1, 0, | ||
+ | 2, 3, 1, | ||
+ | 4, 3 ,2, | ||
+ | 5, 4, 2, | ||
+ | 5, 6, 4, | ||
+ | 7, 6, 5}); | ||
+ | |||
+ | vdb.build(); | ||
+ | |||
+ | auto result = make::sptr<Mesh>(verticesBuf->getName(), verticesBuf); | ||
+ | result->addComponent<Surface>("unnamed", indicesBuf); | ||
+ | |||
+ | result->setMaterialInst(Material::getDefault()->createInstance()); | ||
+ | </syntaxhighlight> | ||
==== SkinnedMesh ==== | ==== SkinnedMesh ==== | ||
+ | <translate><!--T:77--> | ||
+ | Объект, отображающий геометрию с анимацией. | ||
+ | <!--T:78--> | ||
+ | Особенности: | ||
+ | * можно загрузить только из файла; | ||
+ | * представляет собой геометрию со скелетной анимацией; | ||
+ | * содержит в себе набор анимаций; | ||
+ | * содержит в себе API для управления анимациями и позволяет проигрывать несколько анимаций с плавными переходами между ними.</translate> | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">w4::cref<core::Node> createSocket(const std::string& boneName)</syntaxhighlight>|| <translate><!--T:79--> Создаёт узел для крепления на кости с заданным именем. К этому узлу можно добавлять “детей“</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">w4::cref<core::Node> getSocket(const std::string& boneName) const</syntaxhighlight>|| <translate><!--T:80--> Возвращает узел для крепления на кости с заданным именем</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">std::vector<std::string> getAvailableAnimations() const</syntaxhighlight>|| <translate><!--T:81--> Возвращает список имеющихся анимаций</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool haveAnimation(const std::string& animationName) const</syntaxhighlight>|| <translate><!--T:82--> Проверяет наличие анимации с заданным именем</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">size_t getAnimationIndex(const std::string& animationName) const</syntaxhighlight>|| <translate><!--T:83--> Получение индекса анимации по её имени</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">const std::string& getAnimationName(size_t animationIndex) const</syntaxhighlight>|| <translate><!--T:84--> Получение имени анимации по её индексу</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setAnimationStateChangedCallback(std::function<void(Animator&, Animator::State)>) | ||
+ | |||
+ | void setAnimationStateChangedCallback(Animator::State, std::function<void(Animator&)>)</syntaxhighlight>|| <translate><!--T:85--> Установить функтор, который вызовется по изменению состояния конкретной анимации. Метод имеет как вариант только для заданного состояния, так и для любого. Состояния анимаций:</translate> | ||
+ | |||
+ | Animator::State::Idle(<translate><!--T:86--> не проигрывается/закончилась</translate>), | ||
+ | |||
+ | Animator::State::Play(<translate><!--T:87--> проигрывается</translate>), | ||
+ | |||
+ | Animator::State::Pause(<translate><!--T:88--> приостановлена</translate>) | ||
+ | |||
+ | <translate><!--T:89--> Пример:</translate> | ||
+ | <syntaxhighlight lang="c++">node->setAnimationStateChangedCallback(Animator::State::Idle, [](Animator& animator) | ||
+ | { | ||
+ | W4_LOG_DEBUG("Animator stopped:\n" | ||
+ | "\tName: %s," | ||
+ | "\tSpeed: %f," | ||
+ | "\tDuration: %f," | ||
+ | "\tFps: %f", | ||
+ | animator.getName().c_str(), | ||
+ | animator.getSpeed(), | ||
+ | animator.getDuration(), | ||
+ | animator.getFps()); | ||
+ | });</syntaxhighlight> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void play(const std::string& animationName, float duration = 0.f) | ||
+ | |||
+ | void play(size_t animationIndex = 0, float duration = 0.f)</syntaxhighlight>|| <translate><!--T:90--> Проигрывание анимации по индексу или имени. Указывается длительность анимации, если она равна нулю - проигрывается анимация целиком</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void play(std::initializer_list<std::pair<std::string, float>>, float duration = 0.f) | ||
+ | |||
+ | void play(std::initializer_list<std::pair<size_t, float>>, float duration = 0.f)</syntaxhighlight>|| <translate><!--T:91--> Проигрывание списка анимаций с весами. Длительность, если равна нулю - равна анимации с максимальной длительностью</translate> | ||
+ | |||
+ | <translate><!--T:92--> Пример:</translate> | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | skinned->play({{"run", .3f}, {"jump", .7f}}); | ||
+ | </syntaxhighlight> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void pause()</syntaxhighlight>|| <translate><!--T:93--> Приостановить все активные анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void resume()</syntaxhighlight>|| <translate><!--T:94--> Возобновить проигрывание всех приостановленных анимаций</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void stop()</syntaxhighlight>|| <translate><!--T:95--> Остановить проигрывание всех активных анимаций</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool isPlaying() const</syntaxhighlight>|| <translate><!--T:96--> Проверяет, проигрывается ли сейчас любая анимация</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">Animator & getAnimator(size_t) | ||
+ | Animator & getAnimator(const std::string&) | ||
+ | const Animator & getAnimator(size_t) const | ||
+ | const Animator & getAnimator(const std::string&) const | ||
+ | </syntaxhighlight>|| <translate><!--T:97--> Получить объект аниматор по имени или индексу. API объекта Animator описан чуть ниже. Используется для управления анимациями с расширенными возможностями.</translate> | ||
+ | |||
+ | <translate><!--T:98--> '''Управление анимациями через API и SkinnedMesh не являются взаимоисключающими.''' Так, например, общеупотребительной является практика зацикливания анимаций:</translate> | ||
+ | <syntaxhighlight lang="c++">m_skinned->getAnimator("dance").setIsLooped(true);</syntaxhighlight> | ||
+ | |} | ||
===== Animator API ===== | ===== Animator API ===== | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void play()</syntaxhighlight>|| <translate><!--T:99--> | ||
+ | Запуск воспроизведения анимации. | ||
+ | |||
+ | <!--T:100--> | ||
+ | '''В случае, если она уже проигрывается, она будет остановлена и запущена с начала'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void pause()</syntaxhighlight>|| <translate><!--T:101--> Приостанавливает воспроизведение анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void resume()</syntaxhighlight>|| <translate><!--T:102--> Возобновляет воспроизведение анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void stop()</syntaxhighlight>|| <translate><!--T:103--> Останавливает воспроизведение анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void seek(float targetTime)</syntaxhighlight>|| <translate><!--T:104--> Устанавливает текущую позицию в анимации на заданное время. '''Значения автоматически обрезаются сверху и снизу до диапазона [0; длина анимации] или до диапазона, заданного методами setBeginTime/setEndTime'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool isLooped() const</syntaxhighlight>|| <translate><!--T:105--> Возвращает, включен ли для данной анимации режим циклическое воспроизведение</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getSpeed() const</syntaxhighlight>|| <translate><!--T:106--> Получает множитель скорости воспроизведения анимации (по умолчанию 1.0)</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">State getState() const</syntaxhighlight>|| <translate><!--T:107--> Возвращает состояние анимации</translate> | ||
+ | |||
+ | <translate><!--T:108--> Состояния анимаций:</translate> | ||
+ | Animator::State::Idle <translate><!--T:109--> (не проигрывается/закончилась)</translate>, | ||
+ | Animator::State::Play <translate><!--T:110--> (проигрывается)</translate>, | ||
+ | Animator::State::Pause <translate><!--T:111--> (приостановлена)</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setPlayType(PlayType)</syntaxhighlight>|| <translate><!--T:112--> | ||
+ | Устанавливает направление воспроизведения. | ||
+ | |||
+ | <!--T:113--> | ||
+ | Возможные значения параметра:</translate> | ||
+ | Animator::PlayType::<translate><!--T:114--> Forward (вперёд)</translate>, | ||
+ | Animator::PlayType::<translate><!--T:115--> Backward (в обратном направлении)</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">PlayType getPlayType() const</syntaxhighlight>|| <translate><!--T:116--> Возвращает текущее направление проигрывания. Возможные значения см. в '''setPlayType'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getCurrentTime() const</syntaxhighlight>|| <translate><!--T:117--> Возвращает текущую позицию в анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setIsLooped(bool)</syntaxhighlight>|| <translate><!--T:118--> Включает/выключает для данной анимации режим циклического воспроизведение</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setSpeed(float)</syntaxhighlight>|| <translate><!--T:119--> Устанавливает множитель скорости воспроизведения анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setBeginTime(float)</syntaxhighlight>|| <translate><!--T:120--> Устанавливает начальное время анимации. Нужно для частичного воспроизведения анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setEndTime(float)</syntaxhighlight>|| <translate><!--T:121--> Устанавливает конечное время анимации. Нужно для частичного воспроизведения анимации</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">StateHandlerId setStateHandler(State state, StateHandler handler) | ||
+ | void resetStateHandler(StateHandlerId handler)</syntaxhighlight>|| <translate><!--T:122--> Устанавливает и удаляет callback на смену состояния на заданное. Сигнатура '''callback - void(Animator&)'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setTimeHandler(float dt, TimeHandler handler)</syntaxhighlight>|| <translate><!--T:123--> Устанавливает callback на прохождение заданной временной метки. Сигнатура '''callback - void(Animator&)'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getFps() const</syntaxhighlight>|| <translate><!--T:124--> Возвращает характеристику анимации - количество значений в секунду.</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getDuration() const</syntaxhighlight>|| <translate><!--T:125--> Возвращает длительность анимации</translate> | ||
+ | |} | ||
==== ParticlesEmitter ==== | ==== ParticlesEmitter ==== | ||
+ | <translate> | ||
+ | <!--T:127--> | ||
+ | Особенности: | ||
+ | * ParticlesEmitter может быть загружен только из файла; | ||
+ | * Входным форматом файла является CoronaSDK (можно создать в любом онлайн-редакторе, например в http://www.effecthub.com/particle2dx, сохранить json с расширением part и текстуру и убедиться, что путь к текстуре является путём от корня проекта). </translate> | ||
+ | |||
+ | <translate><!--T:128--> Важно понимать и учитывать, что в связи с ограниченными вычислительными и асинхронными возможностями технологии WebAssembly на текущий момент, система частиц отображается на плоскости, развёрнутой к камере.</translate> | ||
+ | |||
+ | '''API ParticlesEmitter:''' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void start()</syntaxhighlight> || <translate><!--T:129--> Запуск генерации частиц</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void pause()</syntaxhighlight> || <translate><!--T:130--> Приостановка генерации частиц. В этом случае обработка уже выпущенных частиц продолжается</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void resume()</syntaxhighlight> || <translate><!--T:131--> Возобновление генерации частиц</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void stop()</syntaxhighlight> || <translate><!--T:132--> Остановка генерации частиц. Обработка всех частиц прекращается</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">State getState() const</syntaxhighlight> || <translate><!--T:133--> Получение состояния</translate> | ||
+ | |||
+ | <translate><!--T:134--> Варианты:</translate> | ||
+ | ParticlesEmitter::State::IDLE, | ||
+ | ParticlesEmitter::State::STARTED, | ||
+ | ParticlesEmitter::State::PAUSED | ||
+ | |} | ||
==== Billboard ==== | ==== Billboard ==== | ||
+ | <translate><!--T:135--> Плоскость заданного при создании размера, всегда ориентированная на камеру. Имеет методы '''getSize''' и '''setSize'''.</translate> | ||
==== Plotter ==== | ==== Plotter ==== | ||
+ | <translate><!--T:136--> Реализация рисования линиями. Данные плоттера представляют собой информацию о точках (позиция и цвет) и набор индексов для их попарного соединения.</translate> | ||
+ | |||
+ | <translate><!--T:137--> Пример:</translate> | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | auto plotter1 = make::sptr<Plotter>("Plotter_1"); | ||
+ | const std::vector<uint32_t> indices = {1, 0}; | ||
+ | const std::vector<LinesVertexFormat> vertices = {{{-10, -10, 0}, {1, 0, 0,1}}, | ||
+ | {{10, 10, 0}, {0, 1, 0, 1}}}; | ||
+ | plotter1->setLines(vertices, indices); | ||
+ | </syntaxhighlight> | ||
+ | <translate><!--T:138--> Представленный выше пример создаст косую линию с цветом, переходящим из красного в зелёный.</translate> | ||
+ | |||
+ | <translate><!--T:139--> Как правило, для создания примитивов используются статические хелперы, возвращающие готовый объект, описанные в классе Plotter:</translate> | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | static w4::sptr<Plotter> build(std::vector<LinesVertexFormat> vertices, std::vector<uint32_t> indices); | ||
+ | static w4::sptr<Plotter> buildFromSegments(std::vector<std::array<w4::math::vec3, 2>> segments, const w4::math::vec4& color); | ||
+ | static w4::sptr<Plotter> buildFromPolyline(std::vector<w4::math::vec3> points, const w4::math::vec4& color); | ||
+ | static w4::sptr<Plotter> buildSphere(float radius, size_t rings, size_t sectors, const w4::math::vec4& color); | ||
+ | static w4::sptr<Plotter> buildOctahedron(std::array<w4::math::vec3, 8> vertices, const w4::math::vec4& color); | ||
+ | static w4::sptr<Plotter> buildCube(std::array<w4::math::vec3, 2> inVertices, const w4::math::vec4& color); | ||
+ | static w4::sptr<Plotter> buildCapsule(float radius, float height, size_t rings, size_t sectors, const w4::math::vec4& color); | ||
+ | static w4::sptr<Plotter> buildMesh(w4::cref<Mesh>, w4::math::vec4::cref color); | ||
+ | static w4::sptr<Plotter> buildMesh(w4::cref<SkinnedMesh>, w4::math::vec4::cref color); | ||
+ | static w4::sptr<Plotter> buildRay(math::vec3::cref startPoint, math::vec3::cref directionPoint, const w4::math::vec4& color); | ||
+ | </syntaxhighlight> | ||
=== DataNode === | === DataNode === | ||
− | + | <translate><!--T:140--> В отличие от '''VisibleNode''', '''DataNode''' не отображается на экране. Далее описывается API унаследованных классов.</translate> | |
==== ArcBallNode ==== | ==== ArcBallNode ==== | ||
+ | <translate><!--T:141--> Аркболл для нижележащих объектов иерархии. Реализует контейнер, позволяющий вращать содержимое, как если бы оно находилось в шаре с инерцией.</translate> | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">ArcBallNode(const std::string& name = core::Object::defaultName, float radius = 1.f)</syntaxhighlight> || <translate><!--T:142--> Конструктор, принимающий имя и радиус "шара"</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setFriction(float v)</syntaxhighlight> || <translate><!--T:143--> Задать коэффициент затухания скорости. По умолчанию = 0.95</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setSensitivity(float v)</syntaxhighlight> || <translate><!--T:144--> Задать множитель чувствительности к прикосновению. По умолчанию = 1</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setRadius(float r)</syntaxhighlight> || <translate><!--T:145--> Задать радиус</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setVelocity(const math::Rotator& v)</syntaxhighlight> || <translate><!--T:146--> Задать угловую скорость</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void enableUpdate(bool flag = true) | ||
+ | void disableUpdate()</syntaxhighlight> || <translate><!--T:147--> Включить/отключить вращение аркболла. '''Не сбрасывает скорость, при включении - может продолжить вращаться (затухание скорости по времени)'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void enableInput(bool flag = true) | ||
+ | void disableInput()</syntaxhighlight> || <translate><!--T:148--> Включение/выключение перехвата прикосновений аркболлом</translate> | ||
+ | |} | ||
==== Camera ==== | ==== Camera ==== | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">Camera(const std::string& name) | ||
+ | Camera(const std::string& name, float fov, float aspect, float near, float far)</syntaxhighlight> || <translate><!--T:149--> Конструкторы перспективной камеры. Можно сразу указать FOV(угол обзора камеры по горизонтали), соотношение сторон и clip плоскости. '''FOV указывается в градусах'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">Camera(const std::string& name, const math::size& screen, float near, float far)</syntaxhighlight> || <translate><!--T:150--> Конструктор ортокамеры. Указываются размеры и clip плоскости камеры</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getFov() const | ||
+ | void setFov(float v)</syntaxhighlight> || <translate><!--T:151--> Получить, установить FOV(угол обзора камеры по горизонтали). '''FOV указывается в градусах'''</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getAspect() const | ||
+ | void setAspect(float v)</syntaxhighlight> || <translate><!--T:152--> Получить/установить соотношение сторон</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getNear() const | ||
+ | void setNear(float v)</syntaxhighlight> || <translate><!--T:153--> Получить/установить near clip плоскость</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getFar() const | ||
+ | void setFar(float v)</syntaxhighlight> || <translate><!--T:154--> Получить/установить far clip плоскость</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool getIsOrtho() const</syntaxhighlight> || <translate><!--T:155--> Получить является ли камера ортокамерой</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setClearColor(const math::vec4& v)</syntaxhighlight> || <translate><!--T:156--> Задать значение цвета для очистки буфера цвета (фоновый цвет для камеры при отсутствии Background или Skybox)</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">ClearMask getClearMask() const | ||
+ | void setClearMask(ClearMask v)</syntaxhighlight> || <translate><!--T:157--> Получить/установить ,битовую маску режима очистки экрана. Это значение будет применяться при рендеринге каждого кадра.</translate> | ||
+ | |||
+ | <translate><!--T:158--> | ||
+ | Значение по умолчанию '''ClearMask::Color | ClearMask::Depth''' | ||
+ | |||
+ | <!--T:159--> | ||
+ | Возможные значения:</translate> | ||
+ | |||
+ | None | ||
+ | |||
+ | <translate><!--T:160--> | ||
+ | Color - очищать буфер цвета | ||
+ | |||
+ | <!--T:161--> | ||
+ | Depth - очищать буфер глубины | ||
+ | |||
+ | <!--T:162--> | ||
+ | Skybox - отрисовывать ли skybox | ||
+ | |||
+ | <!--T:163--> | ||
+ | Background - отрисовывать ли background</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool hasSkybox() const | ||
+ | w4::cref<Skybox> getSkybox() const</syntaxhighlight> || <translate><!--T:164--> Получить текущий объект skybox или nullptr при его отсутствии</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool hasBackground() const | ||
+ | w4::cref<Background> getBackground() const</syntaxhighlight> || <translate><!--T:165--> Получить текущий объект background или nullptr при его отсутствии</translate> | ||
+ | |} | ||
+ | |||
+ | ===== Background ===== | ||
+ | <translate><!--T:166--> Background представляет собой ресурсовыгодный способ очистки буфера цвета чем-то, что сложнее одного цвета.</translate> | ||
+ | |||
+ | <translate><!--T:167--> | ||
+ | Может использоваться двумя способами: | ||
+ | # Вызовом метода '''void setTexture(w4::cref<resources::Texture> texture)'''. При вызове создастся материал по умолчанию (просто отображает текстуру). | ||
+ | # Работа с материалами вручную через методы '''void setMaterial(w4::cref<resources::MaterialInst>)''' и '''w4::sptr<resources::MaterialInst> getMaterial()'''. '''Вертексный шейдер должен использовать нормализованное пространство координат (т.е. [-1; 1])'''</translate> | ||
+ | |||
+ | ===== Skybox ===== | ||
+ | <translate><!--T:168--> В случае если необходимо чтобы фон смещался при движении камеры, можно использовать объект Skybox. Он также может использоваться двумя способами:</translate> | ||
+ | |||
+ | <translate><!--T:169--> 1. Вызовом метода '''void setCubemap(w4::cref<resources::Cubemap> texture)'''. При вызове создастся материал по умолчанию (просто отображает текстуру).</translate> | ||
+ | |||
+ | <translate><!--T:170--> Пример:</translate> | ||
+ | <syntaxhighlight lang="c++"> | ||
+ | auto cubemap = Cubemap::get({"resources/textures/left.png", | ||
+ | "resources/textures/right.png", | ||
+ | "resources/textures/up.png", | ||
+ | "resources/textures/down.png", | ||
+ | "resources/textures/front.png", | ||
+ | "resources/textures/back.png", | ||
+ | }); | ||
+ | m_cam->setClearMask(ClearMask::Color | ClearMask::Depth | ClearMask::Skybox); | ||
+ | m_cam->getSkybox()->setCubemap(cubemap); | ||
+ | </syntaxhighlight> | ||
+ | <translate><!--T:171--> 2. Работа с материалами вручную через методы '''void setMaterial(w4::cref<resources::MaterialInst>)''' и '''w4::sptr<resources::MaterialInst> getMaterial()'''. '''Вертексный шейдер должен использовать нормализованное пространство координат (т.е. [-1; 1])'''</translate> | ||
==== PointLight ==== | ==== PointLight ==== | ||
+ | <translate><!--T:172--> Точечный источник освещения. Имеет следующий API:</translate> | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">math::vec3 getColor() const | ||
+ | void setColor(const math::vec3& c)</syntaxhighlight> || <translate><!--T:173--> Получить/установить цвет света</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getIntensity() const | ||
+ | void setIntensity(float i)</syntaxhighlight> || <translate><!--T:174--> Получить/установить яркость источника света</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">core::LightDecayRate getDecayRate() const | ||
+ | void setDecayRate(core::LightDecayRate)</syntaxhighlight> || <translate><!--T:175--> Получить/установить алгоритм затухания цвета</translate> (None, Linear, Quadratic, Cubic). | ||
+ | |} | ||
==== SpotLight ==== | ==== SpotLight ==== | ||
+ | <translate><!--T:176--> Направленный источник освещения в виде конуса с вершиной в точке излучения.</translate> | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getAngle() const | ||
+ | void setAngle(float r)</syntaxhighlight> || <translate><!--T:177--> Получить/установить верхний угол конуса</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">math::vec3 getColor() const | ||
+ | void setColor(const math::vec3& c)</syntaxhighlight> || <translate><!--T:178--> Получить/установить цвет света</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getIntensity() const | ||
+ | void setIntensity(float i)</syntaxhighlight> || <translate><!--T:179--> Получить/установить яркость источника света</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">core::LightDecayRate getDecayRate() const | ||
+ | void setDecayRate(core::LightDecayRate)</syntaxhighlight> || <translate><!--T:180--> Получить/установить алгоритм затухания цвета</translate> (None, Linear, Quadratic, Cubic) | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getDecayFactor() const | ||
+ | void setDecayFactor(float f)</syntaxhighlight> || <translate><!--T:181--> Получить/установить степень затухания(“прозрачность“) цвета от 0 до 1(по умолчанию = 1)</translate> | ||
+ | |} | ||
==== Spline ==== | ==== Spline ==== | ||
+ | <translate><!--T:182--> Позволяет использовать траектории, импортированные из fbx (b-spline). </translate> | ||
+ | |||
+ | '''<translate><!--T:183--> Важно понимать, что в формате FBX Spline содержит только саму траекторию, но не вращения вокруг неё (spin).</translate>''' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Method !! Description | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool isRepeatable() const | ||
+ | void setRepeatable(bool isRepeatable)</syntaxhighlight> || <translate><!--T:184--> Получить/установить режим повторяющегося “воспроизведения”</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void play(float splineTime, std::function<void(const math::Transform&)> updateHandler, std::function<void(bool)> completionHandler = [](bool){})</syntaxhighlight> || <translate><!--T:185--> Запуск “воспроизведения” сплайна</translate> | ||
+ | |||
+ | <translate><!--T:186--> splineTime - конечное время воспроизведения сплайна(для расчёта множителя скорости)</translate> | ||
+ | |||
+ | <translate><!--T:187--> updateHandler - метод, в который будут приходить Transform</translate> | ||
+ | |||
+ | <translate><!--T:188--> completionHandler - метод, который вызовется при завершении “воспроизведения” (если не задано повторение)</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void stop()</syntaxhighlight> || <translate><!--T:189--> Остановить “воспроизведение” преобразований</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool isPlaying() const</syntaxhighlight> || <translate><!--T:190--> Возвращает, идёт ли “воспроизведение” сплайна</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">bool isPaused() const | ||
+ | void pause()</syntaxhighlight> || <translate><!--T:191--> Приостановка “воспроизведения” сплайна</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">float getDuration() const</syntaxhighlight> || <translate><!--T:192--> Возвращает длительность сплайна в секундах</translate> | ||
+ | |- | ||
+ | | <syntaxhighlight lang="c++">void setPosition(float pos)</syntaxhighlight> || <translate><!--T:193--> Устанавливает позицию в секундах</translate> | ||
+ | |} |
Latest revision as of 13:55, 19 June 2020
Contents
Scope
В статье описаны основные решения, используемые в W4 Game Engine:
- Координатная истема
- Нодовая система
- API основных классов
Coordinate System
Используется left-handed координатная система и вращение производится против часовой стрелки, если смотреть в направлении оси.
Render Tree
Отображаемый на экране результат и зависимость трансформаций объектов друг от друга определяются деревом рендеринга, то есть набором объектов рендеринга. Особенности дерева рендеринга W4 Game Engine:
- Минимальное дерево рендеринга состоит только из корневого узла.
- У каждого узла может быть ноль или более подузлов — «детей», но при этом у узла не может быть более одного “родителя“ (самый верхний узел - “корневой“ - родителя не имеет).
- Для того чтобы объект отобразился на экране и/или пересчитал свои данные, необходимо чтобы он находился в дереве.
- Иерархия встроенных типов узлов показана на схеме.
Назначение и интерфейсы узлов (классов) описаны в следующих параграфах.
Примечания по использованию:
- При рисовании в несколько проходов, у каждого прохода создаётся своё дерево со своим корневым узлом.
- У каждого узла можно узнать локальные трансформации (то есть трансформации относительно “родителя“) и мировые, а также задать их.
- При задании мировых координат узла пересчитываются его локальные координаты, при задании локальных - мировые.
- У узла, не имеющего родителя трансформации совпадают.
- Можно временно отключить узел, при этом отключаются и все его “потомки“.
Node
Node - базовый класс любого узла (в том числе RootNode).
Method | Description |
---|---|
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->setLocalTranslation({1, 0, 0});
parentNode->addChild(childNode);
то узел childNode будет находиться в мировой позиции {1, 0, 0}, а если использовать следующие строки: childNode->setLocalTranslation({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)
VisibleNode
Базовый класс для отображаемого узла.
Особенности:
- Все отображаемые узлы содержат вертексный буфер и состоят из одного и более элемента surface (который содержит индексный буфер).
- Каждому surface можно назначить свой материал.
Все отображаемые узлы имеют следующие дополнительные методы:
Method | Description |
---|---|
const MaterialInstPtr& getMaterialInst(const std::string& surfaceName) const
|
Возвращает материал указанного surface |
const MaterialInstPtr& getMaterialInst() const
|
Получает материал, установленный для всех surface. Если материалы surface отличаются - выдаёт ошибку в лог и возвращает какой-то материал. |
void setMaterialInst(const MaterialInstPtr & m)
|
Устанавливает материал для всех surface |
Mesh
Объект, отображающий геометрию.
Объекты типа Mesh в движке можно создать тремя путями: 1. Загрузить из ресурсов (см. также Mesh Converter):
teapot = Asset::load(Path("resources/teapot", "teapot.asset"))->getRoot()->getChild<Mesh>("Utah Teapot Quads");
2. Вызвать встроенный генератор:
auto sphere = Mesh::create::sphere(радиус, количество параллелей, количество меридианов);
auto box = Mesh::create::box({width, height, depth}); //box - параллелепипед, у которого каждая грань - surface
auto cube = Mesh::create::cube({width, height, depth});
auto mappedCube = Mesh::create::mappedCube({width, height, depth}); //параллелепипед с uv координатами под развёртку
auto plane1 = Mesh::create::plane({width, height}, scaleUv); //плоскость по осям XY. scaleUv = false(по умолчанию) - UV от 0 до 1, true - от 0 до ширины/высоты
auto plane2 = Mesh::create::plane({left, down}, {right, up}, scaleUv; //то же самое
auto cylinder = Mesh::create::cylinder(высота, радиус, количество секторов);
auto cone = Mesh::create::cone(верхний радиус, нижний радиус, высота, количество секторов, количество сегментов по вертикали);
auto torus = Mesh::create::torus(радиус кольца, радиус "трубы", количество сегментов кольца, количество сегментов "трубы", угол дуги кольца(2*PI для замкнутого тора));
auto skybox = Mesh::create::skybox(); //Скайбокс. Единичный куб со сторонами, отображаемыми изнутри.
3. Создать из кода:
auto indicesBuf = make::sptr<UserIndicesBuffer>("Floor indices");
auto verticesBuf = make::sptr<UserVerticesBuffer<Mesh::VertexFormat>>("floor_" + std::to_string(width) + "x" + std::to_string(height) + "-" + std::to_string(offset));
indicesBuf->setNeedExport(false);
verticesBuf->setNeedExport(false);
resources::MeshVertexDataBuilder vdb(verticesBuf, indicesBuf);
//позиция, UV, нормаль
vdb.addVertex({-width / 2 + offset, 0, height / 2}, {offset/width, 0.f}, {0, 1, 0});
vdb.addVertex({width / 2 - offset, 0, height / 2}, {1.f - offset/width, 0.f}, {0, 1, 0});
vdb.addVertex({-width / 2, 0, height / 2 - offset}, {0.f, offset/height}, {0, 1, 0});
vdb.addVertex({width / 2, 0, height / 2 - offset}, {1.f, offset/height}, {0, 1, 0});
vdb.addVertex({width / 2, 0, -height / 2 + offset}, {0.f, 1.f - offset/height}, {0, 1, 0});
vdb.addVertex({-width / 2, 0, -height / 2 + offset}, {1.f, 1.f - offset/height}, {0, 1, 0});
vdb.addVertex({width / 2 - offset, 0, -height / 2}, {offset/width, 1.f}, {0, 1, 0});
vdb.addVertex({-width / 2 + offset, 0, -height / 2}, {1.f - offset/width, 1.f}, {0, 1, 0});
vdb.addIndices({2, 1, 0,
2, 3, 1,
4, 3 ,2,
5, 4, 2,
5, 6, 4,
7, 6, 5});
vdb.build();
auto result = make::sptr<Mesh>(verticesBuf->getName(), verticesBuf);
result->addComponent<Surface>("unnamed", indicesBuf);
result->setMaterialInst(Material::getDefault()->createInstance());
SkinnedMesh
Объект, отображающий геометрию с анимацией.
Особенности:
- можно загрузить только из файла;
- представляет собой геометрию со скелетной анимацией;
- содержит в себе набор анимаций;
- содержит в себе API для управления анимациями и позволяет проигрывать несколько анимаций с плавными переходами между ними.
Method | Description |
---|---|
w4::cref<core::Node> createSocket(const std::string& boneName)
|
Создаёт узел для крепления на кости с заданным именем. К этому узлу можно добавлять “детей“ |
w4::cref<core::Node> getSocket(const std::string& boneName) const
|
Возвращает узел для крепления на кости с заданным именем |
std::vector<std::string> getAvailableAnimations() const
|
Возвращает список имеющихся анимаций |
bool haveAnimation(const std::string& animationName) const
|
Проверяет наличие анимации с заданным именем |
size_t getAnimationIndex(const std::string& animationName) const
|
Получение индекса анимации по её имени |
const std::string& getAnimationName(size_t animationIndex) const
|
Получение имени анимации по её индексу |
void setAnimationStateChangedCallback(std::function<void(Animator&, Animator::State)>)
void setAnimationStateChangedCallback(Animator::State, std::function<void(Animator&)>)
|
Установить функтор, который вызовется по изменению состояния конкретной анимации. Метод имеет как вариант только для заданного состояния, так и для любого. Состояния анимаций:
Animator::State::Idle(не проигрывается/закончилась), Animator::State::Play(проигрывается), Animator::State::Pause(приостановлена) Пример: node->setAnimationStateChangedCallback(Animator::State::Idle, [](Animator& animator)
{
W4_LOG_DEBUG("Animator stopped:\n"
"\tName: %s,"
"\tSpeed: %f,"
"\tDuration: %f,"
"\tFps: %f",
animator.getName().c_str(),
animator.getSpeed(),
animator.getDuration(),
animator.getFps());
});
|
void play(const std::string& animationName, float duration = 0.f)
void play(size_t animationIndex = 0, float duration = 0.f)
|
Проигрывание анимации по индексу или имени. Указывается длительность анимации, если она равна нулю - проигрывается анимация целиком |
void play(std::initializer_list<std::pair<std::string, float>>, float duration = 0.f)
void play(std::initializer_list<std::pair<size_t, float>>, float duration = 0.f)
|
Проигрывание списка анимаций с весами. Длительность, если равна нулю - равна анимации с максимальной длительностью
Пример: skinned->play({{"run", .3f}, {"jump", .7f}});
|
void pause()
|
Приостановить все активные анимации |
void resume()
|
Возобновить проигрывание всех приостановленных анимаций |
void stop()
|
Остановить проигрывание всех активных анимаций |
bool isPlaying() const
|
Проверяет, проигрывается ли сейчас любая анимация |
Animator & getAnimator(size_t)
Animator & getAnimator(const std::string&)
const Animator & getAnimator(size_t) const
const Animator & getAnimator(const std::string&) const
|
Получить объект аниматор по имени или индексу. API объекта Animator описан чуть ниже. Используется для управления анимациями с расширенными возможностями.
Управление анимациями через API и SkinnedMesh не являются взаимоисключающими. Так, например, общеупотребительной является практика зацикливания анимаций: m_skinned->getAnimator("dance").setIsLooped(true);
|
Animator API
Method | Description |
---|---|
void play()
|
Запуск воспроизведения анимации.
В случае, если она уже проигрывается, она будет остановлена и запущена с начала |
void pause()
|
Приостанавливает воспроизведение анимации |
void resume()
|
Возобновляет воспроизведение анимации |
void stop()
|
Останавливает воспроизведение анимации |
void seek(float targetTime)
|
Устанавливает текущую позицию в анимации на заданное время. Значения автоматически обрезаются сверху и снизу до диапазона [0; длина анимации] или до диапазона, заданного методами setBeginTime/setEndTime |
bool isLooped() const
|
Возвращает, включен ли для данной анимации режим циклическое воспроизведение |
float getSpeed() const
|
Получает множитель скорости воспроизведения анимации (по умолчанию 1.0) |
State getState() const
|
Возвращает состояние анимации
Состояния анимаций: Animator::State::Idle (не проигрывается/закончилась), Animator::State::Play (проигрывается), Animator::State::Pause (приостановлена) |
void setPlayType(PlayType)
|
Устанавливает направление воспроизведения.
Возможные значения параметра: Animator::PlayType::Forward (вперёд), Animator::PlayType::Backward (в обратном направлении) |
PlayType getPlayType() const
|
Возвращает текущее направление проигрывания. Возможные значения см. в setPlayType |
float getCurrentTime() const
|
Возвращает текущую позицию в анимации |
void setIsLooped(bool)
|
Включает/выключает для данной анимации режим циклического воспроизведение |
void setSpeed(float)
|
Устанавливает множитель скорости воспроизведения анимации |
void setBeginTime(float)
|
Устанавливает начальное время анимации. Нужно для частичного воспроизведения анимации |
void setEndTime(float)
|
Устанавливает конечное время анимации. Нужно для частичного воспроизведения анимации |
StateHandlerId setStateHandler(State state, StateHandler handler)
void resetStateHandler(StateHandlerId handler)
|
Устанавливает и удаляет callback на смену состояния на заданное. Сигнатура callback - void(Animator&) |
void setTimeHandler(float dt, TimeHandler handler)
|
Устанавливает callback на прохождение заданной временной метки. Сигнатура callback - void(Animator&) |
float getFps() const
|
Возвращает характеристику анимации - количество значений в секунду. |
float getDuration() const
|
Возвращает длительность анимации |
ParticlesEmitter
Особенности:
- ParticlesEmitter может быть загружен только из файла;
- Входным форматом файла является CoronaSDK (можно создать в любом онлайн-редакторе, например в http://www.effecthub.com/particle2dx, сохранить json с расширением part и текстуру и убедиться, что путь к текстуре является путём от корня проекта).
Важно понимать и учитывать, что в связи с ограниченными вычислительными и асинхронными возможностями технологии WebAssembly на текущий момент, система частиц отображается на плоскости, развёрнутой к камере.
API ParticlesEmitter:
Method | Description |
---|---|
void start()
|
Запуск генерации частиц |
void pause()
|
Приостановка генерации частиц. В этом случае обработка уже выпущенных частиц продолжается |
void resume()
|
Возобновление генерации частиц |
void stop()
|
Остановка генерации частиц. Обработка всех частиц прекращается |
State getState() const
|
Получение состояния
Варианты: ParticlesEmitter::State::IDLE, ParticlesEmitter::State::STARTED, ParticlesEmitter::State::PAUSED |
Billboard
Плоскость заданного при создании размера, всегда ориентированная на камеру. Имеет методы getSize и setSize.
Plotter
Реализация рисования линиями. Данные плоттера представляют собой информацию о точках (позиция и цвет) и набор индексов для их попарного соединения.
Пример:
auto plotter1 = make::sptr<Plotter>("Plotter_1");
const std::vector<uint32_t> indices = {1, 0};
const std::vector<LinesVertexFormat> vertices = {{{-10, -10, 0}, {1, 0, 0,1}},
{{10, 10, 0}, {0, 1, 0, 1}}};
plotter1->setLines(vertices, indices);
Представленный выше пример создаст косую линию с цветом, переходящим из красного в зелёный.
Как правило, для создания примитивов используются статические хелперы, возвращающие готовый объект, описанные в классе Plotter:
static w4::sptr<Plotter> build(std::vector<LinesVertexFormat> vertices, std::vector<uint32_t> indices);
static w4::sptr<Plotter> buildFromSegments(std::vector<std::array<w4::math::vec3, 2>> segments, const w4::math::vec4& color);
static w4::sptr<Plotter> buildFromPolyline(std::vector<w4::math::vec3> points, const w4::math::vec4& color);
static w4::sptr<Plotter> buildSphere(float radius, size_t rings, size_t sectors, const w4::math::vec4& color);
static w4::sptr<Plotter> buildOctahedron(std::array<w4::math::vec3, 8> vertices, const w4::math::vec4& color);
static w4::sptr<Plotter> buildCube(std::array<w4::math::vec3, 2> inVertices, const w4::math::vec4& color);
static w4::sptr<Plotter> buildCapsule(float radius, float height, size_t rings, size_t sectors, const w4::math::vec4& color);
static w4::sptr<Plotter> buildMesh(w4::cref<Mesh>, w4::math::vec4::cref color);
static w4::sptr<Plotter> buildMesh(w4::cref<SkinnedMesh>, w4::math::vec4::cref color);
static w4::sptr<Plotter> buildRay(math::vec3::cref startPoint, math::vec3::cref directionPoint, const w4::math::vec4& color);
DataNode
В отличие от VisibleNode, DataNode не отображается на экране. Далее описывается API унаследованных классов.
ArcBallNode
Аркболл для нижележащих объектов иерархии. Реализует контейнер, позволяющий вращать содержимое, как если бы оно находилось в шаре с инерцией.
Method | Description |
---|---|
ArcBallNode(const std::string& name = core::Object::defaultName, float radius = 1.f)
|
Конструктор, принимающий имя и радиус "шара" |
void setFriction(float v)
|
Задать коэффициент затухания скорости. По умолчанию = 0.95 |
void setSensitivity(float v)
|
Задать множитель чувствительности к прикосновению. По умолчанию = 1 |
void setRadius(float r)
|
Задать радиус |
void setVelocity(const math::Rotator& v)
|
Задать угловую скорость |
void enableUpdate(bool flag = true)
void disableUpdate()
|
Включить/отключить вращение аркболла. Не сбрасывает скорость, при включении - может продолжить вращаться (затухание скорости по времени) |
void enableInput(bool flag = true)
void disableInput()
|
Включение/выключение перехвата прикосновений аркболлом |
Camera
Method | Description |
---|---|
Camera(const std::string& name)
Camera(const std::string& name, float fov, float aspect, float near, float far)
|
Конструкторы перспективной камеры. Можно сразу указать FOV(угол обзора камеры по горизонтали), соотношение сторон и clip плоскости. FOV указывается в градусах |
Camera(const std::string& name, const math::size& screen, float near, float far)
|
Конструктор ортокамеры. Указываются размеры и clip плоскости камеры |
float getFov() const
void setFov(float v)
|
Получить, установить FOV(угол обзора камеры по горизонтали). FOV указывается в градусах |
float getAspect() const
void setAspect(float v)
|
Получить/установить соотношение сторон |
float getNear() const
void setNear(float v)
|
Получить/установить near clip плоскость |
float getFar() const
void setFar(float v)
|
Получить/установить far clip плоскость |
bool getIsOrtho() const
|
Получить является ли камера ортокамерой |
void setClearColor(const math::vec4& v)
|
Задать значение цвета для очистки буфера цвета (фоновый цвет для камеры при отсутствии Background или Skybox) |
ClearMask getClearMask() const
void setClearMask(ClearMask v)
|
Получить/установить ,битовую маску режима очистки экрана. Это значение будет применяться при рендеринге каждого кадра.
Значение по умолчанию ClearMask::Color | ClearMask::Depth Возможные значения: None Color - очищать буфер цвета Depth - очищать буфер глубины Skybox - отрисовывать ли skybox Background - отрисовывать ли background |
bool hasSkybox() const
w4::cref<Skybox> getSkybox() const
|
Получить текущий объект skybox или nullptr при его отсутствии |
bool hasBackground() const
w4::cref<Background> getBackground() const
|
Получить текущий объект background или nullptr при его отсутствии |
Background
Background представляет собой ресурсовыгодный способ очистки буфера цвета чем-то, что сложнее одного цвета.
Может использоваться двумя способами:
- Вызовом метода void setTexture(w4::cref<resources::Texture> texture). При вызове создастся материал по умолчанию (просто отображает текстуру).
- Работа с материалами вручную через методы void setMaterial(w4::cref<resources::MaterialInst>) и w4::sptr<resources::MaterialInst> getMaterial(). Вертексный шейдер должен использовать нормализованное пространство координат (т.е. [-1; 1])
Skybox
В случае если необходимо чтобы фон смещался при движении камеры, можно использовать объект Skybox. Он также может использоваться двумя способами:
1. Вызовом метода void setCubemap(w4::cref<resources::Cubemap> texture). При вызове создастся материал по умолчанию (просто отображает текстуру).
Пример:
auto cubemap = Cubemap::get({"resources/textures/left.png",
"resources/textures/right.png",
"resources/textures/up.png",
"resources/textures/down.png",
"resources/textures/front.png",
"resources/textures/back.png",
});
m_cam->setClearMask(ClearMask::Color | ClearMask::Depth | ClearMask::Skybox);
m_cam->getSkybox()->setCubemap(cubemap);
2. Работа с материалами вручную через методы void setMaterial(w4::cref<resources::MaterialInst>) и w4::sptr<resources::MaterialInst> getMaterial(). Вертексный шейдер должен использовать нормализованное пространство координат (т.е. [-1; 1])
PointLight
Точечный источник освещения. Имеет следующий API:
Method | Description |
---|---|
math::vec3 getColor() const
void setColor(const math::vec3& c)
|
Получить/установить цвет света |
float getIntensity() const
void setIntensity(float i)
|
Получить/установить яркость источника света |
core::LightDecayRate getDecayRate() const
void setDecayRate(core::LightDecayRate)
|
Получить/установить алгоритм затухания цвета (None, Linear, Quadratic, Cubic). |
SpotLight
Направленный источник освещения в виде конуса с вершиной в точке излучения.
Method | Description |
---|---|
float getAngle() const
void setAngle(float r)
|
Получить/установить верхний угол конуса |
math::vec3 getColor() const
void setColor(const math::vec3& c)
|
Получить/установить цвет света |
float getIntensity() const
void setIntensity(float i)
|
Получить/установить яркость источника света |
core::LightDecayRate getDecayRate() const
void setDecayRate(core::LightDecayRate)
|
Получить/установить алгоритм затухания цвета (None, Linear, Quadratic, Cubic) |
float getDecayFactor() const
void setDecayFactor(float f)
|
Получить/установить степень затухания(“прозрачность“) цвета от 0 до 1(по умолчанию = 1) |
Spline
Позволяет использовать траектории, импортированные из fbx (b-spline).
Важно понимать, что в формате FBX Spline содержит только саму траекторию, но не вращения вокруг неё (spin).
Method | Description |
---|---|
bool isRepeatable() const
void setRepeatable(bool isRepeatable)
|
Получить/установить режим повторяющегося “воспроизведения” |
void play(float splineTime, std::function<void(const math::Transform&)> updateHandler, std::function<void(bool)> completionHandler = [](bool){})
|
Запуск “воспроизведения” сплайна
splineTime - конечное время воспроизведения сплайна(для расчёта множителя скорости) updateHandler - метод, в который будут приходить Transform completionHandler - метод, который вызовется при завершении “воспроизведения” (если не задано повторение) |
void stop()
|
Остановить “воспроизведение” преобразований |
bool isPlaying() const
|
Возвращает, идёт ли “воспроизведение” сплайна |
bool isPaused() const
void pause()
|
Приостановка “воспроизведения” сплайна |
float getDuration() const
|
Возвращает длительность сплайна в секундах |
void setPosition(float pos)
|
Устанавливает позицию в секундах |