This is a huge change, and basically breaks everything (as per usual!) First of, this includes stuff like shaders so anything involving those are broken and then fixed. A new BuildAssets cmake file is added to aid in running AssetCompiler, and it seems to work fine on the engine base assets. The File API will eventually be revamped to handle this new way of organizing the files and domains will eventually be gotten rid of all together since I probably will replace it with game directory priorities. As it stands right now, there isn't a way to easily replace say - render_options.cfg with your own game-specific version. Apple builds are probably broken by this commit (since I'm moving around content and shader directories) to be fixed later.
580 lines
No EOL
16 KiB
C++
580 lines
No EOL
16 KiB
C++
#include <@APP_INCLUDE@>
|
|
#include <engine.hpp>
|
|
#include <chrono>
|
|
|
|
#include "platform.hpp"
|
|
#include <string_utils.hpp>
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_vulkan.h>
|
|
|
|
#ifdef ENABLE_DX12
|
|
#include "gfx_dx12.hpp"
|
|
#endif
|
|
|
|
#ifdef ENABLE_VULKAN
|
|
#include "gfx_vulkan.hpp"
|
|
#endif
|
|
|
|
#ifdef ENABLE_METAL
|
|
#include "gfx_metal.hpp"
|
|
#endif
|
|
|
|
#include "gfx_dummy.hpp"
|
|
|
|
#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__)
|
|
#include <winrt/Windows.UI.ViewManagement.h>
|
|
#pragma comment(lib, "windowsapp")
|
|
#endif
|
|
|
|
@APP_CLASS@* app = nullptr;
|
|
GFX* gfx_interface = nullptr;
|
|
|
|
std::vector<SDL_Window*> windows;
|
|
std::map<SDL_Window*, SDL_Renderer*> renderers;
|
|
SDL_Window* main_window = nullptr;
|
|
|
|
extern "C" SDL_Window* get_window(const platform::window_ptr index) {
|
|
for(auto& window : windows) {
|
|
if(window == index)
|
|
return window;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SDL_Window* get_window_by_sdl_id(const Uint32 id) {
|
|
for(auto& window : windows) {
|
|
if(SDL_GetWindowID(window) == id)
|
|
return window;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#ifdef ENABLE_METAL
|
|
void* create_metal_surface(platform::window_ptr window, void* surface_creation_info);
|
|
void* get_next_metal_drawable(platform::window_ptr window);
|
|
#endif
|
|
|
|
#ifdef ENABLE_VULKAN
|
|
void* create_vulkan_surface(platform::window_ptr window, void* surface_creation_info);
|
|
#endif
|
|
|
|
static std::map<InputButton, int> inputToKeyCode = { {
|
|
{InputButton::C, SDL_SCANCODE_C},
|
|
{InputButton::V, SDL_SCANCODE_V},
|
|
{InputButton::X, SDL_SCANCODE_X},
|
|
{InputButton::Y, SDL_SCANCODE_Y},
|
|
{InputButton::Z, SDL_SCANCODE_Z},
|
|
{InputButton::Backspace, SDL_SCANCODE_BACKSPACE},
|
|
{InputButton::Enter, SDL_SCANCODE_RETURN},
|
|
{InputButton::W, SDL_SCANCODE_W},
|
|
{InputButton::A, SDL_SCANCODE_A},
|
|
{InputButton::S, SDL_SCANCODE_S},
|
|
{InputButton::D, SDL_SCANCODE_D},
|
|
{InputButton::Q, SDL_SCANCODE_Q},
|
|
{InputButton::Shift, SDL_SCANCODE_LSHIFT},
|
|
{InputButton::Alt, SDL_SCANCODE_LALT},
|
|
{InputButton::Super, SDL_SCANCODE_APPLICATION},
|
|
{InputButton::Escape, SDL_SCANCODE_ESCAPE},
|
|
{InputButton::Tab, SDL_SCANCODE_TAB},
|
|
{InputButton::Ctrl, SDL_SCANCODE_LCTRL},
|
|
{InputButton::Space, SDL_SCANCODE_SPACE},
|
|
{InputButton::LeftArrow, SDL_SCANCODE_LEFT},
|
|
{InputButton::RightArrow, SDL_SCANCODE_RIGHT}
|
|
}};
|
|
|
|
/*
|
|
* Platform functions"
|
|
*/
|
|
|
|
const char* platform::get_name() {
|
|
return SDL_GetPlatform();
|
|
}
|
|
|
|
bool platform::supports_feature(const PlatformFeature feature) {
|
|
if(feature == PlatformFeature::Windowing)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
platform::window_ptr platform::open_window(const std::string_view title, const prism::Rectangle rect, const WindowFlags flags) {
|
|
auto& win = windows.emplace_back();
|
|
|
|
int sdl_flags = SDL_WINDOW_ALLOW_HIGHDPI;
|
|
if(gfx_interface->required_context() == GFXContext::Vulkan)
|
|
sdl_flags |= SDL_WINDOW_VULKAN;
|
|
|
|
if(flags & WindowFlags::Borderless)
|
|
sdl_flags |= SDL_WINDOW_BORDERLESS;
|
|
|
|
if(flags & WindowFlags::Resizable)
|
|
sdl_flags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
if(flags & WindowFlags::Hidden)
|
|
sdl_flags |= SDL_WINDOW_HIDDEN;
|
|
|
|
auto resolution = platform::get_monitor_resolution();
|
|
|
|
int real_x = rect.offset.x;
|
|
int real_y = rect.offset.y;
|
|
|
|
if(rect.offset.x <= -1 || rect.offset.x > resolution.extent.width)
|
|
real_x = SDL_WINDOWPOS_CENTERED;
|
|
|
|
if(rect.offset.y <= -1 || rect.offset.x > resolution.extent.height)
|
|
real_y = SDL_WINDOWPOS_CENTERED;
|
|
|
|
int real_width = rect.extent.width;
|
|
int real_height = rect.extent.height;
|
|
|
|
if(rect.extent.width <= -1 || rect.extent.width > resolution.extent.width)
|
|
real_width = 640;
|
|
|
|
if(rect.extent.height <= -1 || rect.extent.height > resolution.extent.height)
|
|
real_height = 480;
|
|
|
|
win = SDL_CreateWindow(title.data(), real_x, real_y, real_width, real_height, sdl_flags);
|
|
|
|
if(windows.size() == 1)
|
|
main_window = win;
|
|
|
|
#ifdef ENABLE_METAL
|
|
if(gfx_interface->required_context() == GFXContext::Metal) {
|
|
SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
|
renderers[win] = renderer;
|
|
}
|
|
#endif
|
|
|
|
engine->add_window((void*)win, win, {static_cast<uint32_t>(real_width), static_cast<uint32_t>(real_height)});
|
|
app->initialize_render();
|
|
|
|
return win;
|
|
}
|
|
|
|
bool platform::is_main_window(platform::window_ptr index) {
|
|
return index == main_window;
|
|
}
|
|
|
|
void platform::close_window(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
|
|
engine->remove_window(index);
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
utility::erase(windows, window);
|
|
}
|
|
|
|
void platform::force_quit() {
|
|
SDL_Quit();
|
|
}
|
|
|
|
float platform::get_monitor_dpi() {
|
|
float dpi = 1.0f;
|
|
if (!SDL_GetDisplayDPI(0, &dpi, nullptr, nullptr))
|
|
dpi = dpi / 96.0f;
|
|
|
|
return dpi;
|
|
}
|
|
|
|
prism::Rectangle platform::get_monitor_resolution() {
|
|
SDL_Rect r;
|
|
SDL_GetDisplayBounds(0, &r);
|
|
|
|
return {r.x, r.y, static_cast<uint32_t>(r.w), static_cast<uint32_t>(r.h)};
|
|
}
|
|
|
|
prism::Rectangle platform::get_monitor_work_area() {
|
|
SDL_Rect r = {};
|
|
SDL_GetDisplayUsableBounds(0, &r);
|
|
|
|
return {r.x, r.y, static_cast<uint32_t>(r.w), static_cast<uint32_t>(r.h)};
|
|
}
|
|
|
|
prism::Offset platform::get_window_position(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
|
|
int x, y;
|
|
SDL_GetWindowPosition(window, &x, &y);
|
|
|
|
return {(int32_t)x, (int32_t)y};
|
|
}
|
|
|
|
prism::Extent platform::get_window_size(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
|
|
int width, height;
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
|
|
return {(uint32_t)width, (uint32_t)height};
|
|
}
|
|
|
|
prism::Extent platform::get_window_drawable_size(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
|
|
int width, height;
|
|
SDL_Vulkan_GetDrawableSize(window, &width, &height);
|
|
|
|
return {(uint32_t)width, (uint32_t)height};
|
|
}
|
|
|
|
bool platform::is_window_focused(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
return (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0;
|
|
}
|
|
|
|
void platform::set_window_focused(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
SDL_RaiseWindow(window);
|
|
}
|
|
|
|
void platform::set_window_position(const platform::window_ptr index, const prism::Offset offset) {
|
|
auto window = get_window(index);
|
|
|
|
SDL_SetWindowPosition(window, offset.x, offset.y);
|
|
}
|
|
|
|
void platform::set_window_size(const platform::window_ptr index, const prism::Extent extent) {
|
|
auto window = get_window(index);
|
|
|
|
SDL_SetWindowSize(window, extent.width, extent.height);
|
|
}
|
|
|
|
void platform::set_window_title(const platform::window_ptr index, const std::string_view title) {
|
|
auto window = get_window(index);
|
|
|
|
SDL_SetWindowTitle(window, title.data());
|
|
}
|
|
|
|
void platform::show_window(const platform::window_ptr index) {
|
|
auto window = get_window(index);
|
|
|
|
SDL_ShowWindow(window);
|
|
}
|
|
|
|
bool platform::get_key_down(const InputButton key) {
|
|
const Uint8 *state = SDL_GetKeyboardState(NULL);
|
|
|
|
return state[inputToKeyCode[key]] && state[SDL_SCANCODE_DOWN];
|
|
}
|
|
|
|
int platform::get_keycode(const InputButton key) {
|
|
return inputToKeyCode[key];
|
|
}
|
|
|
|
prism::Offset platform::get_cursor_position() {
|
|
int x, y;
|
|
SDL_GetMouseState(&x, &y);
|
|
|
|
return {(int32_t)x, (int32_t)y};
|
|
}
|
|
|
|
prism::Offset platform::get_screen_cursor_position() {
|
|
int x, y;
|
|
SDL_GetGlobalMouseState(&x, &y);
|
|
|
|
return {(int32_t)x, (int32_t)y};
|
|
}
|
|
|
|
bool platform::get_mouse_button_down(const int button) {
|
|
Uint8 sdl_button = SDL_BUTTON_LEFT;
|
|
switch(button) {
|
|
case 0:
|
|
sdl_button = SDL_BUTTON_LEFT;
|
|
break;
|
|
case 1:
|
|
sdl_button = SDL_BUTTON_RIGHT;
|
|
break;
|
|
case 2:
|
|
sdl_button = SDL_BUTTON_MIDDLE;
|
|
break;
|
|
}
|
|
|
|
return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(sdl_button)) != 0;
|
|
}
|
|
|
|
float mouse_wheel_x, mouse_wheel_y;
|
|
|
|
std::tuple<float, float> platform::get_wheel_delta() {
|
|
return {mouse_wheel_x, mouse_wheel_y};
|
|
}
|
|
|
|
std::tuple<float, float> platform::get_right_stick_position() {
|
|
return {0.0f, 0.0f};
|
|
}
|
|
|
|
std::tuple<float, float> platform::get_left_stick_position() {
|
|
return {0.0f, 0.0f};
|
|
}
|
|
|
|
void platform::capture_mouse(const bool capture) {
|
|
SDL_SetRelativeMouseMode((SDL_bool)capture);
|
|
}
|
|
|
|
void platform::begin_text_input() {
|
|
SDL_StartTextInput();
|
|
}
|
|
|
|
void platform::end_text_input() {
|
|
SDL_StopTextInput();
|
|
}
|
|
|
|
bool platform::supports_context(GFXContext context) {
|
|
#ifdef ENABLE_DX12
|
|
if(context == GFXContext::DirectX)
|
|
return true;
|
|
#endif
|
|
|
|
#ifdef ENABLE_VULKAN
|
|
if(context == GFXContext::Vulkan)
|
|
return true;
|
|
#endif
|
|
|
|
#ifdef ENABLE_METAL
|
|
if(context == GFXContext::Metal)
|
|
return true;
|
|
#endif
|
|
|
|
if(context == GFXContext::None)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void platform::initialize_context(const GFXContext context) {
|
|
#ifdef ENABLE_METAL
|
|
if(context == GFXContext::Metal) {
|
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_VULKAN
|
|
if(context == GFXContext::Vulkan) {
|
|
SDL_Vulkan_LoadLibrary(nullptr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void* platform::get_context_information() {
|
|
#ifdef ENABLE_VULKAN
|
|
if(gfx_interface->required_context() == GFXContext::Vulkan) {
|
|
unsigned int count = 0;
|
|
SDL_Vulkan_GetInstanceExtensions(nullptr, &count, nullptr);
|
|
|
|
std::vector<const char*> extensions(count);
|
|
SDL_Vulkan_GetInstanceExtensions(nullptr, &count, extensions.data());
|
|
|
|
auto info = new vulkan_information();
|
|
info->surface_extensions = extensions;
|
|
|
|
return (void*)info;
|
|
}
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void* platform::create_surface(window_ptr window, void* surface_creation_info) {
|
|
#ifdef ENABLE_VULKAN
|
|
if(gfx_interface->required_context() == GFXContext::Vulkan) {
|
|
return create_vulkan_surface(window, surface_creation_info);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_METAL
|
|
if(gfx_interface->required_context() == GFXContext::Metal) {
|
|
return create_metal_surface(window, surface_creation_info);
|
|
}
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void* platform::get_next_image(window_ptr window) {
|
|
#ifdef ENABLE_METAL
|
|
if(gfx_interface->required_context() == GFXContext::Metal) {
|
|
return get_next_metal_drawable(window);
|
|
}
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
template<class GFXBackend>
|
|
void try_initialize() {
|
|
if(gfx_interface == nullptr) {
|
|
auto backend = new GFXBackend();
|
|
const bool supported = backend->is_supported() && platform::supports_context(backend->required_context());
|
|
if(!supported) {
|
|
prism::log("Failed to initialize GFX backend... trying next...");
|
|
} else {
|
|
gfx_interface = backend;
|
|
platform::initialize_context(gfx_interface->required_context());
|
|
}
|
|
}
|
|
}
|
|
|
|
struct gfx_backend_initializer {
|
|
gfx_backend_initializer(std::string_view n, std::function<void()> f) : name(n), init_func(f) {}
|
|
|
|
std::string_view name;
|
|
std::function<void()> init_func;
|
|
};
|
|
|
|
std::vector<gfx_backend_initializer> gfx_backend_order;
|
|
|
|
int main(int argc, char* argv[]) {
|
|
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
|
|
|
engine = new prism::engine(argc, argv);
|
|
|
|
// determine gfx context at the beginning
|
|
#ifdef ENABLE_DX12
|
|
gfx_backend_order.emplace_back(gfx_backend_initializer{"dx12", []{
|
|
try_initialize<gfx_dx12>();
|
|
}});
|
|
#endif
|
|
|
|
#ifdef ENABLE_METAL
|
|
gfx_backend_order.emplace_back(gfx_backend_initializer{"metal", []{
|
|
try_initialize<GFXMetal>();
|
|
}});
|
|
#endif
|
|
|
|
#ifdef ENABLE_VULKAN
|
|
gfx_backend_order.emplace_back(gfx_backend_initializer{"vulkan", []{
|
|
try_initialize<GFXVulkan>();
|
|
}});
|
|
#endif
|
|
|
|
gfx_backend_order.emplace_back(gfx_backend_initializer{"dummy", []{
|
|
try_initialize<GFXDummy>();
|
|
}});
|
|
|
|
for(auto arg : engine->command_line_arguments) {
|
|
utility::move_to_front(gfx_backend_order, [arg](gfx_backend_initializer& init) {
|
|
return arg == "-" + std::string(init.name);
|
|
});
|
|
}
|
|
|
|
for(auto backend : gfx_backend_order) {
|
|
backend.init_func();
|
|
}
|
|
|
|
GFXCreateInfo info = {};
|
|
if(gfx_interface->initialize(info)) {
|
|
engine->set_gfx(gfx_interface);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
prism::set_domain_path(prism::domain::game, "{resource_dir}/game");
|
|
prism::set_domain_path(prism::domain::base, "{resource_dir}/base");
|
|
|
|
app = new @APP_CLASS@();
|
|
engine->set_app(app);
|
|
|
|
app_main(engine);
|
|
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
|
|
while(!engine->is_quitting()) {
|
|
SDL_Event event = {};
|
|
while(SDL_PollEvent(&event)) {
|
|
switch(event.type) {
|
|
case SDL_QUIT:
|
|
engine->quit();
|
|
break;
|
|
case SDL_MOUSEWHEEL: {
|
|
mouse_wheel_x = event.wheel.x;
|
|
mouse_wheel_y = event.wheel.y;
|
|
}
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
{
|
|
int engine_button = 0;
|
|
if(event.button.button == SDL_BUTTON_RIGHT)
|
|
engine_button = 1;
|
|
else if(event.button.button == SDL_BUTTON_MIDDLE)
|
|
engine_button = 2;
|
|
|
|
engine->process_mouse_down(engine_button, {0, 0});
|
|
}
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
{
|
|
engine->process_key_down(event.key.keysym.scancode);
|
|
}
|
|
break;
|
|
case SDL_KEYUP:
|
|
{
|
|
engine->process_key_up(event.key.keysym.scancode);
|
|
}
|
|
break;
|
|
case SDL_WINDOWEVENT:
|
|
{
|
|
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
auto window = get_window_by_sdl_id(event.window.windowID);
|
|
if(window != nullptr)
|
|
engine->resize(window, {static_cast<uint32_t>(event.window.data1), static_cast<uint32_t>(event.window.data2)});
|
|
} else if(event.window.event == SDL_WINDOWEVENT_MOVED) {
|
|
auto window = get_window_by_sdl_id(event.window.windowID);
|
|
if(window != nullptr)
|
|
engine->move(window);
|
|
} else if(event.window.event == SDL_WINDOWEVENT_CLOSE) {
|
|
engine->quit();
|
|
}
|
|
}
|
|
break;
|
|
case SDL_TEXTINPUT:
|
|
{
|
|
engine->process_text_input(event.text.text);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(engine->is_quitting())
|
|
break;
|
|
|
|
auto begin = std::chrono::high_resolution_clock::now();
|
|
|
|
float deltatime = (float)std::chrono::duration_cast<std::chrono::nanoseconds>(begin - end).count() / 1000000000ULL;
|
|
end = begin;
|
|
|
|
engine->update(deltatime);
|
|
engine->begin_frame(deltatime);
|
|
|
|
for(auto window : windows)
|
|
engine->render(window);
|
|
|
|
engine->end_frame();
|
|
}
|
|
|
|
engine->prepare_quit();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__)
|
|
PlatformTheme platform::get_theme() {
|
|
using namespace winrt::Windows::UI::ViewManagement;
|
|
|
|
// TODO: figure out if this works pre-anniversary update/other windows other than 10
|
|
UISettings settings;
|
|
auto background = settings.GetColorValue(UIColorType::Background);
|
|
auto foreground = settings.GetColorValue(UIColorType::Foreground);
|
|
|
|
if (background == winrt::Windows::UI::Colors::White())
|
|
return PlatformTheme::Light;
|
|
else
|
|
return PlatformTheme::Dark;
|
|
}
|
|
#else
|
|
PlatformTheme platform::get_theme() {
|
|
return PlatformTheme::Light;
|
|
}
|
|
#endif |