#include "pch.h" #include "log.h" #include "shader.h" #include "shape.h" #include "texture.h" #include "image.h" #include "app.h" #include "keymap.h" #ifdef __APPLE__ #ifdef TARGET_OS_OSX #include #include #include #include @interface View : NSOpenGLView { CVDisplayLinkRef dl; } @end @implementation View - (instancetype)initWithFrame:(NSRect)frameRect { NSOpenGLPixelFormatAttribute attrs[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, // Must specify the 3.2 Core Profile to use OpenGL 3.2 NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // Multisample NSOpenGLPFAMultisample, NSOpenGLPFASamples, 2, NSOpenGLPFASampleBuffers, 1, 0 }; NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil]; self = [super initWithFrame:frameRect pixelFormat:pf]; [self setPixelFormat:pf]; [self setOpenGLContext:context]; return self; } - (void)prepareOpenGL { NSLog(@"prepare"); // Synchronize buffer swaps with vertical refresh rate GLint swapInt = 1; [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; // Create a display link capable of being used with all active displays CVDisplayLinkCreateWithActiveCGDisplays(&dl); // Set the renderer output callback function CVDisplayLinkSetOutputCallback(dl, &MyDisplayLinkCallback, (__bridge void*)self); // Set the display link for the current renderer CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(dl, cglContext, cglPixelFormat); // Activate the display link CVDisplayLinkStart(dl); CGLEnable([self.openGLContext CGLContextObj], kCGLCECrashOnRemovedFunctions); CGLLockContext([[self openGLContext] CGLContextObj]); App::I.init(); CGLUnlockContext([[self openGLContext] CGLContextObj]); } // This is the renderer output callback function static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { CVReturn result = [(__bridge View*)displayLinkContext getFrameForTime:outputTime]; return result; } - (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime { static double _timeFreq = CVGetHostClockFrequency(); static double _prevTime = (double)outputTime->hostTime / _timeFreq; double hostTime = (double)outputTime->hostTime; double now = hostTime / _timeFreq; // this will not update unless 1/30th of a second has passed since the last update if ( now < _prevTime + (1.0 / 30.0) ) { // Add your drawing codes here [[self openGLContext] makeCurrentContext]; // We draw on a secondary thread through the display link // When resizing the view, -reshape is called automatically on the main // thread. Add a mutex around to avoid the threads accessing the context // simultaneously when resizing CGLLockContext([[self openGLContext] CGLContextObj]); App::I.clear(); App::I.update(now - _prevTime); //[[self openGLContext] flushBuffer]; // returning NO will cause the layer to NOT be redrawn CGLFlushDrawable([[self openGLContext] CGLContextObj]); CGLUnlockContext([[self openGLContext] CGLContextObj]); return NO; } else { // change whatever you want to change here, as a function of time elapsed _prevTime = now; // return YES to have your layer redrawn return YES; } return kCVReturnSuccess; } - (void)dealloc { // Release the display link CVDisplayLinkRelease(dl); } - (void)drawRect:(NSRect)dirtyRect { NSLog(@"drawRect"); // Add your drawing codes here [[self openGLContext] makeCurrentContext]; // We draw on a secondary thread through the display link // When resizing the view, -reshape is called automatically on the main // thread. Add a mutex around to avoid the threads accessing the context // simultaneously when resizing CGLLockContext([[self openGLContext] CGLContextObj]); App::I.update(0); //[[self openGLContext] flushBuffer]; // returning NO will cause the layer to NOT be redrawn CGLFlushDrawable([[self openGLContext] CGLContextObj]); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)reshape { [super reshape]; // We draw on a secondary thread through the display link. However, when // resizing the view, -drawRect is called on the main thread. // Add a mutex around to avoid the threads accessing the context // simultaneously when resizing. CGLLockContext([[self openGLContext] CGLContextObj]); // Get the view size in Points NSRect viewRectPoints = [self bounds]; #if SUPPORT_RETINA_RESOLUTION // Rendering at retina resolutions will reduce aliasing, but at the potential // cost of framerate and battery life due to the GPU needing to render more // pixels. // Any calculations the renderer does which use pixel dimentions, must be // in "retina" space. [NSView convertRectToBacking] converts point sizes // to pixel sizes. Thus the renderer gets the size in pixels, not points, // so that it can set it's viewport and perform and other pixel based // calculations appropriately. // viewRectPixels will be larger than viewRectPoints for retina displays. // viewRectPixels will be the same as viewRectPoints for non-retina displays NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints]; #else //if !SUPPORT_RETINA_RESOLUTION // App will typically render faster and use less power rendering at // non-retina resolutions since the GPU needs to render less pixels. // There is the cost of more aliasing, but it will be no-worse than // on a Mac without a retina display. // Points:Pixels is always 1:1 when not supporting retina resolutions NSRect viewRectPixels = viewRectPoints; #endif // !SUPPORT_RETINA_RESOLUTION App::I.resize(viewRectPixels.size.width, viewRectPixels.size.height); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)renewGState { // Called whenever graphics state updated (such as window resize) // OpenGL rendering is not synchronous with other rendering on the OSX. // Therefore, call disableScreenUpdatesUntilFlush so the window server // doesn't render non-OpenGL content in the window asynchronously from // OpenGL content, which could cause flickering. (non-OpenGL content // includes the title bar and drawing done by the app with other APIs) [[self window] disableScreenUpdatesUntilFlush]; [super renewGState]; } - (void)mouseDown:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_down(0, mouseLoc.x, App::I.height - mouseLoc.y - 1, theEvent.pressure); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)rightMouseDown:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_down(1, mouseLoc.x, App::I.height - mouseLoc.y - 1, theEvent.pressure); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)mouseUp:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_up(0, mouseLoc.x, App::I.height - mouseLoc.y - 1); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)rightMouseUp:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_up(1, mouseLoc.x, App::I.height - mouseLoc.y - 1); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)mouseMoved:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_move(mouseLoc.x, App::I.height - mouseLoc.y - 1, theEvent.pressure); CGLUnlockContext([[self openGLContext] CGLContextObj]); } -(void)mouseDragged:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_move(mouseLoc.x, App::I.height - mouseLoc.y - 1, theEvent.pressure); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)rightMouseDragged:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_move(mouseLoc.x, App::I.height - mouseLoc.y - 1, theEvent.pressure); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)scrollWheel:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; App::I.mouse_scroll(mouseLoc.x, App::I.height - mouseLoc.y - 1, [theEvent deltaY]); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)keyDown:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto keyCode = [theEvent keyCode]; auto chars = [theEvent characters]; App::I.key_down(convert_key(keyCode)); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)keyUp:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto keyCode = [theEvent keyCode]; auto chars = [theEvent characters]; App::I.key_up(convert_key(keyCode)); CGLUnlockContext([[self openGLContext] CGLContextObj]); } @end @interface Window : NSWindow @end @implementation Window - (void)keyDown:(NSEvent *)theEvent { [[self windowController] keyDown:theEvent]; } @end @interface Controller : NSWindowController @end @implementation Controller - (void)keyDown:(NSEvent *)theEvent { unichar c = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; if (c == 27) { [[NSApplication sharedApplication] terminate:nil]; } } - (void)windowDidResize:(NSNotification *)notification { } @end @interface AppOSX : NSApplication { Window* window; Controller* controller; View* view; } @end @implementation AppOSX - (instancetype)init { self = [super init]; [self setActivationPolicy:NSApplicationActivationPolicyRegular]; // make it to the front [self setDelegate:self]; return self; } - (void)applicationDidFinishLaunching:(NSNotification *)notification { App::I.initLog(); App::I.create(); NSRect r = NSMakeRect(0, 0, App::I.width, App::I.height); view = [[View alloc] initWithFrame:r]; auto style = NSTitledWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask; window = [[Window alloc] initWithContentRect:r styleMask:style backing:NSBackingStoreBuffered defer:NO]; [window setTitle:@"PanoPainter 0.1.2 alpha"]; [window center]; [window makeKeyAndOrderFront:controller]; [window setContentView:view]; [window setAcceptsMouseMovedEvents:true]; [window makeFirstResponder:view]; controller = [[Controller alloc] initWithWindow:window]; auto menubar = [NSMenu new]; auto appMenuItem = [NSMenuItem new]; [menubar addItem:appMenuItem]; [self setMainMenu:menubar]; auto appMenu = [NSMenu new]; auto appName = [[NSProcessInfo processInfo] processName]; auto quitTitle = [@"Quit " stringByAppendingString:appName]; auto quitMenuItem = [[NSMenuItem alloc] initWithTitle:quitTitle action:@selector(terminate:) keyEquivalent:@"q"]; [appMenu addItem:quitMenuItem]; [appMenuItem setSubmenu:appMenu]; NSLog(@"app launched"); } @end int main(int argc, const char * argv[]) { AppOSX* app = [AppOSX sharedApplication]; [app run]; return 0; } #endif #endif #ifdef _WIN32 #pragma comment (lib, "opengl32.lib") #pragma comment (lib, "glew32.lib") #pragma comment(lib, "wbemuuid.lib") LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); HINSTANCE hInst; HWND hWnd; HDC hDC; HGLRC hRC; wchar_t* className; bool keys[256]; #include int read_WMI_info() { // see: http://win32easy.blogspot.co.uk/2011/03/wmi-in-c-query-everyting-from-your-os.html HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hRes)) { LOG("Unable to launch COM: %x", hRes); return 1; } if ((FAILED(hRes = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0)))) { LOG("Unable to initialize security: %x", hRes); return 1; } IWbemLocator* pLocator = NULL; if (FAILED(hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pLocator)))) { LOG("Unable to create a WbemLocator: %x", hRes); return 1; } IWbemServices* pService = NULL; if (FAILED(hRes = pLocator->ConnectServer(L"root\\CIMV2", NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pService))) { pLocator->Release(); LOG("Unable to connect to \"CIMV2\": %x", hRes); return 1; } auto log_field = [](const wchar_t* section, IWbemClassObject* clsObj, const wchar_t* field) { VARIANT vRet; VariantInit(&vRet); if (SUCCEEDED(clsObj->Get(field, 0, &vRet, NULL, NULL)) && vRet.vt == VT_BSTR) { LOGW(L"%s %s: %s", section, field, vRet.bstrVal); VariantClear(&vRet); } }; // GET DEVICE INFO { IEnumWbemClassObject* pEnumerator = NULL; if (FAILED(hRes = pService->ExecQuery(L"WQL", L"SELECT * FROM Win32_ComputerSystem", WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator))) { pLocator->Release(); pService->Release(); LOG("Unable to retrive desktop monitors: %x", hRes); return 1; } IWbemClassObject* clsObj = NULL; int numElems; while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE) { if (FAILED(hRes)) break; log_field(L"Machine", clsObj, L"Name"); log_field(L"Machine", clsObj, L"Model"); log_field(L"Machine", clsObj, L"Manufacturer"); clsObj->Release(); } pEnumerator->Release(); } // GET OS INFO { IEnumWbemClassObject* pEnumerator = NULL; if (FAILED(hRes = pService->ExecQuery(L"WQL", L"SELECT * FROM Win32_OperatingSystem", WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator))) { pLocator->Release(); pService->Release(); LOG("Unable to retrive desktop monitors: %x", hRes); return 1; } IWbemClassObject* clsObj = NULL; int numElems; while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE) { if (FAILED(hRes)) break; log_field(L"OS", clsObj, L"Name"); log_field(L"OS", clsObj, L"Version"); log_field(L"OS", clsObj, L"Locale"); log_field(L"OS", clsObj, L"OSProductSuite"); log_field(L"OS", clsObj, L"Manufacturer"); log_field(L"OS", clsObj, L"Description"); clsObj->Release(); } pEnumerator->Release(); } pService->Release(); pLocator->Release(); CoUninitialize(); return 0; } int main() { WNDCLASS wc; PIXELFORMATDESCRIPTOR pfd; App::I.initLog(); read_WMI_info(); App::I.create(); RECT clientRect = { 0, 0, (int)App::I.width, (int)App::I.height }; // Inizialize data structures to zero memset(&wc, 0, sizeof(wc)); memset(&keys, 0, sizeof(keys)); memset(&pfd, 0, sizeof(pfd)); // Create the main window hInst = GetModuleHandle(NULL); className = L"EngineMain"; wc.hInstance = hInst; wc.lpfnWndProc = (WNDPROC)WndProc; wc.lpszClassName = className; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.hCursor = LoadCursor(NULL, IDC_ARROW); RegisterClass(&wc); AdjustWindowRect(&clientRect, WS_OVERLAPPEDWINDOW, false); hWnd = CreateWindow(wc.lpszClassName, L"New Engine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, 0, 0, hInst, 0); // Setup GL Rendering Context pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 16; pfd.iLayerType = PFD_MAIN_PLANE; hDC = GetDC(hWnd); int pxfmt = ChoosePixelFormat(hDC, &pfd); SetPixelFormat(hDC, pxfmt, &pfd); hRC = wglCreateContext(hDC); // Create OpenGL 2.1 or less wglMakeCurrent(hDC, hRC); // Initialize extensions if (glewInit() != GLEW_OK) return 0; LOG("GL version: %s", glGetString(GL_VERSION)); LOG("GL vendor: %s", glGetString(GL_VENDOR)); LOG("GL renderer: %s", glGetString(GL_RENDERER)); // If supported create a 3.1 context if (wglewIsSupported("WGL_ARB_create_context")) { int contex_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 1, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 }; int pixel_attribs[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, WGL_COLOR_BITS_ARB, 32, WGL_DEPTH_BITS_ARB, 24, WGL_STENCIL_BITS_ARB, 8, // WGL_SAMPLE_BUFFERS_ARB, 1, // Number of buffers (must be 1 at time of writing) // WGL_SAMPLES_ARB, 4, // Number of samples 0 }; UINT numFormat; wglMakeCurrent(NULL, NULL); wglDeleteContext(hRC); DestroyWindow(hWnd); hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter 0.1.2 alpha - OpenGL 3.1", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, 0, 0, hInst, 0); hDC = GetDC(hWnd); wglChoosePixelFormatARB(hDC, pixel_attribs, nullptr, 1, &pxfmt, &numFormat); SetPixelFormat(hDC, pxfmt, &pfd); hRC = wglCreateContextAttribsARB(hDC, NULL, contex_attribs); wglMakeCurrent(hDC, hRC); } else { // If not supported, go fuck yourself we are not gonna use that shit return -1; // A negative number because you are a negative one } App::I.init(); ShowWindow(hWnd, SW_NORMAL); MSG msg; bool running = true; unsigned long t0 = GetTickCount(); unsigned long t1; while (running) { // If there any message in the queue process it if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { running = !(msg.message == WM_QUIT/* || gl.keys[VK_ESCAPE]*/); DispatchMessage(&msg); TranslateMessage(&msg); } else // Otherwise render next frame { t1 = GetTickCount(); float dt = (float)(t1 - t0) / 1000.0f; if (dt > 1.0f / 60.0f) { App::I.clear(); App::I.update((float)(t1 - t0) / 1000.0f); t0 = t1; SwapBuffers(hDC); } else { Sleep((DWORD)(1.0f / 60.0f * 1000.f)); } } } // Clean up DestroyWindow(hWnd); UnregisterClass(className, hInst); } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_CLOSE: PostQuitMessage(0); break; case WM_SIZE: App::I.resize((float)LOWORD(lp), (float)HIWORD(lp)); App::I.clear(); App::I.update(0.f); SwapBuffers(hDC); break; case WM_KEYDOWN: keys[wp] = true; App::I.key_down(convert_key((int)wp)); break; case WM_KEYUP: keys[wp] = false; App::I.key_up(convert_key((int)wp)); break; case WM_CHAR: App::I.key_char((int)wp); break; case WM_MOUSEMOVE: App::I.mouse_move((float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp)); break; case WM_LBUTTONDOWN: App::I.mouse_down(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp)); SetCapture(hWnd); break; case WM_LBUTTONUP: App::I.mouse_up(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp)); ReleaseCapture(); break; case WM_RBUTTONDOWN: App::I.mouse_down(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp)); SetCapture(hWnd); break; case WM_RBUTTONUP: App::I.mouse_up(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp)); ReleaseCapture(); break; case WM_MOUSEWHEEL: { POINT pt; pt.x = GET_X_LPARAM(lp); pt.y = GET_Y_LPARAM(lp); ScreenToClient(hWnd, &pt); App::I.mouse_scroll((float)pt.x, (float)pt.y, (float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA); break; } case WM_POINTERUPDATE: { POINTER_TOUCH_INFO touchInfo; POINTER_PEN_INFO penInfo; POINTER_INFO pointerInfo; UINT32 pointerId = GET_POINTERID_WPARAM(wp); POINTER_INPUT_TYPE pointerType = PT_POINTER; // Retrieve common pointer information if (!GetPointerInfo(pointerId, &pointerInfo)) { // failure, call GetLastError() } else { // success, process pointerInfo if (!GetPointerType(pointerId, &pointerType)) { // failure, call GetLastError() // set PT_POINTER to fall to default case below pointerType = PT_POINTER; } switch (pointerType) { case PT_TOUCH: // Retrieve touch information if (!GetPointerTouchInfo(pointerId, &touchInfo)) { // failure, call GetLastError() } else { // success, process touchInfo // mark as handled to skip call to DefWindowProc //fHandled = TRUE; } break; case PT_PEN: // Retrieve pen information if (!GetPointerPenInfo(pointerId, &penInfo)) { // failure, call GetLastError() } else { // success, process penInfo // mark as handled to skip call to DefWindowProc //fHandled = TRUE; //penInfo.pressure } break; default: if (!GetPointerInfo(pointerId, &pointerInfo)) { // failure. } else { // success, proceed with pointerInfo. //fHandled = HandleGenericPointerInfo(&pointerInfo); } break; } } break; } } return DefWindowProc(hWnd, msg, wp, lp); } #endif