Skip to content
Commits on Source (4)
...@@ -2,5 +2,11 @@ ...@@ -2,5 +2,11 @@
A minimalistic simple GUI client for my shocker designed for cross compiling on Windows, Linux, Android, IOS and HTML5. A minimalistic simple GUI client for my shocker designed for cross compiling on Windows, Linux, Android, IOS and HTML5.
Built using SDL3, designed to use minimal dependencies. Alittle rough round the edges, my first time ever using OpenGL! Built using SDL3, designed to use minimal dependencies. Alittle rough round the edges, my first time ever using OpenGL!
### Used libraries
- SDL3 (Dynamic)
- FreeType (Static)
- utf8cpp (Source)
- Glad (Source)
## Building ## Building
Currently the only target Im working on in this early stage is Windows using Visual Studio. Once loaded it should be able to compile easy, all the small dependencies should be included inside this project. Currently the only target Im working on in this early stage is Windows using Visual Studio. Once loaded it should be able to compile easy, all the small dependencies should be included inside this project
\ No newline at end of file
...@@ -163,6 +163,7 @@ ...@@ -163,6 +163,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="..\src\glad.c" /> <ClCompile Include="..\src\glad.c" />
<ClCompile Include="ContainerTabsElement.cpp" /> <ClCompile Include="ContainerTabsElement.cpp" />
<ClCompile Include="include\FontRasterizer.cpp" />
<ClCompile Include="src\ContainerScrollElement.cpp" /> <ClCompile Include="src\ContainerScrollElement.cpp" />
<ClCompile Include="src\GUIElement.cpp" /> <ClCompile Include="src\GUIElement.cpp" />
<ClCompile Include="src\GUIMain.cpp" /> <ClCompile Include="src\GUIMain.cpp" />
...@@ -177,7 +178,6 @@ ...@@ -177,7 +178,6 @@
<ClCompile Include="src\SliderRangeElement.cpp" /> <ClCompile Include="src\SliderRangeElement.cpp" />
<ClCompile Include="src\Types.cpp" /> <ClCompile Include="src\Types.cpp" />
<ClCompile Include="src\Uniform.cpp" /> <ClCompile Include="src\Uniform.cpp" />
<ClCompile Include="src\UniquePtrV.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="include\DynamicBlockBuffer.hpp" /> <ClInclude Include="include\DynamicBlockBuffer.hpp" />
...@@ -198,7 +198,7 @@ ...@@ -198,7 +198,7 @@
<ClInclude Include="include\Span.hpp" /> <ClInclude Include="include\Span.hpp" />
<ClInclude Include="include\Types.hpp" /> <ClInclude Include="include\Types.hpp" />
<ClInclude Include="include\Uniform.hpp" /> <ClInclude Include="include\Uniform.hpp" />
<ClInclude Include="include\UniquePtrV.hpp" /> <ClInclude Include="include\ManagedPtr.hpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\lib\x64\SDL3.dll"> <None Include="..\lib\x64\SDL3.dll">
......
...@@ -30,9 +30,6 @@ ...@@ -30,9 +30,6 @@
<ClCompile Include="..\src\glad.c"> <ClCompile Include="..\src\glad.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\UniquePtrV.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\GUIElement.cpp"> <ClCompile Include="src\GUIElement.cpp">
<Filter>Source Files\GUI</Filter> <Filter>Source Files\GUI</Filter>
</ClCompile> </ClCompile>
...@@ -75,11 +72,11 @@ ...@@ -75,11 +72,11 @@
<ClCompile Include="ContainerTabsElement.cpp"> <ClCompile Include="ContainerTabsElement.cpp">
<Filter>Source Files\GUI\GUIElements</Filter> <Filter>Source Files\GUI\GUIElements</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="include\FontRasterizer.cpp">
<Filter>Source Files\GUI</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="include\UniquePtrV.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\Types.hpp"> <ClInclude Include="include\Types.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
...@@ -134,6 +131,9 @@ ...@@ -134,6 +131,9 @@
<ClInclude Include="include\ElementsText.hpp"> <ClInclude Include="include\ElementsText.hpp">
<Filter>Header Files\GUI</Filter> <Filter>Header Files\GUI</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\ManagedPtr.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Solid.fs"> <None Include="Solid.fs">
......
#include "FontRasterizer.hpp"
#include <cassert>
#include <vector>
#include "utf8.h"
#include <ft2build.h>
#include FT_FREETYPE_H
Font::Text::Text() { }
Font::Text::Text(std::string text) {
data.clear();
utf8::utf8to32(text.begin(), text.end(), back_inserter(data));
}
size_t Font::Text::getLength() const {
return data.size();
}
Font::Atlas::Atlas(Font* font, FontSize fontSize) {
for (size_t i = 0; i < lookupArr.size(); i++)
lookupArr[i] = nullptr;
clearAndResize(textureSizePow2);
}
void Font::Atlas::clearAndResize(size_t sizePow2) {
if (textureSizePow2 == sizePow2)
goto ONLY_CLEAR;
else if (sizePow2 > maxTextureSizePow2)
sizePow2 = maxTextureSizePow2;
textureSizePow2 = sizePow2;
textureSize = 1 << textureSizePow2;
ONLY_CLEAR:
skyline.assign(textureSize, 0);
rawTexture.assign(textureSize * textureSize, 0);
//texture.set(rawTexture, textureSize, textureSize);
}
void Font::Atlas::upscaleRawTexture() {
if (textureSizePow2 == maxTextureSizePow2)
return;
else if (textureSizePow2 > maxTextureSizePow2)
textureSizePow2 = maxTextureSizePow2;
else
textureSizePow2++;
size_t newTextureSize = 1 << textureSizePow2;
assert(newTextureSize > textureSize);
size_t oldTextureSize = textureSize;
textureSize = newTextureSize;
if (newTextureSize > oldTextureSize) { //Downscale pixel fix
for (size_t src = 0, dst = 0, srcMax = oldTextureSize * newTextureSize;
src < srcMax; src += oldTextureSize, dst += newTextureSize) {
for (size_t col = 0; col < newTextureSize; ++col)
rawTexture[dst + col] = rawTexture[src + col];
}
}
skyline.resize(textureSize, 0);
rawTexture.resize(textureSize * textureSize);
if (newTextureSize > oldTextureSize) { //Upscale pixel fix
for (size_t src = oldTextureSize * oldTextureSize, dst = oldTextureSize * newTextureSize;
src > 0; src -= oldTextureSize, dst -= newTextureSize) {
for (size_t col = 0; col < oldTextureSize; ++col)
rawTexture[dst + col] = rawTexture[src + col];
}
}
}
void Font::Atlas::insertGlyphRaw(const Glyph& glyph, Span<const uint8_t> data) {
glyphs.push_back(glyph);
Glyph* newGlyph = &glyphs.back();
if (glyph.unicode32 < lookupArr.size())
lookupArr[glyph.unicode32] = newGlyph;
else
lookupHash.insert(std::pair<char32_t, Glyph*>(glyph.unicode32, newGlyph));
size_t width = glyph.w, height = glyph.h;
assert(glyph.x + width <= textureSize);
assert(glyph.y + height <= textureSize);
assert(width*height == data.dataSize);
for (size_t Ixy = 0, Oy = glyph.x + glyph.y * textureSize, hi = 0;
hi < height; hi++, Oy += textureSize)
for (size_t Oxy = Oy, wi = 0; wi < width; wi++, Ixy++, Oxy++)
rawTexture[Oxy] = data.data[Ixy];
}
void Font::Atlas::insertGlyph(const Glyph& glyphInput, Span<const uint8_t> data) {
Glyph glyph = glyphInput;
assert(data.dataSize == (glyphInput.w * glyphInput.h));
for (uint8_t loopMax = 3; loopMax != 0; loopMax--) {
if (skyline.empty()) skyline.assign(textureSize, 0);
uint16_t bestX = 0;
uint16_t bestY = textureSize;
bool found = false;
for (size_t x = 0; x <= textureSize - glyph.w; x++) {
// Find max height under this candidate
uint16_t yMax = 0;
for (size_t i = 0; i < glyph.w; i++) yMax = std::max(yMax, skyline[x + i]);
if (yMax + glyph.h <= textureSize) {
if (yMax < bestY) {
bestY = yMax;
bestX = x;
found = true;
}
}
}
if (found) {
glyph.x = bestX;
glyph.y = bestY;
for (size_t i = 0; i < glyph.w; i++) skyline[bestX + i] = bestY + glyph.h;
insertGlyphRaw(glyph, data);
glyphs.push_back(glyph);
return;
}
upscaleRawTexture();
skyline.clear();
}
}
void Font::Atlas::generateGlyphs() {
FT_Face face = font->face;
FT_Error error = FT_Set_Pixel_Sizes(face, 0, fontSize);
if (error) return;
glyphs.reserve(glyphs.size() + requestGlyphs.size());
for (uint32_t request : requestGlyphs) {
error = FT_Load_Char(face, 32, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
if (error) {
SDL_Log("Failed to generate glyph %i for font '%s' at size %i", request, font->file.c_str(), fontSize);
continue;
}
FT_GlyphSlot g = face->glyph;
uint16_t width = g->bitmap.width;
uint16_t height = g->bitmap.rows;
size_t bufferSize = g->bitmap.pitch * g->bitmap.rows;
uint16_t xOffset = g->bitmap_left;
uint16_t yOffset = g->bitmap_top;
uint16_t xAdvance = g->advance.x >> 6;
uint16_t yAdvance = g->advance.y >> 6;
insertGlyph(Glyph{
.unicode32 = request,
.ox = xOffset, .oy = yOffset,
.w = width, .h = height,
.ax = xAdvance, .ay = yAdvance
}, Span<const uint8_t>(g->bitmap.buffer, bufferSize));
}
requestGlyphs.clear();
texture.set(rawTexture, textureSize, textureSize);
}
Font::Glyph Font::Atlas::getGlyph(char32_t unicode32) {
Glyph* result = nullptr;
if (unicode32 < lookupArr.size()) {
result = lookupArr[unicode32];
goto RESOLVE_PTR;
}
{
auto found = lookupHash.find(unicode32);
if (found != lookupHash.end()) {
result = found->second;
goto RESOLVE_PTR;
}
}
if (!requestGlyphs.contains(unicode32))
requestGlyphs.insert(unicode32);
RESOLVE_PTR:
if (result == nullptr)
result = lookupArr[0];
if (result == nullptr)
return Glyph();
return *result;
}
size_t Font::Atlas::generateInstances(Rect pos, const Text& text, Span<Instance> buffer) {
return size_t();
}
Font::FontSize Font::Atlas::getFontSize() const {
return fontSize;
}
Font::Font(std::string file, FT_Face face)
: file(file), face(face) { }
Font::Atlas* Font::getAtlas(FontSize fontSize) {
for (Atlas& atlas : atlases)
if (atlas.getFontSize() == fontSize) return &atlas;
size_t newIndex = atlases.size();
atlases.push_back(Atlas(this, fontSize));
return &(atlases[newIndex]);
}
Font* Font::fetchFont(std::string file) {
auto match = fonts.find(file);
if (match != fonts.end()) return &match->second;
if (library == nullptr) return nullptr;
FT_Face face;
FT_Error error = FT_New_Face(library, file.c_str(), 0, &face);
if (error) return nullptr;
auto newMatch = fonts.insert(std::pair<std::string,Font>(file, Font(file, face)));
return &newMatch.first->second;
}
bool Font::init() {
FT_Error error = FT_Init_FreeType(&library);
if (error) {
library = nullptr;
SDL_Log("Failed to init FreeType library: %i", error);
return true;
}
return false;
}
...@@ -9,11 +9,14 @@ ...@@ -9,11 +9,14 @@
#include "Types.hpp" #include "Types.hpp"
#include "Instance.hpp" #include "Instance.hpp"
#include <memory> #include <memory>
#include <ft2build.h>
#include FT_FREETYPE_H
//FreeType //FreeType
//https://github.com/sysfce2/utf8cpp/tree/master //https://github.com/sysfce2/utf8cpp/tree/master
class Font { class Font {
public: public:
typedef uint16_t FontSize;
constexpr static const char32_t GlyphNull = (char32_t) - 1; constexpr static const char32_t GlyphNull = (char32_t) - 1;
enum Align : uint8_t { enum Align : uint8_t {
LEFT, MIDDLE, RIGHT LEFT, MIDDLE, RIGHT
...@@ -23,38 +26,60 @@ public: ...@@ -23,38 +26,60 @@ public:
Align align = LEFT; Align align = LEFT;
Rect bounds{ }; Rect bounds{ };
Color color; Color color;
size_t getLength() const;
Text(); Text();
Text(std::string text); Text(std::string text);
size_t getLength() const;
}; };
struct Glyph { struct Glyph {
char32_t unicode32 ; char32_t unicode32 ;
int16_t x, y; //Bitmap offsets & size
uint16_t w, h; uint16_t ox, oy, w, h;
uint16_t advance; //Position in altas
uint16_t x, y;
//Advances
uint16_t ax, ay;
}; };
class Atlas { class Atlas {
uint16_t fontSize; //constexpr static const uint8_t maxTextureSizePow2 = sizeof(size_t) * 8;
constexpr static const uint8_t maxTextureSizePow2 = 16;
Font* font;
FontSize fontSize;
std::vector<Glyph> glyphs; std::vector<Glyph> glyphs;
std::array<Glyph*, 256> lookupArr8; std::array<Glyph*, 128> lookupArr;
std::unordered_map<char32_t, Glyph*> lookupHash; std::unordered_map<char32_t, Glyph*> lookupHash;
std::unordered_set<char32_t> requestGlyphs; std::unordered_set<char32_t> requestGlyphs;
std::vector<uint16_t> skyline;
std::vector<uint8_t> rawTexture; std::vector<uint8_t> rawTexture;
UniformTexture8 texture; size_t textureSize;
uint8_t textureSizePow2 = 7;
UniformTexture8 texture{ "atlasTexture" };
Atlas(Font* font, FontSize fontSize);
void clearAndResize(size_t size); void clearAndResize(size_t size);
void insertGlyph(const Glyph& glyph); void upscaleRawTexture();
void insertGlyphRaw(const Glyph& glyph, Span<const uint8_t> data);
void insertGlyph(const Glyph& glyph, Span<const uint8_t> data);
public: public:
void generateGlyphs();
Glyph getGlyph(char32_t unicode32); Glyph getGlyph(char32_t unicode32);
size_t generateInstances(Rect pos, const Text& text, Span<Instance> buffer); size_t generateInstances(Rect pos, const Text& text, Span<Instance> buffer);
FontSize getFontSize() const;
friend Font;
}; };
private: private:
std::string file; std::string file;
std::vector<Atlas> atlases; std::vector<Atlas> atlases;
FT_Face face;
Font(std::string file, FT_Face face);
public: public:
Glyph getAtlas(uint16_t fontSize); Atlas* getAtlas(uint16_t fontSize);
static std::unordered_map<std::string, Font> fonts; static std::unordered_map<std::string, Font> fonts;
static Font* fetchFont(std::string file); static Font* fetchFont(std::string file);
static bool init();
protected: protected:
static FT_Library library;
}; };
\ No newline at end of file
...@@ -5,6 +5,8 @@ struct InstanceDataFlat; ...@@ -5,6 +5,8 @@ struct InstanceDataFlat;
#include "Types.hpp" #include "Types.hpp"
#include "UniquePtrV.hpp" #include "UniquePtrV.hpp"
#include "Span.hpp" #include "Span.hpp"
#include "ManagedPtr.hpp"
#include "Uniform.hpp"
struct AShader; struct AShader;
...@@ -13,8 +15,8 @@ struct Instance { ...@@ -13,8 +15,8 @@ struct Instance {
bool skip = false; bool skip = false;
size_t depth = 0; size_t depth = 0;
AShader* shader; AShader* shader;
void* data; ManagedPtr<> data;
Span<Instance*> uniforms; Span<ManagedPtr<Uniform>> uniforms;
Instance(); Instance();
template<typename I> Instance(I* data, AShader* shader, size_t depth = 0) template<typename I> Instance(I* data, AShader* shader, size_t depth = 0)
: data(data), shader(shader), depth(depth) { }; : data(data), shader(shader), depth(depth) { };
......
#pragma once
#include <utility>
#include <cstdint>
#include <cassert>
template <typename TBase = void>
struct ManagedPtr {
private:
struct DataManager {
TBase* (*dataNew)();
void (*dataDelete)(TBase*);
void (*dataCopy)(TBase*, TBase*);
};
template<typename T> class DataManagerT {
static TBase* dataNewT() {
return new T;
}
static void dataDeleteT(TBase* data) {
T* dataT = (T*)data;
delete dataT;
}
static void dataCopyT(TBase* dataFrom, TBase* dataTo) {
T* dataFromT = (T*)dataFrom;
T* dataToT = (T*)dataTo;
*dataToT = *dataFromT;
}
constexpr static const DataManager dataManager{dataNewT, dataDeleteT, dataCopyT};
public:
static const DataManager* get() {
return &dataManager;
}
};
const DataManager* dataManager = nullptr;
TBase* data;
bool owns;
public:
ManagedPtr() : data(nullptr), owns(false) { }
template<typename T>
ManagedPtr(T* data, bool owns = false)
: data(data), owns(owns), dataManager(DataManagerT<T>::get()) { }
template<typename T>
ManagedPtr(const T* data)
: data(new T()), owns(true), dataManager(DataManagerT<T>::get()) {
*(this->data) = *data;
}
template<typename T>
ManagedPtr(const T& data)
: data(new T()), owns(true), dataManager(DataManagerT<T>::get()) {
T* dataT = (T*)this->data;
*dataT = data;
}
ManagedPtr(ManagedPtr& that)
: data(that.data), owns(that.owns), dataManager(that.dataManager) {
that.owns = false;
}
ManagedPtr(const ManagedPtr& that)
: data(that.data), owns(false), dataManager(that.dataManager) {
if (that.owns) duplicate();
}
~ManagedPtr() {
if (owns) dataManager->dataDelete(data);
owns = false;
}
void swap(ManagedPtr& that) {
std::swap(that.dataManager, dataManager);
std::swap(that.data, data);
std::swap(that.owns, owns);
}
void duplicate() {
TBase* newData = dataManager->dataNew();
if (data != nullptr) {
dataManager->dataCopy(data, newData);
if (owns) dataManager->dataDelete(data);
}
data = newData;
owns = true;
}
void copyFrom(const ManagedPtr& that) {
if (dataManager != that.dataManager) {
if (owns && data != nullptr)
dataManager->dataDelete(data);
data = nullptr;
owns = false;
dataManager = that.dataManager;
}
if (data == nullptr || !owns)
data = dataManager->dataNew();
owns = true;
dataManager->dataCopy(that.data, data);
}
bool doesOwn() const {
return owns;
}
TBase* pointer() const {
return data;
}
template<typename T>
T* pointerAs() const {
return static_cast<T*>(data);
}
template<typename T>
T& as() const {
return *static_cast<T*>(data);
}
};
\ No newline at end of file
...@@ -26,7 +26,7 @@ template<typename T> void ShaderInstanced<T>::renderInstances(ObjectBuffer* objb ...@@ -26,7 +26,7 @@ template<typename T> void ShaderInstanced<T>::renderInstances(ObjectBuffer* objb
for (size_t i = 0, im = data.size(); i < im; i++) { for (size_t i = 0, im = data.size(); i < im; i++) {
const Instance& inst = data[i]; const Instance& inst = data[i];
const T* data = (const T*)inst.data; const T* data = inst.data.pointerAs<const T>();
instanceCache[i] = *data; instanceCache[i] = *data;
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "ShaderHelper.hpp" #include "ShaderHelper.hpp"
#include "SDLMain.hpp" #include "SDLMain.hpp"
#include "ShaderInstances.hpp" #include "ShaderInstances.hpp"
#include "FontRasterizer.hpp"
...@@ -49,20 +50,13 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { ...@@ -49,20 +50,13 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
SDL_Log("Failed to init GL context: %s", SDL_GetError()); SDL_Log("Failed to init GL context: %s", SDL_GetError());
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
if (Font::init()) {
#if defined(__glad_h_) return SDL_APP_FAILURE;
}
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) { if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
SDL_Log("Failed to initialize GLAD"); SDL_Log("Failed to initialize GLAD");
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
#elif defined(__glew_h__)
glewExperimental = GL_TRUE;
GLenum glewError = glewInit();
if (glewError != GLEW_OK) {
SDL_Log("Failed to init GLEW: %s", glewGetErrorString(glewError));
return SDL_APP_FAILURE;
}//Initialize OpenGL
#endif
SDL_GL_SetSwapInterval(1); SDL_GL_SetSwapInterval(1);
compileAllShaders(); compileAllShaders();
guiMain = new GUIMain(); guiMain = new GUIMain();
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/****************************************************************************
*
* ftconfig.h
*
* ANSI-specific configuration file (specification only).
*
* Copyright (C) 1996-2024 by
* David Turner, Robert Wilhelm, and Werner Lemberg.
*
* This file is part of the FreeType project, and may only be used,
* modified, and distributed under the terms of the FreeType project
* license, LICENSE.TXT. By continuing to use, modify, or distribute
* this file you indicate that you have read the license and
* understand and accept it fully.
*
*/
/**************************************************************************
*
* This header file contains a number of macro definitions that are used by
* the rest of the engine. Most of the macros here are automatically
* determined at compile time, and you should not need to change it to port
* FreeType, except to compile the library with a non-ANSI compiler.
*
* Note however that if some specific modifications are needed, we advise
* you to place a modified copy in your build directory.
*
* The build directory is usually `builds/<system>`, and contains
* system-specific files that are always included first when building the
* library.
*
* This ANSI version should stay in `include/config/`.
*
*/
#ifndef FTCONFIG_H_
#define FTCONFIG_H_
#include <ft2build.h>
#include FT_CONFIG_OPTIONS_H
#include FT_CONFIG_STANDARD_LIBRARY_H
#include <freetype/config/integer-types.h>
#include <freetype/config/public-macros.h>
#include <freetype/config/mac-support.h>
#endif /* FTCONFIG_H_ */
/* END */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.