/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #import "OpenVulkanoView.h" #include "Base/Logger.hpp" #include "Input/Touch/InputDeviceTouch.hpp" #include "Input/InputManager.hpp" #include "Host/Apple/MetalViewWindow.h" #include #import using namespace OpenVulkano; #pragma mark MetalViewDelegate @interface MetalViewDelegate : NSObject - (void) mtkView:(MTKView *) view drawableSizeWillChange:(CGSize) size; - (void) drawInMTKView:(MTKView *) view; @end @implementation MetalViewDelegate { MetalViewWindow* window; } - (id)initWithWindow:(MetalViewWindow*)win { window = win; window->SetContentScale(UIScreen.mainScreen.nativeScale); return self; } - (void) mtkView:(MTKView *) view drawableSizeWillChange:(CGSize) size { window->SetContentScale(UIScreen.mainScreen.nativeScale); window->OnResize(size.width, size.height); } - (void) drawInMTKView:(MTKView *) view { @autoreleasepool { window->TickHandler(); } } @end #pragma mark - #pragma mark OpenVulkanoView @interface OpenVulkanoView() { CADisplayLink* m_displayLink; MetalViewDelegate* m_mtdelegate; std::map m_touchToIdMap; Input::InputDeviceTouch m_touchDevice; MetalViewWindow m_window; } @end #pragma mark OpenVulkanoView Implementation @implementation OpenVulkanoView /** Returns a Metal-compatible layer. */ +(Class) layerClass { return [CAMetalLayer class]; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self commonInit]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self commonInit]; } return self; } - (void)commonInit { [self setMultipleTouchEnabled:YES]; self.contentScaleFactor = UIScreen.mainScreen.nativeScale; auto size = self.bounds.size; auto sizeX = size.width * self.contentScaleFactor; auto sizeY = size.height * self.contentScaleFactor; m_window.Init(self.layer, {sizeX, sizeY}); m_touchDevice.Init(); //TODO link window Input::InputManager::GetInstance()->RegisterInputDevice(&m_touchDevice); if ([self isKindOfClass:[MTKView class]]) { MTKView* mtkView = (MTKView*)(self); m_mtdelegate = [[MetalViewDelegate alloc] initWithWindow:&m_window]; [mtkView setDelegate:m_mtdelegate]; mtkView.preferredFramesPerSecond = 60; } else { m_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)]; [m_displayLink setPreferredFramesPerSecond: 60]; [m_displayLink addToRunLoop: NSRunLoop.currentRunLoop forMode: NSDefaultRunLoopMode]; m_displayLink.paused = YES; } } - (void)dealloc { for(auto& it : m_touchToIdMap) { m_touchDevice.RemoveTouch(it.second); } Input::InputManager::GetInstance()->UnregisterInputDevice(&m_touchDevice); m_touchDevice.Close(); [m_displayLink release]; [m_mtdelegate release]; OpenVulkano::Logger::WINDOW->info("Destroyed OpenVulkanoView"); [super dealloc]; } - (void)layoutSubviews { [super layoutSubviews]; if (m_displayLink) { // Only use this update method if we use display link instead of metal delegate auto size = self.bounds.size; auto sizeX = size.width * self.contentScaleFactor; auto sizeY = size.height * self.contentScaleFactor; m_window.SetContentScale(self.contentScaleFactor); m_window.OnResize(sizeX, sizeY); } } - (void)SetInterfaceOrientation:(float)orientation { m_window.SetOrientation(orientation); } - (Math::Vector2f)getTouchPosition:(UITouch*)touch { CGPoint uitouchLocation = [touch locationInView:touch.view]; return { uitouchLocation.x * self.contentScaleFactor, uitouchLocation.y * self.contentScaleFactor }; } - (void)touchesBegan:(NSSet*)inTouches withEvent:(UIEvent*)inEvent { for (UITouch* touch in inTouches) { Math::Vector2f touchLocation = [self getTouchPosition: touch]; uint32_t touchId = m_touchDevice.AddTouch(touchLocation); m_touchDevice.TouchDown(touchId); m_touchToIdMap.insert(std::make_pair(touch, touchId)); } } - (void)touchesCancelled:(NSSet*)inTouches withEvent:(UIEvent*)inEvent { for (UITouch* touch in inTouches) { Math::Vector2f touchLocation = [self getTouchPosition: touch]; auto it = m_touchToIdMap.find(touch); if (it != m_touchToIdMap.end()) { m_touchDevice.TouchUp(it->second); m_touchDevice.RemoveTouch(it->second); m_touchToIdMap.erase(it); } } } - (void)touchesEnded:(NSSet*)inTouches withEvent:(UIEvent*)inEvent { for (UITouch* touch in inTouches) { Math::Vector2f touchLocation = [self getTouchPosition: touch]; auto it = m_touchToIdMap.find(touch); if (it != m_touchToIdMap.end()) { m_touchDevice.TouchUp(it->second); m_touchDevice.RemoveTouch(it->second); m_touchToIdMap.erase(it); } } } - (void)touchesMoved:(NSSet*)inTouches withEvent:(UIEvent*)inEvent { for (UITouch* touch in inTouches) { Math::Vector2f touchLocation = [self getTouchPosition: touch]; auto it = m_touchToIdMap.find(touch); if (it != m_touchToIdMap.end()) { m_touchDevice.TouchMoved(it->second, touchLocation); } } } -(void*)GetWindow { return &m_window; } -(void) renderLoop { m_window.TickHandler(); } -(void)WillAppear { if (m_displayLink) m_displayLink.paused = NO; } -(void)DidAppear { if (m_displayLink) m_displayLink.paused = NO; } -(void)WillDisappear { if (m_displayLink) m_displayLink.paused = YES; } -(void)DidDisappear { if (m_displayLink) m_displayLink.paused = YES; } -(void)DidUnload { if (m_displayLink) { [m_displayLink invalidate]; [m_displayLink release]; m_displayLink = nil; } } @end