#define VK_USE_PLATFORM_METAL_EXT #include #include #import "QuartzCore/QuartzCore.hpp" #import #include #include <@APP_INCLUDE@> #ifdef ENABLE_VULKAN #ifdef PLATFORM_IOS #define VK_USE_PLATFORM_IOS_MVK #else #define VK_USE_PLATFORM_TVOS_MVK #endif #include "gfx_vulkan.hpp" #endif #ifdef ENABLE_METAL #include "gfx_metal.hpp" #endif #include "gfx_dummy.hpp" @APP_CLASS@* app = nullptr; GFX* gfx_interface = nullptr; int maxFPS = 60; std::array inputKeys; float rightX = 0.0f, rightY = 0.0f; float leftX = 0.0f, leftY = 0.0f; #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 @interface GameView : UIView @end @implementation GameView + (Class) layerClass { return [CAMetalLayer class]; } - (void)draw { engine->update(1.0f / (float)maxFPS); engine->begin_frame(1.0f / (float)maxFPS); engine->render((void*)1); engine->end_frame(); } - (void) controllerConnected { GCController* controller = [GCController controllers][0]; [[controller extendedGamepad] setValueChangedHandler:^(GCExtendedGamepad * _Nonnull gamepad, GCControllerElement * _Nonnull element) { const auto& handle_element = [element](int index, GCControllerElement* e) { if(element == e) inputKeys[index] = [(GCControllerButtonInput*)e value] == 1.0f; }; handle_element(0, [[controller extendedGamepad] buttonA]); handle_element(1, [[controller extendedGamepad] buttonB]); handle_element(2, [[controller extendedGamepad] buttonX]); handle_element(3, [[controller extendedGamepad] buttonY]); if(element == [[controller extendedGamepad] dpad]) { inputKeys[4] = [[[[controller extendedGamepad] dpad] up] value] == 1.0f; inputKeys[5] = [[[[controller extendedGamepad] dpad] down] value] == 1.0f; inputKeys[6] = [[[[controller extendedGamepad] dpad] left] value] == 1.0f; inputKeys[7] = [[[[controller extendedGamepad] dpad] right] value] == 1.0f; } if(element == [[controller extendedGamepad] leftThumbstick]) { leftX = [[[[controller extendedGamepad] leftThumbstick] xAxis] value]; leftY = [[[[controller extendedGamepad] leftThumbstick] yAxis] value]; } if(element == [[controller extendedGamepad] rightThumbstick]) { rightX = [[[[controller extendedGamepad] rightThumbstick] xAxis] value]; rightY = [[[[controller extendedGamepad] rightThumbstick] yAxis] value]; } }]; } @end @interface GameViewController : UIViewController @end float mouse_x = 0.0f, mouse_y = 0.0f; bool mouse_down = false; @interface GameViewController () { GameView* view; } @end @implementation GameViewController - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView: self.view]; mouse_x = touchPoint.x; mouse_y = touchPoint.y; mouse_down = true; engine->process_mouse_down(0, {static_cast(touchPoint.x), static_cast(touchPoint.y)}); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView: self.view]; mouse_x = touchPoint.x; mouse_y = touchPoint.y; mouse_down = false; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView: self.view]; mouse_x = touchPoint.x; mouse_y = touchPoint.y; } int width, height; int drawable_width, drawable_height; CAMetalLayer* layer; template 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()); } } } - (void)viewDidLoad { [super viewDidLoad]; view = (GameView*)self.view; view.userInteractionEnabled = true; layer = (CAMetalLayer*)view.layer; CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:view selector:@selector(draw)]; displayLink.preferredFramesPerSecond = [UIScreen mainScreen].maximumFramesPerSecond; maxFPS = [UIScreen mainScreen].maximumFramesPerSecond; [displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSDefaultRunLoopMode]; width = [view frame].size.width; height = [view frame].size.height; drawable_width = [view frame].size.width * [view contentScaleFactor]; drawable_height = [view frame].size.height * [view contentScaleFactor]; engine = new prism::engine(0, nullptr); #ifdef ENABLE_METAL try_initialize(); #endif #ifdef ENABLE_VULKAN try_initialize(); #endif try_initialize(); GFXCreateInfo info = {}; if(gfx_interface->initialize(info)) { engine->set_gfx(gfx_interface); } else { return -1; } app = new @APP_CLASS@(); app_main(engine); engine->set_app(app); engine->add_window((void*)CFBridgingRetain([view layer]), (void*)1, {static_cast(width), static_cast(height)}); app->initialize_render(); NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; [nc addObserver:view selector:@selector(controllerConnected) name:GCControllerDidConnectNotification object:nil]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } -(bool)prefersStatusBarHidden { return YES; } @end 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) { } void* platform::get_context_information() { #ifdef ENABLE_VULKAN if(gfx_interface->required_context() == GFXContext::Vulkan) { auto info = new vulkan_information(); info->surface_extensions = {VK_EXT_METAL_SURFACE_EXTENSION_NAME}; 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; } void platform::capture_mouse(const bool capture) { } // TODO: unimplemented PlatformTheme platform::get_theme() { return PlatformTheme::Light; } void platform::begin_text_input() { // TODO: stub } void platform::end_text_input() { // TODO: stub } void* create_vulkan_surface(platform::window_ptr index, void* surface_creation_info) { auto vulkan_surface_info = (vulkan_surface_creation_info*)surface_creation_info; VkMetalSurfaceCreateInfoEXT surfaceInfo = {}; surfaceInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; surfaceInfo.pNext = 0; surfaceInfo.flags = 0; surfaceInfo.pLayer = layer; auto vk_surface = new vulkan_surface(); vkCreateMetalSurfaceEXT((VkInstance)vulkan_surface_info->instance, &surfaceInfo, nullptr, &vk_surface->surface); return vk_surface; } void* create_metal_surface(platform::window_ptr window, void* surface_creation_info) { auto metal_surface_info = (metal_surface_creation_info*)surface_creation_info; layer.device = (__bridge id)metal_surface_info->device; layer.allowsNextDrawableTimeout = true; auto return_surface = new metal_surface(); return_surface->format = static_cast(layer.pixelFormat); return return_surface; } void* get_next_metal_drawable(platform::window_ptr window) { auto drawable = (__bridge CA::MetalDrawable*)[layer nextDrawable]; drawable->retain(); return drawable; } bool platform::is_main_window(platform::window_ptr index) { return true; } void platform::show_window(const platform::window_ptr index) { } bool platform::supports_feature(const PlatformFeature feature) { return false; } prism::Offset platform::get_cursor_position() { return {static_cast(mouse_x), static_cast(mouse_y)}; } std::tuple platform::get_right_stick_position() { return {rightX, rightY}; } std::tuple platform::get_left_stick_position() { return {leftX, leftY}; } bool platform::get_key_down(InputButton key) { if(key == InputButton::ButtonA) return inputKeys[0]; if(key == InputButton::ButtonB) return inputKeys[1]; if(key == InputButton::ButtonX) return inputKeys[2]; if(key == InputButton::ButtonY) return inputKeys[3]; if(key == InputButton::DPadUp) return inputKeys[4]; if(key == InputButton::DPadDown) return inputKeys[5]; if(key == InputButton::DPadLeft) return inputKeys[6]; if(key == InputButton::DPadRight) return inputKeys[7]; return false; } platform::window_ptr platform::open_window(const std::string_view title, const prism::Rectangle rect, const WindowFlags flags) { return (void*)1; } void platform::set_window_title(const platform::window_ptr index, const std::string_view title) { } bool platform::is_window_focused(const platform::window_ptr index) { } void platform::set_window_focused(const platform::window_ptr index) { } prism::Extent platform::get_window_size(const platform::window_ptr index) { return {static_cast(width), static_cast(height)}; } prism::Extent platform::get_window_drawable_size(const platform::window_ptr index) { return {static_cast(drawable_width), static_cast(drawable_height)}; } prism::Offset platform::get_window_position(const platform::window_ptr index) { } void platform::set_window_size(const platform::window_ptr index, const prism::Extent extent) { } void platform::set_window_position(const platform::window_ptr index, const prism::Offset offset) { } void platform::close_window(const platform::window_ptr index) { } int platform::get_keycode(const InputButton button) { } prism::Rectangle platform::get_monitor_resolution() { } prism::Rectangle platform::get_monitor_work_area() { } prism::Offset platform::get_screen_cursor_position() { } bool platform::get_mouse_button_down(const int index) { return mouse_down; } float platform::get_monitor_dpi() { return 2.0f; } std::tuple platform::get_wheel_delta() { } const char* platform::get_name() { return "iOS"; }