2020-09-16 20:14:35 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-26 15:17:35 +00:00
|
|
|
//
|
2020-09-16 20:14:35 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-26 15:17:35 +00:00
|
|
|
// Renderer.cpp
|
|
|
|
//
|
2020-06-28 16:39:18 +00:00
|
|
|
// General rendering functions.
|
2020-06-26 15:17:35 +00:00
|
|
|
//
|
|
|
|
|
2019-08-08 20:16:11 +00:00
|
|
|
#include "renderers/Renderer.h"
|
|
|
|
|
|
|
|
#include "math/Transform4x4f.h"
|
|
|
|
#include "math/Vector2i.h"
|
|
|
|
#include "resources/ResourceManager.h"
|
|
|
|
#include "ImageIO.h"
|
|
|
|
#include "Log.h"
|
|
|
|
#include "Settings.h"
|
2020-09-16 20:14:35 +00:00
|
|
|
#include "Shader_GL21.h"
|
2019-08-08 20:16:11 +00:00
|
|
|
|
2020-06-26 16:03:55 +00:00
|
|
|
#include <SDL2/SDL.h>
|
2019-08-08 20:16:11 +00:00
|
|
|
#include <stack>
|
|
|
|
|
2021-01-21 20:52:28 +00:00
|
|
|
#if defined (_WIN64)
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2019-08-08 20:16:11 +00:00
|
|
|
namespace Renderer
|
|
|
|
{
|
2020-06-26 15:17:35 +00:00
|
|
|
static std::stack<Rect> clipStack;
|
|
|
|
static SDL_Window* sdlWindow = nullptr;
|
|
|
|
static int windowWidth = 0;
|
|
|
|
static int windowHeight = 0;
|
|
|
|
static int screenWidth = 0;
|
|
|
|
static int screenHeight = 0;
|
|
|
|
static int screenOffsetX = 0;
|
|
|
|
static int screenOffsetY = 0;
|
|
|
|
static int screenRotate = 0;
|
|
|
|
static bool initialCursorState = 1;
|
2021-01-13 18:42:06 +00:00
|
|
|
// Screen resolution modifiers relative to the 1920x1080 reference.
|
|
|
|
static float screenHeightModifier;
|
|
|
|
static float screenWidthModifier;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
static void setIcon()
|
|
|
|
{
|
|
|
|
size_t width = 0;
|
|
|
|
size_t height = 0;
|
2020-07-08 15:01:47 +00:00
|
|
|
ResourceData resData = ResourceManager::getInstance()->
|
|
|
|
getFileData(":/graphics/window_icon_256.png");
|
2020-06-26 15:17:35 +00:00
|
|
|
std::vector<unsigned char> rawData =
|
|
|
|
ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height);
|
|
|
|
|
|
|
|
if (!rawData.empty()) {
|
|
|
|
ImageIO::flipPixelsVert(rawData.data(), width, height);
|
|
|
|
|
|
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
|
|
unsigned int rmask = 0xFF000000;
|
|
|
|
unsigned int gmask = 0x00FF0000;
|
|
|
|
unsigned int bmask = 0x0000FF00;
|
|
|
|
unsigned int amask = 0x000000FF;
|
|
|
|
#else
|
|
|
|
unsigned int rmask = 0x000000FF;
|
|
|
|
unsigned int gmask = 0x0000FF00;
|
|
|
|
unsigned int bmask = 0x00FF0000;
|
|
|
|
unsigned int amask = 0xFF000000;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Try creating SDL surface from logo data.
|
2020-09-16 20:14:35 +00:00
|
|
|
SDL_Surface* logoSurface = SDL_CreateRGBSurfaceFrom(
|
|
|
|
static_cast<void*>(rawData.data()),
|
|
|
|
static_cast<int>(width), static_cast<int>(height), 32,
|
|
|
|
static_cast<int>((width * 4)), rmask, gmask, bmask, amask);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
if (logoSurface != nullptr) {
|
|
|
|
SDL_SetWindowIcon(sdlWindow, logoSurface);
|
|
|
|
SDL_FreeSurface(logoSurface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool createWindow()
|
|
|
|
{
|
|
|
|
LOG(LogInfo) << "Creating window...";
|
|
|
|
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
2020-09-04 16:59:19 +00:00
|
|
|
LOG(LogError) << "Couldn't initialize SDL: " << SDL_GetError() << ".";
|
2020-06-26 15:17:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
initialCursorState = (SDL_ShowCursor(0) != 0);
|
|
|
|
|
2021-01-24 22:44:50 +00:00
|
|
|
int displayIndex = Settings::getInstance()->getInt("DisplayIndex");
|
|
|
|
// Check that an invalid value has not been manually entered in the es_settings.cfg file.
|
|
|
|
if (displayIndex != 1 && displayIndex != 2 && displayIndex != 3 && displayIndex != 4) {
|
|
|
|
Settings::getInstance()->setInt("DisplayIndex", 1);
|
|
|
|
displayIndex = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
displayIndex--;
|
|
|
|
}
|
2021-01-24 11:03:44 +00:00
|
|
|
|
2021-01-24 22:44:50 +00:00
|
|
|
int availableDisplays = SDL_GetNumVideoDisplays();
|
2021-01-24 11:03:44 +00:00
|
|
|
if (displayIndex > availableDisplays - 1) {
|
2021-01-24 22:44:50 +00:00
|
|
|
LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1) <<
|
|
|
|
" does not exist, changing to display 1";
|
2021-01-24 11:03:44 +00:00
|
|
|
displayIndex = 0;
|
|
|
|
}
|
|
|
|
else {
|
2021-01-24 22:44:50 +00:00
|
|
|
LOG(LogInfo) << "Using display: " << std::to_string(displayIndex + 1);
|
2021-01-24 11:03:44 +00:00
|
|
|
}
|
|
|
|
|
2020-09-16 20:14:35 +00:00
|
|
|
SDL_DisplayMode displayMode;
|
2021-01-24 11:03:44 +00:00
|
|
|
SDL_GetDesktopDisplayMode(displayIndex, &displayMode);
|
2021-01-21 20:52:28 +00:00
|
|
|
|
|
|
|
#if defined (_WIN64)
|
|
|
|
// Tell Windows that we're DPI aware so that we can set a physical resolution and
|
|
|
|
// avoid any automatic DPI scaling.
|
2021-01-24 11:03:44 +00:00
|
|
|
SetProcessDPIAware();
|
|
|
|
// We need to set the resolution based on the actual display bounds as the numbers
|
|
|
|
// returned by SDL_GetDesktopDisplayMode are calculated based on DPI scaling and
|
|
|
|
// therefore do not necessarily reflect the physical display resolution.
|
|
|
|
SDL_Rect displayBounds;
|
|
|
|
SDL_GetDisplayBounds(displayIndex, &displayBounds);
|
|
|
|
displayMode.w = displayBounds.w;
|
|
|
|
displayMode.h = displayBounds.h;
|
2021-01-21 20:52:28 +00:00
|
|
|
#endif
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
windowWidth = Settings::getInstance()->getInt("WindowWidth") ?
|
2020-09-16 20:14:35 +00:00
|
|
|
Settings::getInstance()->getInt("WindowWidth") : displayMode.w;
|
2020-06-26 15:17:35 +00:00
|
|
|
windowHeight = Settings::getInstance()->getInt("WindowHeight") ?
|
2020-09-16 20:14:35 +00:00
|
|
|
Settings::getInstance()->getInt("WindowHeight") : displayMode.h;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenWidth = Settings::getInstance()->getInt("ScreenWidth") ?
|
2020-09-16 20:14:35 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenWidth") : windowWidth;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenHeight = Settings::getInstance()->getInt("ScreenHeight") ?
|
2020-09-16 20:14:35 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenHeight") : windowHeight;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ?
|
|
|
|
Settings::getInstance()->getInt("ScreenOffsetX") : 0;
|
|
|
|
screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
|
|
|
|
Settings::getInstance()->getInt("ScreenOffsetY") : 0;
|
|
|
|
screenRotate = Settings::getInstance()->getInt("ScreenRotate") ?
|
2020-09-16 20:14:35 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenRotate") : 0;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-01-13 18:42:06 +00:00
|
|
|
screenHeightModifier = static_cast<float>(screenHeight) / 1080.0f;
|
|
|
|
screenWidthModifier = static_cast<float>(screenWidth) / 1920.0f;
|
|
|
|
|
2021-01-24 11:03:44 +00:00
|
|
|
// Prevent the application window from minimizing when switching windows (when launching
|
|
|
|
// games or when manually switching windows using the task switcher).
|
2020-06-26 15:17:35 +00:00
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
|
|
|
|
2020-08-23 20:40:15 +00:00
|
|
|
#if defined(__APPLE__) || defined(__unix__)
|
2020-08-23 20:19:37 +00:00
|
|
|
bool userResolution = false;
|
|
|
|
// Check if the user has changed the resolution from the command line.
|
2020-09-16 20:14:35 +00:00
|
|
|
if (windowWidth != displayMode.w || windowHeight != displayMode.h)
|
2020-08-23 20:19:37 +00:00
|
|
|
userResolution = true;
|
2020-08-23 16:41:08 +00:00
|
|
|
// Not sure if this could be a useful setting for some users.
|
|
|
|
// SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
|
|
|
|
#endif
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
setupWindow();
|
|
|
|
|
|
|
|
unsigned int windowFlags;
|
|
|
|
|
2020-08-23 15:04:30 +00:00
|
|
|
#if defined(_WIN64)
|
2020-07-18 11:21:44 +00:00
|
|
|
// For Windows, always set the mode to windowed, as full screen mode seems to
|
|
|
|
// behave quite erratic. There may be a proper fix for this, but for now windowed
|
|
|
|
// mode seems to behave well and it's almost completely seamless, especially with
|
2021-01-24 22:44:50 +00:00
|
|
|
// a hidden taskbar. As well, setting SDL_WINDOW_BORDERLESS introduces issues too
|
|
|
|
// so unfortunately this needs to be avoided.
|
2021-01-24 12:51:40 +00:00
|
|
|
windowFlags = getWindowFlags();
|
2020-08-23 20:19:37 +00:00
|
|
|
#elif defined(__APPLE__)
|
|
|
|
// This seems to be the only full window mode that somehow works on macOS as a real
|
|
|
|
// fullscreen mode will do lots of weird stuff like preventing window switching
|
|
|
|
// or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost
|
|
|
|
// works, but it "shuffles" windows when starting the emulator and won't return
|
|
|
|
// properly when the game has exited. With the current mode, the top menu is visible
|
|
|
|
// and hides that part of the ES window. Also, the splash screen is not displayed
|
|
|
|
// until the point where ES has almost completely finished loading. I'm not sure
|
|
|
|
// if anything can be done to improve these things as it's quite obvious that
|
|
|
|
// Apple has shipped a broken and/or dysfunctional window manager with their
|
|
|
|
// operating system.
|
|
|
|
if (!userResolution)
|
|
|
|
windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI | getWindowFlags();
|
|
|
|
else
|
|
|
|
// If the user has changed the resolution from the command line, then add a
|
|
|
|
// border to the window.
|
|
|
|
windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | getWindowFlags();
|
2020-07-18 11:21:44 +00:00
|
|
|
#else
|
2020-08-23 20:40:15 +00:00
|
|
|
if (Settings::getInstance()->getBool("Windowed")) {
|
2020-06-26 15:17:35 +00:00
|
|
|
windowFlags = getWindowFlags();
|
2020-08-23 20:40:15 +00:00
|
|
|
}
|
|
|
|
else if (Settings::getInstance()->getString("FullscreenMode") == "borderless") {
|
|
|
|
if(!userResolution)
|
|
|
|
windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALWAYS_ON_TOP | getWindowFlags();
|
|
|
|
else
|
|
|
|
// If the user has changed the resolution from the command line, then add a
|
|
|
|
// border to the window and don't make it stay on top.
|
|
|
|
windowFlags = getWindowFlags();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
windowFlags = SDL_WINDOW_FULLSCREEN | getWindowFlags();
|
|
|
|
}
|
2020-08-18 15:48:21 +00:00
|
|
|
#endif
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-01-24 11:03:44 +00:00
|
|
|
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" <<
|
|
|
|
std::to_string(displayMode.h);
|
|
|
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" <<
|
|
|
|
std::to_string(windowHeight);
|
|
|
|
|
|
|
|
if ((sdlWindow = SDL_CreateWindow("EmulationStation",
|
|
|
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
|
|
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
|
|
|
windowWidth, windowHeight, windowFlags)) == nullptr) {
|
2020-08-18 15:48:21 +00:00
|
|
|
LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError();
|
2020-06-26 15:17:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-30 20:19:37 +00:00
|
|
|
LOG(LogInfo) << "Setting up OpenGL...";
|
|
|
|
|
|
|
|
if (!createContext())
|
|
|
|
return false;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
setIcon();
|
|
|
|
setSwapInterval();
|
|
|
|
|
2020-08-30 20:19:37 +00:00
|
|
|
#if defined(USE_OPENGL_21)
|
|
|
|
LOG(LogInfo) << "Loading shaders...";
|
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
std::vector<std::string> shaderFiles;
|
|
|
|
shaderFiles.push_back(":/shaders/glsl/desaturate.glsl");
|
2020-09-12 17:17:26 +00:00
|
|
|
shaderFiles.push_back(":/shaders/glsl/opacity.glsl");
|
2020-09-12 10:14:48 +00:00
|
|
|
shaderFiles.push_back(":/shaders/glsl/dim.glsl");
|
2020-09-04 16:59:19 +00:00
|
|
|
shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl");
|
|
|
|
shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl");
|
|
|
|
shaderFiles.push_back(":/shaders/glsl/scanlines.glsl");
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); it++) {
|
|
|
|
Shader* loadShader = new Shader();
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
loadShader->loadShaderFile(*it, GL_VERTEX_SHADER);
|
|
|
|
loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER);
|
|
|
|
|
|
|
|
if (!loadShader->createProgram()) {
|
|
|
|
LOG(LogError) << "Could not create shader program.";
|
|
|
|
return false;
|
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
sShaderProgramVector.push_back(loadShader);
|
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
#endif
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroyWindow()
|
|
|
|
{
|
2020-08-30 20:19:37 +00:00
|
|
|
#if defined(USE_OPENGL_21)
|
|
|
|
for (auto it = sShaderProgramVector.cbegin();
|
|
|
|
it != sShaderProgramVector.cend(); it++) {
|
|
|
|
delete *it;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
destroyContext();
|
|
|
|
SDL_DestroyWindow(sdlWindow);
|
|
|
|
|
|
|
|
sdlWindow = nullptr;
|
|
|
|
|
|
|
|
SDL_ShowCursor(initialCursorState);
|
|
|
|
SDL_Quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool init()
|
|
|
|
{
|
|
|
|
if (!createWindow())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Transform4x4f projection = Transform4x4f::Identity();
|
|
|
|
Rect viewport = Rect(0, 0, 0, 0);
|
|
|
|
|
|
|
|
switch (screenRotate) {
|
|
|
|
case 0: {
|
|
|
|
viewport.x = screenOffsetX;
|
|
|
|
viewport.y = screenOffsetY;
|
|
|
|
viewport.w = screenWidth;
|
|
|
|
viewport.h = screenHeight;
|
2020-12-29 11:54:24 +00:00
|
|
|
projection.orthoProjection(0.0f, static_cast<float>(screenWidth),
|
|
|
|
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1: {
|
|
|
|
viewport.x = windowWidth - screenOffsetY - screenHeight;
|
|
|
|
viewport.y = screenOffsetX;
|
|
|
|
viewport.w = screenHeight;
|
|
|
|
viewport.h = screenWidth;
|
2020-12-29 11:54:24 +00:00
|
|
|
projection.orthoProjection(0.0f, static_cast<float>(screenHeight),
|
|
|
|
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
|
2020-09-16 20:14:35 +00:00
|
|
|
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(90)), {0, 0, 1});
|
2020-06-26 15:17:35 +00:00
|
|
|
projection.translate({0, screenHeight * -1.0f, 0});
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: {
|
|
|
|
viewport.x = windowWidth - screenOffsetX - screenWidth;
|
|
|
|
viewport.y = windowHeight - screenOffsetY - screenHeight;
|
|
|
|
viewport.w = screenWidth;
|
|
|
|
viewport.h = screenHeight;
|
2020-12-29 11:54:24 +00:00
|
|
|
projection.orthoProjection(0.0f, static_cast<float>(screenWidth),
|
|
|
|
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
2020-09-16 20:14:35 +00:00
|
|
|
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(180)), {0, 0, 1});
|
2020-06-26 15:17:35 +00:00
|
|
|
projection.translate({screenWidth * -1.0f, screenHeight * -1.0f, 0});
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3: {
|
|
|
|
viewport.x = screenOffsetY;
|
|
|
|
viewport.y = windowHeight - screenOffsetX - screenWidth;
|
|
|
|
viewport.w = screenHeight;
|
|
|
|
viewport.h = screenWidth;
|
2020-12-29 11:54:24 +00:00
|
|
|
projection.orthoProjection(0.0f, static_cast<float>(screenHeight),
|
|
|
|
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
|
2020-09-16 20:14:35 +00:00
|
|
|
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(270)), {0, 0, 1});
|
2020-06-26 15:17:35 +00:00
|
|
|
projection.translate({screenWidth * -1.0f, 0, 0});
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-12 10:14:48 +00:00
|
|
|
mProjectionMatrix = projection;
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
setViewport(viewport);
|
|
|
|
setProjection(projection);
|
|
|
|
swapBuffers();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void deinit()
|
|
|
|
{
|
|
|
|
destroyWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pushClipRect(const Vector2i& _pos, const Vector2i& _size)
|
|
|
|
{
|
|
|
|
Rect box(_pos.x(), _pos.y(), _size.x(), _size.y());
|
|
|
|
|
|
|
|
if (box.w == 0)
|
|
|
|
box.w = screenWidth - box.x;
|
|
|
|
if (box.h == 0)
|
|
|
|
box.h = screenHeight - box.y;
|
|
|
|
|
|
|
|
switch (screenRotate) {
|
2020-09-16 20:14:35 +00:00
|
|
|
case 0:
|
2020-06-26 15:17:35 +00:00
|
|
|
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
|
|
|
|
break;
|
2020-09-16 20:14:35 +00:00
|
|
|
case 1:
|
|
|
|
box = Rect(windowWidth - screenOffsetY - box.y - box.h,
|
|
|
|
screenOffsetX + box.x, box.h, box.w);
|
2020-06-26 15:17:35 +00:00
|
|
|
break;
|
2020-09-16 20:14:35 +00:00
|
|
|
case 2:
|
2020-06-26 15:17:35 +00:00
|
|
|
box = Rect(windowWidth - screenOffsetX - box.x - box.w, windowHeight -
|
|
|
|
screenOffsetY - box.y - box.h, box.w, box.h);
|
|
|
|
break;
|
2020-09-16 20:14:35 +00:00
|
|
|
case 3:
|
|
|
|
box = Rect(screenOffsetY + box.y, windowHeight -
|
|
|
|
screenOffsetX - box.x - box.w, box.h, box.w);
|
2020-06-26 15:17:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the box fits within clipStack.top(), and clip further accordingly.
|
|
|
|
if (clipStack.size()) {
|
|
|
|
const Rect& top = clipStack.top();
|
|
|
|
if ( top.x > box.x) box.x = top.x;
|
|
|
|
if ( top.y > box.y) box.y = top.y;
|
|
|
|
if ((top.x + top.w) < (box.x + box.w)) box.w = (top.x + top.w) - box.x;
|
|
|
|
if ((top.y + top.h) < (box.y + box.h)) box.h = (top.y + top.h) - box.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (box.w < 0) box.w = 0;
|
|
|
|
if (box.h < 0) box.h = 0;
|
|
|
|
|
|
|
|
clipStack.push(box);
|
|
|
|
|
|
|
|
setScissor(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
void popClipRect()
|
|
|
|
{
|
|
|
|
if (clipStack.empty()) {
|
2021-01-15 17:47:01 +00:00
|
|
|
LOG(LogError) << "Tried to popClipRect while the stack was empty";
|
2020-06-26 15:17:35 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clipStack.pop();
|
|
|
|
|
|
|
|
if (clipStack.empty())
|
|
|
|
setScissor(Rect(0, 0, 0, 0));
|
|
|
|
else
|
|
|
|
setScissor(clipStack.top());
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawRect(
|
|
|
|
const float _x,
|
|
|
|
const float _y,
|
|
|
|
const float _w,
|
|
|
|
const float _h,
|
|
|
|
const unsigned int _color,
|
|
|
|
const unsigned int _colorEnd,
|
|
|
|
bool horizontalGradient,
|
2020-09-12 17:17:26 +00:00
|
|
|
const float _opacity,
|
|
|
|
const Transform4x4f& _trans,
|
2020-06-26 15:17:35 +00:00
|
|
|
const Blend::Factor _srcBlendFactor,
|
|
|
|
const Blend::Factor _dstBlendFactor)
|
|
|
|
{
|
2020-12-18 15:49:11 +00:00
|
|
|
const unsigned int color = convertRGBAToABGR(_color);
|
|
|
|
const unsigned int colorEnd = convertRGBAToABGR(_colorEnd);
|
2020-06-26 15:17:35 +00:00
|
|
|
Vertex vertices[4];
|
|
|
|
|
2021-01-16 21:55:38 +00:00
|
|
|
float _wL = _w;
|
|
|
|
float _hL = _h;
|
|
|
|
|
|
|
|
// If the width or height was scaled down to less than 1 pixel, then set it to
|
|
|
|
// 1 pixel so that it will still render on lower resolutions.
|
2021-01-16 22:01:13 +00:00
|
|
|
if (_wL > 0.0f && _wL < 1.0f)
|
|
|
|
_wL = 1.0f;
|
|
|
|
if (_hL > 0.0f && _hL < 1.0f)
|
|
|
|
_hL = 1.0f;
|
2021-01-16 21:55:38 +00:00
|
|
|
|
|
|
|
vertices[0] = { { _x , _y }, { 0.0f, 0.0f }, color };
|
|
|
|
vertices[1] = { { _x , _y + _hL }, { 0.0f, 0.0f }, horizontalGradient ? colorEnd : color };
|
|
|
|
vertices[2] = { { _x + _wL, _y }, { 0.0f, 0.0f }, horizontalGradient ? color : colorEnd };
|
|
|
|
vertices[3] = { { _x + _wL, _y + _hL }, { 0.0f, 0.0f }, colorEnd };
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
// Round vertices.
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
vertices[i].pos.round();
|
|
|
|
|
2020-09-12 17:17:26 +00:00
|
|
|
if (_opacity < 1.0) {
|
|
|
|
vertices[0].shaders = SHADER_OPACITY;
|
|
|
|
vertices[0].opacity = _opacity;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bindTexture(0);
|
|
|
|
}
|
2020-11-11 23:46:59 +00:00
|
|
|
drawTriangleStrips(vertices, 4, _trans, _srcBlendFactor, _dstBlendFactor);
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
|
|
|
|
2020-12-18 15:49:11 +00:00
|
|
|
unsigned int convertRGBAToABGR(const unsigned int _color)
|
2020-08-30 20:19:37 +00:00
|
|
|
{
|
|
|
|
unsigned char red = ((_color & 0xff000000) >> 24) & 255;
|
|
|
|
unsigned char green = ((_color & 0x00ff0000) >> 16) & 255;
|
|
|
|
unsigned char blue = ((_color & 0x0000ff00) >> 8) & 255;
|
|
|
|
unsigned char alpha = ((_color & 0x000000ff)) & 255;
|
|
|
|
|
|
|
|
return alpha << 24 | blue << 16 | green << 8 | red;
|
|
|
|
}
|
|
|
|
|
2020-12-18 15:49:11 +00:00
|
|
|
unsigned int convertABGRToRGBA(const unsigned int _color)
|
2020-08-30 20:19:37 +00:00
|
|
|
{
|
|
|
|
unsigned char alpha = ((_color & 0xff000000) >> 24) & 255;
|
|
|
|
unsigned char blue = ((_color & 0x00ff0000) >> 16) & 255;
|
|
|
|
unsigned char green = ((_color & 0x0000ff00) >> 8) & 255;
|
|
|
|
unsigned char red = ((_color & 0x000000ff)) & 255;
|
|
|
|
|
|
|
|
return red << 24 | green << 16 | blue << 8 | alpha;
|
|
|
|
}
|
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
Shader* getShaderProgram(unsigned int shaderID)
|
2020-08-30 20:19:37 +00:00
|
|
|
{
|
2020-09-04 16:59:19 +00:00
|
|
|
unsigned int index = 0;
|
|
|
|
|
|
|
|
// Find the index in sShaderProgramVector by counting the number
|
|
|
|
// of shifts required to reach 0.
|
|
|
|
while (shaderID > 0) {
|
|
|
|
shaderID = shaderID >> 1;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sShaderProgramVector.size() > index-1)
|
|
|
|
return sShaderProgramVector[index-1];
|
2020-08-30 20:19:37 +00:00
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
};
|
|
|
|
|
2020-09-12 10:14:48 +00:00
|
|
|
const Transform4x4f getProjectionMatrix()
|
|
|
|
{
|
|
|
|
return mProjectionMatrix;
|
|
|
|
}
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
SDL_Window* getSDLWindow() { return sdlWindow; }
|
|
|
|
int getWindowWidth() { return windowWidth; }
|
|
|
|
int getWindowHeight() { return windowHeight; }
|
|
|
|
int getScreenWidth() { return screenWidth; }
|
|
|
|
int getScreenHeight() { return screenHeight; }
|
|
|
|
int getScreenOffsetX() { return screenOffsetX; }
|
|
|
|
int getScreenOffsetY() { return screenOffsetY; }
|
|
|
|
int getScreenRotate() { return screenRotate; }
|
2021-01-13 18:42:06 +00:00
|
|
|
float getScreenWidthModifier() { return screenWidthModifier; }
|
|
|
|
float getScreenHeightModifier() { return screenHeightModifier; }
|
2019-08-08 20:16:11 +00:00
|
|
|
|
|
|
|
} // Renderer::
|