Как стать автором
Обновить

Начало работы с графическим движком Tempest

Tempest Engine — это новый 2D/3D движок для создания игр и мультимедийных приложений. Обладает хорошей поддержкой мобильных и десктопных ОС, различных графических api. Большой упор сделан на RAII, в комбинации с type-safe программированием. В движке реализованна поддержка UI и унифицированное взаимодействие с оконной системой.

Поддерживаемые платформы: Windows, OSX, iOS, Android.

В данный момент движок находится в разработке. Исходный код и примеры доступны на github. Для сборки используется qmake-скрипт — благодаря этому очень легко собрать проект из исходного кода под любую желаемую платформу(с андроидом есть сложности из-за java обвязки — в процессе решения).

Сборка


Первым и важным шагом является сборка движка из исходных кодов. Нужные версии сторонних библиотек (libpng, libjpeg, freetype, zlib) уже есть в репозитарии в папке thirdparty, поэтому отдельно скачивать их не обязательно. Для компилятора от microsoft пока доступна только статическая линковка.

qmake
make

Инициализация


Для первого знакомства будет удачным решением взять и разобрать один из примеров. Возьмем пример OverlayPaint из Tempest/Examples/OverlayPaint. В примере показано использование 2d графики, с помощью класса Tempest::Painter, и 3D фон.

main:

#include "mainwindow.h"
// подключение классов движка
#include <Tempest/Application>
#include <Tempest/Opengl2x>

// для каждой платформы(включая мобильные) используем обычный main, как точку входа
int main() {
  using namespace Tempest;
  Application app; 

  // мы будем использовать opengl; создадим и покажем окно
  Opengl2x api;
  MainWindow w( api );
  w.show();

  return app.exec();
  }

Класс главного окна/активити приложения:

class MainWindow:public Tempest::Window {
  public:
    MainWindow( Tempest::AbstractAPI& api );

  protected:
    // по порядку: сначала объект для render-девайса
    Tempest::Device               device;
    // затем холдеры: каждый отвечает за создание ресурсов указанного типа
    Tempest::TextureHolder        texHolder; // для текстур
    Tempest::VertexBufferHolder   vboHolder; // для вершин
    Tempest::IndexBufferHolder    iboHolder; // для индексов
    Tempest::SpritesHolder         spHolder; // для спрайтов
    ...
    Tempest::SurfaceRender         uiRender;
    ...
    // события, которые будет обрабатывать наше окно
    void paintEvent( Tempest::PaintEvent& e );
    void mouseDownEvent ( Tempest::MouseEvent& e );
    void mouseDragEvent ( Tempest::MouseEvent& e );
    void mouseWheelEvent( Tempest::MouseEvent& e );
    void resizeEvent( Tempest::SizeEvent& e );
    ...
    // главная функция рисования
    void render();
  };

Имплементация:

MainWindow::MainWindow(Tempest::AbstractAPI &api)
   :device( api, handle() ),
    texHolder(device),
    vboHolder(device),
    iboHolder(device),
    shHolder (device),
    spHolder (texHolder),
    uiRender (shHolder)
    {
  // информация об uniform-переменных в шейдере
  udecl.add("mvpMatrix",Decl::Matrix4x4)
       .add("texture",  Decl::Texture2d);

  // описание данных в вершинах
  VertexDeclaration::Declarator decl;
  decl.add( Decl::float3, Usage::Position )
      .add( Decl::float2, Usage::TexCoord );
  vdecl = VertexDeclaration(device, decl);

  texture = texHolder.load("data/texture.png");

  /* в примере используется только вершинный и фрагменты шейдеры, поэтому пути до геометрического шейдера и шейдеров тесселяции не указываются
  */
  shader = shHolder.load({"shader/vs.glsl",
                          "shader/fs.glsl",
                          "","",""});

  T_ASSERT( shader.isValid() );
  }

  /*
   у каждого виджета есть событие для рисования содержимого
  */
void MainWindow::paintEvent(PaintEvent &e) {
  Painter p(e);

  p.setTexture(texture);
  p.drawRect( Rect(0,0, 100, 100), texture.rect() );

  p.setFont( Font("data/arial", 16) );
  p.drawText(100, 100, "This is cat!");
  }

void MainWindow::render() {
  // тут может быть lost-device. Привет, DirectX9!)
  if( !device.startRender() )
    return;

  /* вызов buildVbo вызовет paintEvent, у нашего окна и как следствие "перерисовку" интерфейса. Результат будет сохранен во внутренней буфер вершин. Для приложений со сложным gui, будет удачным решением проверять needToUpdate флаг и рисоваться только по необходимости.
  */
  uiRender.buildVbo(*this, vboHolder, iboHolder, spHolder );
  device.clear( Color(0,0,1), 1 );

  device.beginPaint();
  device.setRenderState( RenderState() );

  // настроим шейте
  setupShaderConstants(shader);
  // рисуем куб
  device.drawIndexed( AbstractAPI::Triangle,
                      shader, vdecl,
                      vbo, ibo,
                      0,0, ibo.size()/3 );
  // рисуем ui
  device.draw( uiRender );
  device.endPaint();

  device.present();
  }

Запущенный пример выглядит следующим образом:

image

Полный исходный код примера

Исходники движка на сайте github
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.