/* * Copyright (c) 2022 Jonathan Schleifer * * All rights reserved. * * This file is part of Obj3DEngine. It may be distributed under the terms of * the Q Public License 1.0, which can be found in the file LICENSE.QPL * included in the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of * this file. */ #include #include #import "O3DGlideRenderer.h" #import "O3DWinAPIWindow.h" static GrScreenResolution_t sizeToScreenResolution(OFSize size) { if (size.width == 320 && size.height == 200) return GR_RESOLUTION_320x200; if (size.width == 320 && size.height == 240) return GR_RESOLUTION_320x240; if (size.width == 400 && size.height == 256) return GR_RESOLUTION_400x256; if (size.width == 512 && size.height == 384) return GR_RESOLUTION_512x384; if (size.width == 640 && size.height == 200) return GR_RESOLUTION_640x200; if (size.width == 640 && size.height == 350) return GR_RESOLUTION_640x350; if (size.width == 640 && size.height == 400) return GR_RESOLUTION_640x400; if (size.width == 640 && size.height == 480) return GR_RESOLUTION_640x480; if (size.width == 800 && size.height == 600) return GR_RESOLUTION_800x600; if (size.width == 960 && size.height == 720) return GR_RESOLUTION_960x720; if (size.width == 856 && size.height == 480) return GR_RESOLUTION_856x480; if (size.width == 512 && size.height == 256) return GR_RESOLUTION_512x256; if (size.width == 1024 && size.height == 768) return GR_RESOLUTION_1024x768; if (size.width == 1280 && size.height == 1024) return GR_RESOLUTION_1280x1024; if (size.width == 1600 && size.height == 1200) return GR_RESOLUTION_1600x1200; if (size.width == 400 && size.height == 300) return GR_RESOLUTION_400x300; #ifdef GR_RESOLUTION_1152x864 if (size.width == 1152 && size.height == 864) return GR_RESOLUTION_1152x864; #endif #ifdef GR_RESOLUTION_1280x960 if (size.width == 1280 && size.height == 960) return GR_RESOLUTION_1280x960; #endif #ifdef GR_RESOLUTION_1600x1024 if (size.width == 1600 && size.height == 1024) return GR_RESOLUTION_1600x1024; #endif #ifdef GR_RESOLUTION_1792x1344 if (size.width == 1792 && size.height == 1344) return GR_RESOLUTION_1792x1344; #endif #ifdef GR_RESOLUTION_1856x1392 if (size.width == 1856 && size.height == 1392) return GR_RESOLUTION_1856x1392; #endif #ifdef GR_RESOLUTION_1920x1440 if (size.width == 1920 && size.height == 1440) return GR_RESOLUTION_1920x1440; #endif #ifdef GR_RESOLUTION_2048x1536 if (size.width == 2048 && size.height == 1536) return GR_RESOLUTION_2048x1536; #endif #ifdef GR_RESOLUTION_2048x2048 if (size.width == 2048 && size.height == 2048) return GR_RESOLUTION_2048x2048; #endif @throw [OFInvalidArgumentException exception]; } static GrScreenRefresh_t floatToScreenRefresh(float refresh) { if (refresh == 60) return GR_REFRESH_60Hz; if (refresh == 70) return GR_REFRESH_70Hz; if (refresh == 72) return GR_REFRESH_72Hz; if (refresh == 75) return GR_REFRESH_75Hz; if (refresh == 80) return GR_REFRESH_80Hz; if (refresh == 90) return GR_REFRESH_90Hz; if (refresh == 100) return GR_REFRESH_100Hz; if (refresh == 85) return GR_REFRESH_85Hz; if (refresh == 120) return GR_REFRESH_120Hz; @throw [OFInvalidArgumentException exception]; } @implementation O3DGlideRenderer @synthesize syncsToVerticalBlank = _syncsToVerticalBlank; static void grGlideShutdownCdecl(void) { grGlideShutdown(); } + (void)initialize { if (self != [O3DGlideRenderer class]) return; grGlideInit(); atexit(grGlideShutdownCdecl); } + (unsigned int)numAvailableDevices { GrHwConfiguration config; if (!grSstQueryHardware(&config)) return 0; if (config.num_sst < 0) return 0; return (unsigned int)config.num_sst; } - (instancetype)initWithResolution: (OFSize)resolution bitsPerPixel: (uint8_t)bitsPerPixel refreshRate: (float)refreshRate options: (nullable OFDictionary *)options { self = [super init]; @try { if (bitsPerPixel != 16) @throw [OFInvalidArgumentException exception]; long long deviceIndex = [[options objectForKey: O3DRendererDeviceIndex] longLongValue]; if (deviceIndex > [self.class numAvailableDevices]) @throw [OFInvalidArgumentException exception]; grSstSelect((int)deviceIndex); FxU32 hWnd = 0; #ifdef OF_WINDOWS _winAPIWindow = [[O3DWinAPIWindow alloc] initWithSize: resolution]; hWnd = (FxU32)_winAPIWindow.hWnd; #endif if (!grSstWinOpen(hWnd, sizeToScreenResolution(resolution), floatToScreenRefresh(refreshRate), GR_COLORFORMAT_RGBA, GR_ORIGIN_LOWER_LEFT, 2, 1)) @throw [OFInitializationFailedException exceptionWithClass: self.class]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { grSstWinClose(); #ifdef OF_WINDOWS [_winAPIWindow release]; #endif [super dealloc]; } - (void)beginFrame { grBufferClear(0, 0, 0); } - (void)drawPolygonWithVertices: (const O3DVertex *)vertices count: (size_t)count { /* * Range is -1 to 1, we add 1, so we get 0 to 2 - so we need to * multiply with half the width / height. */ float halfWidth = grSstScreenWidth() / 2; float halfHeight = grSstScreenHeight() / 2; GrVertex *glideVertices = OFAllocZeroedMemory(count, sizeof(GrVertex)); @try { for (size_t i = 0; i < count; i++) { glideVertices[i].x = (vertices[i].x + 1) * halfWidth; glideVertices[i].y = (vertices[i].y + 1) * halfHeight; glideVertices[i].r = vertices[i].r * 255; glideVertices[i].g = vertices[i].g * 255; glideVertices[i].b = vertices[i].b * 255; glideVertices[i].a = vertices[i].a * 255; } grDrawPolygonVertexList((int)count, glideVertices); } @finally { OFFreeMemory(glideVertices); } } - (void)endFrame { grBufferSwap(_syncsToVerticalBlank ? 1 : 0); } @end