diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bc5ed2..00b9acf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.7) - option(VERSION_STRING "Project version string" "0.0.0") project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) diff --git a/src/application.cpp b/src/application.cpp index 34a7ad6..01d61f1 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -149,6 +149,18 @@ application::application(): // Update window size and viewport size SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]); SDL_GL_GetDrawableSize(sdl_window, &viewport_dimensions[0], &viewport_dimensions[1]); + + // Make OpenGL context current + logger->push_task("Making OpenGL context current"); + if (SDL_GL_MakeCurrent(sdl_window, sdl_gl_context) != 0) + { + logger->pop_task(EXIT_FAILURE); + throw std::runtime_error("Failed to make OpenGL context current"); + } + else + { + logger->pop_task(EXIT_SUCCESS); + } // Load OpenGL functions via GLAD logger->push_task("Loading OpenGL functions"); @@ -184,20 +196,6 @@ application::application(): { logger->pop_task(EXIT_SUCCESS); } - - // Load SDL gamepad mappings - /* - logger->push_task("Loading SDL gamepad mappings from database"); - std::string gamecontrollerdb_path = data_path + "controls/gamecontrollerdb.txt"; - if (SDL_GameControllerAddMappingsFromFile(gamecontrollerdb_path.c_str()) == -1) - { - logger->pop_task(EXIT_FAILURE); - } - else - { - logger->pop_task(EXIT_SUCCESS); - } - */ // Setup rasterizer rasterizer = new gl::rasterizer(); @@ -245,26 +243,12 @@ void application::close(int status) exit_status = status; } -int application::execute(bootloader_type bootloader) +int application::execute(const application::state& initial_state) { try { - // Execute bootloader - if (bootloader) - { - exit_status = bootloader(this); - if (exit_status != EXIT_SUCCESS) - { - return exit_status; - } - } - - // Show window - SDL_ShowWindow(sdl_window); - - // Clear window - rasterizer->clear_framebuffer(true, false, false); - SDL_GL_SwapWindow(sdl_window); + // Enter initial application state + change_state(initial_state); // Perform initial update update(0.0, 0.0); @@ -494,6 +478,31 @@ void application::swap_buffers() SDL_GL_SwapWindow(sdl_window); } +void application::show_window() +{ + SDL_ShowWindow(sdl_window); +} + +void application::hide_window() +{ + SDL_HideWindow(sdl_window); +} + +void application::add_game_controller_mappings(const void* mappings, std::size_t size) +{ + logger->push_task("Adding SDL game controller mappings"); + int mapping_count = SDL_GameControllerAddMappingsFromRW(SDL_RWFromConstMem(mappings, size), 0); + if (mapping_count == -1) + { + logger->pop_task(EXIT_FAILURE); + } + else + { + logger->log("Added " + std::to_string(mapping_count) + " SDL game controller mappings"); + logger->pop_task(EXIT_SUCCESS); + } +} + void application::update(double t, double dt) { translate_sdl_events(); diff --git a/src/application.hpp b/src/application.hpp index ff7f411..429b58b 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -79,11 +79,11 @@ public: /** * Executes the application, causing it to run the bootloader then enter the execution loop until closed. * - * @param bootloader Function which will be executed immediately before the execution loop is entered. The bootloader will be passed a pointer to this application and should return an exit status code. + * @param initial_state Initial state of the application. * * @return Exit status code. */ - int execute(bootloader_type bootloader); + int execute(const application::state& initial_state); /** * Requests the application's execution loop to cleanly terminate, and specifies its exit status code. @@ -186,6 +186,11 @@ public: void swap_buffers(); + void show_window(); + void hide_window(); + + void add_game_controller_mappings(const void* mappings, std::size_t size); + /// Returns the dimensions of the current display. const std::array& get_display_dimensions() const; diff --git a/src/game/context.hpp b/src/game/context.hpp index f74ec0e..c79658d 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -199,6 +199,8 @@ struct context scene::camera* ui_camera; scene::billboard* splash_billboard; scene::billboard* camera_flash_billboard; + scene::text* title_text; + scene::text* title_version_text; // Surface scene scene::collection* surface_scene; diff --git a/src/game/bootloader.cpp b/src/game/states/boot.cpp similarity index 97% rename from src/game/bootloader.cpp rename to src/game/states/boot.cpp index 1135405..747954c 100644 --- a/src/game/bootloader.cpp +++ b/src/game/states/boot.cpp @@ -17,6 +17,7 @@ * along with Antkeeper source code. If not, see . */ +#include "game/states/boot.hpp" #include "animation/animation.hpp" #include "animation/animator.hpp" #include "animation/ease.hpp" @@ -51,7 +52,7 @@ #include "render/compositor.hpp" #include "render/renderer.hpp" #include "resources/resource-manager.hpp" -#include "resources/resource-manager.hpp" +#include "resources/file-buffer.hpp" #include "scene/scene.hpp" #include "game/states/loading.hpp" #include "entity/systems/behavior.hpp" @@ -93,6 +94,10 @@ #include #include +namespace game { +namespace state { +namespace boot { + static constexpr double seconds_per_day = 24.0 * 60.0 * 60.0; static void parse_options(game::context* ctx, int argc, char** argv); @@ -109,7 +114,7 @@ static void setup_controls(game::context* ctx); static void setup_cli(game::context* ctx); static void setup_callbacks(game::context* ctx); -int bootloader(application* app, int argc, char** argv) +void enter(application* app, int argc, char** argv) { // Get application logger debug::logger* logger = app->get_logger(); @@ -142,7 +147,7 @@ int bootloader(application* app, int argc, char** argv) { logger->error("Caught exception: \"" + std::string(e.what()) + "\""); logger->pop_task(EXIT_FAILURE); - return EXIT_FAILURE; + return; } logger->pop_task(EXIT_SUCCESS); @@ -162,9 +167,15 @@ int bootloader(application* app, int argc, char** argv) // Enter initial application state app->change_state(initial_state); - return EXIT_SUCCESS; + return; +} + +void exit(application* app) +{ + } + void parse_options(game::context* ctx, int argc, char** argv) { debug::logger* logger = ctx->logger; @@ -441,6 +452,12 @@ void setup_window(game::context* ctx) // Set title app->set_title((*ctx->strings)["title"]); + // Show window + ctx->app->get_rasterizer()->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + ctx->app->get_rasterizer()->clear_framebuffer(true, false, false); + app->show_window(); + ctx->app->swap_buffers(); + logger->pop_task(EXIT_SUCCESS); } @@ -919,6 +936,20 @@ void setup_controls(game::context* ctx) // Setup input listener ctx->input_listener = new input::listener(); ctx->input_listener->set_event_dispatcher(event_dispatcher); + + // Load SDL game controller mappings database + ctx->logger->push_task("Loading SDL game controller mappings from database"); + file_buffer* game_controller_db = ctx->resource_manager->load("gamecontrollerdb.txt"); + if (!game_controller_db) + { + ctx->logger->pop_task(EXIT_FAILURE); + } + else + { + ctx->app->add_game_controller_mappings(game_controller_db->data(), game_controller_db->size()); + ctx->resource_manager->unload("gamecontrollerdb.txt"); + ctx->logger->pop_task(EXIT_SUCCESS); + } } void setup_cli(game::context* ctx) @@ -1001,3 +1032,7 @@ void setup_callbacks(game::context* ctx) } ); } + +} // namespace boot +} // namespace state +} // namespace game diff --git a/src/game/states/boot.hpp b/src/game/states/boot.hpp new file mode 100644 index 0000000..038bdbb --- /dev/null +++ b/src/game/states/boot.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_BOOT_HPP +#define ANTKEEPER_GAME_STATE_BOOT_HPP + +#include "application.hpp" + +namespace game { +namespace state { + +/// Boot game state functions. +namespace boot { + +void enter(application* app, int argc, char** argv); +void exit(application* app); + +} // namespace boot + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_BOOT_HPP diff --git a/src/game/states/loading.cpp b/src/game/states/loading.cpp index c7d7485..d90ae86 100644 --- a/src/game/states/loading.cpp +++ b/src/game/states/loading.cpp @@ -323,36 +323,6 @@ void load_fonts(game::context* ctx) { build_bitmap_font(*it->second, 96.0f, charset, ctx->title_font, ctx->title_font_material, bitmap_font_shader); } - - // Create title text - scene::text* title_text = new scene::text(); - title_text->set_material(&ctx->title_font_material); - title_text->set_font(&ctx->title_font); - title_text->set_color({1.0f, 1.0f, 1.0f, 1.0f}); - title_text->set_content((*ctx->strings)["title"]); - ctx->ui_scene->add_object(title_text); - - // Align title text - const auto& title_aabb = static_cast&>(title_text->get_local_bounds()); - float title_w = title_aabb.max_point.x - title_aabb.min_point.x; - float title_h = title_aabb.max_point.y - title_aabb.min_point.y; - title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f), 0.0f}); - - // Create version string text - scene::text* version_text = new scene::text(); - version_text->set_material(&ctx->debug_font_material); - version_text->set_font(&ctx->debug_font); - version_text->set_color({1.0f, 1.0f, 1.0f, 1.0f}); - version_text->set_content(ANTKEEPER_VERSION_STRING); - ctx->ui_scene->add_object(version_text); - - // Align version string - const auto& version_aabb = static_cast&>(version_text->get_local_bounds()); - float version_w = version_aabb.max_point.x - version_aabb.min_point.x; - float version_h = version_aabb.max_point.y - version_aabb.min_point.y; - const float version_padding = 12.0f; - auto viewport = ctx->app->get_viewport_dimensions(); - version_text->set_translation({viewport[0] * 0.5f - version_w - version_padding, -viewport[1] * 0.5f + version_padding, 0.0f}); } void cosmogenesis(game::context* ctx) diff --git a/src/game/states/main-menu.cpp b/src/game/states/main-menu.cpp new file mode 100644 index 0000000..2a5e267 --- /dev/null +++ b/src/game/states/main-menu.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/main-menu.hpp" + +namespace game { +namespace state { +namespace main_menu { + +void enter(game::context* ctx) +{ + +} + +void exit(game::context* ctx) +{ + +} + +} // namespace main_menu +} // namespace state +} // namespace game diff --git a/src/game/states/main-menu.hpp b/src/game/states/main-menu.hpp new file mode 100644 index 0000000..4164422 --- /dev/null +++ b/src/game/states/main-menu.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_MAIN_MENU_HPP +#define ANTKEEPER_GAME_STATE_MAIN_MENU_HPP + +#include "game/context.hpp" + +namespace game { +namespace state { + +/// Main menu screen game state functions. +namespace main_menu { + +void enter(game::context* ctx); +void exit(game::context* ctx); + +} // namespace main_menu + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_MAIN_MENU_HPP diff --git a/src/game/states/splash.cpp b/src/game/states/splash.cpp index bc64f99..9157bf3 100644 --- a/src/game/states/splash.cpp +++ b/src/game/states/splash.cpp @@ -18,7 +18,7 @@ */ #include "game/states/splash.hpp" -#include "game/states/brood.hpp" +#include "game/states/title.hpp" #include "animation/screen-transition.hpp" #include "animation/ease.hpp" #include "animation/timeline.hpp" @@ -51,9 +51,9 @@ void enter(game::context* ctx) auto change_state = [ctx]() { application::state next_state; - next_state.name = "brood"; - next_state.enter = std::bind(game::state::brood::enter, ctx); - next_state.exit = std::bind(game::state::brood::exit, ctx); + next_state.name = "title"; + next_state.enter = std::bind(game::state::title::enter, ctx); + next_state.exit = std::bind(game::state::title::exit, ctx); ctx->app->change_state(next_state); }; @@ -83,9 +83,9 @@ void enter(game::context* ctx) ctx->app->swap_buffers(); application::state next_state; - next_state.name = "brood"; - next_state.enter = std::bind(game::state::brood::enter, ctx); - next_state.exit = std::bind(game::state::brood::exit, ctx); + next_state.name = "title"; + next_state.enter = std::bind(game::state::title::enter, ctx); + next_state.exit = std::bind(game::state::title::exit, ctx); ctx->app->change_state(next_state); } diff --git a/src/game/states/states.hpp b/src/game/states/states.hpp index cdbd1ce..62f785b 100644 --- a/src/game/states/states.hpp +++ b/src/game/states/states.hpp @@ -27,7 +27,10 @@ namespace state {} } // namespace game +#include "game/states/boot.hpp" #include "game/states/loading.hpp" #include "game/states/splash.hpp" +#include "game/states/title.hpp" +#include "game/states/main-menu.hpp" #endif // ANTKEEPER_GAME_STATES_HPP diff --git a/src/game/states/title.cpp b/src/game/states/title.cpp new file mode 100644 index 0000000..d594159 --- /dev/null +++ b/src/game/states/title.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/title.hpp" +#include "game/states/main-menu.hpp" +#include "animation/screen-transition.hpp" +#include "animation/ease.hpp" +#include "animation/timeline.hpp" +#include "application.hpp" +#include "scene/text.hpp" +#include "configuration.hpp" + +namespace game { +namespace state { +namespace title { + +void enter(game::context* ctx) +{ + // Setup timing + const float title_fade_in_duration = 0.5f; + const float title_fade_out_duration = 0.5f; + + // Start fade in + ctx->fade_transition->transition(title_fade_in_duration, true, ease::in_quad); + + // Crate fade out function + auto fade_out = [ctx, title_fade_out_duration]() + { + ctx->fade_transition->transition(title_fade_out_duration, false, ease::out_quad); + }; + + // Create change state function + auto change_state = [ctx]() + { + application::state next_state; + next_state.name = "main_menu"; + next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.exit = std::bind(game::state::main_menu::exit, ctx); + + ctx->app->change_state(next_state); + }; + + // Set up title skipper + ctx->input_listener->set_callback + ( + [ctx](const event_base& event) + { + auto id = event.get_event_type_id(); + if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id) + { + ctx->timeline->clear(); + ctx->fade_transition->get_animation()->stop(); + ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + ctx->rasterizer->clear_framebuffer(true, false, false); + ctx->app->swap_buffers(); + + application::state next_state; + next_state.name = "main_menu"; + next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.exit = std::bind(game::state::main_menu::exit, ctx); + + ctx->app->change_state(next_state); + } + } + ); + ctx->input_listener->set_enabled(true); + + // Construct title text + ctx->title_text = new scene::text(); + ctx->title_text->set_material(&ctx->title_font_material); + ctx->title_text->set_font(&ctx->title_font); + ctx->title_text->set_color({1.0f, 1.0f, 1.0f, 1.0f}); + ctx->title_text->set_content((*ctx->strings)["title"]); + ctx->ui_scene->add_object(ctx->title_text); + + // Construct version string text + ctx->title_version_text = new scene::text(); + ctx->title_version_text->set_material(&ctx->debug_font_material); + ctx->title_version_text->set_font(&ctx->debug_font); + ctx->title_version_text->set_color({1.0f, 1.0f, 1.0f, 1.0f}); + ctx->title_version_text->set_content(ANTKEEPER_VERSION_STRING); + ctx->ui_scene->add_object(ctx->title_version_text); + + // Align title text + const auto& title_aabb = static_cast&>(ctx->title_text->get_local_bounds()); + float title_w = title_aabb.max_point.x - title_aabb.min_point.x; + float title_h = title_aabb.max_point.y - title_aabb.min_point.y; + ctx->title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f), 0.0f}); + + // Align version string + const auto& version_aabb = static_cast&>(ctx->title_version_text->get_local_bounds()); + float version_w = version_aabb.max_point.x - version_aabb.min_point.x; + float version_h = version_aabb.max_point.y - version_aabb.min_point.y; + const float version_padding = 12.0f; + auto viewport = ctx->app->get_viewport_dimensions(); + ctx->title_version_text->set_translation({viewport[0] * 0.5f - version_w - version_padding, -viewport[1] * 0.5f + version_padding, 0.0f}); +} + +void exit(game::context* ctx) +{ + // Remove and destruct title and version text + ctx->ui_scene->remove_object(ctx->title_text); + ctx->ui_scene->remove_object(ctx->title_version_text); + delete ctx->title_text; + delete ctx->title_version_text; + ctx->title_text = nullptr; + ctx->title_version_text = nullptr; + + // Disable title skipper + ctx->input_listener->set_enabled(false); + ctx->input_listener->set_callback(nullptr); +} + +} // namespace title +} // namespace state +} // namespace game diff --git a/src/game/states/title.hpp b/src/game/states/title.hpp new file mode 100644 index 0000000..90acecf --- /dev/null +++ b/src/game/states/title.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_TITLE_HPP +#define ANTKEEPER_GAME_STATE_TITLE_HPP + +#include "game/context.hpp" + +namespace game { +namespace state { + +/// Title screen game state functions. +namespace title { + +void enter(game::context* ctx); +void exit(game::context* ctx); + +} // namespace title + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_TITLE_HPP diff --git a/src/main.cpp b/src/main.cpp index 65d0c15..5eacd0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,18 +18,26 @@ */ #include "application.hpp" -#include "game/bootloader.hpp" +#include "game/states/boot.hpp" #include #include #include int main(int argc, char* argv[]) { - auto wrapped_bootloader = std::bind(bootloader, std::placeholders::_1, argc, argv); - try { - return application().execute(wrapped_bootloader); + // Construct application + application app; + + // Setup initial application state + application::state boot_state; + boot_state.name = "boot"; + boot_state.enter = std::bind(game::state::boot::enter, &app, argc, argv); + boot_state.exit = std::bind(game::state::boot::exit, &app); + + // Execute application then return the exit status code + return app.execute(boot_state); } catch (const std::exception& e) { diff --git a/src/resources/file-buffer-loader.cpp b/src/resources/file-buffer-loader.cpp new file mode 100644 index 0000000..1989e41 --- /dev/null +++ b/src/resources/file-buffer-loader.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "resources/resource-loader.hpp" +#include "resources/file-buffer.hpp" +#include + +template <> +file_buffer* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) +{ + file_buffer* buffer = new file_buffer(); + + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + buffer->resize(size); + PHYSFS_readBytes(file, buffer->data(), size); + + return buffer; +} diff --git a/src/game/bootloader.hpp b/src/resources/file-buffer.hpp similarity index 83% rename from src/game/bootloader.hpp rename to src/resources/file-buffer.hpp index ebfb6c1..c19a9c7 100644 --- a/src/game/bootloader.hpp +++ b/src/resources/file-buffer.hpp @@ -17,9 +17,12 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_BOOTLOADER_HPP -#define ANTKEEPER_BOOTLOADER_HPP +#ifndef FILE_BUFFER_HPP +#define FILE_BUFFER_HPP -int bootloader(application* app, int argc, char** argv); +#include +#include -#endif // ANTKEEPER_BOOTLOADER_HPP +typedef std::vector file_buffer; + +#endif // FILE_BUFFER_HPP diff --git a/src/scene/scene.hpp b/src/scene/scene.hpp index 97aeb4a..c37261e 100644 --- a/src/scene/scene.hpp +++ b/src/scene/scene.hpp @@ -34,5 +34,6 @@ namespace scene {} #include "object.hpp" #include "point-light.hpp" #include "spot-light.hpp" +#include "text.hpp" #endif // ANTKEEPER_SCENE_HPP