Put a number of OpenRCT2::Ui items in the OpenRCT2::Ui namespace

This commit is contained in:
Duncan 2024-03-07 22:36:49 +00:00 committed by GitHub
parent dec5140f20
commit cb100db56e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 980 additions and 926 deletions

View File

@ -11,6 +11,8 @@
#include <SDL.h>
using namespace OpenRCT2::Ui;
SDLException::SDLException(const std::string& message)
: runtime_error(message.c_str())
{

View File

@ -12,19 +12,22 @@
#include <stdexcept>
#include <string>
/**
* An exception which wraps an SDL error.
*/
class SDLException : public std::runtime_error
namespace OpenRCT2::Ui
{
public:
explicit SDLException(const std::string& message);
explicit SDLException(const char* message);
/**
* Throws an SDL exception with a message containing the given call information
* and the message given by SDL_GetError.
* An exception which wraps an SDL error.
*/
static void Throw(const char* call);
};
class SDLException : public std::runtime_error
{
public:
explicit SDLException(const std::string& message);
explicit SDLException(const char* message);
/**
* Throws an SDL exception with a message containing the given call information
* and the message given by SDL_GetError.
*/
static void Throw(const char* call);
};
} // namespace OpenRCT2::Ui

View File

@ -20,35 +20,35 @@ namespace OpenRCT2
{
struct IContext;
struct IPlatformEnvironment;
namespace Ui
{
struct FileDialogDesc;
class InGameConsole;
struct IUiContext;
struct IPlatformUiContext
{
virtual ~IPlatformUiContext() = default;
virtual void SetWindowIcon(SDL_Window* window) abstract;
virtual bool IsSteamOverlayAttached() abstract;
virtual void ShowMessageBox(SDL_Window* window, const std::string& message) abstract;
virtual bool HasMenuSupport() abstract;
virtual int32_t ShowMenuDialog(
const std::vector<std::string>& options, const std::string& title, const std::string& text) abstract;
virtual void OpenFolder(const std::string& path) abstract;
virtual void OpenURL(const std::string& url) abstract;
virtual std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) abstract;
virtual std::string ShowDirectoryDialog(SDL_Window* window, const std::string& title) abstract;
virtual bool HasFilePicker() const abstract;
};
[[nodiscard]] std::unique_ptr<IUiContext> CreateUiContext(const std::shared_ptr<IPlatformEnvironment>& env);
[[nodiscard]] std::unique_ptr<IPlatformUiContext> CreatePlatformUiContext();
[[nodiscard]] InGameConsole& GetInGameConsole();
} // namespace Ui
} // namespace OpenRCT2
namespace OpenRCT2::Ui
{
struct FileDialogDesc;
class InGameConsole;
struct IUiContext;
struct IPlatformUiContext
{
virtual ~IPlatformUiContext() = default;
virtual void SetWindowIcon(SDL_Window* window) abstract;
virtual bool IsSteamOverlayAttached() abstract;
virtual void ShowMessageBox(SDL_Window* window, const std::string& message) abstract;
virtual bool HasMenuSupport() abstract;
virtual int32_t ShowMenuDialog(
const std::vector<std::string>& options, const std::string& title, const std::string& text) abstract;
virtual void OpenFolder(const std::string& path) abstract;
virtual void OpenURL(const std::string& url) abstract;
virtual std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) abstract;
virtual std::string ShowDirectoryDialog(SDL_Window* window, const std::string& title) abstract;
virtual bool HasFilePicker() const abstract;
};
[[nodiscard]] std::unique_ptr<IUiContext> CreateUiContext(const std::shared_ptr<IPlatformEnvironment>& env);
[[nodiscard]] std::unique_ptr<IPlatformUiContext> CreatePlatformUiContext();
[[nodiscard]] InGameConsole& GetInGameConsole();
} // namespace OpenRCT2::Ui

View File

@ -34,7 +34,7 @@ namespace OpenRCT2::Audio
{
if (SDL_Init(SDL_INIT_AUDIO) < 0)
{
SDLException::Throw("SDL_Init(SDL_INIT_AUDIO)");
Ui::SDLException::Throw("SDL_Init(SDL_INIT_AUDIO)");
}
_audioMixer = std::make_unique<AudioMixer>();
}

View File

@ -15,90 +15,93 @@
#include <openrct2/core/Imaging.h>
#include <stdexcept>
static std::vector<uint8_t> ReadToVector(std::istream& stream)
namespace OpenRCT2::Ui
{
std::vector<uint8_t> result;
if (!stream.eof() && !stream.fail())
static std::vector<uint8_t> ReadToVector(std::istream& stream)
{
stream.seekg(0, std::ios_base::end);
auto size = stream.tellg();
result.resize(size);
stream.seekg(0, std::ios_base::beg);
stream.read(reinterpret_cast<char*>(result.data()), size);
}
return result;
}
// TODO Bitmaps aren't very complicated to read so we should probably just write our
// own implementation in libopenrct2 and spare the AOT implementation registration.
static Image ReadBitmap(std::istream& istream, IMAGE_FORMAT format)
{
auto buffer = ReadToVector(istream);
auto sdlStream = SDL_RWFromConstMem(buffer.data(), static_cast<int>(buffer.size()));
auto bitmap = SDL_LoadBMP_RW(sdlStream, 1);
if (bitmap != nullptr)
{
auto numChannels = bitmap->format->BytesPerPixel;
if (numChannels < 3 || bitmap->format->BitsPerPixel < 24)
std::vector<uint8_t> result;
if (!stream.eof() && !stream.fail())
{
SDL_FreeSurface(bitmap);
throw std::runtime_error("Only 24-bit bitmaps are supported.");
stream.seekg(0, std::ios_base::end);
auto size = stream.tellg();
result.resize(size);
stream.seekg(0, std::ios_base::beg);
stream.read(reinterpret_cast<char*>(result.data()), size);
}
return result;
}
// Copy pixels over, then discard the surface
if (SDL_LockSurface(bitmap) == 0)
// TODO Bitmaps aren't very complicated to read so we should probably just write our
// own implementation in libopenrct2 and spare the AOT implementation registration.
static Image ReadBitmap(std::istream& istream, IMAGE_FORMAT format)
{
auto buffer = ReadToVector(istream);
auto sdlStream = SDL_RWFromConstMem(buffer.data(), static_cast<int>(buffer.size()));
auto bitmap = SDL_LoadBMP_RW(sdlStream, 1);
if (bitmap != nullptr)
{
Image image;
image.Width = bitmap->w;
image.Height = bitmap->h;
image.Depth = 32;
image.Pixels.resize(bitmap->w * bitmap->h * 4);
image.Stride = bitmap->w * 4;
// Clear image with 0xFF
std::fill(image.Pixels.begin(), image.Pixels.end(), 0xFF);
// Copy pixels over
auto src = static_cast<const uint8_t*>(bitmap->pixels);
auto dst = image.Pixels.data();
if (numChannels == 4)
auto numChannels = bitmap->format->BytesPerPixel;
if (numChannels < 3 || bitmap->format->BitsPerPixel < 24)
{
for (int32_t y = 0; y < bitmap->h; y++)
{
std::memcpy(dst, src, bitmap->w);
src += bitmap->pitch;
dst += bitmap->w;
}
SDL_FreeSurface(bitmap);
throw std::runtime_error("Only 24-bit bitmaps are supported.");
}
else
// Copy pixels over, then discard the surface
if (SDL_LockSurface(bitmap) == 0)
{
for (int32_t y = 0; y < bitmap->h; y++)
Image image;
image.Width = bitmap->w;
image.Height = bitmap->h;
image.Depth = 32;
image.Pixels.resize(bitmap->w * bitmap->h * 4);
image.Stride = bitmap->w * 4;
// Clear image with 0xFF
std::fill(image.Pixels.begin(), image.Pixels.end(), 0xFF);
// Copy pixels over
auto src = static_cast<const uint8_t*>(bitmap->pixels);
auto dst = image.Pixels.data();
if (numChannels == 4)
{
for (int32_t x = 0; x < bitmap->w; x++)
for (int32_t y = 0; y < bitmap->h; y++)
{
std::memcpy(dst, src, 3);
src += 3;
dst += 4;
std::memcpy(dst, src, bitmap->w);
src += bitmap->pitch;
dst += bitmap->w;
}
src += bitmap->pitch - (bitmap->w * 3);
}
else
{
for (int32_t y = 0; y < bitmap->h; y++)
{
for (int32_t x = 0; x < bitmap->w; x++)
{
std::memcpy(dst, src, 3);
src += 3;
dst += 4;
}
src += bitmap->pitch - (bitmap->w * 3);
}
}
SDL_UnlockSurface(bitmap);
SDL_FreeSurface(bitmap);
return image;
}
SDL_UnlockSurface(bitmap);
SDL_FreeSurface(bitmap);
return image;
throw std::runtime_error("Unable to lock surface.");
}
else
{
throw std::runtime_error(SDL_GetError());
}
SDL_FreeSurface(bitmap);
throw std::runtime_error("Unable to lock surface.");
}
else
void RegisterBitmapReader()
{
throw std::runtime_error(SDL_GetError());
Imaging::SetReader(IMAGE_FORMAT::BITMAP, ReadBitmap);
}
}
void RegisterBitmapReader()
{
Imaging::SetReader(IMAGE_FORMAT::BITMAP, ReadBitmap);
}
} // namespace OpenRCT2::Ui

View File

@ -9,4 +9,7 @@
#pragma once
void RegisterBitmapReader();
namespace OpenRCT2::Ui
{
void RegisterBitmapReader();
}

View File

@ -13,41 +13,38 @@
#include <openrct2/common.h>
#include <openrct2/drawing/IDrawingEngine.h>
namespace OpenRCT2
namespace OpenRCT2::Ui
{
namespace Ui
struct IUiContext;
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> CreateSoftwareDrawingEngine(
const std::shared_ptr<IUiContext>& uiContext);
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> CreateHardwareDisplayDrawingEngine(
const std::shared_ptr<IUiContext>& uiContext);
#ifndef DISABLE_OPENGL
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> CreateOpenGLDrawingEngine(
const std::shared_ptr<IUiContext>& uiContext);
#endif
class DrawingEngineFactory final : public Drawing::IDrawingEngineFactory
{
using namespace OpenRCT2::Drawing;
struct IUiContext;
[[nodiscard]] std::unique_ptr<IDrawingEngine> CreateSoftwareDrawingEngine(const std::shared_ptr<IUiContext>& uiContext);
[[nodiscard]] std::unique_ptr<IDrawingEngine> CreateHardwareDisplayDrawingEngine(
const std::shared_ptr<IUiContext>& uiContext);
#ifndef DISABLE_OPENGL
[[nodiscard]] std::unique_ptr<IDrawingEngine> CreateOpenGLDrawingEngine(const std::shared_ptr<IUiContext>& uiContext);
#endif
class DrawingEngineFactory final : public IDrawingEngineFactory
public:
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> Create(
DrawingEngine type, const std::shared_ptr<IUiContext>& uiContext) override
{
public:
[[nodiscard]] std::unique_ptr<IDrawingEngine> Create(
DrawingEngine type, const std::shared_ptr<IUiContext>& uiContext) override
switch (type)
{
switch (type)
{
case DrawingEngine::Software:
return CreateSoftwareDrawingEngine(uiContext);
case DrawingEngine::SoftwareWithHardwareDisplay:
return CreateHardwareDisplayDrawingEngine(uiContext);
case DrawingEngine::Software:
return CreateSoftwareDrawingEngine(uiContext);
case DrawingEngine::SoftwareWithHardwareDisplay:
return CreateHardwareDisplayDrawingEngine(uiContext);
#ifndef DISABLE_OPENGL
case DrawingEngine::OpenGL:
return CreateOpenGLDrawingEngine(uiContext);
case DrawingEngine::OpenGL:
return CreateOpenGLDrawingEngine(uiContext);
#endif
default:
return nullptr;
}
default:
return nullptr;
}
};
} // namespace Ui
} // namespace OpenRCT2
}
};
} // namespace OpenRCT2::Ui

View File

@ -11,6 +11,8 @@
# include "ApplyPaletteShader.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -12,27 +12,30 @@
#include "GLSLTypes.h"
#include "OpenGLShaderProgram.h"
class ApplyPaletteShader final : public OpenGLShaderProgram
namespace OpenRCT2::Ui
{
private:
GLuint uTexture;
GLuint uPalette;
class ApplyPaletteShader final : public OpenGLShaderProgram
{
private:
GLuint uTexture;
GLuint uPalette;
GLuint vPosition;
GLuint vTextureCoordinate;
GLuint vPosition;
GLuint vTextureCoordinate;
GLuint _vbo;
GLuint _vao;
GLuint _vbo;
GLuint _vao;
public:
ApplyPaletteShader();
~ApplyPaletteShader() override;
public:
ApplyPaletteShader();
~ApplyPaletteShader() override;
static void SetTexture(GLuint texture);
void SetPalette(const vec4* glPalette);
static void SetTexture(GLuint texture);
void SetPalette(const vec4* glPalette);
void Draw();
void Draw();
private:
void GetLocations();
};
private:
void GetLocations();
};
} // namespace OpenRCT2::Ui

View File

@ -11,6 +11,8 @@
# include "ApplyTransparencyShader.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -11,32 +11,34 @@
#include "GLSLTypes.h"
#include "OpenGLShaderProgram.h"
class ApplyTransparencyShader final : public OpenGLShaderProgram
namespace OpenRCT2::Ui
{
private:
GLuint uOpaqueTex;
GLuint uOpaqueDepth;
GLuint uTransparentTex;
GLuint uTransparentDepth;
GLuint uPaletteTex;
GLuint uBlendPaletteTex;
class ApplyTransparencyShader final : public OpenGLShaderProgram
{
private:
GLuint uOpaqueTex;
GLuint uOpaqueDepth;
GLuint uTransparentTex;
GLuint uTransparentDepth;
GLuint uPaletteTex;
GLuint uBlendPaletteTex;
GLuint vPosition;
GLuint vTextureCoordinate;
GLuint vPosition;
GLuint vTextureCoordinate;
GLuint _vbo;
GLuint _vao;
GLuint _vbo;
GLuint _vao;
public:
ApplyTransparencyShader();
~ApplyTransparencyShader() override;
public:
ApplyTransparencyShader();
~ApplyTransparencyShader() override;
static void SetTextures(
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex,
GLuint blendPaletteTex);
void Draw();
static void SetTextures(
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex,
GLuint blendPaletteTex);
void Draw();
private:
void GetLocations();
};
private:
void GetLocations();
};
} // namespace OpenRCT2::Ui

View File

@ -15,114 +15,116 @@
#include <openrct2/common.h>
#include <vector>
template<typename T> class CommandBatch
namespace OpenRCT2::Ui
{
private:
std::vector<T> _instances;
size_t _numInstances;
template<typename T> class CommandBatch
{
private:
std::vector<T> _instances;
size_t _numInstances;
public:
CommandBatch()
: _numInstances(0)
{
}
[[nodiscard]] bool empty() const // NOLINT(readability-identifier-naming)
{
return _numInstances == 0;
}
void clear() // NOLINT(readability-identifier-naming)
{
_numInstances = 0;
}
T& allocate() // NOLINT(readability-identifier-naming)
{
if (_numInstances + 1 > _instances.size())
public:
CommandBatch()
: _numInstances(0)
{
_instances.resize((_numInstances + 1) << 1);
}
return _instances[_numInstances++];
}
T& insert(const T& value) // NOLINT(readability-identifier-naming)
{
if (_numInstances + 1 > _instances.size())
[[nodiscard]] bool empty() const // NOLINT(readability-identifier-naming)
{
_instances.resize((_numInstances + 1) << 1);
return _numInstances == 0;
}
void clear() // NOLINT(readability-identifier-naming)
{
_numInstances = 0;
}
T& allocate() // NOLINT(readability-identifier-naming)
{
if (_numInstances + 1 > _instances.size())
{
_instances.resize((_numInstances + 1) << 1);
}
return _instances[_numInstances++];
}
T& insert(const T& value) // NOLINT(readability-identifier-naming)
{
if (_numInstances + 1 > _instances.size())
{
_instances.resize((_numInstances + 1) << 1);
}
return _instances[_numInstances++] = value;
}
[[nodiscard]] size_t size() const // NOLINT(readability-identifier-naming)
{
return _numInstances;
}
const T* data() const // NOLINT(readability-identifier-naming)
{
return _instances.data();
}
const T& operator[](size_t idx) const
{
return _instances.at(idx);
}
return _instances[_numInstances++] = value;
}
[[nodiscard]] size_t size() const // NOLINT(readability-identifier-naming)
{
return _numInstances;
}
const T* data() const // NOLINT(readability-identifier-naming)
{
return _instances.data();
}
const T& operator[](size_t idx) const
{
return _instances.at(idx);
}
typename std::vector<T>::iterator begin() // NOLINT(readability-identifier-naming)
{
return _instances.begin();
}
typename std::vector<T>::const_iterator begin() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin();
}
typename std::vector<T>::const_iterator cbegin() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin();
}
typename std::vector<T>::iterator end() // NOLINT(readability-identifier-naming)
{
return _instances.begin() + _numInstances;
}
typename std::vector<T>::const_iterator end() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin() + _numInstances;
}
typename std::vector<T>::const_iterator cend() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin() + _numInstances;
}
};
struct DrawLineCommand
{
ivec4 clip;
ivec4 bounds;
GLuint colour;
GLint depth;
};
// Per-instance data for images
struct DrawRectCommand
{
ivec4 clip;
GLint texColourAtlas;
vec4 texColourBounds;
GLint texMaskAtlas;
vec4 texMaskBounds;
ivec3 palettes;
GLint flags;
GLuint colour;
ivec4 bounds;
GLint depth;
enum
{
FLAG_NO_TEXTURE = (1u << 2u),
FLAG_MASK = (1u << 3u),
FLAG_CROSS_HATCH = (1u << 4u),
FLAG_TTF_TEXT = (1u << 5u),
// bits 8 to 16 used to store hinting threshold.
FLAG_TTF_HINTING_THRESHOLD_MASK = 0xff00
typename std::vector<T>::iterator begin() // NOLINT(readability-identifier-naming)
{
return _instances.begin();
}
typename std::vector<T>::const_iterator begin() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin();
}
typename std::vector<T>::const_iterator cbegin() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin();
}
typename std::vector<T>::iterator end() // NOLINT(readability-identifier-naming)
{
return _instances.begin() + _numInstances;
}
typename std::vector<T>::const_iterator end() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin() + _numInstances;
}
typename std::vector<T>::const_iterator cend() const // NOLINT(readability-identifier-naming)
{
return _instances.cbegin() + _numInstances;
}
};
};
using LineCommandBatch = CommandBatch<DrawLineCommand>;
using RectCommandBatch = CommandBatch<DrawRectCommand>;
struct DrawLineCommand
{
ivec4 clip;
ivec4 bounds;
GLuint colour;
GLint depth;
};
// Per-instance data for images
struct DrawRectCommand
{
ivec4 clip;
GLint texColourAtlas;
vec4 texColourBounds;
GLint texMaskAtlas;
vec4 texMaskBounds;
ivec3 palettes;
GLint flags;
GLuint colour;
ivec4 bounds;
GLint depth;
enum
{
FLAG_NO_TEXTURE = (1u << 2u),
FLAG_MASK = (1u << 3u),
FLAG_CROSS_HATCH = (1u << 4u),
FLAG_TTF_TEXT = (1u << 5u),
// bits 8 to 16 used to store hinting threshold.
FLAG_TTF_HINTING_THRESHOLD_MASK = 0xff00
};
};
using LineCommandBatch = CommandBatch<DrawLineCommand>;
using RectCommandBatch = CommandBatch<DrawRectCommand>;
} // namespace OpenRCT2::Ui

View File

@ -13,6 +13,8 @@
# include "OpenGLFramebuffer.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -12,30 +12,32 @@
#include "DrawCommands.h"
#include "GLSLTypes.h"
#include "OpenGLShaderProgram.h"
class DrawLineShader final : public OpenGLShaderProgram
namespace OpenRCT2::Ui
{
private:
GLuint uScreenSize;
class DrawLineShader final : public OpenGLShaderProgram
{
private:
GLuint uScreenSize;
GLuint vClip;
GLuint vBounds;
GLuint vColour;
GLuint vDepth;
GLuint vClip;
GLuint vBounds;
GLuint vColour;
GLuint vDepth;
GLuint vVertMat;
GLuint vVertMat;
GLuint _vbo;
GLuint _vboInstances;
GLuint _vao;
GLuint _vbo;
GLuint _vboInstances;
GLuint _vao;
public:
DrawLineShader();
~DrawLineShader() override;
public:
DrawLineShader();
~DrawLineShader() override;
void SetScreenSize(int32_t width, int32_t height);
void DrawInstances(const LineCommandBatch& instances);
void SetScreenSize(int32_t width, int32_t height);
void DrawInstances(const LineCommandBatch& instances);
private:
void GetLocations();
};
private:
void GetLocations();
};
} // namespace OpenRCT2::Ui

View File

@ -11,6 +11,8 @@
# include "DrawRectShader.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -14,49 +14,51 @@
#include "OpenGLShaderProgram.h"
#include <SDL_pixels.h>
class DrawRectShader final : public OpenGLShaderProgram
namespace OpenRCT2::Ui
{
private:
GLuint uScreenSize;
GLuint uTexture;
GLuint uPaletteTex;
class DrawRectShader final : public OpenGLShaderProgram
{
private:
GLuint uScreenSize;
GLuint uTexture;
GLuint uPaletteTex;
GLuint uPeelingTex;
GLuint uPeeling;
GLuint uPeelingTex;
GLuint uPeeling;
GLuint vVertMat;
GLuint vVertVec;
GLuint vVertMat;
GLuint vVertVec;
GLuint vClip;
GLuint vTexColourAtlas;
GLuint vTexColourBounds;
GLuint vTexMaskAtlas;
GLuint vTexMaskBounds;
GLuint vPalettes;
GLuint vFlags;
GLuint vColour;
GLuint vBounds;
GLuint vDepth;
GLuint vClip;
GLuint vTexColourAtlas;
GLuint vTexColourBounds;
GLuint vTexMaskAtlas;
GLuint vTexMaskBounds;
GLuint vPalettes;
GLuint vFlags;
GLuint vColour;
GLuint vBounds;
GLuint vDepth;
GLuint _vbo;
GLuint _vboInstances;
GLuint _vao;
GLuint _vbo;
GLuint _vboInstances;
GLuint _vao;
GLsizei _instanceCount = 0;
size_t _maxInstancesBufferSize;
GLsizei _instanceCount = 0;
size_t _maxInstancesBufferSize;
public:
DrawRectShader();
~DrawRectShader() override;
public:
DrawRectShader();
~DrawRectShader() override;
void SetScreenSize(int32_t width, int32_t height);
void EnablePeeling(GLuint peelingTex);
void DisablePeeling();
void SetScreenSize(int32_t width, int32_t height);
void EnablePeeling(GLuint peelingTex);
void DisablePeeling();
void SetInstances(const RectCommandBatch& instances);
void DrawInstances();
void SetInstances(const RectCommandBatch& instances);
void DrawInstances();
private:
void GetLocations();
};
private:
void GetLocations();
};
} // namespace OpenRCT2::Ui

View File

@ -13,100 +13,102 @@
#include <openrct2/common.h>
#pragma pack(push, 1)
namespace detail
namespace OpenRCT2::Ui
{
template<typename T_> struct Vec2
#pragma pack(push, 1)
namespace detail
{
using ValueType = T_;
union
template<typename T_> struct Vec2
{
ValueType x;
ValueType s;
ValueType r;
using ValueType = T_;
union
{
ValueType x;
ValueType s;
ValueType r;
};
union
{
ValueType y;
ValueType t;
ValueType g;
};
};
union
template struct Vec2<GLfloat>;
template struct Vec2<GLint>;
template<typename T_> struct Vec3
{
ValueType y;
ValueType t;
ValueType g;
using ValueType = T_;
union
{
ValueType x;
ValueType s;
ValueType r;
};
union
{
ValueType y;
ValueType t;
ValueType g;
};
union
{
ValueType z;
ValueType p;
ValueType b;
};
};
};
template struct Vec2<GLfloat>;
template struct Vec2<GLint>;
template struct Vec3<GLfloat>;
template struct Vec3<GLint>;
template<typename T_> struct Vec3
{
using ValueType = T_;
union
template<typename T_> struct Vec4
{
ValueType x;
ValueType s;
ValueType r;
using ValueType = T_;
union
{
ValueType x;
ValueType s;
ValueType r;
};
union
{
ValueType y;
ValueType t;
ValueType g;
};
union
{
ValueType z;
ValueType p;
ValueType b;
};
union
{
ValueType w;
ValueType q;
ValueType a;
};
};
union
{
ValueType y;
ValueType t;
ValueType g;
};
union
{
ValueType z;
ValueType p;
ValueType b;
};
};
template struct Vec3<GLfloat>;
template struct Vec3<GLint>;
template struct Vec4<GLfloat>;
template struct Vec4<GLint>;
template<typename T_> struct Vec4
{
using ValueType = T_;
} // namespace detail
union
{
ValueType x;
ValueType s;
ValueType r;
};
union
{
ValueType y;
ValueType t;
ValueType g;
};
union
{
ValueType z;
ValueType p;
ValueType b;
};
union
{
ValueType w;
ValueType q;
ValueType a;
};
};
using vec2 = detail::Vec2<GLfloat>;
using ivec2 = detail::Vec2<GLint>;
template struct Vec4<GLfloat>;
template struct Vec4<GLint>;
using vec3 = detail::Vec3<GLfloat>;
using ivec3 = detail::Vec3<GLint>;
} // namespace detail
using vec2 = detail::Vec2<GLfloat>;
using ivec2 = detail::Vec2<GLint>;
using vec3 = detail::Vec3<GLfloat>;
using ivec3 = detail::Vec3<GLint>;
using vec4 = detail::Vec4<GLfloat>;
using ivec4 = detail::Vec4<GLint>;
using vec4 = detail::Vec4<GLfloat>;
using ivec4 = detail::Vec4<GLint>;
#pragma pack(pop)
} // namespace OpenRCT2::Ui

View File

@ -38,7 +38,9 @@ static const char* TryLoadAllProcAddresses()
# endif /* #if OPENGL_NO_LINK */
namespace OpenGLState
using namespace OpenRCT2::Ui;
namespace OpenRCT2::Ui::OpenGLState
{
uint16_t ActiveTexture;
GLuint CurrentProgram;
@ -48,7 +50,7 @@ namespace OpenGLState
ActiveTexture = UINT16_MAX;
CurrentProgram = UINT32_MAX;
}
} // namespace OpenGLState
} // namespace OpenRCT2::Ui::OpenGLState
void OpenGLAPI::SetTexture(uint16_t index, GLenum type, GLuint texture)
{

View File

@ -113,26 +113,29 @@ using PFNGLGETTEXIMAGEPROC = void(APIENTRYP)(GLenum target, GLint level, GLenum
#endif /* OPENGL_NO_LINK */
inline void CheckGLError()
namespace OpenRCT2::Ui
{
GLenum error = glGetError();
while (error != GL_NO_ERROR)
inline void CheckGLError()
{
LOG_ERROR("OpenGL Error 0x%04X", error);
error = glGetError();
GLenum error = glGetError();
while (error != GL_NO_ERROR)
{
LOG_ERROR("OpenGL Error 0x%04X", error);
error = glGetError();
}
}
}
namespace OpenGLAPI
{
bool Initialise();
void SetTexture(uint16_t index, GLenum type, GLuint texture);
} // namespace OpenGLAPI
namespace OpenGLAPI
{
bool Initialise();
void SetTexture(uint16_t index, GLenum type, GLuint texture);
} // namespace OpenGLAPI
namespace OpenGLState
{
extern uint16_t ActiveTexture;
extern GLuint CurrentProgram;
namespace OpenGLState
{
extern uint16_t ActiveTexture;
extern GLuint CurrentProgram;
void Reset();
} // namespace OpenGLState
void Reset();
} // namespace OpenGLState
} // namespace OpenRCT2::Ui

View File

@ -16,6 +16,8 @@
# include <memory>
# include <openrct2/common.h>
using namespace OpenRCT2::Ui;
constexpr GLuint BACKBUFFER_ID = 0;
OpenGLFramebuffer::OpenGLFramebuffer(SDL_Window* window)

View File

@ -16,49 +16,51 @@
#include <vector>
struct SDL_Window;
class OpenGLFramebuffer
namespace OpenRCT2::Ui
{
private:
GLuint _id;
GLuint _texture;
GLuint _depth;
int32_t _width;
int32_t _height;
public:
explicit OpenGLFramebuffer(SDL_Window* window);
OpenGLFramebuffer(int32_t width, int32_t height, bool depth = true, bool integer = true, bool word = false);
~OpenGLFramebuffer();
OpenGLFramebuffer(const OpenGLFramebuffer&) = delete;
OpenGLFramebuffer& operator=(const OpenGLFramebuffer&) = delete;
GLuint GetWidth() const
class OpenGLFramebuffer
{
return _width;
}
GLuint GetHeight() const
{
return _height;
}
GLuint GetTexture() const
{
return _texture;
}
GLuint GetDepthTexture() const
{
return _depth;
}
private:
GLuint _id;
GLuint _texture;
GLuint _depth;
int32_t _width;
int32_t _height;
void Bind() const;
void BindDraw() const;
void BindRead() const;
void GetPixels(DrawPixelInfo& dpi) const;
public:
explicit OpenGLFramebuffer(SDL_Window* window);
OpenGLFramebuffer(int32_t width, int32_t height, bool depth = true, bool integer = true, bool word = false);
~OpenGLFramebuffer();
void SwapColourBuffer(OpenGLFramebuffer& other);
GLuint SwapDepthTexture(GLuint depth);
void Copy(OpenGLFramebuffer& src, GLenum filter);
OpenGLFramebuffer(const OpenGLFramebuffer&) = delete;
OpenGLFramebuffer& operator=(const OpenGLFramebuffer&) = delete;
static GLuint CreateDepthTexture(int32_t width, int32_t height);
};
GLuint GetWidth() const
{
return _width;
}
GLuint GetHeight() const
{
return _height;
}
GLuint GetTexture() const
{
return _texture;
}
GLuint GetDepthTexture() const
{
return _depth;
}
void Bind() const;
void BindDraw() const;
void BindRead() const;
void GetPixels(DrawPixelInfo& dpi) const;
void SwapColourBuffer(OpenGLFramebuffer& other);
GLuint SwapDepthTexture(GLuint depth);
void Copy(OpenGLFramebuffer& src, GLenum filter);
static GLuint CreateDepthTexture(int32_t width, int32_t height);
};
} // namespace OpenRCT2::Ui

View File

@ -18,7 +18,7 @@
# include <openrct2/core/Path.hpp>
# include <openrct2/core/String.hpp>
using namespace OpenRCT2;
using namespace OpenRCT2::Ui;
OpenGLShader::OpenGLShader(const char* name, GLenum type)
: _type(type)

View File

@ -14,43 +14,45 @@
#include <memory>
#include <openrct2/common.h>
#include <string>
class OpenGLShader final
namespace OpenRCT2::Ui
{
private:
static constexpr uint64_t MaxSourceSize = 8 * 1024 * 1024; // 8 MiB
class OpenGLShader final
{
private:
static constexpr uint64_t MaxSourceSize = 8 * 1024 * 1024; // 8 MiB
GLenum _type;
GLuint _id = 0;
GLenum _type;
GLuint _id = 0;
public:
OpenGLShader(const char* name, GLenum type);
~OpenGLShader();
public:
OpenGLShader(const char* name, GLenum type);
~OpenGLShader();
GLuint GetShaderId();
GLuint GetShaderId();
private:
std::string GetPath(const std::string& name);
static std::string ReadSourceCode(const std::string& path);
};
private:
std::string GetPath(const std::string& name);
static std::string ReadSourceCode(const std::string& path);
};
class OpenGLShaderProgram
{
private:
GLuint _id = 0;
std::unique_ptr<OpenGLShader> _vertexShader;
std::unique_ptr<OpenGLShader> _fragmentShader;
class OpenGLShaderProgram
{
private:
GLuint _id = 0;
std::unique_ptr<OpenGLShader> _vertexShader;
std::unique_ptr<OpenGLShader> _fragmentShader;
public:
explicit OpenGLShaderProgram(const char* name);
explicit OpenGLShaderProgram(const OpenGLShaderProgram&) = delete;
explicit OpenGLShaderProgram(OpenGLShaderProgram&&) = default;
virtual ~OpenGLShaderProgram();
public:
explicit OpenGLShaderProgram(const char* name);
explicit OpenGLShaderProgram(const OpenGLShaderProgram&) = delete;
explicit OpenGLShaderProgram(OpenGLShaderProgram&&) = default;
virtual ~OpenGLShaderProgram();
GLuint GetAttributeLocation(const char* name);
GLuint GetUniformLocation(const char* name);
void Use();
GLuint GetAttributeLocation(const char* name);
GLuint GetUniformLocation(const char* name);
void Use();
private:
bool Link();
};
private:
bool Link();
};
} // namespace OpenRCT2::Ui

View File

@ -13,6 +13,8 @@
# include "OpenGLFramebuffer.h"
using namespace OpenRCT2::Ui;
constexpr GLfloat depthValue[1] = { 1.0f };
constexpr GLfloat depthValueTransparent[1] = { 0.0f };
constexpr GLuint indexValue[4] = { 0, 0, 0, 0 };

View File

@ -14,44 +14,46 @@
#include "OpenGLFramebuffer.h"
#include <openrct2/common.h>
/**
* Class to maintain two different framebuffers where the active framebuffer
* will swap between the two, copying the other's pixels in the process for
* performing pre-processing filters.
*
* When you need to bind the current frame to a shader, call SwapCopy and
* then bind the value of GetSourceTexture to your shader.
*/
class SwapFramebuffer final
namespace OpenRCT2::Ui
{
private:
OpenGLFramebuffer _opaqueFramebuffer;
OpenGLFramebuffer _transparentFramebuffer;
OpenGLFramebuffer _mixFramebuffer;
GLuint _backDepth;
/**
* Class to maintain two different framebuffers where the active framebuffer
* will swap between the two, copying the other's pixels in the process for
* performing pre-processing filters.
*
* When you need to bind the current frame to a shader, call SwapCopy and
* then bind the value of GetSourceTexture to your shader.
*/
class SwapFramebuffer final
{
private:
OpenGLFramebuffer _opaqueFramebuffer;
OpenGLFramebuffer _transparentFramebuffer;
OpenGLFramebuffer _mixFramebuffer;
GLuint _backDepth;
public:
SwapFramebuffer(int32_t width, int32_t height);
~SwapFramebuffer();
public:
SwapFramebuffer(int32_t width, int32_t height);
~SwapFramebuffer();
const OpenGLFramebuffer& GetFinalFramebuffer() const
{
return _opaqueFramebuffer;
}
GLuint GetBackDepthTexture() const
{
return _backDepth;
}
void BindOpaque()
{
_opaqueFramebuffer.Bind();
}
void BindTransparent()
{
_transparentFramebuffer.Bind();
}
const OpenGLFramebuffer& GetFinalFramebuffer() const
{
return _opaqueFramebuffer;
}
GLuint GetBackDepthTexture() const
{
return _backDepth;
}
void BindOpaque()
{
_opaqueFramebuffer.Bind();
}
void BindTransparent()
{
_transparentFramebuffer.Bind();
}
void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex);
void Clear();
};
void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex);
void Clear();
};
} // namespace OpenRCT2::Ui

View File

@ -19,6 +19,8 @@
# include <stdexcept>
# include <vector>
using namespace OpenRCT2::Ui;
constexpr uint32_t UNUSED_INDEX = 0xFFFFFFFF;
TextureCache::TextureCache()

View File

@ -29,220 +29,223 @@ struct DrawPixelInfo;
struct PaletteMap;
enum class FilterPaletteID : int32_t;
struct GlyphId
namespace OpenRCT2::Ui
{
ImageIndex Image;
uint64_t Palette;
struct Hash
struct GlyphId
{
size_t operator()(const GlyphId& k) const
ImageIndex Image;
uint64_t Palette;
struct Hash
{
size_t hash = k.Image * 7;
hash += (k.Palette & 0xFFFFFFFFUL) * 13;
hash += (k.Palette >> 32uL) * 23;
return hash;
size_t operator()(const GlyphId& k) const
{
size_t hash = k.Image * 7;
hash += (k.Palette & 0xFFFFFFFFUL) * 13;
hash += (k.Palette >> 32uL) * 23;
return hash;
}
};
struct Equal
{
bool operator()(const GlyphId& lhs, const GlyphId& rhs) const
{
return lhs.Image == rhs.Image && lhs.Palette == rhs.Palette;
}
};
};
// This is the maximum width and height of each atlas, basically the
// granularity at which new atlases are allocated (2048 -> 4 MB of VRAM)
constexpr int32_t TEXTURE_CACHE_MAX_ATLAS_SIZE = 2048;
// Pixel dimensions of smallest supported slots in texture atlases
// Must be a power of 2!
constexpr int32_t TEXTURE_CACHE_SMALLEST_SLOT = 32;
struct BasicTextureInfo
{
GLuint index;
vec4 normalizedBounds;
};
// Location of an image (texture atlas index, slot and normalized coordinates)
struct AtlasTextureInfo : public BasicTextureInfo
{
GLuint slot;
ivec4 bounds;
ImageIndex image;
};
// Represents a texture atlas that images of a given maximum size can be allocated from
// Atlases are all stored in the same 2D texture array, occupying the specified index
// Slots in atlases are always squares.
class Atlas final
{
private:
GLuint _index = 0;
int32_t _imageSize = 0;
int32_t _atlasWidth = 0;
int32_t _atlasHeight = 0;
std::vector<GLuint> _freeSlots;
int32_t _cols = 0;
int32_t _rows = 0;
public:
Atlas(GLuint index, int32_t imageSize)
: _index(index)
, _imageSize(imageSize)
{
}
void Initialise(int32_t atlasWidth, int32_t atlasHeight)
{
_atlasWidth = atlasWidth;
_atlasHeight = atlasHeight;
_cols = std::max(1, _atlasWidth / _imageSize);
_rows = std::max(1, _atlasHeight / _imageSize);
_freeSlots.resize(_cols * _rows);
for (size_t i = 0; i < _freeSlots.size(); i++)
{
_freeSlots[i] = static_cast<GLuint>(i);
}
}
AtlasTextureInfo Allocate(int32_t actualWidth, int32_t actualHeight)
{
assert(!_freeSlots.empty());
GLuint slot = _freeSlots.back();
_freeSlots.pop_back();
auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight);
AtlasTextureInfo info{};
info.index = _index;
info.slot = slot;
info.bounds = bounds;
info.normalizedBounds = NormalizeCoordinates(bounds);
return info;
}
void Free(const AtlasTextureInfo& info)
{
assert(_index == info.index);
_freeSlots.push_back(info.slot);
}
// Checks if specified image would be tightly packed in this atlas
// by checking if it is within the right power of 2 range
[[nodiscard]] bool IsImageSuitable(int32_t actualWidth, int32_t actualHeight) const
{
int32_t imageOrder = CalculateImageSizeOrder(actualWidth, actualHeight);
int32_t atlasOrder = log2(_imageSize);
return imageOrder == atlasOrder;
}
[[nodiscard]] int32_t GetFreeSlots() const
{
return static_cast<int32_t>(_freeSlots.size());
}
static int32_t CalculateImageSizeOrder(int32_t actualWidth, int32_t actualHeight)
{
int32_t actualSize = std::max(actualWidth, actualHeight);
if (actualSize < TEXTURE_CACHE_SMALLEST_SLOT)
{
actualSize = TEXTURE_CACHE_SMALLEST_SLOT;
}
return static_cast<int32_t>(ceil(log2f(static_cast<float>(actualSize))));
}
private:
[[nodiscard]] ivec4 GetSlotCoordinates(GLuint slot, int32_t actualWidth, int32_t actualHeight) const
{
int32_t row = slot / _cols;
int32_t col = slot % _cols;
return ivec4{
_imageSize * col,
_imageSize * row,
_imageSize * col + actualWidth,
_imageSize * row + actualHeight,
};
}
[[nodiscard]] vec4 NormalizeCoordinates(const ivec4& coords) const
{
return vec4{
coords.x / static_cast<float>(_atlasWidth),
coords.y / static_cast<float>(_atlasHeight),
coords.z / static_cast<float>(_atlasWidth),
coords.w / static_cast<float>(_atlasHeight),
};
}
};
struct Equal
class TextureCache final
{
bool operator()(const GlyphId& lhs, const GlyphId& rhs) const
{
return lhs.Image == rhs.Image && lhs.Palette == rhs.Palette;
}
};
};
private:
bool _initialized = false;
// This is the maximum width and height of each atlas, basically the
// granularity at which new atlases are allocated (2048 -> 4 MB of VRAM)
constexpr int32_t TEXTURE_CACHE_MAX_ATLAS_SIZE = 2048;
GLuint _atlasesTexture = 0;
GLint _atlasesTextureDimensions = 0;
GLuint _atlasesTextureCapacity = 0;
GLuint _atlasesTextureIndices = 0;
GLint _atlasesTextureIndicesLimit = 0;
std::vector<Atlas> _atlases;
std::unordered_map<GlyphId, AtlasTextureInfo, GlyphId::Hash, GlyphId::Equal> _glyphTextureMap;
std::vector<AtlasTextureInfo> _textureCache;
std::array<uint32_t, SPR_IMAGE_LIST_END> _indexMap;
// Pixel dimensions of smallest supported slots in texture atlases
// Must be a power of 2!
constexpr int32_t TEXTURE_CACHE_SMALLEST_SLOT = 32;
struct BasicTextureInfo
{
GLuint index;
vec4 normalizedBounds;
};
// Location of an image (texture atlas index, slot and normalized coordinates)
struct AtlasTextureInfo : public BasicTextureInfo
{
GLuint slot;
ivec4 bounds;
ImageIndex image;
};
// Represents a texture atlas that images of a given maximum size can be allocated from
// Atlases are all stored in the same 2D texture array, occupying the specified index
// Slots in atlases are always squares.
class Atlas final
{
private:
GLuint _index = 0;
int32_t _imageSize = 0;
int32_t _atlasWidth = 0;
int32_t _atlasHeight = 0;
std::vector<GLuint> _freeSlots;
int32_t _cols = 0;
int32_t _rows = 0;
public:
Atlas(GLuint index, int32_t imageSize)
: _index(index)
, _imageSize(imageSize)
{
}
void Initialise(int32_t atlasWidth, int32_t atlasHeight)
{
_atlasWidth = atlasWidth;
_atlasHeight = atlasHeight;
_cols = std::max(1, _atlasWidth / _imageSize);
_rows = std::max(1, _atlasHeight / _imageSize);
_freeSlots.resize(_cols * _rows);
for (size_t i = 0; i < _freeSlots.size(); i++)
{
_freeSlots[i] = static_cast<GLuint>(i);
}
}
AtlasTextureInfo Allocate(int32_t actualWidth, int32_t actualHeight)
{
assert(!_freeSlots.empty());
GLuint slot = _freeSlots.back();
_freeSlots.pop_back();
auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight);
AtlasTextureInfo info{};
info.index = _index;
info.slot = slot;
info.bounds = bounds;
info.normalizedBounds = NormalizeCoordinates(bounds);
return info;
}
void Free(const AtlasTextureInfo& info)
{
assert(_index == info.index);
_freeSlots.push_back(info.slot);
}
// Checks if specified image would be tightly packed in this atlas
// by checking if it is within the right power of 2 range
[[nodiscard]] bool IsImageSuitable(int32_t actualWidth, int32_t actualHeight) const
{
int32_t imageOrder = CalculateImageSizeOrder(actualWidth, actualHeight);
int32_t atlasOrder = log2(_imageSize);
return imageOrder == atlasOrder;
}
[[nodiscard]] int32_t GetFreeSlots() const
{
return static_cast<int32_t>(_freeSlots.size());
}
static int32_t CalculateImageSizeOrder(int32_t actualWidth, int32_t actualHeight)
{
int32_t actualSize = std::max(actualWidth, actualHeight);
if (actualSize < TEXTURE_CACHE_SMALLEST_SLOT)
{
actualSize = TEXTURE_CACHE_SMALLEST_SLOT;
}
return static_cast<int32_t>(ceil(log2f(static_cast<float>(actualSize))));
}
private:
[[nodiscard]] ivec4 GetSlotCoordinates(GLuint slot, int32_t actualWidth, int32_t actualHeight) const
{
int32_t row = slot / _cols;
int32_t col = slot % _cols;
return ivec4{
_imageSize * col,
_imageSize * row,
_imageSize * col + actualWidth,
_imageSize * row + actualHeight,
};
}
[[nodiscard]] vec4 NormalizeCoordinates(const ivec4& coords) const
{
return vec4{
coords.x / static_cast<float>(_atlasWidth),
coords.y / static_cast<float>(_atlasHeight),
coords.z / static_cast<float>(_atlasWidth),
coords.w / static_cast<float>(_atlasHeight),
};
}
};
class TextureCache final
{
private:
bool _initialized = false;
GLuint _atlasesTexture = 0;
GLint _atlasesTextureDimensions = 0;
GLuint _atlasesTextureCapacity = 0;
GLuint _atlasesTextureIndices = 0;
GLint _atlasesTextureIndicesLimit = 0;
std::vector<Atlas> _atlases;
std::unordered_map<GlyphId, AtlasTextureInfo, GlyphId::Hash, GlyphId::Equal> _glyphTextureMap;
std::vector<AtlasTextureInfo> _textureCache;
std::array<uint32_t, SPR_IMAGE_LIST_END> _indexMap;
GLuint _paletteTexture = 0;
GLuint _blendPaletteTexture = 0;
GLuint _paletteTexture = 0;
GLuint _blendPaletteTexture = 0;
#ifndef __MACOSX__
std::shared_mutex _mutex;
using shared_lock = std::shared_lock<std::shared_mutex>;
using unique_lock = std::unique_lock<std::shared_mutex>;
std::shared_mutex _mutex;
using shared_lock = std::shared_lock<std::shared_mutex>;
using unique_lock = std::unique_lock<std::shared_mutex>;
#else
std::mutex _mutex;
using shared_lock = std::unique_lock<std::mutex>;
using unique_lock = std::unique_lock<std::mutex>;
std::mutex _mutex;
using shared_lock = std::unique_lock<std::mutex>;
using unique_lock = std::unique_lock<std::mutex>;
#endif
public:
TextureCache();
~TextureCache();
void InvalidateImage(ImageIndex image);
BasicTextureInfo GetOrLoadImageTexture(const ImageId imageId);
BasicTextureInfo GetOrLoadGlyphTexture(const ImageId imageId, const PaletteMap& paletteMap);
BasicTextureInfo GetOrLoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
public:
TextureCache();
~TextureCache();
void InvalidateImage(ImageIndex image);
BasicTextureInfo GetOrLoadImageTexture(const ImageId imageId);
BasicTextureInfo GetOrLoadGlyphTexture(const ImageId imageId, const PaletteMap& paletteMap);
BasicTextureInfo GetOrLoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
GLuint GetAtlasesTexture();
GLuint GetPaletteTexture();
GLuint GetBlendPaletteTexture();
static GLint PaletteToY(FilterPaletteID palette);
GLuint GetAtlasesTexture();
GLuint GetPaletteTexture();
GLuint GetBlendPaletteTexture();
static GLint PaletteToY(FilterPaletteID palette);
private:
void CreateTextures();
void GeneratePaletteTexture();
void EnlargeAtlasesTexture(GLuint newEntries);
AtlasTextureInfo LoadImageTexture(const ImageId image);
AtlasTextureInfo LoadGlyphTexture(const ImageId image, const PaletteMap& paletteMap);
AtlasTextureInfo AllocateImage(int32_t imageWidth, int32_t imageHeight);
AtlasTextureInfo LoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
static DrawPixelInfo GetImageAsDPI(const ImageId imageId);
static DrawPixelInfo GetGlyphAsDPI(const ImageId imageId, const PaletteMap& paletteMap);
void FreeTextures();
private:
void CreateTextures();
void GeneratePaletteTexture();
void EnlargeAtlasesTexture(GLuint newEntries);
AtlasTextureInfo LoadImageTexture(const ImageId image);
AtlasTextureInfo LoadGlyphTexture(const ImageId image, const PaletteMap& paletteMap);
AtlasTextureInfo AllocateImage(int32_t imageWidth, int32_t imageHeight);
AtlasTextureInfo LoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
static DrawPixelInfo GetImageAsDPI(const ImageId imageId);
static DrawPixelInfo GetGlyphAsDPI(const ImageId imageId, const PaletteMap& paletteMap);
void FreeTextures();
static DrawPixelInfo CreateDPI(int32_t width, int32_t height);
static void DeleteDPI(DrawPixelInfo dpi);
};
static DrawPixelInfo CreateDPI(int32_t width, int32_t height);
static void DeleteDPI(DrawPixelInfo dpi);
};
} // namespace OpenRCT2::Ui

View File

@ -15,194 +15,196 @@
# include <map>
# include <vector>
/*
* Structure to store locations of vertical bounding box edge.
*/
struct XData
namespace OpenRCT2::Ui
{
int32_t xposition;
bool begin;
int32_t top, bottom;
};
using SweepLine = std::vector<XData>;
/*
* Creates a list of vertical bounding box edges, stored as xdata and sorted
* from left to right. If multiple edges are at the same x coordinate, Then
* edges for boxes to the left will appear before edges for boxes to the right.
*/
static inline SweepLine CreateXList(const RectCommandBatch& transparent)
{
SweepLine x_sweep;
x_sweep.reserve(transparent.size() * 2);
for (const DrawRectCommand& command : transparent)
/*
* Structure to store locations of vertical bounding box edge.
*/
struct XData
{
int32_t left = std::min(std::max(command.bounds.x, command.clip.x), command.clip.z);
int32_t top = std::min(std::max(command.bounds.y, command.clip.y), command.clip.w);
int32_t right = std::min(std::max(command.bounds.z, command.clip.x), command.clip.z);
int32_t bottom = std::min(std::max(command.bounds.w, command.clip.y), command.clip.w);
int32_t xposition;
bool begin;
int32_t top, bottom;
};
using SweepLine = std::vector<XData>;
assert(left <= right);
assert(top <= bottom);
if (left == right)
continue;
if (top == bottom)
continue;
x_sweep.push_back({ left, true, top, bottom });
x_sweep.push_back({ right, false, top, bottom });
}
std::sort(x_sweep.begin(), x_sweep.end(), [](const XData& a, const XData& b) -> bool {
if (a.xposition != b.xposition)
return a.xposition < b.xposition;
return !a.begin && b.begin;
});
return x_sweep;
}
/*
* Structure that stores intervals. YData.count stores how many intervals have
* an endpoint at this position, and YData.depth stores how many intervals
* intersect on the left limit of this point. In other words, IntervalTree
* stores half-closed intervals, with the left endpoint open, and the right
* endpoint closed. The IntervalTree uses std::map because it stores the values
* sorted, and we can traverse it in order using its bidirectional iterators.
*/
struct YData
{
int32_t count, depth;
};
using IntervalTree = std::map<int32_t, YData>;
/*
* Inserts the interval's top endpoint into the interval tree. If the endpoint
* already exists in the interval tree, it stacks the endpoints.
*/
static inline IntervalTree::iterator InsertTopEndpoint(IntervalTree& y_intersect, int32_t top)
{
auto top_in = y_intersect.insert({ top, { 1, 0 } });
IntervalTree::iterator top_it = top_in.first;
if (top_in.second)
/*
* Creates a list of vertical bounding box edges, stored as xdata and sorted
* from left to right. If multiple edges are at the same x coordinate, Then
* edges for boxes to the left will appear before edges for boxes to the right.
*/
static inline SweepLine CreateXList(const RectCommandBatch& transparent)
{
IntervalTree::iterator top_next = std::next(top_it);
if (top_next != y_intersect.end())
SweepLine x_sweep;
x_sweep.reserve(transparent.size() * 2);
for (const DrawRectCommand& command : transparent)
{
top_it->second.depth = top_next->second.depth;
int32_t left = std::min(std::max(command.bounds.x, command.clip.x), command.clip.z);
int32_t top = std::min(std::max(command.bounds.y, command.clip.y), command.clip.w);
int32_t right = std::min(std::max(command.bounds.z, command.clip.x), command.clip.z);
int32_t bottom = std::min(std::max(command.bounds.w, command.clip.y), command.clip.w);
assert(left <= right);
assert(top <= bottom);
if (left == right)
continue;
if (top == bottom)
continue;
x_sweep.push_back({ left, true, top, bottom });
x_sweep.push_back({ right, false, top, bottom });
}
}
else
{
++top_it->second.count;
}
return top_it;
}
/*
* Inserts the interval's bottom endpoint into the interval tree. If the
* endpoint already exists in the interval tree, it stacks the endpoint.
* This function can produce a new maximum depth.
*/
static inline IntervalTree::iterator InsertBottomEndpoint(IntervalTree& y_intersect, int32_t bottom)
{
auto bottom_in = y_intersect.insert({ bottom, { 1, 1 } });
IntervalTree::iterator bottom_it = bottom_in.first;
if (bottom_in.second)
std::sort(x_sweep.begin(), x_sweep.end(), [](const XData& a, const XData& b) -> bool {
if (a.xposition != b.xposition)
return a.xposition < b.xposition;
return !a.begin && b.begin;
});
return x_sweep;
}
/*
* Structure that stores intervals. YData.count stores how many intervals have
* an endpoint at this position, and YData.depth stores how many intervals
* intersect on the left limit of this point. In other words, IntervalTree
* stores half-closed intervals, with the left endpoint open, and the right
* endpoint closed. The IntervalTree uses std::map because it stores the values
* sorted, and we can traverse it in order using its bidirectional iterators.
*/
struct YData
{
IntervalTree::iterator bottom_next = std::next(bottom_it);
if (bottom_next != y_intersect.end())
int32_t count, depth;
};
using IntervalTree = std::map<int32_t, YData>;
/*
* Inserts the interval's top endpoint into the interval tree. If the endpoint
* already exists in the interval tree, it stacks the endpoints.
*/
static inline IntervalTree::iterator InsertTopEndpoint(IntervalTree& y_intersect, int32_t top)
{
auto top_in = y_intersect.insert({ top, { 1, 0 } });
IntervalTree::iterator top_it = top_in.first;
if (top_in.second)
{
bottom_it->second.depth = bottom_next->second.depth + 1;
}
}
else
{
++bottom_it->second.count;
++bottom_it->second.depth;
}
return bottom_it;
}
/*
* Removes the interval's top endpoint, handling stacked endpoints.
*/
static inline void RemoveTopEndpoint(IntervalTree& y_intersect, IntervalTree::iterator top_it)
{
if (top_it->second.count == 1)
{
y_intersect.erase(top_it);
}
else
{
--top_it->second.count;
}
}
/*
* Removes the interval's bottom endpoint, handling stacked endpoints.
*/
static inline void RemoveBottomEndpoint(IntervalTree& y_intersect, IntervalTree::iterator bottom_it)
{
if (bottom_it->second.count == 1)
{
y_intersect.erase(bottom_it);
}
else
{
--bottom_it->second.count;
--bottom_it->second.depth;
}
}
/*
* Determines an approximation of the number of depth peeling iterations needed
* to render the command batch. It will never underestimate the number of
* iterations, but it can overestimate, usually by no more than +2.
*/
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent)
{
int32_t max_depth = 1;
SweepLine x_sweep = CreateXList(transparent);
IntervalTree y_intersect{};
for (const XData& x : x_sweep)
{
if (x.begin)
{
IntervalTree::iterator top_it = InsertTopEndpoint(y_intersect, x.top);
IntervalTree::iterator bottom_it = InsertBottomEndpoint(y_intersect, x.bottom);
max_depth = std::max(max_depth, bottom_it->second.depth);
/*
* Increment the depth for endpoings that intersect this interval
*/
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
IntervalTree::iterator top_next = std::next(top_it);
if (top_next != y_intersect.end())
{
max_depth = std::max(max_depth, ++it->second.depth);
top_it->second.depth = top_next->second.depth;
}
}
else
{
IntervalTree::iterator top_it = y_intersect.find(x.top);
IntervalTree::iterator bottom_it = y_intersect.find(x.bottom);
++top_it->second.count;
}
return top_it;
}
/*
* Decrement the depth for endpoings that intersected this interval
*/
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
/*
* Inserts the interval's bottom endpoint into the interval tree. If the
* endpoint already exists in the interval tree, it stacks the endpoint.
* This function can produce a new maximum depth.
*/
static inline IntervalTree::iterator InsertBottomEndpoint(IntervalTree& y_intersect, int32_t bottom)
{
auto bottom_in = y_intersect.insert({ bottom, { 1, 1 } });
IntervalTree::iterator bottom_it = bottom_in.first;
if (bottom_in.second)
{
IntervalTree::iterator bottom_next = std::next(bottom_it);
if (bottom_next != y_intersect.end())
{
--it->second.depth;
bottom_it->second.depth = bottom_next->second.depth + 1;
}
}
else
{
++bottom_it->second.count;
++bottom_it->second.depth;
}
return bottom_it;
}
RemoveTopEndpoint(y_intersect, top_it);
RemoveBottomEndpoint(y_intersect, bottom_it);
/*
* Removes the interval's top endpoint, handling stacked endpoints.
*/
static inline void RemoveTopEndpoint(IntervalTree& y_intersect, IntervalTree::iterator top_it)
{
if (top_it->second.count == 1)
{
y_intersect.erase(top_it);
}
else
{
--top_it->second.count;
}
}
return max_depth;
}
/*
* Removes the interval's bottom endpoint, handling stacked endpoints.
*/
static inline void RemoveBottomEndpoint(IntervalTree& y_intersect, IntervalTree::iterator bottom_it)
{
if (bottom_it->second.count == 1)
{
y_intersect.erase(bottom_it);
}
else
{
--bottom_it->second.count;
--bottom_it->second.depth;
}
}
/*
* Determines an approximation of the number of depth peeling iterations needed
* to render the command batch. It will never underestimate the number of
* iterations, but it can overestimate, usually by no more than +2.
*/
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent)
{
int32_t max_depth = 1;
SweepLine x_sweep = CreateXList(transparent);
IntervalTree y_intersect{};
for (const XData& x : x_sweep)
{
if (x.begin)
{
IntervalTree::iterator top_it = InsertTopEndpoint(y_intersect, x.top);
IntervalTree::iterator bottom_it = InsertBottomEndpoint(y_intersect, x.bottom);
max_depth = std::max(max_depth, bottom_it->second.depth);
/*
* Increment the depth for endpoings that intersect this interval
*/
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
{
max_depth = std::max(max_depth, ++it->second.depth);
}
}
else
{
IntervalTree::iterator top_it = y_intersect.find(x.top);
IntervalTree::iterator bottom_it = y_intersect.find(x.bottom);
/*
* Decrement the depth for endpoings that intersected this interval
*/
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
{
--it->second.depth;
}
RemoveTopEndpoint(y_intersect, top_it);
RemoveBottomEndpoint(y_intersect, bottom_it);
}
}
return max_depth;
}
} // namespace OpenRCT2::Ui
#endif /* DISABLE_OPENGL */

View File

@ -13,9 +13,12 @@
#include <openrct2/common.h>
/*
* Determines an approximation of the number of depth peeling iterations needed
* to render the command batch. It will never underestimate the number of
* iterations, but it can overestimate, usually by no more than +2.
*/
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent);
namespace OpenRCT2::Ui
{
/*
* Determines an approximation of the number of depth peeling iterations needed
* to render the command batch. It will never underestimate the number of
* iterations, but it can overestimate, usually by no more than +2.
*/
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent);
} // namespace OpenRCT2::Ui