This is laying some groundwork for a much better way of determining the best GFX api to use at runtime, and making it easier to support more GFX backends in the future.
519 lines
No EOL
14 KiB
C++
519 lines
No EOL
14 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_VULKAN
|
|
#include "gfx_vulkan.hpp"
|
|
#endif
|
|
|
|
#ifdef ENABLE_METAL
|
|
#include "gfx_metal.hpp"
|
|
#endif
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
#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;
|
|
|
|
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_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
|
|
}
|
|
|
|
void* platform::get_context_information() {
|
|
#ifdef ENABLE_VULKAN
|
|
if(gfx_interface->required_context() == GFXContext::Vulkan) {
|
|
// dummy window
|
|
auto dummy = SDL_CreateWindow("", 0, 0, 1, 1, SDL_WINDOW_VULKAN);
|
|
|
|
unsigned int count = 0;
|
|
SDL_Vulkan_GetInstanceExtensions(dummy, &count, nullptr);
|
|
|
|
std::vector<const char*> extensions(count);
|
|
SDL_Vulkan_GetInstanceExtensions(dummy, &count, extensions.data());
|
|
|
|
SDL_DestroyWindow(dummy);
|
|
|
|
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;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
|
|
|
// determine gfx context at the beginning
|
|
#ifdef ENABLE_METAL
|
|
gfx_interface = new GFXMetal();
|
|
#else
|
|
gfx_interface = new GFXVulkan();
|
|
#endif
|
|
|
|
platform::initialize_context(gfx_interface->required_context());
|
|
|
|
engine = new prism::engine(argc, argv);
|
|
|
|
app = new @APP_CLASS@();
|
|
engine->set_app(app);
|
|
|
|
GFXCreateInfo info = {};
|
|
|
|
if(gfx_interface->initialize(info)) {
|
|
engine->set_gfx(gfx_interface);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
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 |