diff options
Diffstat (limited to 'Client/Source')
95 files changed, 5775 insertions, 0 deletions
diff --git a/Client/Source/Common/DataBuffer.cpp b/Client/Source/Common/DataBuffer.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Common/DataBuffer.cpp diff --git a/Client/Source/Common/DataBuffer.h b/Client/Source/Common/DataBuffer.h new file mode 100644 index 0000000..02bfc2b --- /dev/null +++ b/Client/Source/Common/DataBuffer.h @@ -0,0 +1,29 @@ +#pragma once + +enum EDataBufferType +{ + DataBufferMode_Binary, // 不以\0结尾,不包括\0 + DataBufferMode_Text, // 以\0结尾,包括\0 +}; + +// 描述内存对象,会作为 +// 1. 多线程读取文件的返回 +class DataBuffer +{ +public: + DataBuffer() + {} + ~DataBuffer() + { + delete data; + } + + union { + char* data; + unsigned char* udata; + char* sdata; + }; + int length; + EDataBufferType type; + +};
\ No newline at end of file diff --git a/Client/Source/Debug/Log.cpp b/Client/Source/Debug/Log.cpp new file mode 100644 index 0000000..1b0ddf4 --- /dev/null +++ b/Client/Source/Debug/Log.cpp @@ -0,0 +1,164 @@ +#include "../Threading/Mutex.h" +#include "log.h" +#include <iostream> +#include <ctime> +#include <unordered_set> +#include <stdarg.h> + +using namespace std; + +#ifdef RAGDOLL_WIN +#include <windows.h> +static HANDLE s_ConsoleHandle = 0; +#endif + +unordered_set<string> s_OpenTags; + +Mutex s_Mutex; + +#ifdef RAGDOLL_DEBUG +// https://stackoverflow.com/questions/997946/how-to-get-current-time-and-date-in-c +// Get current date/time, format is YYYY-MM-DD.HH:mm:ss +const std::string currentDateTime() { + time_t now = time(0); + struct tm tstruct; + char buf[80]; + tstruct = *localtime(&now); + // Visit http://en.cppreference.com/w/cpp/chrono/c/strftime + // for more information about date/time format + strftime(buf, sizeof(buf), "%Y-%m-%d %X", &tstruct); + + return buf; +} + +static void SetOutputColor(int i) { + if (s_ConsoleHandle == 0) { + s_ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); + } + if (i == 0) { + SetConsoleTextAttribute(s_ConsoleHandle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + else if(i == 1) { + SetConsoleTextAttribute(s_ConsoleHandle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } + else { + SetConsoleTextAttribute(s_ConsoleHandle, FOREGROUND_RED | FOREGROUND_INTENSITY); + } +} + +void log_error_null_param(const char* funcName, const char* param) +{ + log_error("Null parameter in %s called %s", funcName, param); +} + +void log_open_tag(const char* tag) +{ + s_OpenTags.insert(tag); +} + +void log_info(const char* fmt, ...) +{ + Lock(s_Mutex) { + SetOutputColor(0); + va_list pArgs = NULL; + va_start(pArgs, fmt); + printf("%s [Info] ", currentDateTime().c_str()); + vprintf(fmt, pArgs); + printf("\n"); + va_end(pArgs); + } +} + +void log_warning(const char* fmt, ...) +{ + Lock(s_Mutex) { + SetOutputColor(1); + va_list pArgs = NULL; + va_start(pArgs, fmt); + printf("%s [Warning] ", currentDateTime().c_str()); + vprintf(fmt, pArgs); + printf("\n"); + va_end(pArgs); + } +} + +void log_error(const char* fmt, ...) +{ + Lock(s_Mutex) { + SetOutputColor(2); + va_list pArgs = NULL; + va_start(pArgs, fmt); + printf("%s [Error] ", currentDateTime().c_str()); + vprintf(fmt, pArgs); + printf("\n"); + va_end(pArgs); + } +} + +#define CHECK_TAG(tag)\ +if (s_OpenTags.count(tag) == 0)\ + return; + +void log_info_tag(const char* tag, const char* fmt, ...) +{ + CHECK_TAG(tag); + + Lock(s_Mutex) { + SetOutputColor(0); + va_list pArgs = NULL; + va_start(pArgs, fmt); + printf("%s [Info] [%s] ", currentDateTime().c_str(), tag); + vprintf(fmt, pArgs); + printf("\n"); + va_end(pArgs); + } +} + +void log_warning_tag(const char* tag, const char* fmt, ...) +{ + CHECK_TAG(tag); + + Lock(s_Mutex) { + SetOutputColor(1); + va_list pArgs = NULL; + va_start(pArgs, fmt); + printf("%s [Warning] [%s] ", currentDateTime().c_str(), tag); + vprintf(fmt, pArgs); + printf("\n"); + va_end(pArgs); + } +} + +void log_error_tag(const char* tag, const char* fmt, ...) +{ + CHECK_TAG(tag); + + Lock(s_Mutex) { + SetOutputColor(2); + va_list pArgs = NULL; + va_start(pArgs, fmt); + printf("%s [Error] [%s] ", currentDateTime().c_str(), tag); + vprintf(fmt, pArgs); + printf("\n"); + va_end(pArgs); + } +} + +#else +void log_open_tag(std::string tag) {} +void log_info(std::string log) {} +void log_warning(std::string log){} +void log_error(std::string log){} +void log_error_null_param(std::string funcName, std::string param){} +void log_info(string tag, std::string log) {} +void log_warning(string tag, std::string log) {} +void log_error(string tag, std::string log) {} + +void log_open_tag(const char* tag) {} +void log_info(const char* log, ...) {} +void log_warning(const char* log, ...) {} +void log_error(const char* log, ...) {} +void log_info(const char* tag, const char* log, ...) {} +void log_warning(const char* tag, const char* log, ...) {} +void log_error(const char* tag, const char* log, ...) {} +#endif
\ No newline at end of file diff --git a/Client/Source/Debug/Log.h b/Client/Source/Debug/Log.h new file mode 100644 index 0000000..8547102 --- /dev/null +++ b/Client/Source/Debug/Log.h @@ -0,0 +1,14 @@ +#pragma once +#include <string> + +void log_open_tag(const char* tag); + +void log_info(const char* fmt, ...); +void log_warning(const char* fmt, ...); +void log_error(const char* fmt, ...); + +void log_error_null_param(const char* funcName, const char* param); + +void log_info_tag(const char* tag, const char* fmt, ...); +void log_warning_tag(const char* tag, const char* fmt, ...); +void log_error_tag(const char* tag, const char* fmt, ...); diff --git a/Client/Source/GUI/Font.cpp b/Client/Source/GUI/Font.cpp new file mode 100644 index 0000000..1efe551 --- /dev/null +++ b/Client/Source/GUI/Font.cpp @@ -0,0 +1,302 @@ +#include "freetype.h" +#include "Font.h" + +#include "../Math/Functions.h" +#include "../Math/Math.h" +#include "../Debug/Log.h" +#include "../Utilities/Assert.h" +#include "../Graphics/ImageData.h" + +using namespace character; + +/* +* http://madig.github.io/freetype-web/documentation/tutorial/ +* https://unicode-table.com/en/#cjk-unified-ideographs-extension-a +* https://learnopengl.com/In-Practice/Text-Rendering +* https://www.zhihu.com/question/294660079 +* https://baike.baidu.com/item/%E5%9F%BA%E6%9C%AC%E5%A4%9A%E6%96%87%E7%A7%8D%E5%B9%B3%E9%9D%A2/10788078 +* https://stackoverflow.com/questions/27331819/whats-the-difference-between-a-character-a-code-point-a-glyph-and-a-grapheme +*/ + +static std::string s_FontError; + +static std::vector<unsigned char> s_PixelBuffer; +static const int s_SizePerPixel = sizeof(unsigned char); + +Font::Font(std::string path, TextGeneratingSettings settings) + : m_Characters() +{ + m_AtlasMargin = settings.margin; + m_GlyphPadding = settings.padding; + m_AtlasSize = settings.atlasSize; + + if (FT_Init_FreeType(&m_FTLibrary)) + { + s_FontError = "Init freetype error. Font path " + path; + throw FontException(s_FontError.c_str()); + } + + if (FT_New_Face(m_FTLibrary, path.c_str(), 0, &m_FTFace)) + { + s_FontError = "Create freetype face error. Font path " + path; + throw FontException(s_FontError.c_str()); + } +} + +Font::Font(DataBuffer* db, TextGeneratingSettings settings) + : m_Characters() +{ + m_AtlasMargin = settings.margin; + m_GlyphPadding = settings.padding; + m_AtlasSize = settings.atlasSize; + + if (FT_Init_FreeType(&m_FTLibrary)) + { + s_FontError = "Init freetype error."; + throw FontException(s_FontError.c_str()); + } + + if (FT_New_Memory_Face(m_FTLibrary, db->udata, db->length, 0, &m_FTFace)) + { + s_FontError = "Create freetype face error."; + throw FontException(s_FontError.c_str()); + } +} + +character::Hash Font::GetHash(Unicode codepoint, int pixelSize) +{ + character::Hash hash; + hash.codepoint = codepoint; + hash.size = pixelSize; + return hash; +} + +const Character* Font::GetCharacter(character::Unicode codepoint, int pixelSize) +{ + character::Hash hash = GetHash(codepoint, pixelSize); + auto iter = m_Characters.find(hash.hashCode); + if (iter == m_Characters.end()) + { + if (RenderCharacter(codepoint, pixelSize)) + { + iter = m_Characters.find(hash.hashCode); + } + else + { + return NULL; + } + } + Assert(iter != m_Characters.end()); + return &iter->second; +} + +void Font::RenderCharacters(character::Unicode* codepoint, int n, int pixelSize) +{ + for (int i = 0; i < n; ++i) + { + RenderCharacter(codepoint[i], pixelSize); + } +} + +void Font::RenderCharacters(std::vector<character::Unicode>& codepoint, int pixelSize) +{ + int n = codepoint.size(); + for (int i = 0; i < n; ++i) + { + RenderCharacter(codepoint[i], pixelSize); + } +} + +bool Font::RenderCharacter(character::Unicode codepoint, int pixelSize) +{ + character::Hash hash = GetHash(codepoint, pixelSize); + if (m_Characters.size() > 0 && m_Characters.count(hash.hashCode) != 0) + return true; + FT_Set_Pixel_Sizes(m_FTFace, 0, pixelSize); + + // bug: 发现有时候渲染的结果是FT_PIXEL_MODE_MONO(1-bits),试过不同的flags组合还是不对,最后手动转换为8-bits + + FT_Int32 flags = FT_LOAD_RENDER; + if (FT_Load_Char(m_FTFace, codepoint, flags)) + return false; + + Character character; + + int w = m_FTFace->glyph->bitmap.width; + int h = m_FTFace->glyph->bitmap.rows; + + const unsigned char* pixels = m_FTFace->glyph->bitmap.buffer; + if (pixels != NULL) // 有些字体中space、tab没有字形的 + { + s_PixelBuffer.resize(w * h); + if (m_FTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) + { + // 1-bit monochrome + int pitch = m_FTFace->glyph->bitmap.pitch; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + int index = x + y * w; + s_PixelBuffer[index] = ((pixels[pitch * y + x / 8]) & (1 << (7 - x % 8))) ? 255 : 0; + } + } + } + else if (m_FTFace->glyph->bitmap.pixel_mode) + { + // 8-bit grayscale + memcpy(&s_PixelBuffer[0], pixels, s_SizePerPixel * w * h); + } + + //TextHelper::print_glyph(&s_PixelBuffer[0], w, h); + + GlyphAtals* atlas = RequestAtlas(pixelSize, Vector2f(w, h)); + Assert(atlas); + + Rect rect = GetRenderChartAndMove(atlas, Vector2f(w, h)); + + try + { + atlas->altas->UpdateSubImage(rect, EPixelFormat::PixelFormat_R, EPixelElementType::PixelType_UNSIGNED_BYTE, &s_PixelBuffer[0]); + } + catch (TextureException& e) + { + s_PixelBuffer.clear(); + std::string error = e.what(); + error = "Render Character Error: " + error; + log_error(e.what()); + return false; + } + s_PixelBuffer.clear(); + + character.atlas = atlas->index; + character.position = rect; + character.bearing = Vector2f(m_FTFace->glyph->bitmap_left, m_FTFace->glyph->bitmap_top); + } + else // space、tab + { + character.atlas = FONT_NOT_IN_ATLAS_PLACEHOLDER; + character.position = Rect(0,0,0,0); + character.bearing = Vector2f(0, 0); + } + + character.advance = m_FTFace->glyph->advance.x * 1/64.f; + + m_Characters.insert(std::pair<unsigned int, Character>(hash.hashCode, character)); + + return true; +} + +const GlyphAtals* Font::GetGlyphAtlas(int index) +{ + if (index >= m_Atlases.size()) + return NULL; + return &m_Atlases[index]; +} + +Rect Font::GetRenderChartAndMove(GlyphAtals* atlas, Vector2f preferSize) +{ + Rect rect; + Vector2f space; + space.x = atlas->width - atlas->cursor.x - m_AtlasMargin; + space.y = atlas->height - atlas->cursor.y - m_AtlasMargin; + if (space.x > preferSize.x && space.y > preferSize.y) + { + rect.x = atlas->cursor.x; + rect.y = atlas->cursor.y; + rect.width = preferSize.x; + rect.height = preferSize.y; + atlas->cursor.x += rect.width + m_GlyphPadding; + atlas->rowHeight = MathUtils::Max((float)atlas->rowHeight, preferSize.y); + } + else if (space.y - atlas->rowHeight - m_GlyphPadding - m_AtlasMargin > preferSize.y) + { + rect.x = m_AtlasMargin; + rect.y = atlas->cursor.y + m_GlyphPadding + atlas->rowHeight; + rect.width = preferSize.x; + rect.height = preferSize.y; + atlas->cursor.x = m_AtlasMargin; + atlas->cursor.y += atlas->rowHeight + m_GlyphPadding; + atlas->rowHeight = rect.height; + } + else + { + Assert(false); + } + return rect; +} + +GlyphAtals* Font::RequestAtlas(int pixelSize, Vector2f preferSize) +{ + GlyphAtals* atlas = NULL; + auto iter = m_AtlasCache.find(pixelSize); + if (iter == m_AtlasCache.end() || !HasEnoughSpace(iter->second, preferSize)) + { + Assert(m_Atlases.size() < FONT_NOT_IN_ATLAS_PLACEHOLDER); + + Texture* tex = CreateAtlas(); + GlyphAtals newAtlas = GlyphAtals(); + newAtlas.altas = tex; + newAtlas.width = m_AtlasSize.x; + newAtlas.height = m_AtlasSize.y; + newAtlas.index = m_Atlases.size(); + newAtlas.cursor = Vector2f(m_AtlasMargin, m_AtlasMargin); + + m_Atlases.push_back(newAtlas); + atlas = &m_Atlases[m_Atlases.size() - 1]; + + if (iter != m_AtlasCache.end()) + m_AtlasCache.erase(pixelSize); + m_AtlasCache.insert(std::pair<int, GlyphAtals*>(pixelSize, atlas)); + } + else + { + atlas = iter->second; + } + Assert(atlas); + return atlas; +} + +Texture* Font::CreateAtlas() +{ + TextureSetting setting; + setting.filterMode = ETextureFilterMode::Nearest; + setting.wrapMode = ETextureWrapMode::Clamp; + setting.format = ETextureFormat::R8; + setting.type = ETextureType::TEX_2D; + setting.keepImageData = false; + Texture* tex = new Texture(setting, m_AtlasSize.x, m_AtlasSize.y); + return tex; +} + +bool Font::HasEnoughSpace(GlyphAtals* atlas, Vector2f preferSize) +{ + if (atlas == NULL) + return false; + Vector2f space; + space.x = atlas->width - atlas->cursor.x - m_AtlasMargin; + space.y = atlas->height - atlas->cursor.y - m_AtlasMargin; + if (space.x > preferSize.x && space.y > preferSize.y) + return true; + if (space.y - atlas->rowHeight - m_GlyphPadding - m_AtlasMargin > preferSize.y) + return true; + return false; +} + +Character GetCharacter(character::Unicode Unicode, int pixelSize) +{ + return Character(); +} + +void TextHelper::print_glyph(unsigned char* glyph, int width, int height) +{ + for (int r = 0; r < height; ++r) + { + for (int c = 0; c < width; ++c) + { + unsigned char ch = glyph[c + r * width]; + printf("%c", ch == 0 ? ' ' : '+'); + } + printf("\n"); + } +} diff --git a/Client/Source/GUI/Font.h b/Client/Source/GUI/Font.h new file mode 100644 index 0000000..6fd3c19 --- /dev/null +++ b/Client/Source/GUI/Font.h @@ -0,0 +1,149 @@ +#pragma once + +#include "../Utilities/Singleton.h" +#include "../Graphics/Texture.h" +#include "../Common/DataBuffer.h" +#include "../Math/Math.h" + +#include "freetype.h" + +#include <string> +#include <unordered_map> +#include <exception> +#include <vector> + +//https://github.com/kaienfr/Font + +struct Character { + unsigned int atlas; // atlas索引 + Rect position; // 在altas里的位置,以左上角为原点的,和GL的Y镜像 + Vector2f bearing; // 左上角相对于原点的偏移 + unsigned int advance; // 总宽,算上了间隔 +}; + +namespace character +{ +#if GAMELAB_WIN + typedef unsigned short Unicode; // unicode codepoint(BMP,U+0000至U+FFFF) +#else + typedef unsigned int Unicode; // unicode codepoint(BMP,U+0000至U+FFFF) +#endif + +#pragma pack(1) + union Hash { + unsigned int hashCode; + struct { + Unicode codepoint; + unsigned short size;//字体大小 + }; +#pragma pack(pop) + + bool operator==(const Hash &other) const + { + return codepoint == other.codepoint && size == other.size; + } + }; +} + +struct UnicodeString +{ + character::Unicode* str; + int length; +}; + +namespace std +{ + template <> + struct hash<character::Hash> + { + std::size_t operator()(const character::Hash& k) const + { + return k.hashCode; + } + }; +} + +struct GlyphAtals +{ + int index; + Texture* altas; // 贴图 + int width, height; // 尺寸 + + Vector2f cursor; // 游标,从左上角(0,0)开始 + int rowHeight; // 当前行的高度 +}; + +struct TextGeneratingSettings +{ + Vector2f atlasSize; // atlas的尺寸 + int margin; // atlas的边界 + int padding; // glyph相互之间的间距,防止采样的时候越界 +}; + +class FontException : public std::exception +{ +public: + FontException(const char* what) + : std::exception(what) + { + } +}; + +enum EEncoding +{ + Encoding_ASCII, + Encoding_UTF8, + Encoding_UTF16, +}; + +// 没有字形,但是有advance的文字,比如space和tab +#define FONT_NOT_IN_ATLAS_PLACEHOLDER (INT_MAX) + +// 单个字体 +class Font +{ +public: + Font(std::string path, TextGeneratingSettings setting)/*throw FontException*/; + Font(DataBuffer* db, TextGeneratingSettings setting)/*throw FontException*/; + + const Character* GetCharacter(character::Unicode codepoint, int pixelSize); + const GlyphAtals* GetGlyphAtlas(int index); + + // pre-bake + bool RenderCharacter(character::Unicode codepoint, int pixelSize); + void RenderCharacters(character::Unicode* codepoint, int n, int pixelSize); + void RenderCharacters(std::vector<character::Unicode>& codepoint, int pixelSize); + + GET(Vector2f, AtlasSize, m_AtlasSize); + +private: + Texture* CreateAtlas(); + GlyphAtals* RequestAtlas(int pixelSize, Vector2f preferSize); + Rect GetRenderChartAndMove(GlyphAtals* atlas, Vector2f preferSize); + bool HasEnoughSpace(GlyphAtals* atlas, Vector2f preferSize); + character::Hash GetHash(character::Unicode Unicode, int pixelSize); + + //------------------------------------------------------------------------- + + std::unordered_map<unsigned int , Character> m_Characters; // 渲染完的文字 + + std::vector<GlyphAtals> m_Atlases; // 当前所有的atlas,由font完全拥有所有权,所以是lightuserdata + std::unordered_map<int/*pixelSize*/, GlyphAtals*> m_AtlasCache; // 快速找到可用的atlas + + bool m_IsEnabled; + + Vector2f m_AtlasSize; + int m_AtlasMargin; + int m_GlyphPadding; + + FT_Library m_FTLibrary; + FT_Face m_FTFace; + +}; + +namespace TextHelper +{ + + void print_glyph(unsigned char* glyph, int width, int height); + +} diff --git a/Client/Source/GUI/TextMeshGenerator.cpp b/Client/Source/GUI/TextMeshGenerator.cpp new file mode 100644 index 0000000..766a9b0 --- /dev/null +++ b/Client/Source/GUI/TextMeshGenerator.cpp @@ -0,0 +1,89 @@ +#include "TextMeshGenerator.h" + +// 计算一个哈希值 +static uint32_t GetStrHash(const UnicodeString& str) +{ + if (str.length == 0) + return 0; + + int len = str.length; + uint32_t hash = (uint32_t)len; // seed + size_t step = (len >> 5) + 1; + for (int l = len; l >= step; l -= step) + { + unsigned short unicode = str.str[l - 1]; + unsigned char hicode = *((unsigned char*)&unicode); + unsigned char locode = *(((unsigned char*)&unicode) + 1); + hash = hash ^ ((hash << 5) + (hash >> 2) + hicode + locode); + } + return hash; +} + +const UITextMesh* TextMeshGenerator::GetTextMesh( + const UnicodeString& str + , Font* font + , int pixelSize + , int lineHeight + , Color32 col32 + , ETextAnchor anchor + , ETextAlignment alignment + , bool wordwrap + , float preferred +){ + uint32_t hash = GetStrHash(str); + UITextMeshList* tm = NULL; + bool hasHash = m_TextMeshes.count(hash); + if (hasHash) + { + tm = m_TextMeshes[hash]; + while (tm) + { + UITextMesh* mesh = tm->mesh; + if (mesh->GetFont() != font + || mesh->GetPixelSize() != pixelSize + || mesh->GetLineHeight() != lineHeight + || mesh->GetAnchor() != anchor + || mesh->GetAlignment() != alignment + || mesh->GetWordwrap() != wordwrap + || mesh->GetPreferred() != preferred + || mesh->GetColor() != col32 + ){ + tm = tm->next; + continue; + } + const UnicodeString& content = mesh->GetContent(); + if (content.length != str.length) + { + tm = tm->next; + continue; + } + if (memcmp(str.str, content.str, sizeof(character::Unicode) * str.length) == 0) + { + return tm->mesh; + } + tm = tm->next; + } + } + tm = new UITextMeshList(); + try { + tm->mesh = new UITextMesh(str, font, pixelSize, lineHeight, col32, anchor, alignment, wordwrap, preferred); + log_info("Text", "New UITextMesh"); + } + catch(TextMeshException& e) + { + delete tm; + throw; + } + if (hasHash) + { + UITextMeshList* list = m_TextMeshes[hash]; + tm->next = list; + m_TextMeshes[hash] = tm; + } + else + { + tm->next = NULL; + m_TextMeshes.insert(std::pair<uint32_t, UITextMeshList*>(hash, tm)); + } + return tm->mesh; +} diff --git a/Client/Source/GUI/TextMeshGenerator.h b/Client/Source/GUI/TextMeshGenerator.h new file mode 100644 index 0000000..acc0a0a --- /dev/null +++ b/Client/Source/GUI/TextMeshGenerator.h @@ -0,0 +1,31 @@ +#pragma once + +#include <vector> +#include <unordered_map> + +#include "../Debug/Log.h" +#include "../Graphics/Color.h" + +#include "UITextMesh.h" +#include "Font.h" + +// 逐步回收长期没用到的textmesh +class GraduallyReleaseTextMesh +{ +}; + +struct UITextMeshList { + UITextMesh* mesh; + UITextMeshList* next; +}; + +class TextMeshGenerator : public Singleton<TextMeshGenerator> +{ +public: + const UITextMesh* GetTextMesh(const UnicodeString& str, Font* font, int pixelSize, int lineHeight, Color32 col32, ETextAnchor anchor, ETextAlignment alignment, bool wordwrap, float preferred); + +private: + std::unordered_map<uint32_t, UITextMeshList*> m_TextMeshes; +}; + +#define g_TextMeshGenerator (*TextMeshGenerator::Instance())
\ No newline at end of file diff --git a/Client/Source/GUI/UI9Slicing.cpp b/Client/Source/GUI/UI9Slicing.cpp new file mode 100644 index 0000000..c0d8f60 --- /dev/null +++ b/Client/Source/GUI/UI9Slicing.cpp @@ -0,0 +1,112 @@ +#include "../Math/Math.h" + +#include "UI9Slicing.h" + +#include <cstring> +#include <vector> + +using namespace std; + +static vector<UIVertexLayout> s_Vertices; +static vector<UIIndex> s_Indices; + +UI9Slicing::UI9Slicing(int mode, Vector2f horizontal, Vector2f vertical, Vector2f texPixelSize, Vector2f size) +{ + m_Slicing = mode; + m_Horizontal = horizontal.Clamp(0, texPixelSize.x, 0, texPixelSize.x); + m_Vertical = vertical.Clamp(0, texPixelSize.y, 0, texPixelSize.y); + + if (m_Horizontal[0] + m_Horizontal[1] > texPixelSize.x || m_Vertical[0] + m_Vertical[1] > texPixelSize.y) + { + throw UIMeshException("UI9Slicing wrong parameter."); + } + m_TexSize = texPixelSize; + m_Size = size; +} + +void UI9Slicing::Draw() +{ + s_Indices.clear(); + s_Vertices.clear(); + + Vector2f tileSize = Vector2f(m_TexSize.x - m_Horizontal[0] - m_Horizontal[1], m_TexSize.y - m_Vertical[0] - m_Vertical[1]); + Vector2f fillSize = Vector2f(m_Size.x - m_Horizontal[0] - m_Horizontal[1], m_Size.y - m_Vertical[0] - m_Vertical[1]); + // horizonal和vertical对应的uv,注意uv在左下角,mesh在左上角 + Vector2f tileUVx = Vector2f(m_Horizontal[0] / m_TexSize.x, (m_TexSize.x - m_Horizontal[1]) / m_TexSize.x); + Vector2f tileUVy = Vector2f((m_TexSize.y - m_Vertical[0]) / m_TexSize.y, m_Vertical[1] / m_TexSize.y); + + // fill vertices + int row = 2 + ((fillSize.y <= 0) ? 0 : (m_Slicing == Slicing_Simple ? 2 : ceilf((float)fillSize.y / tileSize.y) + 1)); + int colum = 2 + ((fillSize.x <= 0) ? 0 : (m_Slicing == Slicing_Simple ? 2 : ceilf((float)fillSize.x / tileSize.x) + 1)); + + for (int r = 0; r < row; ++r) + { + UIVertexLayout vert; + vert.color = Color32::white; + if (r == 0) + { + vert.position.y = 0; + vert.uv.y = 1; + } + else if (r == row - 1) + { + vert.position.y = m_Size.y; + vert.uv.y = 0; + } + else { + if (m_Slicing == Slicing_Tiled) { + vert.position.y = clamp(m_Vertical[0] + (r - 1) * tileSize.y, m_Vertical[0], m_Size.y - m_Vertical[1]); + vert.uv.y = odd(r - 1) ? tileUVy[1] : tileUVy[0]; + } else { + vert.position.y = odd(r - 1) ? m_Size.y - m_Vertical[1] : m_Vertical[0]; + vert.uv.y = odd(r - 1) ? tileUVy[1] : tileUVy[0]; + } + } + for (int c = 0; c < colum; ++c) + { + if (c == 0) + { + vert.position.x = 0; + vert.uv.x = 0; + } + else if (c == colum - 1) + { + vert.position.x = m_Size.x; + vert.uv.x = 1; + } + else { + if (m_Slicing == Slicing_Tiled) { + vert.position.x = clamp(m_Horizontal[0] + (c - 1) * tileSize.x, m_Horizontal[0], m_Size.x - m_Horizontal[0]); + vert.uv.x = odd(c - 1) ? tileUVx[1] : tileUVx[0]; + } else { + vert.position.x = odd(c - 1) ? (m_Size.x - m_Horizontal[1]) : m_Horizontal[0]; + vert.uv.x = odd(c - 1) ? tileUVx[1] : tileUVx[0]; + } + } + s_Vertices.push_back(vert); + + if (c < colum - 1 && r < row - 1) { + int index = c + r * colum; + s_Indices.push_back(index); s_Indices.push_back(index + colum); s_Indices.push_back(index + colum + 1); + s_Indices.push_back(index + colum + 1); s_Indices.push_back(index + 1); s_Indices.push_back(index); + } + } + } + + void* vb; + void* ib; + + int vertCount = s_Vertices.size(); + int indicesCount = s_Indices.size(); + + g_SharedVBO.GetChunk(sizeof(UIVertexLayout), sizeof(UIIndex), vertCount, indicesCount, Primitive_Triangle, &vb, &ib); + + memcpy(vb, &s_Vertices[0], vertCount * sizeof(UIVertexLayout)); + memcpy(ib, &s_Indices[0], indicesCount* sizeof(UIIndex)); + + s_Indices.resize(0); + s_Vertices.resize(0); + + g_SharedVBO.ReleaseChunk(vertCount, indicesCount); + g_SharedVBO.DrawChunk(s_UIVertexLayout); +}
\ No newline at end of file diff --git a/Client/Source/GUI/UI9Slicing.h b/Client/Source/GUI/UI9Slicing.h new file mode 100644 index 0000000..6a37447 --- /dev/null +++ b/Client/Source/GUI/UI9Slicing.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UIMesh.h" + +enum ESlicing +{ + Slicing_Simple, + Slicing_Tiled, +}; + +// 九宫格 +class UI9Slicing : public UIMesh +{ +public: + UI9Slicing(int mode, Vector2f horizontal, Vector2f vertical, Vector2f texPixelSize, Vector2f size)/*throw UIMeshException*/; + + void Draw() override; + +private: + int m_Slicing; + Vector2f m_Horizontal; // 左右两条切割线到左边和右边的距离 + Vector2f m_Vertical; // 上下两条切割线到上下的距离 + Vector2f m_TexSize; + Vector2f m_Size; +}; diff --git a/Client/Source/GUI/UILine.cpp b/Client/Source/GUI/UILine.cpp new file mode 100644 index 0000000..8aa8c1d --- /dev/null +++ b/Client/Source/GUI/UILine.cpp @@ -0,0 +1,48 @@ +#include "UILine.h" + +struct UILineLayout +{ + Vector2f position; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UILineLayout)); + + layout.attributes.push_back(POSITION); +}); + +void UILine::Draw() +{ + const int nVerts = 2; + const int nIndices = 2; + + float pos[] = { + m_From.x, m_From.y, + m_To.x, m_To.y, + }; + + uint16 indices[] = { + 0, 1 + }; + + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(UILineLayout), sizeof(uint16), nVerts, nIndices, Primitive_Line, (void**)&vb, (void**)&ib); + + UILineLayout* dst = (UILineLayout*)vb; + + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[2 * i], pos[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(nVerts, nIndices); + g_SharedVBO.DrawChunk(layout); +}
\ No newline at end of file diff --git a/Client/Source/GUI/UILine.h b/Client/Source/GUI/UILine.h new file mode 100644 index 0000000..a47b1f1 --- /dev/null +++ b/Client/Source/GUI/UILine.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../Utilities/StaticInitiator.h" +#include "UIMesh.h" + +class UILine : public UIMesh +{ +public: + UILine(Vector2f from, Vector2f to) + { + m_From = from; + m_To = to; + } + + void Draw() override; + +private: + Vector2f m_From; + Vector2f m_To; + +}; diff --git a/Client/Source/GUI/UIMesh.cpp b/Client/Source/GUI/UIMesh.cpp new file mode 100644 index 0000000..9616c46 --- /dev/null +++ b/Client/Source/GUI/UIMesh.cpp @@ -0,0 +1,15 @@ +#include "UIMesh.h" + +CustomVertexLayout UIMesh::s_UIVertexLayout; +unsigned int UIMesh::s_SizePerVertex; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UIVertexLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(UIVertexLayout)); + VertexAttributeDescriptor COLOR = VertexAttributeDescriptor(sizeof(Vector2f) * 2, 4, VertexAttrFormat_Unsigned_Byte, sizeof(UIVertexLayout), true); + UIMesh::s_UIVertexLayout.attributes.push_back(POSITION); + UIMesh::s_UIVertexLayout.attributes.push_back(UV); + UIMesh::s_UIVertexLayout.attributes.push_back(COLOR); + + UIMesh::s_SizePerVertex = sizeof(UIVertexLayout); +}); diff --git a/Client/Source/GUI/UIMesh.h b/Client/Source/GUI/UIMesh.h new file mode 100644 index 0000000..11f157a --- /dev/null +++ b/Client/Source/GUI/UIMesh.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../Utilities/Exception.h" +#include "../Graphics/DynamicMesh.h" +#include "../Math/Math.h" +#include "../Utilities/StaticInitiator.h" +#include "../Graphics/CustomVertexLayout.h" +#include "../Graphics/DefaultVertexLayout.h" +#include "../Graphics/Color.h" +#include "../Graphics/GfxDevice.h" + +// UI默认的顶点布局 +struct UIVertexLayout +{ + UIVertexLayout(Vector2f pos = Vector2f::zero, Vector2f texCoord = Vector2f::zero, Color32 col = Color32()) + { + position = pos; + uv = texCoord; + color = col; + } + + Vector2f position; + Vector2f uv; + Color32 color; +}; + +typedef uint16 UIIndex; + +CustomException(UIMeshException); + +// 所有的UIMesh都是左上角为原点 +class UIMesh : public DynamicMesh +{ +public: + UIMesh() : DynamicMesh() {} + virtual ~UIMesh() {} + + virtual void Draw() = 0; + + static CustomVertexLayout s_UIVertexLayout; + static unsigned int s_SizePerVertex; +}; diff --git a/Client/Source/GUI/UIQuad.cpp b/Client/Source/GUI/UIQuad.cpp new file mode 100644 index 0000000..d3c7825 --- /dev/null +++ b/Client/Source/GUI/UIQuad.cpp @@ -0,0 +1,64 @@ +#include "../Math/Math.h" +#include "../Graphics/GfxDevice.h" + +#include "UIQuad.h" + +struct UIQuadLayout +{ + Vector2f position; + Vector2f uv; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UIQuadLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(UIQuadLayout)); + + layout.attributes.push_back(POSITION); + layout.attributes.push_back(UV); +}); + +void UIQuad::Draw() +{ + const int nVerts = 4; + const int nIndices = 6; + + float pos[] = { + m_Left, m_Bottom, // left-bottom + m_Right, m_Bottom, // right-bottom + m_Right, m_Top, // right-top + m_Left, m_Top, // top-left + }; + + float uv[] = { + 0, 0, + 1, 0, + 1, 1, + 0, 1, + }; + + uint16 indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(UIQuadLayout), sizeof(uint16), nVerts, nIndices, Primitive_Triangle, (void**)&vb, (void**)&ib); + + UIQuadLayout* dst = (UIQuadLayout*)vb; + + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[2 * i], pos[2 * i + 1]); + dst[i].uv.Set(uv[2 * i], uv[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(nVerts, nIndices); + g_SharedVBO.DrawChunk(layout); +}
\ No newline at end of file diff --git a/Client/Source/GUI/UIQuad.h b/Client/Source/GUI/UIQuad.h new file mode 100644 index 0000000..278848a --- /dev/null +++ b/Client/Source/GUI/UIQuad.h @@ -0,0 +1,21 @@ +#pragma once +#include "../Utilities/StaticInitiator.h" +#include "UIMesh.h" + +class UIQuad : public UIMesh +{ +public : + UIQuad(float l, float r, float t, float b) + { + m_Left = l; + m_Right = r; + m_Top = t; + m_Bottom = b; + } + + void Draw() override; + +private: + float m_Left, m_Right, m_Top, m_Bottom; + +}; diff --git a/Client/Source/GUI/UISquare.cpp b/Client/Source/GUI/UISquare.cpp new file mode 100644 index 0000000..f5c3702 --- /dev/null +++ b/Client/Source/GUI/UISquare.cpp @@ -0,0 +1,53 @@ +#include "../Math/Math.h" +#include "../Graphics/GfxDevice.h" + +#include "UISquare.h" + +struct UISquareLayout +{ + Vector2f position; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UISquareLayout)); + + layout.attributes.push_back(POSITION); +}); + +void UISquare::Draw() +{ + const int nVerts = 4; + const int nIndices = 6; + + float pos[] = { + m_Left, m_Bottom, // left-bottom + m_Right, m_Bottom, // right-bottom + m_Right, m_Top, // right-top + m_Left, m_Top, // top-left + }; + + int indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(UISquareLayout), sizeof(uint16), 4, 6, Primitive_Triangle, (void**)&vb, (void**)&ib); + + UISquareLayout* dst = (UISquareLayout*)vb; + + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[2 * i], pos[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(4, 6); + g_SharedVBO.DrawChunk(layout); +} diff --git a/Client/Source/GUI/UISquare.h b/Client/Source/GUI/UISquare.h new file mode 100644 index 0000000..8de07f0 --- /dev/null +++ b/Client/Source/GUI/UISquare.h @@ -0,0 +1,22 @@ +#pragma once +#include "../Utilities/StaticInitiator.h" +#include "UIMesh.h" + +// 纯色方形 +class UISquare : public UIMesh +{ +public: + UISquare(float l, float r, float t, float b) + { + m_Left = l; + m_Right = r; + m_Top = t; + m_Bottom = b; + } + + void Draw() override; + +private: + float m_Left, m_Right, m_Top, m_Bottom; + +}; diff --git a/Client/Source/GUI/UITextMesh.cpp b/Client/Source/GUI/UITextMesh.cpp new file mode 100644 index 0000000..13bd8dc --- /dev/null +++ b/Client/Source/GUI/UITextMesh.cpp @@ -0,0 +1,278 @@ +#include "../Graphics/CustomVertexLayout.h" +#include "../Utilities/StaticInitiator.h" +#include "../Math/Math.h" +#include "../Graphics/Color.h" +#include "../Graphics/GfxDevice.h" +#include "../Utilities/AutoInvoke.h" +#include "../Graphics/DefaultVertexLayout.h" +#include "../Debug/Log.h" + +#include "UITextMesh.h" + +#include <vector> +#include <unordered_map> + +using namespace std; + +struct TextMeshVBOLayout +{ + Vector2f position; + Vector2f uv; + Color32 color; +}; + +static CustomVertexLayout s_TextMeshVBOLayout; + +static unsigned int s_VertexPerText; +static unsigned int s_SizePerVertex; +static unsigned int s_SizePerText; +static unsigned int s_SizePerIndex; +static unsigned int s_IndicesPerText; + +struct TextInfo { + const Character* ch; + float offset; + int line; // 从0开始 +}; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(TextMeshVBOLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(TextMeshVBOLayout)); + VertexAttributeDescriptor COLOR = VertexAttributeDescriptor(sizeof(Vector2f)*2, 4, VertexAttrFormat_Unsigned_Byte, sizeof(TextMeshVBOLayout), true); + s_TextMeshVBOLayout.attributes.push_back(POSITION); + s_TextMeshVBOLayout.attributes.push_back(UV); + s_TextMeshVBOLayout.attributes.push_back(COLOR); + + s_VertexPerText = 4; + s_SizePerVertex = sizeof(TextMeshVBOLayout); + + s_IndicesPerText = 6; + s_SizePerIndex = VertexLayout::GetDefaultIndexSize(); + + s_SizePerText = sizeof(TextMeshVBOLayout) * 4; +}); + +UITextMesh::UITextMesh( + const UnicodeString& str // 文本 + , Font* font // 字体 + , int pixelSize // 大小 + , int lineHeight // 行高 + , Color32 color32 // 颜色 + , ETextAnchor anchor // 锚点 + , ETextAlignment alignment // 对齐方式 + , bool wordwrap // 自动换行 + , float preferred // 自动换行区域大小 +){ + m_Font = font; + m_PixelSize = pixelSize; + m_LineHeight = lineHeight; + m_Color = color32; + m_Alignment = alignment; + m_Anchor = anchor; + m_Wordwrap = wordwrap; + m_Preferred = preferred; + m_Content.length = str.length; + m_Content.str = (character::Unicode*)malloc(str.length * sizeof(character::Unicode)); + memcpy(m_Content.str, str.str, str.length * sizeof(character::Unicode)); + + // 记录文本按atlas分类 + static unordered_map<unsigned int, vector<TextInfo>> s_TextInfos; + s_TextInfos.clear(); + + // 记录文本每行的长度 + static unordered_map<int, int> s_LineWidths; + s_LineWidths.clear(); + + // 记录每行的偏移长度 + static unordered_map<int, int> s_LineOffsets; + s_LineOffsets.clear(); + + InvokeWhenLeave([]() { + s_LineWidths.clear(); + s_LineOffsets.clear(); + s_TextInfos.clear(); + }); + + const Vector2f atlasSize = font->GetAtlasSize(); + + //----------------------------------------------------------------- + // 处理了换行和自动换行之后的文本区域大小 + Vector2f textRegion; + // 按照不同的atlas分类到s_TextInfos + float offset = 0; + int line = 0; + for (int i = 0; i < str.length; ++i) + { + character::Unicode c = str.str[i]; + const Character* ch = font->GetCharacter(c, pixelSize); + if (ch == NULL) + continue; + + if (wordwrap) // 自动换行 + { + if (offset + ch->bearing.x + ch->position.width > preferred) + { + + ++line; + offset = 0; + } + } + + unsigned int atlasIndex = ch->atlas; + if (atlasIndex != FONT_NOT_IN_ATLAS_PLACEHOLDER) //非空格 + { +// 换行符Unix'\n', Windows'\r\n', MacOS '\r' +#define CHECK_BREAKS() \ + if (c == '\n' || c == '\r') \ + { \ + ++line; \ + offset = 0; \ + if (c == '\r' && ((i + 1) < str.length && str.str[i + 1] == '\n')) /*skip\n*/ \ + ++i; \ + continue; \ + } + + CHECK_BREAKS(); + + TextInfo info; + info.ch = ch; + info.offset = offset; + info.line = line; + + auto list = s_TextInfos.find(atlasIndex); + if (list == s_TextInfos.end()) + s_TextInfos.insert(std::pair<unsigned int, vector<TextInfo>>(atlasIndex, vector<TextInfo>())); + + vector<TextInfo>& v = s_TextInfos[atlasIndex]; + v.push_back(info); + } + else + { + // 有些字体换行没有字形,所以也需要在这里处理 + CHECK_BREAKS(); + } + + offset += ch->advance; + + textRegion.x = max(offset, textRegion.x); + + if (s_LineWidths.count(line) == 0) + s_LineWidths.insert(std::pair<int, int>(line, 0)); + s_LineWidths[line] = max(offset, s_LineWidths[line]); + } + + textRegion.y = (line + 1) * lineHeight; + + if (s_TextInfos.size() == 0) + { + return; + } + + Vector2i textOffset; + Vector2f halfRegion = Vector2f(textRegion.x/ 2.f, textRegion.y / 2.f); + switch (anchor) + { + case TextAnchor_UpperLeft: textOffset.Set(0, 0); break; + case TextAnchor_UpperCenter: textOffset.Set(-halfRegion.x, 0); break; + case TextAnchor_UpperRight: textOffset.Set(-textRegion.x, 0); break; + case TextAnchor_MiddleLeft: textOffset.Set(0, -halfRegion.y); break; + case TextAnchor_MiddleCenter: textOffset.Set(-halfRegion.x, -halfRegion.y); break; + case TextAnchor_MiddleRight: textOffset.Set(-textRegion.x, -halfRegion.y); break; + case TextAnchor_LowerLeft: textOffset.Set(0, -textRegion.y); break; + case TextAnchor_LowerCenter: textOffset.Set(-halfRegion.x, -textRegion.y); break; + case TextAnchor_LowerRight: textOffset.Set(-textRegion.x, -textRegion.y); break; + } + + for (int i = 0; i < line; ++i) + { + int lineLen = s_LineWidths.count(i) != 0 ? s_LineWidths[i] : 0; + int lineOffset = 0; + switch (alignment) + { + case TextAlignment_Left: lineOffset = 0; break; + case TextAlignment_Center: lineOffset = (textRegion.x - lineLen)/2.f; break; + case TextAlignment_Right: lineOffset = textRegion.x - lineLen; break; + } + s_LineOffsets.insert(std::pair<int, int>(i, lineOffset)); + } + + //----------------------------------------------------------------- + + // 填充VBO和IBO + for (auto iter : s_TextInfos) { + unsigned int atlasIndex = iter.first; // atlas atlasIndex + vector<TextInfo>& texts = iter.second; + int textCount = texts.size(); + + VertexBuffer* vb = new VertexBuffer(textCount * s_SizePerText, textCount * s_IndicesPerText * s_SizePerIndex, VertexBuffer::VertexBufferType_Static); + void* pVB; + uint16* pIB; + + vb->GetChunk(s_SizePerVertex, s_SizePerIndex, s_VertexPerText * textCount, s_IndicesPerText * textCount, EPrimitive::Primitive_Triangle, &pVB,(void**) &pIB); + + TextMeshVBOLayout* dst = (TextMeshVBOLayout*)pVB; + for (int i = 0; i < textCount; ++i) + { + TextInfo& text = texts[i]; + + int vOff = i * s_VertexPerText; + int lineXOff = s_LineOffsets.count(text.line) ? s_LineOffsets[text.line] : 0; + int lineYOff = text.line * lineHeight; + // 左上角是原点 + float pos[] = { + textOffset.x + lineXOff + text.offset + text.ch->bearing.x, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y + text.ch->position.height, // bottom-left + textOffset.x + lineXOff + text.offset + text.ch->bearing.x + text.ch->position.width, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y + text.ch->position.height, // bottom-right + textOffset.x + lineXOff + text.offset + text.ch->bearing.x + text.ch->position.width, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y, // top-right + textOffset.x + lineXOff + text.offset + text.ch->bearing.x, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y, // top-left + }; + Vector4f uvQuad = Vector4f(text.ch->position.x / atlasSize.x, text.ch->position.y / atlasSize.y, text.ch->position.width / atlasSize.x, text.ch->position.height / atlasSize.y); + float uv[] = { + uvQuad.x, uvQuad.y + uvQuad.w, + uvQuad.x + uvQuad.z, uvQuad.y + uvQuad.w, + uvQuad.x + uvQuad.z, uvQuad.y, + uvQuad.x, uvQuad.y, + }; + for (int j = 0; j < s_VertexPerText; ++j) + { + dst[vOff + j].position.Set(pos[2 * j], pos[2 * j + 1]); + dst[vOff + j].uv.Set(uv[2 * j], uv[2 * j + 1]); + dst[vOff + j].color = color32; + } + + int iOff = i * s_IndicesPerText; + int indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + for (int j = 0; j < s_IndicesPerText; ++j) + pIB[iOff + j] = vOff + indices[j]; + } + + vb->FlushChunk(s_VertexPerText * textCount, s_IndicesPerText * textCount); + + m_VBOs.insert(std::pair<int, VertexBuffer*>(atlasIndex, vb)); + } + + WipeGLError(); +} + +void UITextMesh::Draw() const +{ + for (auto subText : m_VBOs) + { + int atlasIndex = subText.first; // atlasIndex of atlas + VertexBuffer* vbo = subText.second; + + const GlyphAtals* atlas = m_Font->GetGlyphAtlas(atlasIndex); + if (atlas == NULL) + { + log_error("Render text failed, no glyph atlas."); + continue; + } + + g_GfxDevice.SetUniformTexture("gamelab_main_tex", atlas->altas); + + WipeGLError(); + vbo->Draw(s_TextMeshVBOLayout); + } +} diff --git a/Client/Source/GUI/UITextMesh.h b/Client/Source/GUI/UITextMesh.h new file mode 100644 index 0000000..869db80 --- /dev/null +++ b/Client/Source/GUI/UITextMesh.h @@ -0,0 +1,69 @@ +#pragma once + +#include "../Graphics/VertexBuffer.h" +#include "../Utilities/Exception.h" +#include "../Graphics/Color.h" + +#include "Font.h" + +#include <unordered_map> + +CustomException(TextMeshException); + +enum ETextAnchor +{ + TextAnchor_UpperLeft, + TextAnchor_UpperCenter, + TextAnchor_UpperRight, + TextAnchor_MiddleLeft, + TextAnchor_MiddleCenter, + TextAnchor_MiddleRight, + TextAnchor_LowerLeft, + TextAnchor_LowerCenter, + TextAnchor_LowerRight, +}; + +enum ETextAlignment { + TextAlignment_Left, + TextAlignment_Center, + TextAlignment_Right, +}; + +namespace TextHelper +{ +} + +class UITextMesh +{ +public: + void Draw() const; + +private: + friend class TextMeshGenerator; + + UITextMesh(const UnicodeString& str, Font* font, int pixelSize, int lineHeight, Color32 color32 = Color32::white, ETextAnchor anchor = TextAnchor_UpperLeft, ETextAlignment alignment = TextAlignment_Left, bool wordwrap = false, float preferred = 0)/*throw TextMeshException*/; + ~UITextMesh(); + + GET(const Font*, Font, m_Font); + GET(int, PixelSize, m_PixelSize); + GET(int, LineHeight, m_LineHeight); + GET(Color32, Color, m_Color); + GET(ETextAlignment, Alignment, m_Alignment); + GET(ETextAnchor, Anchor, m_Anchor); + GET(bool, Wordwrap, m_Wordwrap); + GET(float, Preferred, m_Preferred); + GET(const UnicodeString&, Content, m_Content); + + std::unordered_map<int, VertexBuffer*> m_VBOs; + + Font* m_Font; + UnicodeString m_Content; + int m_PixelSize; + int m_LineHeight; + Color32 m_Color; + ETextAlignment m_Alignment; + ETextAnchor m_Anchor; + bool m_Wordwrap; + float m_Preferred; + +};
\ No newline at end of file diff --git a/Client/Source/GUI/freetype.h b/Client/Source/GUI/freetype.h new file mode 100644 index 0000000..4b54e20 --- /dev/null +++ b/Client/Source/GUI/freetype.h @@ -0,0 +1,4 @@ +#pragma once + +#include <ft2build.h> +#include FT_FREETYPE_H diff --git a/Client/Source/GUI/utf8.cpp b/Client/Source/GUI/utf8.cpp new file mode 100644 index 0000000..8a3a086 --- /dev/null +++ b/Client/Source/GUI/utf8.cpp @@ -0,0 +1,415 @@ +/* + * Description: UTF-8 瀛楃涓茬殑瑙g爜鍜岀紪鐮佸嚱鏁 + * unicode 瀛楃澶勭悊鍑芥暟 + * History: yang@haipo.me, 2013/05/29, create + * + * This code is in the public domain. + * You may use this code any way you wish, private, educational, + * or commercial. It's free. + */ + +# include <stdint.h> +# include <stddef.h> + +# include "utf8.h" + +namespace utf8 +{ + + ucs4_t getu8c(char **src, int *illegal) + { + static char umap[256] = { 0 }; + static int umap_init_flag = 0; + + if (umap_init_flag == 0) + { + int i; + + for (i = 0; i < 0x100; ++i) + { + if (i < 0x80) + { + umap[i] = 1; + } + else if (i >= 0xc0 && i < 0xe0) + { + umap[i] = 2; + } + else if (i >= 0xe0 && i < 0xf0) + { + umap[i] = 3; + } + else if (i >= 0xf0 && i < 0xf8) + { + umap[i] = 4; + } + else if (i >= 0xf8 && i < 0xfc) + { + umap[i] = 5; + } + else if (i >= 0xfc && i < 0xfe) + { + umap[i] = 6; + } + else + { + umap[i] = 0; + } + } + + umap_init_flag = 1; + } + + uint8_t *s = (uint8_t *)(*src); + int r_illegal = 0; + + while (umap[*s] == 0) + { + ++s; + ++r_illegal; + } + + uint8_t *t; + int byte_num; + uint32_t uc; + int i; + + repeat_label: + t = s; + byte_num = umap[*s]; + uc = *s++ & (0xff >> byte_num); + + for (i = 1; i < byte_num; ++i) + { + if (umap[*s]) + { + r_illegal += s - t; + goto repeat_label; + } + else + { + uc = (uc << 6) + (*s & 0x3f); + s += 1; + } + } + + *src = (char *)s; + if (illegal) + { + *illegal = r_illegal; + } + + return uc; + } + + size_t u8decode(char const *str, ucs4_t *des, size_t n, int *illegal) + { + if (n == 0) + return 0; + + char *s = (char *)str; + size_t i = 0; + ucs4_t uc = 0; + int r_illegal_all = 0, r_illegal; + + while ((uc = getu8c(&s, &r_illegal))) + { + if (i < (n - 1)) + { + des[i++] = uc; + r_illegal_all += r_illegal; + } + else + { + break; + } + } + + des[i] = 0; + if (illegal) + { + *illegal = r_illegal_all + r_illegal; + } + + return i; + } + +# define IF_CAN_HOLD(left, n) do { \ + size_t m = (size_t)(n); \ + if ((size_t)(left) < (m + 1)) return -2; \ + (left) -= m; \ +} while (0) + + int putu8c(ucs4_t uc, char **des, size_t *left) + { + if (uc < 0) + return -1; + + if (uc < (0x1 << 7)) + { + IF_CAN_HOLD(*left, 1); + + **des = (char)uc; + *des += 1; + **des = 0; + + return 1; + } + + int byte_num; + + if (uc < (0x1 << 11)) + { + byte_num = 2; + } + else if (uc < (0x1 << 16)) + { + byte_num = 3; + } + else if (uc < (0x1 << 21)) + { + byte_num = 4; + } + else if (uc < (0x1 << 26)) + { + byte_num = 5; + } + else + { + byte_num = 6; + } + + IF_CAN_HOLD(*left, byte_num); + + int i; + for (i = byte_num - 1; i > 0; --i) + { + *(uint8_t *)(*des + i) = (uc & 0x3f) | 0x80; + uc >>= 6; + } + + *(uint8_t *)(*des) = uc | (0xff << (8 - byte_num)); + + *des += byte_num; + **des = 0; + + return byte_num; + } + + size_t u8encode(ucs4_t *us, char *des, size_t n, int *illegal) + { + if (n == 0) + return 0; + + char *s = des; + size_t left = n; + size_t len = 0; + int r_illegal = 0; + + *s = 0; + while (*us) + { + int ret = putu8c(*us, &s, &left); + if (ret > 0) + { + len += ret; + } + else if (ret == -1) + { + r_illegal += 1; + } + else + { + break; + } + + ++us; + } + + if (illegal) + { + *illegal = r_illegal; + } + + return len; + } + + /* 鍏ㄨ瀛楃 */ + int isufullwidth(ucs4_t uc) + { + if (uc == 0x3000) + return 1; + + if (uc >= 0xff01 && uc <= 0xff5e) + return 1; + + return 0; + } + + /* 鍏ㄨ瀛楁瘝 */ + int isufullwidthalpha(ucs4_t uc) + { + if (uc >= 0xff21 && uc <= 0xff3a) + return 1; + + if (uc >= 0xff41 && uc <= 0xff5a) + return 2; + + return 0; + } + + /* 鍏ㄨ鏁板瓧 */ + int isufullwidthdigit(ucs4_t uc) + { + if (uc >= 0xff10 && uc <= 0xff19) + return 1; + + return 0; + } + + /* 鍏ㄨ杞崐瑙 */ + ucs4_t ufull2half(ucs4_t uc) + { + if (uc == 0x3000) + return ' '; + + if (uc >= 0xff01 && uc <= 0xff5e) + return uc - 0xfee0; + + return uc; + } + + /* 鍗婅杞叏瑙 */ + ucs4_t uhalf2full(ucs4_t uc) + { + if (uc == ' ') + return 0x3000; + + if (uc >= 0x21 && uc <= 0x7e) + return uc + 0xfee0; + + return uc; + } + + /* 涓棩闊╄秺缁熶竴琛ㄦ剰鏂囧瓧 */ + int isuchiness(ucs4_t uc) + { + /* 鏈鍒濇湡缁熶竴姹夊瓧 */ + if (uc >= 0x4e00 && uc <= 0x9fcc) + return 1; + + /* 鎵╁睍 A 鍖 */ + if (uc >= 0x3400 && uc <= 0x4db5) + return 2; + + /* 鎵╁睍 B 鍖 */ + if (uc >= 0x20000 && uc <= 0x2a6d6) + return 3; + + /* 鎵╁睍 C 鍖 */ + if (uc >= 0x2a700 && uc <= 0x2b734) + return 4; + + /* 鎵╁睍 D 鍖 */ + if (uc >= 0x2b740 && uc <= 0x2b81f) + return 5; + + /* 鎵╁睍 E 鍖 */ + if (uc >= 0x2b820 && uc <= 0x2f7ff) + return 6; + + /* 鍙版咕鍏煎姹夊瓧 */ + if (uc >= 0x2f800 && uc <= 0x2fa1d) + return 7; + + /* 鍖楁湞椴滃吋瀹规眽瀛 */ + if (uc >= 0xfa70 && uc <= 0xfad9) + return 8; + + /* 鍏煎姹夊瓧 */ + if (uc >= 0xf900 && uc <= 0xfa2d) + return 9; + + /* 鍏煎姹夊瓧 */ + if (uc >= 0xfa30 && uc <= 0xfa6d) + return 10; + + return 0; + } + + /* 涓枃鏍囩偣 */ + int isuzhpunct(ucs4_t uc) + { + if (uc >= 0x3001 && uc <= 0x3002) + return 1; + + if (uc >= 0x3008 && uc <= 0x300f) + return 1; + + if (uc >= 0xff01 && uc <= 0xff0f) + return 1; + + if (uc >= 0xff1a && uc <= 0xff20) + return 1; + + if (uc >= 0xff3b && uc <= 0xff40) + return 1; + + if (uc >= 0xff5b && uc <= 0xff5e) + return 1; + + if (uc >= 0x2012 && uc <= 0x201f) + return 1; + + if (uc >= 0xfe41 && uc <= 0xfe44) + return 1; + + if (uc >= 0xfe49 && uc <= 0xfe4f) + return 1; + + if (uc >= 0x3010 && uc <= 0x3017) + return 1; + + return 0; + } + + /* 鏃ユ枃骞冲亣鍚 */ + int isuhiragana(ucs4_t uc) + { + if (uc >= 0x3040 && uc <= 0x309f) + return 1; + + return 0; + } + + /* 鏃ユ枃鐗囧亣鍚 */ + int isukatakana(ucs4_t uc) + { + if (uc >= 0x30a0 && uc <= 0x30ff) + return 1; + + if (uc >= 0x31f0 && uc <= 0x31ff) + return 2; + + return 0; + } + + /* 闊╂枃 */ + int isukorean(ucs4_t uc) + { + /* 闊╂枃鎷奸煶 */ + if (uc >= 0xac00 && uc <= 0xd7af) + return 1; + + /* 闊╂枃瀛楁瘝 */ + if (uc >= 0x1100 && uc <= 0x11ff) + return 2; + + /* 闊╂枃鍏煎瀛楁瘝 */ + if (uc >= 0x3130 && uc <= 0x318f) + return 3; + + return 0; + } + +} diff --git a/Client/Source/GUI/utf8.h b/Client/Source/GUI/utf8.h new file mode 100644 index 0000000..b561b44 --- /dev/null +++ b/Client/Source/GUI/utf8.h @@ -0,0 +1,113 @@ +/* + * Description: UTF-8 瀛楃涓茬殑瑙g爜鍜岀紪鐮佸嚱鏁 + * unicode 瀛楃澶勭悊鍑芥暟 + * History: yang@haipo.me, 2013/05/29, create + * + * This code is in the public domain. + * You may use this code any way you wish, private, educational, + * or commercial. It's free. + */ + +# pragma once + +# include <stdint.h> +# include <stddef.h> + +namespace utf8 +{ + + + /* + * 鏍囧噯 C 骞舵病鏈夎瀹 wchar_t 鐨勪綅鏁般備絾 GNU C Lib 淇濊瘉 wchar_t 鏄 32 浣嶇殑锛 + * 鎵浠ュ彲浠ョ敤 wchar.h 涓畾涔夌殑鍑芥暟鏉ュ儚 wchar_t 涓鏍锋搷绾 ucs4_t. + * http://www.gnu.org/software/libc/manual/html_node/Extended-Char-Intro.html + */ + typedef int32_t ucs4_t; + + /* + * 浠 UTF-8 缂栫爜鐨勫瓧绗︿覆 *src 涓鍙栦竴涓 unicode 瀛楃锛屽苟鏇存柊 *src 鐨勫笺 + * + * 濡傛灉閬囧埌闈炴硶 UTF-8 缂栫爜锛屽垯璺宠繃闈炴硶閮ㄥ垎銆 + * 濡傛灉 illegal 鍙傛暟涓嶄负 NULL, 鍒 *illegal 琛ㄧず闈炴硶 UTF-8 缂栫爜瀛楄妭鏁般 + */ + ucs4_t getu8c(char **src, int *illegal); + + /* + * 灏 src 鎸囧悜鐨 UTF-8 缂栫爜瀛楃涓茶В鐮佷负 unicode锛屾斁鍦ㄩ暱搴︿负 n 鐨勬暟缁 des 涓紝 + * 骞跺湪鏈熬娣诲姞 0. 濡傛灉 des 涓嶈冻浠ュ瓨鏀炬墍鏈夌殑瀛楃锛屽垯鏈澶氫繚瀛 n - 1 涓 unicode + * 瀛楃骞惰ˉ 0. + * + * 濡傛灉閬囧埌闈炴硶 UTF-8 缂栫爜锛屽垯璺宠繃闈炴硶閮ㄥ垎銆 + * 濡傛灉 illegal 涓嶄负 NULL, 鍒 *illegal 琛ㄧず闈炴硶 UTF-8 缂栫爜鐨勫瓧鑺傛暟銆 + */ + size_t u8decode(char const *str, ucs4_t *des, size_t n, int *illegal); + + /* + * 灏 unicode 瀛楃 uc 缂栫爜涓 UTF-8 缂栫爜锛屾斁鍒伴暱搴︿负 *left 鐨勫瓧绗︿覆 *des 涓 + * + * 濡傛灉 *des 涓嶈冻浠ュ瓨鏀 uc 瀵瑰簲鐨 UTF-8 瀛楃涓诧紝杩斿洖涓涓礋鍊笺 + * 濡傛灉鎴愬姛锛屾洿鏂 *des 鍜 *left 鐨勫笺 + */ + int putu8c(ucs4_t uc, char **des, size_t *left); + + /* + * 灏嗕互 0 缁撳熬鐨 unicode 鏁扮粍 us 缂栫爜涓 UTF-8 瀛楃涓诧紝鏀惧埌闀垮害涓 n 鐨勫瓧绗︿覆 des 涓 + * + * 璐熸暟涓洪潪娉曠殑 unicode 瀛楃銆 + * 濡傛灉 illegal 涓嶄负 NULL锛屽垯 *illegal 琛ㄧず闈炴硶鐨 unicode 瀛楃鏁般 + */ + size_t u8encode(ucs4_t *us, char *des, size_t n, int *illegal); + + /* + * 鍒ゆ柇鏄惁涓哄叏瑙掑瓧绗 + */ + int isufullwidth(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓哄叏瑙掑瓧姣 + */ + int isufullwidthalpha(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓哄叏瑙掓暟瀛 + */ + int isufullwidthdigit(ucs4_t uc); + + /* + * 鍏ㄨ瀛楃杞崐瑙掑瓧绗︺ + * 濡傛灉 uc 涓哄叏瑙掑瓧绗︼紝鍒欒繑鍥炲搴旂殑鍗婅瀛楃锛屽惁鍒欒繑鍥 uc 鏈韩銆 + */ + ucs4_t ufull2half(ucs4_t uc); + + /* + * 鍗婅瀛楃杞叏瑙掑瓧绗 + * 濡傛灉 uc 涓哄崐瑙掑瓧绗︼紝鍒欒繑鍥炲搴旂殑鍏ㄨ瀛楃锛屽惁鍒欒繑鍥 uc 鏈韩銆 + */ + ucs4_t uhalf2full(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓烘眽瀛楀瓧绗︼紙涓棩闊╄秺缁熶竴琛ㄦ剰鏂囧瓧锛 + */ + int isuchiness(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓轰腑鏂囨爣鐐 + */ + int isuzhpunct(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓烘棩鏂囧钩鍋囧悕瀛楃 + */ + int isuhiragana(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓烘棩鏂囩墖鍋囧悕瀛楃 + */ + int isukatakana(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓洪煩鏂囧瓧绗 + */ + int isukorean(ucs4_t uc); + +} diff --git a/Client/Source/Graphics/Color.cpp b/Client/Source/Graphics/Color.cpp new file mode 100644 index 0000000..88b1ed8 --- /dev/null +++ b/Client/Source/Graphics/Color.cpp @@ -0,0 +1,4 @@ +#include "Color.h" + +const Color32 Color32::white = Color32(255, 255, 255, 255); + diff --git a/Client/Source/Graphics/Color.h b/Client/Source/Graphics/Color.h new file mode 100644 index 0000000..f7bb937 --- /dev/null +++ b/Client/Source/Graphics/Color.h @@ -0,0 +1,48 @@ +#pragma once + +struct Color +{ + Color(float r = 0, float g = 0, float b = 0, float a = 0) + { + this->r = r; + this->g = g; + this->b = b; + this->a = a; + } + void Set(float r = 0, float g = 0, float b = 0, float a = 0) + { + this->r = r; + this->g = g; + this->b = b; + this->a = a; + } + float r, g, b, a; +}; + +struct Color32 +{ + Color32(unsigned char r = 0, unsigned char g = 0, unsigned char b = 0, unsigned char a = 0) + { + this->r = r; + this->g = g; + this->b = b; + this->a = a; + } + void Set(unsigned char r = 0, unsigned char g = 0, unsigned char b = 0, unsigned char a = 0) + { + this->r = r; + this->g = g; + this->b = b; + this->a = a; + } + + bool operator !=(const Color32& col) + { + return !(r == col.r && g == col.g && b == col.b && a == col.a); + } + + unsigned char r, g, b, a; + + static const Color32 white; +}; + diff --git a/Client/Source/Graphics/CustomVertexLayout.cpp b/Client/Source/Graphics/CustomVertexLayout.cpp new file mode 100644 index 0000000..aa6be9b --- /dev/null +++ b/Client/Source/Graphics/CustomVertexLayout.cpp @@ -0,0 +1,23 @@ +#include "CustomVertexLayout.h" + +namespace VertexLayout +{ + + void SetupCustomVertexLayout(CustomVertexLayout& info) + { + glBindBuffer(GL_ARRAY_BUFFER, info.buffer); + for (int i = 0; i < info.attributes.size(); ++i) + { + VertexAttributeDescriptor& attr = info.attributes[i]; + glEnableVertexAttribArray(i); + int numCompo = attr.componentNum; + GLenum compoType = VertexAttribute::ConvertAttrFormatToGLFormat(attr.componentFormat); + bool normalized = attr.normalize; + uint stride = attr.stride; + const void* pointer = attr.pointer; + + glVertexAttribPointer(i, numCompo, compoType, normalized ? GL_TRUE : GL_FALSE, stride, pointer); + } + } + +} diff --git a/Client/Source/Graphics/CustomVertexLayout.h b/Client/Source/Graphics/CustomVertexLayout.h new file mode 100644 index 0000000..3b54039 --- /dev/null +++ b/Client/Source/Graphics/CustomVertexLayout.h @@ -0,0 +1,33 @@ +#pragma once + +#include <vector> +#include "OpenGL.h" +#include "GPUDataBuffer.h" +#include "VertexAttribute.h" + +struct CustomVertexLayout +{ + GLuint buffer; // 创建时留空 + std::vector<VertexAttributeDescriptor> attributes; + + CustomVertexLayout() + { + int n = buffer; + } + + // 重置pointer(startOffset)为0 + void RestorePointer() + { + for (int i = 0; i < attributes.size(); ++i) + { + attributes[i].pointer = 0; + } + } +}; + +namespace VertexLayout +{ + + extern void SetupCustomVertexLayout(CustomVertexLayout& layout); + +} diff --git a/Client/Source/Graphics/DefaultVertexLayout.cpp b/Client/Source/Graphics/DefaultVertexLayout.cpp new file mode 100644 index 0000000..a468100 --- /dev/null +++ b/Client/Source/Graphics/DefaultVertexLayout.cpp @@ -0,0 +1,119 @@ +#include "DefaultVertexLayout.h" + +namespace VertexLayout +{ + // 默认vertex layout + static const int kVertexAttrSize[VertexAttr_Count] = { + 3 * sizeof(float), // position + 3 * sizeof(float), // normal + 4 * sizeof(float), // tangent + 4 * sizeof(byte), // color + 2 * sizeof(float), // uv + 2 * sizeof(float), // uv2 + 2 * sizeof(float), // uv3 + 2 * sizeof(float), // uv4 + }; + + static const int kVertexAttrDimension[VertexAttr_Count] = { + 3, // position + 3, // normal + 4, // tangent + 4, // color + 2, // uv + 2, // uv2 + 2, // uv3 + 2, // uv4 + }; + + bool IsGLVertexAttrNeedNormalized(uint attr/*, uint format*/) + { + if (attr == VertexAttr_Color) + return true; + /* + if (format == VertexAttrFormat_Color || format == VertexAttrFormat_Byte) + return true; + */ + return false; + } + + uint GetDefaultShaderChannelFormat(uint attr) + { + return attr == VertexAttr_Color ? VertexAttrFormat_Color : VertexAttrFormat_Float; + } + + uint32 GetDefaultVertexAttrSize(int attr) + { + return kVertexAttrSize[attr]; + } + + uint GetDefaultVertexAttrDimension(uint attr) + { + return kVertexAttrDimension[attr]; + } + + uint GetDefaultShaderChannelDimension(uint attr) + { + return attr == VertexAttr_Color ? 4 : GetDefaultVertexAttrDimension(attr); + } + + GLenum GetDefaultVertexAttrcomponentFormat(uint attr) + { + uint componentFormat = GetDefaultShaderChannelFormat(attr); + return VertexAttribute::ConvertAttrFormatToGLFormat(componentFormat); + } + + uint32 GetDynamicChunkStride(uint32 vertexAttrMask) + { + uint32 stride = 0; + for (int i = 0; i < vertexAttrMask; ++i) + { + if (vertexAttrMask & Mask(i)) + stride += VertexLayout::GetDefaultVertexAttrSize(i); + } + return stride; + } + + static uint32 sEnabledArrays = 0; + + void SetupDefaultVertexLayout(const DefaultVertexLayout& info) + { + glBindBuffer(GL_ARRAY_BUFFER, info.buffer); + + for (int attrIdx = 0; attrIdx < VertexAttr_Count; ++attrIdx) + { + if (info.enableMask & Mask(attrIdx)) + { + if (!sEnabledArrays & Mask(attrIdx)) + glEnableVertexAttribArray(attrIdx); + int numCompo = info.attributes[attrIdx].componentNum; + GLenum compoType = VertexLayout::GetDefaultVertexAttrcomponentFormat(attrIdx); + bool normalized = VertexLayout::IsGLVertexAttrNeedNormalized(attrIdx); + uint stride = info.attributes[attrIdx].stride; + const void* pointer = info.attributes[attrIdx].pointer; + + glVertexAttribPointer(attrIdx, numCompo, compoType, normalized ? GL_TRUE : GL_FALSE, stride, pointer); + } + else if (sEnabledArrays & Mask(attrIdx)) + glDisableVertexAttribArray(attrIdx); + } + sEnabledArrays = info.enableMask; + } + + void InvalidateVertexInputCache() + { + sEnabledArrays = 0; + for (int attrIdx = 0; attrIdx < VertexAttr_Count; ++attrIdx) + glDisableVertexAttribArray(attrIdx); + } + + // 索引保存为 unsgined short (GL_UNSIGNED_SHORT) + uint GetDefaultIndexSize() + { + return sizeof(uint16); + } + GLenum GetDefaultIndexFormat() + { + return GL_UNSIGNED_SHORT; + } + +} diff --git a/Client/Source/Graphics/DefaultVertexLayout.h b/Client/Source/Graphics/DefaultVertexLayout.h new file mode 100644 index 0000000..2222ff1 --- /dev/null +++ b/Client/Source/Graphics/DefaultVertexLayout.h @@ -0,0 +1,55 @@ +#pragma once + +#include <vector> + +#include "../Utilities/Type.h" +#include "../Utilities/UtilMacros.h" + +#include "OpenGL.h" +#include "GPUDataBuffer.h" +#include "VertexAttribute.h" + +// 默认的顶点布局,适用于Mesh导入的结果,以保持shader的一致性 +// 如果需要修改布局,比如编辑器UI中,用CustomVertexLayout + +// 默认的顶点属性以及顺序 +enum EVertexAttr +{ + VertexAttr_Position = 0, + VertexAttr_Normal, + VertexAttr_Tangent, + VertexAttr_Color, + VertexAttr_UV, + VertexAttr_UV2, + VertexAttr_UV3, + VertexAttr_UV4, + + VertexAttr_Count +}; + +// GPU侧的默认顶点属性 +struct DefaultVertexLayout +{ + uint32 enableMask; + GLuint buffer; // vbo或者0(pinned memory) + VertexAttributeDescriptor attributes[VertexAttr_Count]; +}; + +namespace VertexLayout +{ + // ibo无论是default还是custom布局都是short + uint GetDefaultIndexSize(); + GLenum GetDefaultIndexFormat(); + + uint32 GetDynamicChunkStride(uint32 vertexAttrMask); + bool IsGLVertexAttrNeedNormalized(uint attr); + uint GetDefaultShaderChannelFormat(uint attr); + uint32 GetDefaultVertexAttrSize(int attr); + uint GetDefaultVertexAttrDimension(uint attr); + uint GetDefaultShaderChannelDimension(uint attr); + GLenum GetDefaultVertexAttrComponentType(uint attr); + + void SetupDefaultVertexLayout(const DefaultVertexLayout& info); + void InvalidateVertexInputCache(); + +} diff --git a/Client/Source/Graphics/DeviceDefine.h b/Client/Source/Graphics/DeviceDefine.h new file mode 100644 index 0000000..db1c138 --- /dev/null +++ b/Client/Source/Graphics/DeviceDefine.h @@ -0,0 +1,69 @@ +#ifndef DEVICE_DEFINE_H +#define DEVICE_DEFINE_H + +enum EDeviceEnable +{ + Enable_DepthTest = 1, + Enable_DepthWrite = 1 << 1, + Enable_StencilTest = 1 << 2, + Enable_StencilWrite = 1 << 3, + Enable_Cull = 1 << 4, + Enable_Blend = 1 << 5, + Enable_AntiAliasing = 1 << 6, +}; + +enum EDepthTest +{ + DepthTest_Greater = 1, + DepthTest_GreaterEqual, + DepthTest_Less, + DepthTest_LessEqual, + DepthTest_Equal, + DepthTest_NotEqual, + DepthTest_Always, +}; + +enum EStencilTest { + StencilTest_Always, + StencilTest_Never, + StencilTest_Less, + StencilTest_Equal, + StencilTest_NotEqual, + StencilTest_LessEqual, + StencilTest_Greater, + StencilTest_GreaterEqual, +}; + +enum EStencilOp { + StencilOp_Keep, + StencilOp_Zero, + StencilOp_Replace, + StencilOp_Incr, + StencilOp_IncrWrap, + StencilOp_Decr, + StencilOp_DecrWrap, + StencilOp_Invert, +}; + +enum EDeviceClear +{ + Clear_DepthBuffer = 1, + Clear_StencilBuffer = 1 << 1, + Clear_ColorBuffer = 1 << 2, +}; + +enum ECullFace +{ + Cull_Front = 1, + Cull_Back = 2, + Cull_None = 3, + Cull_All = 4, +}; + +enum EBlendMode +{ + Blend_Additive = 1, + Blend_Substract = 1, +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/DynamicMesh.h b/Client/Source/Graphics/DynamicMesh.h new file mode 100644 index 0000000..94fef4d --- /dev/null +++ b/Client/Source/Graphics/DynamicMesh.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Utilities/StaticInitiator.h" + +// 填充g_SharedVBO的动态mesh +class DynamicMesh +{ +public: + DynamicMesh() {}; + virtual ~DynamicMesh() {}; + + virtual void Draw() = 0; + +}; diff --git a/Client/Source/Graphics/DynamicVertexBuffer.cpp b/Client/Source/Graphics/DynamicVertexBuffer.cpp new file mode 100644 index 0000000..8041e30 --- /dev/null +++ b/Client/Source/Graphics/DynamicVertexBuffer.cpp @@ -0,0 +1,260 @@ +#include "DynamicVertexBuffer.h" +#include "CustomVertexLayout.h" + +DynamicVertexBuffer::DynamicVertexBuffer() +{ +} + +DynamicVertexBuffer::~DynamicVertexBuffer() +{ +} + +//------------------------------------------------------------------------------------------------------------ +// Defualt Vertex Layout + +//GetChunk +//-> ReleaseChunk +//-> DrawChunk + +void DynamicVertexBuffer::GetChunk(uint attrsMask, int maxVerts, int maxIndices, EPrimitive primitive, void **out_vb, void **out_ib) +{ + Assert(out_vb && out_ib); + + uint stride = VertexLayout::GetDynamicChunkStride(attrsMask); // data size of single vertex + GetChunk(stride, maxVerts, VertexLayout::GetDefaultIndexSize(), maxIndices, primitive, out_vb, out_ib); + + // default layout + m_CurAttrMask = attrsMask; + m_CurStride = stride; +} + +void DynamicVertexBuffer::DrawChunk() +{ + DefaultVertexLayout vertexArray; + FillDefaultVertexLayout(vertexArray); + + // bind vertex attributes data + VertexLayout::SetupDefaultVertexLayout(vertexArray); + + const void* indexPtr = m_CurIB ? 0 : (m_CurIBData.empty() ? 0 : &m_CurIBData[0]); + + if (m_CurIB) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_CurIB->GetHandle()); + + GLenum indexFormat = VertexLayout::GetDefaultIndexFormat(); + + switch (m_CurPrimitive) + { + case Primitive_Triangle: + glDrawElements(GL_TRIANGLES, m_CurIndexCount, indexFormat, indexPtr); + break; + case Primitive_Line: + glDrawElements(GL_LINE, m_CurIndexCount, indexFormat, indexPtr); + break; + case Primitive_Point: + glDrawElements(GL_POINT, m_CurIndexCount, indexFormat, indexPtr); + break; + } + + if (m_CurIB) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // End draw + Clean(); +} + +void DynamicVertexBuffer::FillDefaultVertexLayout(DefaultVertexLayout& dst) +{ + const byte* basepointer = m_CurVB ? 0 : &m_CurVBData[0]; + const GLuint buffer = m_CurVB ? m_CurVB->GetHandle() : 0; + + int attrOffsets[VertexAttr_Count] = { 0 }; + { + uint32 curOffset = 0; + for (uint idx = 0; idx < VertexAttr_Count; ++idx) + { + if (m_CurAttrMask & Mask(idx)) + { + attrOffsets[idx] = curOffset; + curOffset += VertexLayout::GetDefaultVertexAttrSize(idx); + } + } + } + + dst.buffer = buffer; + + for (uint32 attrIdx = 0; attrIdx < VertexAttr_Count; ++attrIdx) + { + if (m_CurAttrMask & Mask(attrIdx)) + { + dst.attributes[attrIdx].pointer = basepointer + attrOffsets[attrIdx]; + dst.attributes[attrIdx].componentFormat = VertexLayout::GetDefaultShaderChannelFormat(attrIdx); + dst.attributes[attrIdx].componentNum = VertexLayout::GetDefaultShaderChannelDimension(attrIdx); + dst.attributes[attrIdx].stride = m_CurStride; + + dst.enableMask |= Mask(attrIdx); + } + } +} + +//------------------------------------------------------------------------------------------------------------ +// Custom Vertex Layout + +// 用buffersize为依据决定用vbo或者pinned memory +void DynamicVertexBuffer::GetChunk(uint sizePerVert, uint sizePerIndex, int maxVerts, int maxIndices, EPrimitive primitive, void **out_vb, void **out_ib) +{ + Assert(out_vb && out_ib); + + m_CurStride = sizePerVert; + uint vbufferSize = sizePerVert * maxVerts; + uint ibufferSize = sizePerIndex * maxIndices; + + const bool mapVertexBuffer = vbufferSize >= kDataBufferThreshold; + const bool mapIndexBuffer = ibufferSize >= kDataBufferThreshold; + + GLenum usage = GL_STREAM_DRAW; + + GPU::DataBuffer* vertexBuffer = mapVertexBuffer ? GPU::ClaimBuffer(vbufferSize, usage) : 0; + GPU::DataBuffer* indexBuffer = mapIndexBuffer ? GPU::ClaimBuffer(ibufferSize, usage) : 0; + + if (vertexBuffer && vertexBuffer->GetSize() < vbufferSize) + vertexBuffer->Restore(vbufferSize, usage); + + if (indexBuffer && indexBuffer->GetSize() < ibufferSize) + indexBuffer->Restore(ibufferSize, usage); + + if (!mapVertexBuffer && m_CurVBData.size() < vbufferSize) + m_CurVBData.resize(vbufferSize); + + if (!mapIndexBuffer && m_CurIBData.size() < ibufferSize) + m_CurIBData.resize(ibufferSize); + + const GLenum access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + + if (vertexBuffer) + *out_vb = vertexBuffer->MapRange(0, vbufferSize, access); + else + *out_vb = &m_CurVBData[0]; + + if (indexBuffer) + *out_ib = indexBuffer->MapRange(0, ibufferSize, access); + else + *out_ib = &m_CurIBData[0]; + + m_CurVB = vertexBuffer; + m_CurIB = indexBuffer; + + m_CurPrimitive = primitive; +} + +void DynamicVertexBuffer::FillCustomVertexLayout(CustomVertexLayout& dst) +{ + const byte* basepointer = m_CurVB ? 0 : &m_CurVBData[0]; + const GLuint buffer = m_CurVB ? m_CurVB->GetHandle() : 0; + + dst.buffer = buffer; + + for (int i = 0; i < dst.attributes.size(); ++i) + { + int offset = dst.attributes[i].startOffset; + dst.attributes[i].pointer = basepointer + offset; + } +} + +void DynamicVertexBuffer::DrawChunk(CustomVertexLayout& layout) +{ + const byte* basepointer = m_CurVB ? 0 : &m_CurVBData[0]; + const GLuint buffer = m_CurVB ? m_CurVB->GetHandle() : 0; + + FillCustomVertexLayout(layout); + + VertexLayout::SetupCustomVertexLayout(layout); + + layout.RestorePointer(); + + const void* indexPtr = m_CurIB ? 0 : (m_CurIBData.empty() ? 0 : &m_CurIBData[0]); + + if (m_CurIB) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_CurIB->GetHandle()); + + GLenum indexFormat = VertexLayout::GetDefaultIndexFormat(); + + switch (m_CurPrimitive) + { + case Primitive_Triangle: + glDrawElements(GL_TRIANGLES, m_CurIndexCount, indexFormat, indexPtr); + break; + case Primitive_Line: + glDrawElements(GL_LINES, m_CurIndexCount, indexFormat, indexPtr); + break; + case Primitive_Point: + glDrawElements(GL_POINTS, m_CurIndexCount, indexFormat, indexPtr); + break; + } + + if (m_CurIB) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // End draw + Clean(); +} + +//------------------------------------------------------------------------------------------------------------ +// Both Default and Custom Vertex Layout + +void DynamicVertexBuffer::ReleaseChunk(int actualVerts, int actualIndices) +{ + int actualVBufferSize = m_CurStride * actualVerts; + int actualIBufferSize = VertexLayout::GetDefaultIndexSize() * actualIndices; + + const GLenum usage = GL_STREAM_DRAW; + + if (m_CurVB) + { + m_CurVB->FlushMapedRange(0, actualVBufferSize); + m_CurVB->UnMap(); + } + else if (actualVBufferSize >= kDataBufferThreshold) + { + m_CurVB = GPU::ClaimBuffer(actualVBufferSize, usage); + m_CurVB->RestoreWithData(actualVBufferSize, usage, &m_CurVBData[0]); + } + + if (m_CurIB) + { + m_CurIB->FlushMapedRange(0, actualIBufferSize); + m_CurIB->UnMap(); + } + else if (actualIBufferSize >= kDataBufferThreshold) + { + m_CurIB = GPU::ClaimBuffer(0, usage); + m_CurIB->RestoreWithData(0, usage, &m_CurIBData[0]); + } + + m_CurVertexCount = actualVerts; + m_CurIndexCount = actualIndices; +} + +void DynamicVertexBuffer::Clean() +{ + if (m_CurVB) + { + GPU::ReleaseBuffer(m_CurVB); + m_CurVB = 0; + } + + if (m_CurIB) + { + GPU::ReleaseBuffer(m_CurIB); + m_CurIB = 0; + } + + m_CurPrimitive = Primitive_Triangle; + m_CurAttrMask = 0; + m_CurStride = 0; + m_CurVertexCount = 0; + m_CurIndexCount = 0; + + m_CurVBData.clear(); + m_CurIBData.clear(); +} diff --git a/Client/Source/Graphics/DynamicVertexBuffer.h b/Client/Source/Graphics/DynamicVertexBuffer.h new file mode 100644 index 0000000..ad65d80 --- /dev/null +++ b/Client/Source/Graphics/DynamicVertexBuffer.h @@ -0,0 +1,54 @@ +#pragma once +#include <vector> + +#include "../Utilities/UtilMacros.h" + +#include "OpenGL.h" +#include "GPUDataBuffer.h" +#include "DefaultVertexLayout.h" +#include "CustomVertexLayout.h" +#include "Primitive.h" + +// 动态填充的VBO +class DynamicVertexBuffer +{ +public: + DynamicVertexBuffer(); + ~DynamicVertexBuffer(); + + // default layout + void GetChunk(uint attrs, int maxVerts, int maxIndices, EPrimitive primitive, void **out_vb, void **out_ib); + void DrawChunk(); + + // custom layout + void GetChunk(uint sizePerVert, uint sizePerIndex, int maxVerts, int maxIndices, EPrimitive primitive, void **out_vb, void **out_ib); + void DrawChunk(CustomVertexLayout& layout); + + // common + void ReleaseChunk(int actualVerts, int actualIndices); + +private: + + void FillCustomVertexLayout(CustomVertexLayout& dst); + void FillDefaultVertexLayout(DefaultVertexLayout& dst); + + void Clean(); + + // 如果数据大小小于这个限制,用内存数据,而不是glBufferData + static const int kDataBufferThreshold = 1024; + + GPU::DataBuffer *m_CurVB; + GPU::DataBuffer *m_CurIB; + + std::vector<byte> m_CurVBData; + std::vector<uint16> m_CurIBData; + + EPrimitive m_CurPrimitive; + + uint m_CurAttrMask; // default layout only + uint m_CurStride; // default layout only + + uint m_CurVertexCount; + uint m_CurIndexCount; + +};
\ No newline at end of file diff --git a/Client/Source/Graphics/FrameBuffer.cpp b/Client/Source/Graphics/FrameBuffer.cpp new file mode 100644 index 0000000..af4d831 --- /dev/null +++ b/Client/Source/Graphics/FrameBuffer.cpp @@ -0,0 +1,12 @@ +#include "FrameBuffer.h" + +// 有些版本的OpenGL不支持绑定多个RT +bool FrameBuffer::BindRenderTexture(RenderTexture* rt, int location /* = 0 */) +{ + return false; +} + +bool FrameBuffer::Blit(FrameBuffer* target) +{ + return false; +} diff --git a/Client/Source/Graphics/FrameBuffer.h b/Client/Source/Graphics/FrameBuffer.h new file mode 100644 index 0000000..4b9104b --- /dev/null +++ b/Client/Source/Graphics/FrameBuffer.h @@ -0,0 +1,35 @@ +#ifndef FRAME_BUFFER_H +#define FRAME_BUFFER_H + +#include "OpenGL.h" +#include "RenderTexture.h" + +// Fbo,所有绑定的texture和renderbuffer的target都是可读写的GL_FRAMEBUFFER +class FrameBuffer +{ +public: + enum FrameBufferUsage + { + FrameBufferUsage_None = 0, + FrameBufferUsage_Depth = 1, + FrameBufferUsage_Stencil = 2, + FrameBufferUsage_DepthStencil = 3, + }; + + FrameBuffer(FrameBufferUsage usage, int width, int height); + ~FrameBuffer(); + + bool Blit(FrameBuffer* target); + + bool BindRenderTexture(RenderTexture* rt, int location = 0); + + GET(int, Width, m_Width); + GET(int, Height, m_Height); + +private: + int m_Width, m_Height; + FrameBufferUsage m_Usage; + +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/GPUDataBuffer.cpp b/Client/Source/Graphics/GPUDataBuffer.cpp new file mode 100644 index 0000000..f5149d9 --- /dev/null +++ b/Client/Source/Graphics/GPUDataBuffer.cpp @@ -0,0 +1,207 @@ +#include <math.h> + +#include "GPUDataBuffer.h" + +namespace GPU +{ + + // 修改buffer数据要绑定到这个目标上,其他情况下用GetHandle()绑定到其他目标 + // GL_COPY_READ_BUFFER + static const GLenum kBufferTarget = GL_COPY_WRITE_BUFFER; + + DataBuffer::DataBuffer() + { + glGenBuffers(1, &m_Handle); + m_Size = 0; + } + + DataBuffer::~DataBuffer() + { + glDeleteBuffers(1, &m_Handle); + } + + void DataBuffer::Restore(int size, GLenum usage) + { + RestoreWithData(size, usage, 0); + } + + void DataBuffer::RestoreWithData(int size, GLenum usage, const void* data) + { + glBindBuffer(kBufferTarget, m_Handle); + glBufferData(kBufferTarget, size, data, usage); + glBindBuffer(kBufferTarget, 0); + m_Size = size; + m_Usage = usage; + } + + // glBufferSubData + // glMapBuffer + // glMapBufferRange (best one) + + void DataBuffer::Upload(int offset, int size, const void* data) + { + glBindBuffer(kBufferTarget, m_Handle); + glBufferSubData(kBufferTarget, offset, size, data); + glBindBuffer(kBufferTarget, 0); + } + + void* DataBuffer::Map(uint32 access) + { + glBindBuffer(kBufferTarget, m_Handle); + void* ptr = glMapBuffer(kBufferTarget, access); + glBindBuffer(kBufferTarget, 0); + return ptr; + } + + void* DataBuffer::MapRange(int offset, int size, uint32 access) + { + glBindBuffer(kBufferTarget, m_Handle); + void* ptr = glMapBufferRange(kBufferTarget, offset, size, access); + glBindBuffer(kBufferTarget, 0); + return ptr; + } + + void DataBuffer::FlushMapedRange(int offset, int size) + { + glBindBuffer(kBufferTarget, m_Handle); + glFlushMappedBufferRange(kBufferTarget, offset, size); + glBindBuffer(kBufferTarget, 0); + } + + void DataBuffer::UnMap() + { + glBindBuffer(kBufferTarget, m_Handle); + glUnmapBuffer(kBufferTarget); + glBindBuffer(kBufferTarget, 0); + } + + void DataBuffer::Orphan() + { + glBindBuffer(kBufferTarget, m_Handle); + glBufferData(kBufferTarget, 0, 0, GL_STREAM_DRAW); + glBindBuffer(kBufferTarget, 0); + } + +//--------------------------------------------------------------------------------------- + + static bool IsBufferPoolCreated = false; + + BufferPool::BufferPool() + :m_LiveBuffers() + { + Assert(!IsBufferPoolCreated); + IsBufferPoolCreated = true; + } + + BufferPool::~BufferPool() + { + } + + static const float kBufferAllocateWeight = 10.0f; //! Default weight for buffer allocation from scratch. + static const float kBufferSizeWeightFactor = 1.f / 8096.f; //!< Weight factor for size difference. + static const float kBufferUsageDiffWeight = 8.f; //!< Weight factor for usage difference. + + static int ComputeBufferWeight(DataBuffer* buffer, int desiredSize, GLenum desiredUsage) + { + const int bufferSize = buffer->GetSize(); + const GLenum bufferUsage = buffer->GetUsage(); + + if (bufferSize == 0) + return kBufferAllocateWeight; + + const int sizeDiff = std::abs(bufferSize - desiredSize); + + return float(sizeDiff)*kBufferSizeWeightFactor + ((bufferUsage != desiredUsage) ? kBufferUsageDiffWeight : 0.f); + } + + DataBuffer* BufferPool::ClaimBuffer(int size, GLenum usage) + { + const float maxWeight = kBufferAllocateWeight; + const int maxCandidates = 5; // Number of potential candidates to consider actually. + + const int sizeClass = GetSizeClass(size); + int numCandidates = 0; // Number of potential candidates considered + int bestBufferNdx = -1; + float bestWeight = std::numeric_limits<float>::infinity(); + + for (int idx = 0; idx < m_LiveBuffers[sizeClass].size(); ++idx) + { + DataBuffer* buffer = m_LiveBuffers[sizeClass][idx]; + const float weight = ComputeBufferWeight(buffer, size, usage); + + if (weight < maxWeight && weight < bestWeight) + { + bestWeight = weight; + bestBufferNdx = idx; + ++numCandidates; + } + + if (numCandidates >= maxCandidates) + break; // Do not try other buffers, sorry. + } + + if (bestBufferNdx >= 0) + { + DataBuffer* selectedBuffer = m_LiveBuffers[sizeClass][bestBufferNdx]; + + if (bestBufferNdx + 1 != m_LiveBuffers[sizeClass].size()) + std::swap(m_LiveBuffers[sizeClass][bestBufferNdx], m_LiveBuffers[sizeClass].back()); + m_LiveBuffers[sizeClass].pop_back(); + + return selectedBuffer; + } + else + return new DataBuffer(); + + } + + void BufferPool::ReleaseBuffer(DataBuffer* buffer) + { + InsertToLive(buffer); + } + + void BufferPool::OnEndFrame() + { + UpdatePendingBuffersArray(); + } + + void BufferPool::UpdatePendingBuffersArray() + { + } + + void BufferPool::InsertToLive(DataBuffer* buffer) + { + const int bufferSize = buffer->GetSize(); + const int sizeClass = GetSizeClass(bufferSize); + + m_LiveBuffers[sizeClass].push_back(buffer); + } + + uint BufferPool::GetSizeClass(uint bufferSize) + { + for (int idx = 0; idx < kSizeClassCount; ++idx) + { + if (bufferSize < GetSizeClassLimit(idx)) + return idx; + } + Assert(false); + return 0; + } + + DataBuffer* ClaimBuffer(int size, GLenum usage) + { + return BufferPool::Instance()->ClaimBuffer(size, usage); + } + + void ReleaseBuffer(DataBuffer* buffer) + { + BufferPool::Instance()->ReleaseBuffer(buffer); + } + + int BufferPool::GetSizeClassLimit(int classNdx) + { + // (0, 2^10] 2^11 2^12 2^13 2^14 2^15 INT_MAX + return classNdx + 1 < kSizeClassCount ? (1 << (classNdx*kSizeClassStepLog2 + kSizeClassBaseLog2)) : INT_MAX; + } + +} diff --git a/Client/Source/Graphics/GPUDataBuffer.h b/Client/Source/Graphics/GPUDataBuffer.h new file mode 100644 index 0000000..3d24514 --- /dev/null +++ b/Client/Source/Graphics/GPUDataBuffer.h @@ -0,0 +1,75 @@ +#ifndef GPU_DATABUFFER_H +#define GPU_DATABUFFER_H + +#include <vector> + +#include "../Utilities/Type.h" +#include "../Utilities/Singleton.h" +#include "../Utilities/UtilMacros.h" +#include "../Utilities/Assert.h" +#include "OpenGL.h" + +namespace GPU +{ + + class DataBuffer + { + public: + DataBuffer(); + ~DataBuffer(); + + void Upload(int offset, int size, const void* data); + void* Map(uint32 access); + void* MapRange(int offset, int size, uint32 access); // 性能最佳 + void FlushMapedRange(int offset, int size); + + void UnMap(); + + void Orphan(); + + void Restore(int size, GLenum usage); + void RestoreWithData(int size, GLenum usage, const void* data); + + GET(int, Size, m_Size); + GET(GLenum, Usage, m_Usage); + GET(GLuint, Handle, m_Handle); + + private: + friend class BufferPool; + + GLuint m_Handle; + int m_Size; + GLenum m_Usage; + }; + + class BufferPool : public Singleton<BufferPool> + { + public: + BufferPool(); + ~BufferPool(); + + DataBuffer* ClaimBuffer(int size, GLenum usage); + void ReleaseBuffer(DataBuffer* buffer); + + void OnEndFrame(); + + private: + static const int kSizeClassBaseLog2 = 10; + static const int kSizeClassStepLog2 = 1; + static const int kSizeClassCount = 7; + + int GetSizeClassLimit(int classNdx); + void UpdatePendingBuffersArray(); + void InsertToLive(DataBuffer* buffer); + uint GetSizeClass(uint bufferSize); + + std::vector<DataBuffer*> m_LiveBuffers[kSizeClassCount]; + + }; + + DataBuffer* ClaimBuffer(int size = 0, GLenum usage = GL_ARRAY_BUFFER); + void ReleaseBuffer(DataBuffer* buffer); + +} + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/GfxDevice.cpp b/Client/Source/Graphics/GfxDevice.cpp new file mode 100644 index 0000000..231ee71 --- /dev/null +++ b/Client/Source/Graphics/GfxDevice.cpp @@ -0,0 +1,169 @@ +#include <vector> +#include "GfxDevice.h" +#include "../Math/Math.h" + +static bool deviceInited = false; +// +//static const std::vector<byte> s_AvailableTextureUnitPreset = {0,1,2,3,4,5,6,7}; // 最多支持8个贴图 +//static std::vector<byte> s_TextureUnitBucket = s_AvailableTextureUnitPreset; + +static const int kMaxAvailableTextureUnitCount = 8; // 最多支持8个贴图 +static int s_CurAvailableTextureUnit = 0; + +static int ClaimTextureUnit() +{ + int unit = s_CurAvailableTextureUnit; + s_CurAvailableTextureUnit = (s_CurAvailableTextureUnit + 1) % kMaxAvailableTextureUnitCount; + return unit; +} + +GfxDevice g_GfxDevice; + +GfxDevice::GfxDevice() +{ + Assert(!deviceInited); + deviceInited = true; +} + +GfxDevice::~GfxDevice() +{ +} + +void GfxDevice::Initialize(GfxDeviceSetting setting) +{ +} + +void GfxDevice::Enable(EDeviceEnable enabled) +{ + +} + +void GfxDevice::Disable(EDeviceEnable enabled) +{ + +} + +void GfxDevice::IsEnable(uint flag) +{ + +} + +void GfxDevice::SetDepthTest(EDepthTest testing) +{ + +} + +void GfxDevice::SetCullFace(ECullFace face) +{ + +} + +void GfxDevice::SetStencilMask(byte stencilMask) +{ + +} + +void GfxDevice::SetStencilOp(EStencilOp op) +{ + +} + +void GfxDevice::SetAntiAliasing(int level /*= 0*/) +{ + +} + +void GfxDevice::Clear(int clearFlag) +{ + +} + +void GfxDevice::UseShader(Shader* shader, int idx) +{ + if (shader == NULL) + return; + + GLuint id = shader->GetID(); + if (id == 0) + return; + + glUseProgram(id); + + shader->ExecuteCommand(); + + m_Shader.shader = shader; +} + +void GfxDevice::UnuseShader() +{ + if (m_Shader) + { + m_Shader.shader = NULL; + } + glUseProgram(0); +} + +void GfxDevice::SetUniformVec2(const char* name, Vector2f vec2) +{ + if (!m_Shader) + return; + GLint loc = glGetUniformLocation(m_Shader.GetID(), name); + glUniform2f(loc, vec2.x, vec2.y); +} + +void GfxDevice::SetUniformVec3(const char* name, Vector3f vec3) +{ + if (!m_Shader) + return; + GLint loc = glGetUniformLocation(m_Shader.GetID(), name); + glUniform3f(loc, vec3.x, vec3.y, vec3.z); +} + +void GfxDevice::SetUniformVec4(const char* name, Vector4f vec4) +{ + if (!m_Shader) + return; + GLint loc = glGetUniformLocation(m_Shader.GetID(), name); + glUniform4f(loc, vec4.x, vec4.y, vec4.z, vec4.w); +} + +void GfxDevice::SetUniformMat4(const char* name, Matrix44 mat4) +{ + if (!m_Shader) + return; + GLint loc = glGetUniformLocation(m_Shader.GetID(), name); + glUniformMatrix4fv(loc, 1, GL_TRUE, &mat4.m[0][0]); +} + +void GfxDevice::SetUniformTexture(const char* name, Texture* texture) +{ + int texUnit = ClaimTextureUnit(); + glActiveTexture(GL_TEXTURE0 + texUnit); + glBindTexture(GL_TEXTURE_2D, texture->GetGpuID()); + + GLint loc = glGetUniformLocation(m_Shader.GetID(), name); + glUniform1i(loc, texUnit); +} + +void GfxDevice::BeginFrame() +{ + m_IsInsideFrame = true; + +} + +void GfxDevice::EndFrame() +{ + //GPU::BufferPool::Instance()->OnEndFrame(); + + m_IsInsideFrame = false; +} + +void GfxDevice::PresentFrame() +{ +// swap buffers +} + +bool GfxDevice::IsInsideFrame() +{ + return m_IsInsideFrame; +} diff --git a/Client/Source/Graphics/GfxDevice.h b/Client/Source/Graphics/GfxDevice.h new file mode 100644 index 0000000..d81d574 --- /dev/null +++ b/Client/Source/Graphics/GfxDevice.h @@ -0,0 +1,109 @@ +#ifndef DEVICE_H +#define DEVICE_H +#include "../Math/Math.h" + +#include "../Utilities/Type.h" +#include "../Utilities/Assert.h" +#include "../Graphics/Shader.h" +#include "../Math/Vector2.hpp" +#include "../Math/Vector3.hpp" +#include "../Math/Vector4.hpp" +#include "../Math/Matrix44.h" + +#include "Shader.h" +#include "Texture.h" +#include "DeviceDefine.h" +#include "VertexBuffer.h" +#include "DynamicVertexBuffer.h" +#include "Color.h" + +struct GfxDeviceSetting +{ + ETextureFilterMode filterMode; // 默认的贴图过滤模式 + ETextureWrapMode wrapMode; // 默认的贴图平铺模式 +}; + +// 当前绑定的shader +struct ShaderState +{ + Shader* shader; + operator bool() + { + return shader != NULL; + } + GLuint GetID() { + if (shader == nullptr) + return 0; + GLint id = shader->GetID(); + return id; + } +}; + +// 对渲染相关API的封装 +class GfxDevice +{ +public: + GfxDevice(); + ~GfxDevice(); + + void Initialize(GfxDeviceSetting setting); + + void Enable(EDeviceEnable enabled); + void Disable(EDeviceEnable enabled); + void IsEnable(uint flag); + + void SetDepthTest(EDepthTest testing); + void SetCullFace(ECullFace face); + void SetStencilMask(byte stencilMask); + void SetStencilOp(EStencilOp op); + + void SetAntiAliasing(int level = 0); + + void Clear(int clearFlag); + + void UseShader(Shader* shader, int idx); + void UnuseShader(); + void SetUniformVec2(const char* name, Vector2f vec2); + void SetUniformVec3(const char* name, Vector3f vec3); + void SetUniformVec4(const char* name, Vector4f vec4); + void SetUniformMat4(const char* name, Matrix44 mat4); + void SetUniformTexture(const char* name, Texture* texture); + + void BeginFrame(); + void EndFrame(); + void PresentFrame(); + + bool IsInsideFrame(); + + DynamicVertexBuffer* GetSharedVBO() { return &m_DynamicVBO; } + + GET_SET(Color, ClearColor, m_ClearColor); + GET_SET(ETextureFilterMode, DefaultFilterMode, m_DefaultFilterMode); + GET_SET(ETextureWrapMode, DefaultWrapMode, m_DefaultWrapMode); + +private: + bool m_IsInsideFrame; + + // 渲染状态 + uint m_EnableFlag; + Color m_ClearColor; + EDepthTest m_DepthTest; + EStencilTest m_StencilTest; + EStencilOp m_StencilOp; + byte m_StencilMask; + + ShaderState m_Shader; // 当前绑定的shader + + // 贴图默认设置 + ETextureFilterMode m_DefaultFilterMode; + ETextureWrapMode m_DefaultWrapMode; + + DynamicVertexBuffer m_DynamicVBO; // 共享的VBO,用来做立即渲染 + +}; + +extern GfxDevice g_GfxDevice; + +#define g_SharedVBO (*g_GfxDevice.GetSharedVBO()) + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/GlyphAtlas.cpp b/Client/Source/Graphics/GlyphAtlas.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Graphics/GlyphAtlas.cpp diff --git a/Client/Source/Graphics/GlyphAtlas.h b/Client/Source/Graphics/GlyphAtlas.h new file mode 100644 index 0000000..b0ab858 --- /dev/null +++ b/Client/Source/Graphics/GlyphAtlas.h @@ -0,0 +1,6 @@ +#ifndef TEXT_ATLAS_H +#define TEXT_ATLAS_H + + + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/ImageData.cpp b/Client/Source/Graphics/ImageData.cpp new file mode 100644 index 0000000..8e2e4bf --- /dev/null +++ b/Client/Source/Graphics/ImageData.cpp @@ -0,0 +1,2 @@ +#include "ImageData.h" + diff --git a/Client/Source/Graphics/ImageData.h b/Client/Source/Graphics/ImageData.h new file mode 100644 index 0000000..9ebc738 --- /dev/null +++ b/Client/Source/Graphics/ImageData.h @@ -0,0 +1,39 @@ +#ifndef IMAGE_DATA_H +#define IMAGE_DATA_H + +#include <vector> + +enum EPixelFormat +{ + PixelFormat_RGBA, + PixelFormat_RGB, + PixelFormat_R, + PixelFormat_RG, + PixelFormat_BGR, + PixelFormat_BGRA +}; + +enum EPixelElementType +{ + PixelType_UNSIGNED_BYTE, + PixelType_UNSIGNED_INT, + PixelType_BYTE, + PixelType_INT, + PixelType_FLOAT, +}; + +// 图片像素数据 +class ImageData +{ +public: + ImageData() + { + } + + void* pixels; // 像素数据,格式和类型参考 http://docs.gl/gl3/glTexImage2D pixel data的format和type + EPixelFormat format; + EPixelElementType type; + int width, height; +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/OpenGL.cpp b/Client/Source/Graphics/OpenGL.cpp new file mode 100644 index 0000000..9ed2e50 --- /dev/null +++ b/Client/Source/Graphics/OpenGL.cpp @@ -0,0 +1,7 @@ +#include "OpenGL.h" + +#pragma comment(lib, "opengl32.lib") + +std::string g_sharedGLErrorMsg = ""; + + diff --git a/Client/Source/Graphics/OpenGL.h b/Client/Source/Graphics/OpenGL.h new file mode 100644 index 0000000..d93fb5a --- /dev/null +++ b/Client/Source/Graphics/OpenGL.h @@ -0,0 +1,41 @@ +#ifndef OPENGL_H +#define OPENGL_H + +#include "glad/glad.h" +#include <string> +#include <exception> + +//http://docs.gl/gl3/glClear + +#define CheckGLError(action)\ +if(true){ \ + GLenum error; \ + while ((error = glGetError()) != GL_NO_ERROR) { \ + action \ + } \ +} + +#define WipeGLError() \ +if(true){\ + GLenum error; \ + while ((error = glGetError()) != GL_NO_ERROR) { \ + throw GLException(error); \ + } \ +} + +extern std::string g_sharedGLErrorMsg; + +class GLException : public std::exception +{ +public: + GLException(const char* what) + : std::exception(what) + {} + GLException(int glError) + { + g_sharedGLErrorMsg = std::to_string(glError); + std::exception(g_sharedGLErrorMsg.c_str()); + } +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/Point.cpp b/Client/Source/Graphics/Point.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Graphics/Point.cpp diff --git a/Client/Source/Graphics/Point.h b/Client/Source/Graphics/Point.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Graphics/Point.h diff --git a/Client/Source/Graphics/PolyLine.cpp b/Client/Source/Graphics/PolyLine.cpp new file mode 100644 index 0000000..911b3e5 --- /dev/null +++ b/Client/Source/Graphics/PolyLine.cpp @@ -0,0 +1,9 @@ +#include "PolyLine.h" +#include "Color.h" +#include "../Math/Math.h" + +struct PolyLineVBOLayout +{ + Vector3f position; + Color32 color; +}; diff --git a/Client/Source/Graphics/PolyLine.h b/Client/Source/Graphics/PolyLine.h new file mode 100644 index 0000000..816d18e --- /dev/null +++ b/Client/Source/Graphics/PolyLine.h @@ -0,0 +1,18 @@ +#ifndef POLY_LINE_H +#define POLY_LINE_H + +#include "../Math/Vector3.hpp" + +class PolyLine +{ +public: + PolyLine(); + ~PolyLine(); + + void Draw(); + +private: + +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/Primitive.h b/Client/Source/Graphics/Primitive.h new file mode 100644 index 0000000..6636221 --- /dev/null +++ b/Client/Source/Graphics/Primitive.h @@ -0,0 +1,8 @@ +#pragma once + +enum EPrimitive +{ + Primitive_Triangle = 1, + Primitive_Line = 2, + Primitive_Point = 3, +}; diff --git a/Client/Source/Graphics/Quad.cpp b/Client/Source/Graphics/Quad.cpp new file mode 100644 index 0000000..40c896d --- /dev/null +++ b/Client/Source/Graphics/Quad.cpp @@ -0,0 +1,62 @@ +#include "../Graphics/GfxDevice.h" +#include "../Math/Math.h" +#include "../Math/Math.h" + +#include "Quad.h" + +struct QuadVBOLayout +{ + Vector3f position; + Vector2f uv; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 3, VertexAttrFormat_Float, sizeof(QuadVBOLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector3f), 2, VertexAttrFormat_Float, sizeof(QuadVBOLayout)); + + layout.attributes.push_back(POSITION); + layout.attributes.push_back(UV); +}); + +void Quad::Draw() +{ + const int nVerts = 4; + const int nIndices = 6; + + float pos[] = { + m_Left, m_Bottom, 0, // left-bottom + m_Right, m_Bottom, 0, // right-bottom + m_Right, m_Top, 0, // right-top + m_Left, m_Top, 0, // top-left + }; + float uv[] = { + 0, 0, + 1, 0, + 1, 1, + 0, 1, + }; + int indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(QuadVBOLayout), sizeof(uint16), 4, 6, Primitive_Triangle, (void**)&vb, (void**)&ib); + + QuadVBOLayout* dst = (QuadVBOLayout*)vb; + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[3 * i], pos[3 * i + 1], pos[3 * i + 2]); + dst[i].uv.Set(uv[2 * i], uv[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(4, 6); + g_SharedVBO.DrawChunk(layout); +} diff --git a/Client/Source/Graphics/Quad.h b/Client/Source/Graphics/Quad.h new file mode 100644 index 0000000..ee6c5be --- /dev/null +++ b/Client/Source/Graphics/Quad.h @@ -0,0 +1,26 @@ +#ifndef QUAD_H +#define QUAD_H + +#include "../Utilities/UtilMacros.h" +#include "DynamicMesh.h" + +class Quad : public DynamicMesh +{ +public: + Quad(float l, float r, float t, float b); + + void Set(float l, float r, float t, float b); + + GET_SET(float, Left, m_Left); + GET_SET(float, Right, m_Right); + GET_SET(float, Top, m_Top); + GET_SET(float, Bottom, m_Bottom); + + void Draw() override; + +private: + float m_Left, m_Right, m_Top, m_Bottom; + +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/RenderCommands.cpp b/Client/Source/Graphics/RenderCommands.cpp new file mode 100644 index 0000000..336bf20 --- /dev/null +++ b/Client/Source/Graphics/RenderCommands.cpp @@ -0,0 +1,9 @@ +#include "RenderCommands.h" + +void ReleaseRenderCommandGroup(RenderCommandGroup& group) +{ + for (int i = 0; i < group.size(); ++i) + { + delete group[i]; + } +} diff --git a/Client/Source/Graphics/RenderCommands.h b/Client/Source/Graphics/RenderCommands.h new file mode 100644 index 0000000..f66b4e2 --- /dev/null +++ b/Client/Source/Graphics/RenderCommands.h @@ -0,0 +1,164 @@ +锘#pragma once +#include "OpenGL.h" +#include <vector> + +struct RenderCommand +{ + virtual void Execute() = 0; +}; + +typedef std::vector<RenderCommand*> RenderCommandGroup; +void ReleaseRenderCommandGroup(RenderCommandGroup& group); + +// Cull Off|Front|Back|Both +struct Cmd_Cull : RenderCommand +{ + enum ECullFace { + Cull_Disable, + Cull_Front, + Cull_Back, + Cull_Both, + }; + + ECullFace cull; + + void Execute() override + { + if (cull == ECullFace::Cull_Disable) + { + glDisable(GL_CULL_FACE); + return; + } + glEnable(GL_CULL_FACE); + switch (cull) + { + case ECullFace::Cull_Front: glCullFace(GL_FRONT); break; + case ECullFace::Cull_Back: glCullFace(GL_BACK); break; + case ECullFace::Cull_Both: glCullFace(GL_FRONT_AND_BACK); break; + } + } +}; + +// Blend Off +// Blend <srcFac> <dstFac> +struct Cmd_Blend : RenderCommand +{ + enum EBlend + { + Blend_Zero, // 0 + Blend_One, // 1 + Blend_Src_Color, // 婧愰鑹插悜閲廋炉source + Blend_One_Minus_Src_Color, // 1鈭扖炉source + Blend_Dst_Color, // 鐩爣棰滆壊鍚戦噺C炉destination + Blend_One_Minus_Dst_Color, // 1鈭扖炉destination + Blend_Src_Alpha, // C炉source鐨刟lpha鍊 + Blend_One_Minus_Src_Alpha, // 1鈭 C炉source鐨刟lpha鍊 + Blend_Dst_Alpha, // C炉destination鐨刟lpha鍊 + Blend_One_Minus_Dst_Alpha, // 1鈭 C炉destination鐨刟lpha鍊 + Blend_Constant_Color, // 甯搁鑹插悜閲廋炉constant + Blend_One_Minus_Constant_Color, // 1鈭扖炉constant + Blend_Constant_Alpha, // C炉constant鐨刟lpha鍊 + Blend_One_Minus_Constant_Alpha, // 1鈭 C炉constant鐨刟lpha鍊 + }; + + bool enable; + EBlend srcFac; + EBlend dstFac; + + void Execute() override + { + if (!enable) + glDisable(GL_BLEND); + glEnable(GL_BLEND); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + GLenum src, dst; + switch (srcFac) + { + case EBlend::Blend_Zero: src = GL_ZERO; break; + case EBlend::Blend_One: src = GL_ONE; break; + case EBlend::Blend_Src_Color: src = GL_SRC_COLOR; break; + case EBlend::Blend_One_Minus_Src_Color: src = GL_ONE_MINUS_SRC_COLOR; break; + case EBlend::Blend_Dst_Color: src = GL_DST_COLOR; break; + case EBlend::Blend_One_Minus_Dst_Color: src = GL_ONE_MINUS_DST_COLOR; break; + case EBlend::Blend_Src_Alpha: src = GL_SRC_ALPHA; break; + case EBlend::Blend_One_Minus_Src_Alpha: src = GL_ONE_MINUS_SRC_ALPHA; break; + case EBlend::Blend_Dst_Alpha: src = GL_DST_ALPHA; break; + case EBlend::Blend_One_Minus_Dst_Alpha: src = GL_ONE_MINUS_DST_ALPHA; break; + case EBlend::Blend_Constant_Color: src = GL_CONSTANT_COLOR; break; + case EBlend::Blend_One_Minus_Constant_Color: src = GL_ONE_MINUS_CONSTANT_COLOR; break; + case EBlend::Blend_Constant_Alpha: src = GL_CONSTANT_ALPHA; break; + case EBlend::Blend_One_Minus_Constant_Alpha: src = GL_ONE_MINUS_CONSTANT_ALPHA; break; + } + switch (dstFac) + { + case EBlend::Blend_Zero: dst = GL_ZERO; break; + case EBlend::Blend_One: dst = GL_ONE; break; + case EBlend::Blend_Src_Color: dst = GL_SRC_COLOR; break; + case EBlend::Blend_One_Minus_Src_Color: dst = GL_ONE_MINUS_SRC_COLOR; break; + case EBlend::Blend_Dst_Color: dst = GL_DST_COLOR; break; + case EBlend::Blend_One_Minus_Dst_Color: dst = GL_ONE_MINUS_DST_COLOR; break; + case EBlend::Blend_Src_Alpha: dst = GL_SRC_ALPHA; break; + case EBlend::Blend_One_Minus_Src_Alpha: dst = GL_ONE_MINUS_SRC_ALPHA; break; + case EBlend::Blend_Dst_Alpha: dst = GL_DST_ALPHA; break; + case EBlend::Blend_One_Minus_Dst_Alpha: dst = GL_ONE_MINUS_DST_ALPHA; break; + case EBlend::Blend_Constant_Color: dst = GL_CONSTANT_COLOR; break; + case EBlend::Blend_One_Minus_Constant_Color: dst = GL_ONE_MINUS_CONSTANT_COLOR; break; + case EBlend::Blend_Constant_Alpha: dst = GL_CONSTANT_ALPHA; break; + case EBlend::Blend_One_Minus_Constant_Alpha: dst = GL_ONE_MINUS_CONSTANT_ALPHA; break; + } + glBlendFunc(src, dst); + } +}; + +// DepthTest Off|<func> +struct Cmd_DepthTest : RenderCommand +{ + enum EDepthTest + { + DepthTest_Off, // 涓嶈繘琛屾繁搴︽祴璇 + DepthTest_Always, // 姘歌繙閫氳繃娴嬭瘯 + DepthTest_Never, // 姘歌繙涓嶉氳繃娴嬭瘯 + DepthTest_Less, // 鍦ㄧ墖娈垫繁搴﹀煎皬浜庣紦鍐插尯鐨勬繁搴︽椂閫氳繃娴嬭瘯 + DepthTest_Equal, // 鍦ㄧ墖娈垫繁搴﹀肩瓑浜庣紦鍐插尯鐨勬繁搴︽椂閫氳繃娴嬭瘯 + DepthTest_Lequal, // 鍦ㄧ墖娈垫繁搴﹀煎皬浜庣瓑浜庣紦鍐插尯鐨勬繁搴︽椂閫氳繃娴嬭瘯 + DepthTest_Greater, // 鍦ㄧ墖娈垫繁搴﹀煎ぇ浜庣紦鍐插尯鐨勬繁搴︽椂閫氳繃娴嬭瘯 + DepthTest_Notequal, // 鍦ㄧ墖娈垫繁搴﹀间笉绛変簬缂撳啿鍖虹殑娣卞害鏃堕氳繃娴嬭瘯 + DepthTest_Gequal, // 鍦ㄧ墖娈垫繁搴﹀煎ぇ浜庣瓑浜庣紦鍐插尯鐨勬繁搴︽椂閫氳繃娴嬭瘯 + }; + + EDepthTest test; + + void Execute() override + { + if (test == EDepthTest::DepthTest_Off) + { + glDisable(GL_DEPTH_TEST); + return; + } + glEnable(GL_DEPTH_TEST); + switch (test) + { + case EDepthTest::DepthTest_Always: glDepthFunc(GL_ALWAYS); break; + case EDepthTest::DepthTest_Never: glDepthFunc(GL_NEVER); break; + case EDepthTest::DepthTest_Less: glDepthFunc(GL_LESS); break; + case EDepthTest::DepthTest_Equal: glDepthFunc(GL_EQUAL); break; + case EDepthTest::DepthTest_Lequal: glDepthFunc(GL_LEQUAL); break; + case EDepthTest::DepthTest_Greater: glDepthFunc(GL_GREATER); break; + case EDepthTest::DepthTest_Notequal: glDepthFunc(GL_NOTEQUAL); break; + case EDepthTest::DepthTest_Gequal: glDepthFunc(GL_GEQUAL); break; + } + } +}; + +// DepthWrite Off|On +struct Cmd_DepthWrite : RenderCommand +{ + bool write; + void Execute() override + { + if(write) + glDepthMask(GL_TRUE); + else + glDepthMask(GL_FALSE); + } +}; diff --git a/Client/Source/Graphics/RenderTexture.cpp b/Client/Source/Graphics/RenderTexture.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Graphics/RenderTexture.cpp diff --git a/Client/Source/Graphics/RenderTexture.h b/Client/Source/Graphics/RenderTexture.h new file mode 100644 index 0000000..a501f50 --- /dev/null +++ b/Client/Source/Graphics/RenderTexture.h @@ -0,0 +1,16 @@ +#ifndef RENDER_TEXTURE_H +#define RENDER_TEXTURE_H + +#include "Texture.h" + +// 离屏渲染 +class RenderTexture : public Texture +{ +public: + RenderTexture(ETextureFormat format); + +}; + +RenderTexture* CreateRenderTexture(int width, int height, ETextureFormat format); + +#endif
\ No newline at end of file diff --git a/Client/Source/Graphics/Shader.cpp b/Client/Source/Graphics/Shader.cpp new file mode 100644 index 0000000..899578b --- /dev/null +++ b/Client/Source/Graphics/Shader.cpp @@ -0,0 +1,154 @@ +#include <exception> + +#include "../Debug/log.h" +#include "Shader.h" +#include "OpenGL.h" +#include "ShaderCompiler.h" + +using namespace std; + +std::string shaderError = ""; +void checkCompileshaderErrorors(GLuint shader, std::string type) +{ + GLint success; + GLchar infoLog[1024]; + if (type != "PROGRAM") + { + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) + { + glGetShaderInfoLog(shader, 1024, NULL, infoLog); + shaderError = "ERROR::SHADER_COMPILATION_ERROR of type: " + type + "\n" + infoLog; + } + } + else + { + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (!success) + { + glGetProgramInfoLog(shader, 1024, NULL, infoLog); + shaderError = "ERROR::SHADER_COMPILATION_ERROR of type: " + type + "\n" + infoLog; + } + } + if (!success) + { + throw ShaderCompileExecption(shaderError.c_str()); + } +} + +Shader::Shader() +{ +} + +Shader::Shader(std::string& glsllShader) +{ + // stl的string会在大小超过阈值的情况下在栈里分配,并用RAII保证释放 + std::string vsh ; + std::string fsh ; + try + { + GLSLCompiler::Compile(glsllShader, vsh, fsh, m_Commands); + } + catch (GLSLCompileException& e) + { + ReleaseRenderCommandGroup(m_Commands); + throw ShaderCompileExecption(e.what()); + } + CompileProgram(vsh.c_str(), fsh.c_str()); +} + +Shader::Shader(const char* vert, const char* frag) +{ + CompileProgram(vert, frag); +} + +void Shader::CompileProgram(const char* vert, const char* frag, bool keepSrc) +{ + const char* vertCode = vert; + const char* fragCode = frag; + // vertex shader + m_VertID = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(m_VertID, 1, &vertCode, NULL);//为了支持中文注释,长度传NULL + glCompileShader(m_VertID); + checkCompileshaderErrorors(m_VertID, "VERTEX"); + // fragment Shader + m_FragID = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(m_FragID, 1, &fragCode, NULL); + glCompileShader(m_FragID); + checkCompileshaderErrorors(m_FragID, "FRAGMENT"); + // create program + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertID); + glAttachShader(m_ProgramID, m_FragID); + glLinkProgram(m_ProgramID); + checkCompileshaderErrorors(m_ProgramID, "PROGRAM"); +} + +Shader::~Shader() +{ + glDeleteProgram(m_ProgramID); + glDeleteShader(m_VertID); + glDeleteShader(m_FragID); +} + +void Shader::ReCompile(std::string& vert, std::string frag) +{ + const char* vertCode = vert.c_str(); + const char* fragCode = frag.c_str(); + // vertex shader + glShaderSource(m_VertID, 1, &vertCode, NULL); + glCompileShader(m_VertID); + checkCompileshaderErrorors(m_VertID, "VERTEX"); + // fragment Shader + glShaderSource(m_FragID, 1, &fragCode, NULL); + glCompileShader(m_FragID); + checkCompileshaderErrorors(m_FragID, "FRAGMENT"); + // create program + glAttachShader(m_ProgramID, m_VertID); + glAttachShader(m_ProgramID, m_FragID); + glLinkProgram(m_ProgramID); + checkCompileshaderErrorors(m_FragID, "PROGRAM"); +} + +void Shader::ReCompileVert(std::string& vert) +{ + glDeleteShader(m_VertID); + const char* vertCode = vert.c_str(); + // vertex shader + glShaderSource(m_VertID, 1, &vertCode, NULL); + glCompileShader(m_VertID); + checkCompileshaderErrorors(m_VertID, "VERTEX"); + // create program + glAttachShader(m_ProgramID, m_VertID); + glAttachShader(m_ProgramID, m_FragID); + glLinkProgram(m_ProgramID); + checkCompileshaderErrorors(m_FragID, "PROGRAM"); +} + +void Shader::ReCompileFrag(std::string frag) +{ + glDeleteShader(m_FragID); + const char* fragCode = frag.c_str(); + // fragment Shader + glShaderSource(m_FragID, 1, &fragCode, NULL); + glCompileShader(m_FragID); + checkCompileshaderErrorors(m_FragID, "FRAGMENT"); + // create program + glAttachShader(m_ProgramID, m_VertID); + glAttachShader(m_ProgramID, m_FragID); + glLinkProgram(m_ProgramID); + checkCompileshaderErrorors(m_FragID, "PROGRAM"); +} + +bool Shader::IsValid() +{ + return m_ProgramID != 0; +} + +void Shader::ExecuteCommand() +{ + for (int i = 0; i < m_Commands.size(); ++i) + { + m_Commands[i]->Execute(); + } +}
\ No newline at end of file diff --git a/Client/Source/Graphics/Shader.h b/Client/Source/Graphics/Shader.h new file mode 100644 index 0000000..8d5fc0e --- /dev/null +++ b/Client/Source/Graphics/Shader.h @@ -0,0 +1,47 @@ +#pragma once +#include <string> +#include <exception> +#include "OpenGL.h" +#include "../Utilities/UtilMacros.h" +#include "../Utilities/Assert.h" +#include "../Debug/Log.h" +#include "RenderCommands.h" + +// 着色器程序 +class Shader +{ +public: + Shader()/*throw(ShaderCompileExecption)*/; + Shader(std::string& glsllShader)/*throw(ShaderCompileExecption)*/; + Shader(const char* vert, const char* frag)/*throw(ShaderCompileExecption)*/; + ~Shader(); + + void ReCompile(std::string& vert, std::string frag)/*throw(ShaderCompileExecption)*/; + void ReCompileVert(std::string& vert)/*throw(ShaderCompileExecption)*/; + void ReCompileFrag(std::string frag)/*throw(ShaderCompileExecption)*/; + + bool IsValid(); + + void ExecuteCommand(); + + GET(GLint, ID, m_ProgramID); + +private: + void CompileProgram(const char* vert, const char* frag, bool keepSrc = false); + + RenderCommandGroup m_Commands; // 渲染前的状态设置 + + GLint m_ProgramID; + GLint m_FragID; + GLint m_VertID; + +}; + +class ShaderCompileExecption : public std::exception +{ +public: + ShaderCompileExecption(const char* what) + : std::exception(what) + { + } +}; diff --git a/Client/Source/Graphics/ShaderCompiler.cpp b/Client/Source/Graphics/ShaderCompiler.cpp new file mode 100644 index 0000000..362b28e --- /dev/null +++ b/Client/Source/Graphics/ShaderCompiler.cpp @@ -0,0 +1,347 @@ +#include "ShaderCompiler.h" +#include <sstream> +#include <algorithm> + +using namespace std; + +static const char* VSH_BEGIN = "VSH_BEGIN"; +static const char* VSH_END = "VSH_END"; +static const char* FSH_BEGIN = "FSH_BEGIN"; +static const char* FSH_END = "FSH_END"; +static const char* CMD_BEGIN = "CMD_BEGIN"; +static const char* CMD_END = "CMD_END"; + +std::string s_CompileError = ""; + +// GLSL分为四部分 +// * CMD_BEGIN 和 CMD_END 之间的命令 +// * VERTEX_SHADER_BEGIN 和 VERTEX_SHADER_END之间的顶点着色器 +// * FRAGMENT_SHADER_BEGIN 和 FRAGMENT_SHADER_END之间的片段着色器 +// * 三者之外的公共部分 +void GLSLCompiler::Compile(std::string& src, std::string& vsh, std::string& fsh, RenderCommandGroup& group)/*throw GLSLCompileException*/ +{ +#define CheckLabel(label) {\ + int pos = src.find(label);\ + if(pos == string::npos || !IsLabelActive(src, label)) {\ + s_CompileError = std::string("Compile Shader Error: No ") + #label + " label";\ + throw GLSLCompileException(s_CompileError.c_str());\ + }} + + CheckLabel(VSH_BEGIN); + CheckLabel(VSH_END); + CheckLabel(FSH_BEGIN); + CheckLabel(FSH_END); + + vsh = GetContent(src, VSH_BEGIN, VSH_END); + fsh = GetContent(src, FSH_BEGIN, FSH_END); + + bool hasCmd = IsLabelActive(src, CMD_BEGIN) && IsLabelActive(src, CMD_END); + if (hasCmd) + { + string cmd = GetContent(src, CMD_BEGIN, CMD_END); + if (cmd.size() > 0) + { + ParseCmd(cmd, group); + } + else + { + hasCmd = false; + } + } + + string common; + common = TrimContent(src, VSH_BEGIN, VSH_END); + common = TrimContent(common, FSH_BEGIN, FSH_END); + if (hasCmd) + common = TrimContent(common, CMD_BEGIN, CMD_END); + + vsh = common + vsh; + fsh = common + fsh; +} + +std::string GLSLCompiler::GetContent(std::string& src, const char* from, const char* to) +{ + int begin = src.find(from); + int end = src.find(to); + if (begin == string::npos || end == string::npos) + { + return ""; + } + std::string content = src.substr(begin + strlen(from), end - begin - strlen(from)); + return content; +} + +std::string GLSLCompiler::TrimContent(std::string& src, const char* from, const char* to) +{ + int begin = src.find(from); + int end = src.find(to); + string result = src.erase(begin, end + strlen(to) - begin); + return result; +} + +bool GLSLCompiler::IsLabelActive(std::string& src, const char* label) +{ + int pos = src.find(label); + if (pos == string::npos) + return false; + for (int i = pos - 1; i >= 0; --i) + { + int second = i; + int first = i - 1; + if (first < 0) + break; + if (src[second] == '\n' || src[first] == '\r') + break; + if (src[first] == '/' && src[second] == '/') + return false; + } + return true; +} + +bool GLSLCompiler::IsCommandActive(std::string& src, const char* label) +{ + int pos = src.find(label); + if (pos == string::npos) + return false; + for (int i = pos - 1; i >= 0; --i) + { + int second = i; + int first = i - 1; + if (first < 0) + break; + if (src[second] == '\n' || src[first] == '\r') + break; + if (src[first] == '/' && src[second] == '/') + return false; + } + return true; +} + +void GLSLCompiler::ParseCmd(std::string& cmds, RenderCommandGroup& group) +{ + istringstream ss = istringstream(cmds); + string line; + while (getline(ss, line)) + { + if(line.find('\r') != string::npos) + line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + if (IsLineCommentd(line)) + continue; + int cb, ce; // cmd begin, end + if (!FindCmdPos(line, &cb, &ce)) + continue; + string cmdName = line.substr(cb, ce - cb + 1); + string cmdValue = line.substr(ce + 1, line.size() - ce - 1); + if (cmdName == "Cull") CommandCull(cmdValue, group); + else if (cmdName == "Blend") CommandBlend(cmdValue, group); + else if (cmdName == "DepthTest") CommandDepthTest(cmdValue, group); + else if (cmdName == "DepthWrite") CommandDepthWrite(cmdValue, group); + else + { + s_CompileError = string("Unknown command " + cmdName); + throw GLSLCompileException(s_CompileError.c_str()); + } + } +} + +#define IsSeperator(c) (c == ' ' || c == '\r' || c == '\n' || c == /*tab*/9) + +// 找到行内的第一个单词,作为命令名 +bool GLSLCompiler::FindCmdPos(std::string& line, int* start, int* end) +{ + for (int i = 0; i < line.size(); ++i) + { + if (IsSeperator(line[i])) + continue; + *start = i; + for (int j = i + 1; j < line.size(); ++j) + { + if (IsSeperator(line[j])) + { + *end = j - 1; + return true; + } + } + } + return false; +} + +bool GLSLCompiler::IsLineCommentd(std::string& line) +{ + for (int i = 0; i < line.size(); ++i) + { + if (IsSeperator(line[i])) + continue; + int first = i; + int second = i + 1; + if (second == line.size()) + return false; + if (line[first] == '/' && line[second] == '/') + return true; + return false; + } + return false; +} + +#define MAX_PARAM 2 + +void GLSLCompiler::CommandCull(std::string& p, RenderCommandGroup& group) +{ + std::string params[1]; + GetParams("Cull", p, params, 1); + + Cmd_Cull* pCull = new Cmd_Cull(); + Cmd_Cull& cull = *pCull; + if (params[0] == "Off") cull.cull = Cmd_Cull::Cull_Disable; + else if (params[0] == "Front") cull.cull = Cmd_Cull::Cull_Front; + else if (params[0] == "Back") cull.cull = Cmd_Cull::Cull_Back; + else if (params[0] == "Both") cull.cull = Cmd_Cull::Cull_Both; + else + { + delete pCull; + s_CompileError = string("Compile Shader Error: Invalid parameter of Cull: " + params[0]); + throw GLSLCompileException(s_CompileError.c_str()); + } + group.push_back(pCull); +} + +void GLSLCompiler::CommandBlend(std::string& p, RenderCommandGroup& group) +{ + std::string params[2]; + GetParams("Blend", p, params, 2); + + Cmd_Blend* pblend = new Cmd_Blend(); + Cmd_Blend& blend = *pblend; + if (params[0] == "Off") + { + blend.enable = false; + group.push_back(pblend); + return; + } + + blend.enable = true; + + if (params[0] == "Zero") blend.srcFac = Cmd_Blend::Blend_Zero; + else if (params[0] == "One") blend.srcFac = Cmd_Blend::Blend_One; + else if (params[0] == "SrcColor") blend.srcFac = Cmd_Blend::Blend_Src_Color; + else if (params[0] == "OneMinusSrcColor") blend.srcFac = Cmd_Blend::Blend_One_Minus_Src_Color; + else if (params[0] == "DstColor") blend.srcFac = Cmd_Blend::Blend_Dst_Color; + else if (params[0] == "OneMinusDstColor") blend.srcFac = Cmd_Blend::Blend_One_Minus_Dst_Color; + else if (params[0] == "SrcAlpha") blend.srcFac = Cmd_Blend::Blend_Src_Alpha; + else if (params[0] == "OneMinusSrcAlpha") blend.srcFac = Cmd_Blend::Blend_One_Minus_Src_Alpha; + else if (params[0] == "DstAlpha") blend.srcFac = Cmd_Blend::Blend_Dst_Alpha; + else if (params[0] == "OneMinusDstAlpha") blend.srcFac = Cmd_Blend::Blend_One_Minus_Dst_Alpha; + else if (params[0] == "ConstantColor") blend.srcFac = Cmd_Blend::Blend_Constant_Color; + else if (params[0] == "OneMinusConstantColor") blend.srcFac = Cmd_Blend::Blend_One_Minus_Constant_Color; + else if (params[0] == "ConstantAlpha") blend.srcFac = Cmd_Blend::Blend_Constant_Alpha; + else if (params[0] == "OneMinusConstantAlpha") blend.srcFac = Cmd_Blend::Blend_One_Minus_Constant_Alpha; + else + { + delete pblend; + s_CompileError = string("Compile Shader Error: Invalid parameter of Blend: " + params[0]); + throw GLSLCompileException(s_CompileError.c_str()); + } + + if (params[1] == "Zero") blend.dstFac = Cmd_Blend::Blend_Zero; + else if (params[1] == "One") blend.dstFac = Cmd_Blend::Blend_One; + else if (params[1] == "SrcColor") blend.dstFac = Cmd_Blend::Blend_Src_Color; + else if (params[1] == "OneMinusSrcColor") blend.dstFac = Cmd_Blend::Blend_One_Minus_Src_Color; + else if (params[1] == "DstColor") blend.dstFac = Cmd_Blend::Blend_Dst_Color; + else if (params[1] == "OneMinusDstColor") blend.dstFac = Cmd_Blend::Blend_One_Minus_Dst_Color; + else if (params[1] == "SrcAlpha") blend.dstFac = Cmd_Blend::Blend_Src_Alpha; + else if (params[1] == "OneMinusSrcAlpha") blend.dstFac = Cmd_Blend::Blend_One_Minus_Src_Alpha; + else if (params[1] == "DstAlpha") blend.dstFac = Cmd_Blend::Blend_Dst_Alpha; + else if (params[1] == "OneMinusDstAlpha") blend.dstFac = Cmd_Blend::Blend_One_Minus_Dst_Alpha; + else if (params[1] == "ConstantColor") blend.dstFac = Cmd_Blend::Blend_Constant_Color; + else if (params[1] == "OneMinusConstantColor") blend.dstFac = Cmd_Blend::Blend_One_Minus_Constant_Color; + else if (params[1] == "ConstantAlpha") blend.dstFac = Cmd_Blend::Blend_Constant_Alpha; + else if (params[1] == "OneMinusConstantAlpha") blend.dstFac = Cmd_Blend::Blend_One_Minus_Constant_Alpha; + else + { + delete pblend; + s_CompileError = string("Compile Shader Error: Invalid parameter of Blend: " + params[1]); + throw GLSLCompileException(s_CompileError.c_str()); + } + + group.push_back(pblend); +} + +void GLSLCompiler::CommandDepthTest(std::string& p, RenderCommandGroup& group) +{ + std::string params[1]; + GetParams("DepthTest", p, params, 1); + + Cmd_DepthTest* pTest = new Cmd_DepthTest(); + Cmd_DepthTest& test = *pTest; + if (params[0] == "Off") test.test = Cmd_DepthTest::DepthTest_Off; + else if (params[0] == "Always") test.test = Cmd_DepthTest::DepthTest_Always; + else if (params[0] == "Never") test.test = Cmd_DepthTest::DepthTest_Never; + else if (params[0] == "Less") test.test = Cmd_DepthTest::DepthTest_Less; + else if (params[0] == "Equal") test.test = Cmd_DepthTest::DepthTest_Equal; + else if (params[0] == "LEqual") test.test = Cmd_DepthTest::DepthTest_Lequal; + else if (params[0] == "Greater") test.test = Cmd_DepthTest::DepthTest_Greater; + else if (params[0] == "NotEqual") test.test = Cmd_DepthTest::DepthTest_Notequal; + else if (params[0] == "GEqual") test.test = Cmd_DepthTest::DepthTest_Gequal; + else + { + delete pTest; + s_CompileError = string("Compile Shader Error: Invalid parameter of DepthTest: " + params[0]); + throw GLSLCompileException(s_CompileError.c_str()); + } + group.push_back(pTest); +} + +void GLSLCompiler::CommandDepthWrite(std::string& p, RenderCommandGroup& group) +{ + std::string params[1]; + GetParams("DepthWrite", p, params, 1); + + Cmd_DepthWrite* pwrite = new Cmd_DepthWrite(); + Cmd_DepthWrite& write = *pwrite; + if (params[0] == "Off") write.write = false; + else if (params[0] == "On") write.write = true; + else + { + delete pwrite; + s_CompileError = string("Compile Shader Error: Invalid parameter of DepthWrite: " + params[0]); + throw GLSLCompileException(s_CompileError.c_str()); + } + group.push_back(pwrite); +} + +void GLSLCompiler::GetParams(const char* cmdName, std::string& params, std::string* out, int n) +{ + int index = 0; + for (int i = 0; i < params.size(); ++i) + { + if (IsSeperator(params[i])) + continue; + int j = i + 1; + for (; j < params.size(); ++j) + { + if (j == params.size() - 1) + { + if (index >= n) + { + s_CompileError = string("Compile Shader Error: Invalid parameter count of ") + cmdName +" : " + params; + throw GLSLCompileException(s_CompileError.c_str()); + } + if(!IsSeperator(params[j])) + out[index++] = params.substr(i, j - i + 1); + else + out[index++] = params.substr(i, j - i); + return; + } + if (!IsSeperator(params[j])) + continue; + if (index >= n) + { + s_CompileError = string("Compile Shader Error: Invalid parameter count of ") + cmdName + " : " + params; + throw GLSLCompileException(s_CompileError.c_str()); + } + out[index++] = params.substr(i, j - i); + break; + } + i = j; + } +} diff --git a/Client/Source/Graphics/ShaderCompiler.h b/Client/Source/Graphics/ShaderCompiler.h new file mode 100644 index 0000000..f374567 --- /dev/null +++ b/Client/Source/Graphics/ShaderCompiler.h @@ -0,0 +1,55 @@ +#pragma once + +#include <exception> +#include <string> +#include "../Threading/Mutex.h" +#include "../Threading/Job.h" +#include "../Graphics/RenderCommands.h" + +// 编译GLSL(GameLab Shader) + +// in: .glsl path +// out: vsh & fsh +class CompileGLSLJob : public Job +{ + +}; + +// in: glsl shader +// out: vsh & fsh +class CompileGLSLShaderJob : public Job +{ + +}; + +class GLSLCompileException : public std::exception +{ +public: + GLSLCompileException(const char* what) + : std::exception(what) + { + } + +}; + +class GLSLCompiler +{ +public: + static void Compile(std::string& src, std::string& vsh, std::string& fsh, RenderCommandGroup& cmd)/*throw GLSLCompileException*/; + +private: + static std::string GetContent(std::string& src, const char* from, const char* to); + static std::string TrimContent(std::string& src, const char* from, const char* to); + static bool IsLabelActive(std::string& src, const char* label); + + static void ParseCmd(std::string& cmd, RenderCommandGroup& group); + static bool IsCommandActive(std::string& src, const char* label); + static bool FindCmdPos(std::string& line, int* start, int* end); + static bool IsLineCommentd(std::string& line); + static void CommandCull(std::string& params, RenderCommandGroup& group); + static void CommandBlend(std::string& params, RenderCommandGroup& group); + static void CommandDepthTest(std::string& params, RenderCommandGroup& group); + static void CommandDepthWrite(std::string& params, RenderCommandGroup& group); + static void GetParams(const char* cmdName, std::string& params, std::string* out, int n); + +}; diff --git a/Client/Source/Graphics/Texture.cpp b/Client/Source/Graphics/Texture.cpp new file mode 100644 index 0000000..79dd5ea --- /dev/null +++ b/Client/Source/Graphics/Texture.cpp @@ -0,0 +1,230 @@ +#include "../Math/Math.h" + +#include "ImageData.h" +#include "Texture.h" + +Texture::Texture(TextureSetting setting, ImageData* imgData) +{ + Init(setting, imgData); + // force not keep imgData + m_KeepPixelData = false; +} + +void Texture::Init(TextureSetting setting, ImageData* imgData) +{ + m_Width = imgData->width; + m_Height = imgData->height; + m_Type = setting.type; + m_Format = setting.format; + m_WrapMode = setting.wrapMode; + m_FilterMode = setting.filterMode; + m_KeepPixelData = setting.keepImageData; + + glGenTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, m_GPUID); + + CheckGLError( + glDeleteTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + switch (m_WrapMode) { + case ETextureWrapMode::Clamp: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + break; + case ETextureWrapMode::Repeat: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + case ETextureWrapMode::Mirror: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + break; + default: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + break; + } + + CheckGLError( + glDeleteTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + switch (m_FilterMode) { + case ETextureFilterMode::Linear: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + case ETextureFilterMode::Nearest: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + default: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + } + + GLint srcFormat = GL_RGB; + switch (imgData->format) + { + case PixelFormat_R: srcFormat = GL_RED; break; + case PixelFormat_RGB: srcFormat = GL_RGB; break; + case PixelFormat_RGBA: srcFormat = GL_RGBA; break; + default: Assert(false); + } + GLint srcType = GL_UNSIGNED_BYTE; + switch (imgData->type) + { + case PixelType_UNSIGNED_BYTE: srcType = GL_UNSIGNED_BYTE; break; + default: Assert(false); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgData->width, imgData->height, 0, srcFormat, srcType, imgData->pixels); + + CheckGLError( + glDeleteTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); +} + +Texture::Texture(TextureSetting setting, int w, int h) +{ + m_KeepPixelData = false; + + m_Width = w; + m_Height = h; + m_Type = setting.type; + m_Format = setting.format; + m_WrapMode = setting.wrapMode; + m_FilterMode = setting.filterMode; + m_KeepPixelData = setting.keepImageData; + + WipeGLError(); + + glGenTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, m_GPUID); + + CheckGLError( + glDeleteTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + switch (m_WrapMode) { + case ETextureWrapMode::Clamp: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + break; + case ETextureWrapMode::Repeat: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + case ETextureWrapMode::Mirror: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + break; + default: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + break; + } + + CheckGLError( + glDeleteTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + switch (m_FilterMode) { + case ETextureFilterMode::Linear: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + case ETextureFilterMode::Nearest: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + default: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + } + GLenum internalFormat = GL_RGB; + if (m_Format == ETextureFormat::R8) + { + internalFormat = GL_RED; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, NULL); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + CheckGLError( + glDeleteTextures(1, &m_GPUID); + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +Texture::~Texture() +{ + glDeleteTextures(1, &m_GPUID); +} + +void Texture::UpdateSubImage(Rect rect, int format, int type, const void* data) +{ + glBindTexture(GL_TEXTURE_2D, m_GPUID); + + CheckGLError( + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + int alignment = 4; + if (m_Format == ETextureFormat::R8) + { + alignment = 1; + } + + GLenum fmt = GL_RED; + switch (format) + { + case EPixelFormat::PixelFormat_BGR: fmt = GL_BGR; break; + case EPixelFormat::PixelFormat_BGRA:fmt = GL_BGRA; break; + case EPixelFormat::PixelFormat_R: fmt = GL_RED; break; + case EPixelFormat::PixelFormat_RG: fmt = GL_RG; break; + case EPixelFormat::PixelFormat_RGB: fmt = GL_RGB; break; + case EPixelFormat::PixelFormat_RGBA: fmt = GL_RGBA; break; + } + GLenum t = GL_UNSIGNED_BYTE; + switch (type) + { + case EPixelElementType::PixelType_UNSIGNED_BYTE: t = GL_UNSIGNED_BYTE; break; + case EPixelElementType::PixelType_UNSIGNED_INT: t = GL_UNSIGNED_INT; break; + case EPixelElementType::PixelType_BYTE: t = GL_BYTE; break; + case EPixelElementType::PixelType_INT: t = GL_INT; break; + case EPixelElementType::PixelType_FLOAT: t = GL_FLOAT; break; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width, rect.height, fmt, t, data); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + CheckGLError( + glBindTexture(GL_TEXTURE_2D, 0); + throw TextureException(error); + ); + + glBindTexture(GL_TEXTURE_2D, 0); +}
\ No newline at end of file diff --git a/Client/Source/Graphics/Texture.h b/Client/Source/Graphics/Texture.h new file mode 100644 index 0000000..cf423e1 --- /dev/null +++ b/Client/Source/Graphics/Texture.h @@ -0,0 +1,94 @@ +#pragma once +#include <exception> +#include <string> + +#include "../Math/Math.h" +#include "../Utilities/UtilMacros.h" +#include "../Utilities/Assert.h" + +#include "OpenGL.h" + +class ImageData; + +enum ETextureType +{ + TEX_2D, + TEX_CUBE, +}; + +enum ETextureFormat +{ + RGBA32, + RGB24, + RGB16, + R8, + A8, +}; + +enum ETextureWrapMode +{ + Clamp, + Repeat, + Mirror, +}; + +enum ETextureFilterMode +{ + Nearest, + Linear, +}; + +// GPU侧的导入设置 +struct TextureSetting +{ + bool keepImageData; // 是否保存图片数据 + int type; // 图片类型 + int format; // 内部格式 + int wrapMode; // 包围 + int filterMode; // 滤波 +}; + +class TextureException : public std::exception +{ +public: + TextureException(const char* what) + : std::exception(what) + {} + TextureException(int glError) + { + g_sharedGLErrorMsg = std::to_string(glError); + std::exception(g_sharedGLErrorMsg.c_str()); + } +}; + +class Texture +{ +public: + Texture(TextureSetting setting, int width, int height)/*throw TextureException*/; // 空贴图 + Texture(TextureSetting setting, ImageData* imgData)/*throw TextureException*/; + ~Texture(); + + void UpdateSubImage(Rect rect, int format, int type, const void* data); + + GET(int, Width, m_Width); + GET(int, Height, m_Height); + + GET(GLuint, GpuID, m_GPUID); + +private: + void Init(TextureSetting setting, ImageData* imgData); + + //------------------------------------------------------------------------------ + + GLuint m_GPUID; + + int m_Width, m_Height; + + int m_Type; + int m_Format; + int m_FilterMode; + int m_WrapMode; + + bool m_KeepPixelData; // 是否保存图像像素数据,默认导入后不保存 + +};
\ No newline at end of file diff --git a/Client/Source/Graphics/VertexAttribute.cpp b/Client/Source/Graphics/VertexAttribute.cpp new file mode 100644 index 0000000..e5f2ea7 --- /dev/null +++ b/Client/Source/Graphics/VertexAttribute.cpp @@ -0,0 +1,20 @@ +#include "VertexAttribute.h" + +namespace VertexAttribute +{ + + // map VertexAttrFormat to OpenGL type + static GLenum kGLVertexAttrFormat[VertexAttrFormat_Count] = { + GL_FLOAT, // VertexAttrFormat_Float + GL_HALF_FLOAT, // VertexAttrFormat_Float16 + GL_UNSIGNED_BYTE, // VertexAttrFormat_Color + GL_BYTE, // VertexAttrFormat_Byte + GL_UNSIGNED_BYTE, // VertexAttrFormat_Unsigned_Byte + }; + + GLenum ConvertAttrFormatToGLFormat(uint fmt) + { + return kGLVertexAttrFormat[fmt]; + } + +}
\ No newline at end of file diff --git a/Client/Source/Graphics/VertexAttribute.h b/Client/Source/Graphics/VertexAttribute.h new file mode 100644 index 0000000..8f7bc82 --- /dev/null +++ b/Client/Source/Graphics/VertexAttribute.h @@ -0,0 +1,52 @@ +#pragma once + +#include <vector> + +#include "../Utilities/UtilMacros.h" + +#include "OpenGL.h" +#include "GPUDataBuffer.h" + +// component format +enum VertexAttrFormat +{ + VertexAttrFormat_Float = 0, // position\normal\tangent\uv\uv2\uv3\uv4 + VertexAttrFormat_Float16, + VertexAttrFormat_Color, // color + VertexAttrFormat_Byte, + VertexAttrFormat_Unsigned_Byte, + + VertexAttrFormat_Count +}; + +struct VertexAttributeDescriptor +{ + //union { + const void* pointer; // 内存地址,刚创建时留空 + int startOffset; // 显存中相对VBO的偏移值 + //}; + uint componentNum; // 向量维度1,2,3,4 + uint componentFormat; // 每个分量的类型 + uint16 stride; // 间隔 + bool normalize; // 是否归一化,只用于CustomVertexLayout + + // for default vertex layout + VertexAttributeDescriptor() {} + // for custom vertex layout + VertexAttributeDescriptor(int startOff, uint num, uint fmt, uint16 strd, bool normalized = false) + { + startOffset = startOff; + componentNum = num; + componentFormat = fmt; + stride = strd; + normalize = normalized; + } + +}; + +namespace VertexAttribute +{ + + extern GLenum ConvertAttrFormatToGLFormat(uint fmt); + +} diff --git a/Client/Source/Graphics/VertexBuffer.cpp b/Client/Source/Graphics/VertexBuffer.cpp new file mode 100644 index 0000000..d95bf58 --- /dev/null +++ b/Client/Source/Graphics/VertexBuffer.cpp @@ -0,0 +1,109 @@ +#include "VertexBuffer.h" + +VertexBuffer::VertexBuffer(int vbSize, int ibSize, VertexBufferType type) +{ + m_VB = GPU::ClaimBuffer(vbSize, GL_STATIC_DRAW); + m_IB = GPU::ClaimBuffer(ibSize, GL_STATIC_DRAW); +} + +VertexBuffer::~VertexBuffer() +{ + GPU::ReleaseBuffer(m_VB); + GPU::ReleaseBuffer(m_IB); +} +// GetChunk +// <fill> +// FlushChunk +// DrawChunk +void VertexBuffer::GetChunk(uint sizePerVert, uint sizePerIndex, int maxVerts, int maxIndices, EPrimitive primitive, void **out_vb, void **out_ib) +{ + m_SizePerVertex = sizePerVert; + + uint vbufferSize = sizePerVert * maxVerts; + uint ibufferSize = sizePerIndex * maxIndices; + + GLenum usage = GL_STATIC_DRAW; + m_VB->Restore(vbufferSize, usage); + m_IB->Restore(ibufferSize, usage); + + const GLenum access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + + *out_vb = m_VB->MapRange(0, vbufferSize, access); + *out_ib = m_IB->MapRange(0, ibufferSize, access); + + m_Primitive = primitive; + + WipeGLError(); +} + +void VertexBuffer::FlushChunk(int actualVerts, int actualIndices) +{ + int actualVBufferSize = m_SizePerVertex * actualVerts; + int actualIBufferSize = VertexLayout::GetDefaultIndexSize() * actualIndices; + + m_CurIndexCount = actualIndices; + + m_VB->FlushMapedRange(0, actualVBufferSize); + m_IB->FlushMapedRange(0, actualIBufferSize); + + m_VB->UnMap(); + m_IB->UnMap(); + + WipeGLError(); +} + +void VertexBuffer::Draw(CustomVertexLayout& layout) +{ + const byte* basepointer = 0; + const GLuint buffer = m_VB->GetHandle(); + + FillCustomVertexLayout(layout); + + VertexLayout::SetupCustomVertexLayout(layout); + + layout.RestorePointer(); + + const void* indexPtr = 0; + + CheckGLError( + throw GLException(error); + ); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IB->GetHandle()); + + CheckGLError( + throw GLException(error); + ); + GLenum indexFormat = VertexLayout::GetDefaultIndexFormat(); + + switch (m_Primitive) + { + case Primitive_Triangle: + glDrawElements(GL_TRIANGLES, m_CurIndexCount, indexFormat, indexPtr); + break; + case Primitive_Line: + glDrawElements(GL_LINE, m_CurIndexCount, indexFormat, indexPtr); + break; + case Primitive_Point: + glDrawElements(GL_POINT, m_CurIndexCount, indexFormat, indexPtr); + break; + } + + CheckGLError( + throw GLException(error); + ); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void VertexBuffer::FillCustomVertexLayout(CustomVertexLayout& dst) +{ + const byte* basepointer = 0; + const GLuint buffer = m_VB->GetHandle(); + + dst.buffer = buffer; + + for (int i = 0; i < dst.attributes.size(); ++i) + { + int offset = dst.attributes[i].startOffset; + dst.attributes[i].pointer = basepointer + offset; + } +}
\ No newline at end of file diff --git a/Client/Source/Graphics/VertexBuffer.h b/Client/Source/Graphics/VertexBuffer.h new file mode 100644 index 0000000..b907337 --- /dev/null +++ b/Client/Source/Graphics/VertexBuffer.h @@ -0,0 +1,52 @@ +#pragma once + +#include <vector> + +#include "../Utilities/UtilMacros.h" + +#include "OpenGL.h" +#include "GPUDataBuffer.h" +#include "DefaultVertexLayout.h" +#include "CustomVertexLayout.h" +#include "Primitive.h" + +// 实际使用过程中,通常是一个VBO和一个IBO一起,submesh对应的是IBO中的不同的段,而不是不同的IBO + +class VertexBuffer +{ +public: + enum VertexBufferType + { + VertexBufferType_Static, // device + VertexBufferType_Stream, // pinned (best performance) + VertexBufferType_Dynamic,// device + }; + + VertexBuffer(int vbSize, int ibSize, VertexBufferType type); + ~VertexBuffer(); + + // 填充数据 + void GetChunk(uint sizePerVert, uint sizePerIndex, int maxVerts, int maxIndices, EPrimitive primitive, void **out_vb, void **out_ib); + // 提交数据 + void FlushChunk(int actualVerts, int actualIndices); + + GET(GPU::DataBuffer*, VB, m_VB); + GET(GPU::DataBuffer*, IB, m_IB); + + void Draw(CustomVertexLayout& layout); + +private: + void FillCustomVertexLayout(CustomVertexLayout& dst); + + VertexBufferType m_Type; + + GPU::DataBuffer* m_VB; + GPU::DataBuffer* m_IB; + + int m_SizePerVertex; + + int m_CurIndexCount; + + EPrimitive m_Primitive; + +}; diff --git a/Client/Source/Math/Functions.cpp b/Client/Source/Math/Functions.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Math/Functions.cpp diff --git a/Client/Source/Math/Functions.h b/Client/Source/Math/Functions.h new file mode 100644 index 0000000..3dd17dd --- /dev/null +++ b/Client/Source/Math/Functions.h @@ -0,0 +1,30 @@ +#pragma once + +namespace MathUtils +{ + template <typename T> + inline T Max(T a, T b) + { + return a < b ? b : a; + } + + template <typename T> + inline T Min(T a, T b) + { + return a < b ? a : b; + } + +} + +#define max(a, b)\ +((a)>(b)?(a):(b)) + +#define min(a, b)\ +(a)<(b)?(a):(b) + +#define clamp(v, lo, hi)\ +max((lo), min((v), (hi))) + +// 奇数返回1,否则0 +#define odd(n) \ +(((n)&1)?1:0) diff --git a/Client/Source/Math/Math.h b/Client/Source/Math/Math.h new file mode 100644 index 0000000..0cd7d84 --- /dev/null +++ b/Client/Source/Math/Math.h @@ -0,0 +1,6 @@ +#include "Vector2.hpp" +#include "Matrix22.h" +#include "Matrix44.h" +#include "rect.h" +#include "Functions.h" + diff --git a/Client/Source/Math/Matrix22.h b/Client/Source/Math/Matrix22.h new file mode 100644 index 0000000..c777084 --- /dev/null +++ b/Client/Source/Math/Matrix22.h @@ -0,0 +1,10 @@ +#pragma once + +class Matrix22 +{ +public: + float m[2][2]; // row major + + + +}; diff --git a/Client/Source/Math/Matrix44.h b/Client/Source/Math/Matrix44.h new file mode 100644 index 0000000..562ac54 --- /dev/null +++ b/Client/Source/Math/Matrix44.h @@ -0,0 +1,8 @@ +#pragma once + +class Matrix44 +{ +public: + float m[4][4]; // row major + +}; diff --git a/Client/Source/Math/Rect.h b/Client/Source/Math/Rect.h new file mode 100644 index 0000000..63c6aa1 --- /dev/null +++ b/Client/Source/Math/Rect.h @@ -0,0 +1,13 @@ +#pragma once + +struct Rect +{ + Rect(float x = 0, float y = 0, float w = 0, float h = 0) + { + this->x = x; + this->y = y; + this->width = w; + this->height = h; + } + float x, y, width, height; +}; diff --git a/Client/Source/Math/Vector2.hpp b/Client/Source/Math/Vector2.hpp new file mode 100644 index 0000000..65f2c10 --- /dev/null +++ b/Client/Source/Math/Vector2.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include "../Utilities/Assert.h" +#include "../Math/Functions.h" + +template <typename T> +class Vector2Template +{ +public: + Vector2Template(T x = 0, T y = 0) + { + this->x = x; + this->y = y; + } + inline void Set(T x, T y) + { + this->x = x; + this->y = y; + } + + Vector2Template Clamp(T xmin, T xmax, T ymin, T ymax) + { + Vector2Template v; + v.x = clamp(x, xmin, xmax); + v.y = clamp(y, ymin, ymax); + return v; + } + + T operator[](int i) + { + if (i == 0) return x; + else if (i == 1) return y; + Assert(false); + } + + bool operator == (const Vector2Template& v) const + { + return v.x == x && v.y == y; + } + + bool operator != (const Vector2Template& v) const + { + return v.x != x || v.y != y; + } + + Vector2Template<T> operator - (const Vector2Template& v) const + { + Vector2Template<T> res = Vector2Template<T>(x - v.x, y - v.y); + return res; + } + + + float x, y; + + static Vector2Template<T> zero; + static Vector2Template<T> one ; + +}; + +using Vector2f = Vector2Template<float>; +using Vector2i = Vector2Template<int>; + + +template<typename T> +Vector2Template<T> Vector2Template<T>::zero = Vector2Template(0, 0); +template<typename T> +Vector2Template<T> Vector2Template<T>::one = Vector2Template(1, 1); diff --git a/Client/Source/Math/Vector3.hpp b/Client/Source/Math/Vector3.hpp new file mode 100644 index 0000000..eee913b --- /dev/null +++ b/Client/Source/Math/Vector3.hpp @@ -0,0 +1,32 @@ +#pragma once + +template<typename T> +struct Vector3Template +{ + T x, y, z; + Vector3Template(T x = 0, T y = 0, T z = 0) + { + this->x = x; + this->y = y; + this->z = z; + } + inline void Set(T x, T y, T z) + { + this->x = x; + this->y = y; + this->z = z; + } + + + static Vector3Template zero; + static Vector3Template one; + +}; + +template<typename T> +Vector3Template<T> Vector3Template<T>::zero = Vector3Template(0, 0, 0); +template<typename T> +Vector3Template<T> Vector3Template<T>::one = Vector3Template(1, 1, 1); + +using Vector3f = Vector3Template<float>; + diff --git a/Client/Source/Math/Vector4.hpp b/Client/Source/Math/Vector4.hpp new file mode 100644 index 0000000..8fbf7e6 --- /dev/null +++ b/Client/Source/Math/Vector4.hpp @@ -0,0 +1,33 @@ +#pragma once + +template<typename T> +struct Vector4Template +{ + T x, y, z, w; + + Vector4Template(T x = 0, T y = 0, T z = 0, T w = 0) + { + this->x = x; + this->y = y; + this->z = z; + this->w = w; + } + + inline void Set(T x, T y, T z, T w) + { + this->x = x; + this->y = y; + this->z = z; + this->w = w; + } + + static Vector4Template zero; + static Vector4Template one; +}; + +template<typename T> +Vector4Template<T> Vector4Template<T>::zero = Vector4Template(0, 0, 0, 0); +template<typename T> +Vector4Template<T> Vector4Template<T>::one = Vector4Template(1, 1, 1, 1); + +using Vector4f = Vector4Template<float>;
\ No newline at end of file diff --git a/Client/Source/RagdollMain.cpp b/Client/Source/RagdollMain.cpp new file mode 100644 index 0000000..acf70c8 --- /dev/null +++ b/Client/Source/RagdollMain.cpp @@ -0,0 +1,89 @@ +// python -m glad --out-path=build --api="gl=2.1" --extensions="" --generator="c" +// g++ example/c++/sdl.cpp build/src/glad.c -Ibuild/include -lSDL2 -ldl + +#include <iostream> + +#include <glad/glad.h> +#include <SDL2/SDL.h> + +int main(int argc, char **argv) { + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + std::cerr << "SDL2 video subsystem couldn't be initialized. Error: " + << SDL_GetError() + << std::endl; + exit(1); + } + + SDL_Window* window = SDL_CreateWindow("Glad Sample", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 800, 600, SDL_WINDOW_SHOWN | + SDL_WINDOW_OPENGL); + + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, + SDL_RENDERER_ACCELERATED); + + if (renderer == nullptr) { + std::cerr << "SDL2 Renderer couldn't be created. Error: " + << SDL_GetError() + << std::endl; + exit(1); + } + + // Create a OpenGL context on SDL2 + SDL_GLContext gl_context = SDL_GL_CreateContext(window); + + // Load GL extensions using glad + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { + std::cerr << "Failed to initialize the OpenGL context." << std::endl; + exit(1); + } + + // Loaded OpenGL successfully. + std::cout << "OpenGL version loaded: " << GLVersion.major << "." + << GLVersion.minor << std::endl; + + // Create an event handler + SDL_Event event; + // Loop condition + bool running = true; + + while (running) { + SDL_PollEvent(&event); + + switch (event.type) { + case SDL_QUIT: + running = false; + break; + + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + running = false; + break; + } + } + + glClearColor(0, 0, 0, 1); + + // You'd want to use modern OpenGL here + glColor3d(0, 1, 0); + glBegin(GL_TRIANGLES); + glVertex2f(.2, 0); + glVertex2f(.01, .2); + glVertex2f(-.2, 0); + glEnd(); + + SDL_GL_SwapWindow(window); + } + + // Destroy everything to not leak memory. + SDL_GL_DeleteContext(gl_context); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + + SDL_Quit(); + + return 0; +} diff --git a/Client/Source/Threading/Job.cpp b/Client/Source/Threading/Job.cpp new file mode 100644 index 0000000..3bfdb4d --- /dev/null +++ b/Client/Source/Threading/Job.cpp @@ -0,0 +1,13 @@ +#include "Job.h" +#include "../Utilities/UIDGenerator.h" + +static UIDGenerator s_JobIDGenerator; + +Job::Job() + : m_ID(s_JobIDGenerator.GenUID()) +{ +} + +Job::~Job() +{ +} diff --git a/Client/Source/Threading/Job.h b/Client/Source/Threading/Job.h new file mode 100644 index 0000000..db89feb --- /dev/null +++ b/Client/Source/Threading/Job.h @@ -0,0 +1,17 @@ +#pragma once + +// 任务的抽象基类 +class Job +{ +public: + Job(); + virtual ~Job(); + + virtual void Process() = 0; + virtual bool IsFinished() = 0; + virtual void Dispacth(void* param) = 0; // call in main thread + +protected: + int m_ID; // Job ID + +};
\ No newline at end of file diff --git a/Client/Source/Threading/JobSystem.cpp b/Client/Source/Threading/JobSystem.cpp new file mode 100644 index 0000000..378d74f --- /dev/null +++ b/Client/Source/Threading/JobSystem.cpp @@ -0,0 +1,56 @@ +#include "JobSystem.h" +#include "../Debug/Log.h" + +JobSystem::JobSystem() + : m_Initialized(false) +{ + +} + +JobSystem::~JobSystem() +{ + +} + +void JobSystem::Initilize(int workThreadCount) +{ + if (m_Initialized) + { + log_error("JobSystem has already initialized."); + return; + } + + if (workThreadCount <= 0) + return; + + m_ThreadCount = workThreadCount; + m_Cur = 0; + + for (int i = 0; i < workThreadCount; ++i) + { + WorkThread* thread = new WorkThread(); + thread->Resume(); + m_Threads.push_back(thread); + } + + m_Initialized = true; +} + +void JobSystem::Dispatch(void* param) +{ + for (int i = 0; i < m_Threads.size(); ++i) + { + m_Threads[i]->Dispatch(param); + } +} + +void JobSystem::AddJobAtEnd(Job* job) +{ + WorkThread* thread = SelectThread(); + thread->AddJobAtEnd(job); +} + +WorkThread* JobSystem::SelectThread() +{ + return m_Threads[(++m_Cur)% m_ThreadCount]; +}
\ No newline at end of file diff --git a/Client/Source/Threading/JobSystem.h b/Client/Source/Threading/JobSystem.h new file mode 100644 index 0000000..ded4370 --- /dev/null +++ b/Client/Source/Threading/JobSystem.h @@ -0,0 +1,25 @@ +#pragma once +#include "../Utilities/Singleton.h" +#include "../Threading/Thread.h" +#include <vector> + +class JobSystem : public Singleton<JobSystem> +{ +public: + JobSystem(); + ~JobSystem(); + + void AddJobAtEnd(Job* job); + + void Initilize(int workThreadCount = 1); + void Dispatch(void* param); + +private: + WorkThread* SelectThread(); + + bool m_Initialized; + std::vector<WorkThread*> m_Threads; + int m_Cur; + int m_ThreadCount; + +};
\ No newline at end of file diff --git a/Client/Source/Threading/Mutex.cpp b/Client/Source/Threading/Mutex.cpp new file mode 100644 index 0000000..cda7e89 --- /dev/null +++ b/Client/Source/Threading/Mutex.cpp @@ -0,0 +1,27 @@ +#include "Thread.h" +#include "Mutex.h" + +#include "../Utilities/Type.h" + +Mutex::Mutex() +{ + m_Handle = ::CreateMutex(NULL, FALSE, NULL); + if (!m_Handle) + throw ThreadException("Cant use win32 mutex."); +} + +Mutex::~Mutex() +{ + ::CloseHandle(m_Handle); + m_Handle = NULL; +} + +void Mutex::LockSelf() +{ + ::WaitForSingleObject(m_Handle, (~(uint32)0)); +} + +void Mutex::UnlockSelf() +{ + ::ReleaseMutex(m_Handle); +}
\ No newline at end of file diff --git a/Client/Source/Threading/Mutex.h b/Client/Source/Threading/Mutex.h new file mode 100644 index 0000000..eed69aa --- /dev/null +++ b/Client/Source/Threading/Mutex.h @@ -0,0 +1,39 @@ +#pragma once +#include <windows.h> + +class Mutex +{ +public: + Mutex(); + ~Mutex(); + + void LockSelf(); + void UnlockSelf(); + +private: + HANDLE m_Handle; + +}; + + +class MutexLocker +{ +public: + MutexLocker(Mutex& mutex) + : m(mutex) + { + m.LockSelf(); + }; + ~MutexLocker() + { + m.UnlockSelf(); + } + operator bool() { return false; }; +private: + void* operator new(size_t); + Mutex& m; +}; + +#define Lock(m) \ +if(MutexLocker lock_##m = m){} else + diff --git a/Client/Source/Threading/Semaphore.cpp b/Client/Source/Threading/Semaphore.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Threading/Semaphore.cpp diff --git a/Client/Source/Threading/Semaphore.h b/Client/Source/Threading/Semaphore.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Client/Source/Threading/Semaphore.h diff --git a/Client/Source/Threading/Thread.cpp b/Client/Source/Threading/Thread.cpp new file mode 100644 index 0000000..0bede98 --- /dev/null +++ b/Client/Source/Threading/Thread.cpp @@ -0,0 +1,70 @@ +#include <string> +#include "Thread.h" +#include "../Utilities/Assert.h" +#include "../Utilities/Type.h" + +static std::string s_ThreadErr; + +static DWORD WINAPI ThreadMain(LPVOID param) +{ + Thread* thread = (Thread*)param; + thread->Run(); + return NULL; +} + +Thread::Thread(uint32 stacksize) +{ + m_Handle = ::CreateThread( + NULL + , stacksize + , ThreadMain + , this + , CREATE_SUSPENDED + , NULL); + if (m_Handle == 0) + { + s_ThreadErr = "Create Thread Failed. ErrorCode=" + std::to_string(GetLastError()); + throw ThreadException(s_ThreadErr.c_str()); + } +} + +Thread::~Thread() +{ + CloseHandle(m_Handle); +} + +void Thread::Resume() +{ + ::ResumeThread(m_Handle); +} + +bool Thread::IsRunning() +{ + if (m_Handle) { + DWORD exitCode = 0; + // https://blog.csdn.net/yuanmeng567/article/details/19485719 + ::GetExitCodeThread(m_Handle, &exitCode); + return exitCode == STILL_ACTIVE; + } + return false; +} + +void Thread::Join() +{ + ::WaitForSingleObject(m_Handle, INFINITE); +} + +void Thread::Kill() +{ + ::TerminateThread(m_Handle, FALSE); +} + +void Thread::Sleep(uint ms) +{ + ::Sleep(ms); +} + +bool Thread::IsCurrent() +{ + return m_Handle == ::GetCurrentThread(); +} diff --git a/Client/Source/Threading/Thread.h b/Client/Source/Threading/Thread.h new file mode 100644 index 0000000..77d8dff --- /dev/null +++ b/Client/Source/Threading/Thread.h @@ -0,0 +1,71 @@ +#pragma once +#include <windows.h> +#include <vector> +#include <exception> + +#include "../Utilities/Type.h" + +#include "Job.h" +#include "Mutex.h" + +class ThreadException : public std::exception +{ +public: + ThreadException(const char* what) + : std::exception(what) + { + } +}; + +class Thread +{ +public: + Thread(uint32 stacksize = 0)/*throw ThreadExeception*/; + virtual ~Thread(); + + virtual void Run() = 0; + + virtual void Resume(); + virtual void Join() ; + virtual void Kill() ; + + virtual void Sleep(uint ms) ; + + virtual bool IsRunning() ; + virtual bool IsCurrent() ; + +protected: + HANDLE m_Handle; + +}; + +// 任务系统的线程 +class WorkThread : public Thread +{ +public: + void Run() override; + void Dispatch(void* param); // call in main thread + + void AddJobAtEnd(Job* job); + +private: + Mutex m_PendingMutex; + Mutex m_FinishedMutex; + + std::vector<Job*> m_PendingJobs; + std::vector<Job*> m_FinishedJobs; + +}; + +// 执行小段代码的线程 +class CodePieceThread : public Thread +{ +public: + typedef void(*CodePiece)(); + + void Run() override; + +private: + std::vector<CodePiece> m_CodePieces; + +};
\ No newline at end of file diff --git a/Client/Source/Threading/WorkThread.cpp b/Client/Source/Threading/WorkThread.cpp new file mode 100644 index 0000000..4ea6262 --- /dev/null +++ b/Client/Source/Threading/WorkThread.cpp @@ -0,0 +1,50 @@ +#include "Thread.h" + +#include "../Debug/Log.h" + +void WorkThread::Run() +{ + while (true) + { + Lock(m_PendingMutex) + { + for (auto iter = m_PendingJobs.begin(); iter != m_PendingJobs.end();) + { + Job* job = *iter; + job->Process(); + if (job->IsFinished()) + { + Lock(m_FinishedMutex) { + m_FinishedJobs.push_back(job); + } + iter = m_PendingJobs.erase(iter); + continue; + } + ++iter; + } + if (m_PendingJobs.size() == 0) + ::Sleep(100); + } + } +} + +void WorkThread::Dispatch(void* param) +{ + Lock(m_FinishedMutex) + { + for (int i = 0; i < m_FinishedJobs.size(); ++i) + { + m_FinishedJobs[i]->Dispacth(param); + delete m_FinishedJobs[i]; + } + m_FinishedJobs.clear(); + } +} + +void WorkThread::AddJobAtEnd(Job* job) +{ + Lock(m_PendingMutex) + { + m_PendingJobs.push_back(job); + } +}
\ No newline at end of file diff --git a/Client/Source/Utilities/Assert.h b/Client/Source/Utilities/Assert.h new file mode 100644 index 0000000..526ca32 --- /dev/null +++ b/Client/Source/Utilities/Assert.h @@ -0,0 +1,11 @@ +#ifndef ASSERT_H +#define ASSERT_H + +#include <assert.h> + +#define Assert(c) assert((c)) + +#define DebugAssertIf(c) assert(c) +#define AssertIf(c) assert(c) + +#endif
\ No newline at end of file diff --git a/Client/Source/Utilities/AutoInvoke.h b/Client/Source/Utilities/AutoInvoke.h new file mode 100644 index 0000000..7d9bb83 --- /dev/null +++ b/Client/Source/Utilities/AutoInvoke.h @@ -0,0 +1,27 @@ +#pragma once + +typedef void(*AutoInvokeAction)(); + +// RAII auto call +class AutoInvokerWhenLeave +{ +public: + AutoInvokerWhenLeave(AutoInvokeAction func) + { + m_Func = func; + }; + ~AutoInvokerWhenLeave() + { + if (m_Func) + { + m_Func(); + } + } +private: + AutoInvokeAction m_Func; + +}; + +#define InvokeWhenLeave(func) \ +AutoInvokerWhenLeave auto_invoker = AutoInvokerWhenLeave(func); + diff --git a/Client/Source/Utilities/Exception.h b/Client/Source/Utilities/Exception.h new file mode 100644 index 0000000..3795bc6 --- /dev/null +++ b/Client/Source/Utilities/Exception.h @@ -0,0 +1,12 @@ +#pragma once +#include <exception> + +#define CustomException(Name) \ +class Name : public std::exception \ +{ \ +public: \ + Name(const char* what) \ + : std::exception(what) \ + { \ + } \ +} diff --git a/Client/Source/Utilities/Singleton.h b/Client/Source/Utilities/Singleton.h new file mode 100644 index 0000000..f864a42 --- /dev/null +++ b/Client/Source/Utilities/Singleton.h @@ -0,0 +1,43 @@ +#ifndef SINGLETON_H +#define SINGLETON_H + +template<class T> +class Singleton +{ +public: + + static T* Instance() + { + if (!instance) instance = new T; + return instance; + } + + static void Destroy() + { + delete instance; + instance = nullptr; + } + +protected: + + Singleton() + { + instance = static_cast<T*>(this); + }; + + virtual ~Singleton() {}; + + static T* instance; + +private: + + Singleton(const Singleton& singleton); + + Singleton& operator = (const Singleton& singleton); + +}; + +template<class T> +T* Singleton<T>::instance = nullptr; + +#endif
\ No newline at end of file diff --git a/Client/Source/Utilities/StaticInitiator.h b/Client/Source/Utilities/StaticInitiator.h new file mode 100644 index 0000000..af264af --- /dev/null +++ b/Client/Source/Utilities/StaticInitiator.h @@ -0,0 +1,30 @@ +#pragma once + +//https://stackoverflow.com/questions/1005685/c-static-initialization-order +//https://stackoverflow.com/questions/211237/static-variables-initialisation-order + +// 静态构造函数 +#include "StaticConstructor/include/StaticConstructor.h" + +typedef void(*StaticFunc) (); + +// 自动初始化静态数据 +class StaticFuncInvoker +{ +public: + // Default Constructor: + StaticFuncInvoker(StaticFunc func) + { + if (func) + func(); + } +}; + +// 调用具名的static函数 +#define InvokeStaticFunc(func)\ + static StaticFuncInvoker staticInvokerOf_##func(func); + +// 用来初始化当前cpp里的静态变量,需要注意static变量初始化顺序(Static Initialization Order) +#define InitializeStaticVariables(lambda)\ + static StaticFuncInvoker staticInvoker(lambda); + diff --git a/Client/Source/Utilities/Type.h b/Client/Source/Utilities/Type.h new file mode 100644 index 0000000..da6208d --- /dev/null +++ b/Client/Source/Utilities/Type.h @@ -0,0 +1,25 @@ +#ifndef TYPE_H +#define TYPE_H + +#include <cstdlib> +#include <stdint.h> + +typedef int8_t int8; +typedef uint8_t uint8; +typedef unsigned char byte; +typedef char sbyte; +typedef int16_t int16; +typedef uint16_t uint16; +typedef uint16_t UInt16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef uint32_t UInt32; +typedef int64_t int64; +typedef uint64_t uint64; +typedef uint64_t UInt64; + +typedef uint32_t uint; +typedef int32_t sint; +typedef int32_t SInt32; + +#endif
\ No newline at end of file diff --git a/Client/Source/Utilities/UIDGenerator.h b/Client/Source/Utilities/UIDGenerator.h new file mode 100644 index 0000000..81918f0 --- /dev/null +++ b/Client/Source/Utilities/UIDGenerator.h @@ -0,0 +1,20 @@ +#ifndef UID_GENERATOR_H +#define UID_GENERATOR_H + +class UIDGenerator +{ +public: + UIDGenerator(int from = 0) + : m_Index(from) + { + }; + ~UIDGenerator(){}; + + int GenUID(){return m_Index++;}; + +private: + int m_Index; + +}; + +#endif
\ No newline at end of file diff --git a/Client/Source/Utilities/UtilMacros.h b/Client/Source/Utilities/UtilMacros.h new file mode 100644 index 0000000..29bb7b0 --- /dev/null +++ b/Client/Source/Utilities/UtilMacros.h @@ -0,0 +1,19 @@ +#ifndef UTIL_MACROS_H +#define UTIL_MACROS_H + +#define GET_SET(TYPE,PROP,VAR) \ + inline void Set##PROP (TYPE val) { VAR = val; } \ + inline TYPE Get##PROP () {return VAR; } + +#define GET(TYPE, PROP, VAR) \ + inline TYPE Get##PROP () {return VAR; } + +#define SET(TYPE, PROP, VAR) \ + inline void Set##PROP (TYPE val) { VAR = val; } \ + +#define Mask(v) (1 << v) + +#define cast(T, v) \ +((T)(v)) + +#endif
\ No newline at end of file |