Difference between revisions of "Basic Concept"

From Ciliz|W4
 
(48 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
<languages />
 
== Scope ==
 
== Scope ==
 
+
<translate>
В статье описаны основные решения, используемые в W4 Game Engine, и их API.
+
<!--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|<translate><!--T:5--> Иерархия типов</translate>|500 px]]
  
[[File:Concept_02.png]]
+
<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->setTranslation({1, 0, 0});
+
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->setTranslation({1, 0, 0});
+
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&)
+
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&)'''
  
Последний метод вызывается только для узлов, чей тип унаследован от T
+
<translate><!--T:21--> Последний метод вызывает функтор только для тех узлов, чей тип унаследован от T</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <syntaxhighlight lang="c++">void setWorldRotation(math::Rotator::cref, math::vec3::cref worldPt)</syntaxhighlight> || <translate><!--T:22--> Установка поворота относительно точки в мировых координатах</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <syntaxhighlight lang="c++">void rotateAroundPoint(const math::Rotator& rotator, const math::vec3& worldPt)</syntaxhighlight> || <translate><!--T:23--> Вращение вокруг точки в мировых координатах</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <syntaxhighlight lang="c++">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</syntaxhighlight> || <translate><!--T:24--> Приведение типа узла (прим. указываемый тип не проверяется)</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <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> || <translate><!--T:26--> Получение ”ребёнка” по имени с приведением типа (прим. указываемый тип не проверяется)</translate>
 +
|-
 +
| <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> || <translate><!--T:28--> Поиск “ребёнка“ по имени</translate>
 +
|-
 +
| <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
 +
 
 +
void setWorldTranslation(math::vec3::cref)
 +
 
 +
math::vec3::cref getLocalTranslation() const
 +
 
 +
void setLocalTranslation(math::vec3::cref)
 +
 
 +
</syntaxhighlight> || <translate><!--T:30--> Получение и установка позиции в мировых или локальных координатах</translate>
 +
|-
 +
| <syntaxhighlight lang="c++">math::vec3::cref getWorldScale() const
 +
 
 +
void setWorldScale(math::vec3::cref)
 +
 
 +
math::vec3::cref getLocalScale() const
 +
 
 +
void setLocalScale(math::vec3::cref)
 +
 
 +
</syntaxhighlight> || <translate><!--T:31--> Получение и установка масштаба в мировых или локальных координатах</translate>
 +
|-
 +
| <syntaxhighlight lang="c++">math::vec3 getWorldUp() const
 +
 
 +
math::vec3 getWorldForward() const
 +
 
 +
math::vec3 getWorldRight() const</syntaxhighlight> || <translate><!--T:32--> Получение нормализованных векторов в направлениях вверх, вперёд и вправо пространства модели в мировых координатах</translate>
 +
|-
 +
| <syntaxhighlight lang="c++">math::Transform::cref getWorldTransform()    const
 +
 
 +
void setWorldTransform(math::Transform::cref)
 +
 
 +
math::Transform::cref getLocalTransform() const
 +
 
 +
void setLocalTransform(math::Transform::cref)
 +
</syntaxhighlight> || <translate><!--T:33--> Получение и установка трансформации в мировых или локальных координатах. Трансформация - сочетание позиции, поворота и масштаба</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <syntaxhighlight lang="c++">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)
 +
</syntaxhighlight> || <translate><!--T:34--> Получение и установка поворота в мировых или локальных координатах. Методы rotateWorld/Local добавляют поворот к текущим значениям</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <syntaxhighlight lang="c++">const std::unordered_set<w4::sptr<Node>>& getChildren() const</syntaxhighlight> || <translate><!--T:35--> Получение списка “детей“</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <syntaxhighlight lang="c++">bool isEnabled() const</syntaxhighlight> || <translate><!--T:36--> Возвращает, включен ли узел</translate>
 
|-
 
|-
| <syntaxhighlight lang="c++">XXXXXXXXX</syntaxhighlight> || Example
+
| <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

Other languages:
English • ‎русский

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 представляет собой ресурсовыгодный способ очистки буфера цвета чем-то, что сложнее одного цвета.

Может использоваться двумя способами:

  1. Вызовом метода void setTexture(w4::cref<resources::Texture> texture). При вызове создастся материал по умолчанию (просто отображает текстуру).
  2. Работа с материалами вручную через методы 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)
Устанавливает позицию в секундах