World Conquest Chronicles

World Conquest Chronicles

Anna Graphics, v1.0

Скриншот тестовой сцены

(CC BY-SA 4.0, Copyright © 2015 thewizardplusplus)

Трёхмерный графический движок.

Мажорный релиз.

Возможности

  • отображение статических мешей;
  • трансформации мешей (перемещение, вращение, масштабирование);
  • управление отображением мешей (вкл./выкл.);
  • шейдинг:
    • текстурирование:
      • текстурирование мешей;
      • дефолтная текстура (шахматы) для мешей без текстуры;
      • альфа-тест;
      • альфа-канал;
      • загрузчик текстур из формата BMP32 (с поддержкой альфа-канала);
    • эмбиентное освещение:
      • эмбиентное освещение;
      • управление подверженностью мешей эмбиентному освещению;
    • туман:
      • туман;
      • управление подверженностью мешей туману;
  • анимация:
    • анимация субмешей;
    • анимация трансформаций (перемещения, вращения, масштабирования);
    • хранение анимации в отдельной сущности — треке;
  • камера:
    • перспективная камера;
    • ортогональная камера;
    • трансформации камеры (перемещение, вращение, параметры фрустума — FOV, near-плоскость, far-плоскость);
  • встроенные объекты:
    • плейн;
    • куб;
    • скайбокс;
  • загрузка мешей:
    • загрузка мешей из файла своего формата;
    • экспортер мешей в свой формат для пакета Blender;
  • кроссплатформенное окно:
    • кроссплатформенное окно в полноэкранном режиме;
    • автоматическое определение разрешения экрана;
    • определение нажатых на клавиатуре клавиш;
    • определение нажатых на мыши кнопок;
    • определение позиции курсора мыши;
    • управление позицией курсора мыши;
  • используемый GAPI — OpenGL;
  • поддерживаемые ОС:
    • Linux;
    • Windows.

Пример использования

#include <anna/graphics/OpenGlGraphicApi.h>
#include <anna/graphics/FogParameters.h>
#include <anna/graphics/Skybox.h>
#include <pthread.h>
#include <ctime>
#include <iostream>

using namespace thewizardplusplus::anna::graphics;
using namespace thewizardplusplus::anna::maths;

static const std::string DATA_PATH = "data/";
static const std::string TEXTURES_PATH = DATA_PATH + "textures/";
static const std::string MODELS_PATH = DATA_PATH + "models/";
static const float DEFAULT_CAMERA_ANGLE = 6.23;
static const size_t NUMBER_OF_TREES = 75;
static const float CAMERA_ROTATION_RADIUS = 5;
static const float CAMERA_ROTATION_SPEED = 0.5;
static const float ANIMATION_SPEED = 25;

Camera* MakeCamera(World* world);
void SetFog(GraphicApi* gapi);
std::string GetBasePath(const std::string& app_path);
void MakeSkybox(World* world, GraphicApi* gapi, const std::string& base_path);
void MakeGround(World* world, GraphicApi* gapi, const std::string& base_path);
float GetRandomNumber(float minimum, float maximum);
void MakeForest(World* world, GraphicApi* gapi, const std::string& base_path);
void MakeWindmill(World* world, GraphicApi* gapi, const std::string& base_path);
float GetCurrentTime(void);
void RotateCamera(Camera* camera, float current_time);
void UpdateWorld(World* world, float elapsed_time);
void DrawWorld(World* world, GraphicApi* gapi, Window* window);

int main(int number_of_arguments, char* arguments[]) try {
    (void)number_of_arguments;

    World* world = new World();
    Camera* camera = MakeCamera(world);

    GraphicApi* gapi = GraphicApi::create<OpenGlGraphicApi>();
    SetFog(gapi);

    std::string base_path = GetBasePath(arguments[0]);
    MakeSkybox(world, gapi, base_path);
    MakeGround(world, gapi, base_path);
    MakeForest(world, gapi, base_path);
    MakeWindmill(world, gapi, base_path);

    Window* window = gapi->getWindow();
    float last_time = GetCurrentTime();
    float camera_angle = DEFAULT_CAMERA_ANGLE;
    while (!window->isPressedKey(KeyCode::KEY_ESCAPE)) {
        float current_time = GetCurrentTime();
        float elapsed_time = current_time - last_time;
        last_time = current_time;

        if (window->isPressedKey(KeyCode::KEY_LEFT)) {
            camera_angle += elapsed_time;
        } else if (window->isPressedKey(KeyCode::KEY_RIGHT)) {
            camera_angle += -elapsed_time;
        }
        RotateCamera(camera, camera_angle);

        UpdateWorld(world, elapsed_time);
        DrawWorld(world, gapi, window);
    }

    delete gapi;
    delete camera;
    delete world;
} catch (const std::exception& exception) {
    std::cerr << exception.what() << std::endl;
}

Camera* MakeCamera(World* world) {
    Camera* camera = new Camera();
    camera->setPosition(0, 0, 2);
    camera->setRotation(15, 0, 0);

    world->setCamera(camera);

    return camera;
}

void SetFog(GraphicApi* gapi) {
    FogParameters fog_parameters(Vector3D<float>(0, 0, 0), 1, 1, 7.5);
    gapi->setFogParameters(fog_parameters);

    gapi->setFogMode(true);
}

std::string GetBasePath(const std::string& app_path) {
    std::string base_path;
    size_t separator_index = app_path.rfind('/');
    if (separator_index != std::string::npos) {
        base_path = app_path.substr(0, separator_index + 1);
    }

    return base_path;
}

void MakeSkybox(World* world, GraphicApi* gapi, const std::string& base_path) {
    Object* skybox = new Skybox(
        gapi->createTexture(
            base_path
            + TEXTURES_PATH
            + "skybox_axis_z_positive_direction.bmp"
        ),
        gapi->createTexture(
            base_path
            + TEXTURES_PATH
            + "skybox_axis_z_negative_direction.bmp"
        ),
        gapi->createTexture(
            base_path
            + TEXTURES_PATH
            + "skybox_axis_x_positive_direction.bmp"
        ),
        gapi->createTexture(
            base_path
            + TEXTURES_PATH
            + "skybox_axis_y_positive_direction.bmp"
        ),
        gapi->createTexture(
            base_path
            + TEXTURES_PATH
            + "skybox_axis_x_negative_direction.bmp"
        ),
        gapi->createTexture(
            base_path
            + TEXTURES_PATH
            + "skybox_axis_y_negative_direction.bmp"
        )
    );
    skybox->setScale(1000, 1000, 1000);

    world->addObject(skybox);
}

void MakeGround(World* world, GraphicApi* gapi, const std::string& base_path) {
    AnimateObject* ground = AnimateObject::load(
        base_path
            + MODELS_PATH
            + "ground.ao",
        gapi,
        false
    );
    ground->setScale(10, 10, 10);

    world->addObject(ground);
}

void MakeForest(World* world, GraphicApi* gapi, const std::string& base_path) {
    for (size_t i = 0; i < NUMBER_OF_TREES; i++) {
        AnimateObject* tree = AnimateObject::load(
            base_path
                + MODELS_PATH
                + "tree.ao",
            gapi,
            false
        );
        tree->setScale(0.1, 0.1, 0.1);

        float angle = GetRandomNumber(0, 360);
        float radius = GetRandomNumber(1.25, 7.5);
        float x = radius * std::cos(Maths::toRadians(angle));
        float y = radius * std::sin(Maths::toRadians(angle));
        tree->setPosition(x, y, -0.25);

        world->addObject(tree);
    }
}

float GetRandomNumber(float minimum, float maximum) {
    static bool prng_initialized = false;
    if (!prng_initialized) {
        std::srand(std::clock());
        prng_initialized = true;
    }

    return
        static_cast<float>(std::rand()) / RAND_MAX
        * (maximum - minimum)
        + minimum;
}

void MakeWindmill(
    World* world,
    GraphicApi* gapi,
    const std::string& base_path
) {
    AnimateObject* windmill = AnimateObject::load(
        base_path
            + MODELS_PATH
            + "windmill.ao",
        gapi
    );
    windmill->setPosition(0, 0, -0.2);
    windmill->setRotation(0, 0, -120);
    windmill->setScale(0.25, 0.25, 0.25);
    windmill->play(true);

    world->addAnimateObject(windmill);
}

float GetCurrentTime(void) {
    return static_cast<float>(std::clock()) / CLOCKS_PER_SEC;
}

void RotateCamera(Camera* camera, float angle) {
    float x =
        CAMERA_ROTATION_RADIUS
        * std::cos(CAMERA_ROTATION_SPEED * angle);
    float y =
        CAMERA_ROTATION_RADIUS
        * std::sin(CAMERA_ROTATION_SPEED * angle);
    camera->setPosition(x, y, camera->getPosition().z);
    camera->setRotation(
        camera->getRotation().x,
        camera->getRotation().y,
        Maths::toDegrees(std::atan2(y, x)) - 90
    );
}

void UpdateWorld(World* world, float elapsed_time) {
    world->update(1000 * ANIMATION_SPEED * elapsed_time);
}

void DrawWorld(World* world, GraphicApi* gapi, Window* window) {
    gapi->clear();
    gapi->drawWorld(world);
    window->update();
}

Репозиторий

Ссылка: https://github.com/thewizardplusplus/anna-graphics/tree/v1.0.

Содержание: код, документация, пример использования, экспортер для пакета Blender.

Лицензия: MIT.

Авторские права

Модель дерева

Автор: Christian Nietsche.

Лицензия: CC0 1.0.

Ссылка: http://www.blendswap.com/blends/view/6855.

Модель ветряной мельницы

Автор: ahedov.

Лицензия: CC BY-SA 3.0.

Ссылка: http://www.blendswap.com/blends/view/69174.

Текстура земли

Автор: Dim.

Лицензия: CC BY 3.0.

Ссылка: https://opengameart.org/node/8143.

Текстуры скайбокса

Сгенерированы при помощи инструмента Night sky skybox generator.

Автор инструмента: MysticMike.

Лицензия на инструмент: GPL-2.0-or-later.

Ссылка на инструмент: http://opengameart.org/content/night-sky-skybox-generator.