Archived
1
Fork 0
This repository has been archived on 2025-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
prism/platforms/sdl/main.cpp.in
Joshua Goins ca2c2c9d3d Move all engine-specific models, materials etc. to a new base directory
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.
2022-05-21 18:28:48 -04:00

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