summaryrefslogtreecommitdiff
path: root/Client/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Client/Source')
-rw-r--r--Client/Source/Common/DataBuffer.cpp0
-rw-r--r--Client/Source/Common/DataBuffer.h29
-rw-r--r--Client/Source/Debug/Log.cpp164
-rw-r--r--Client/Source/Debug/Log.h14
-rw-r--r--Client/Source/GUI/Font.cpp302
-rw-r--r--Client/Source/GUI/Font.h149
-rw-r--r--Client/Source/GUI/TextMeshGenerator.cpp89
-rw-r--r--Client/Source/GUI/TextMeshGenerator.h31
-rw-r--r--Client/Source/GUI/UI9Slicing.cpp112
-rw-r--r--Client/Source/GUI/UI9Slicing.h25
-rw-r--r--Client/Source/GUI/UILine.cpp48
-rw-r--r--Client/Source/GUI/UILine.h21
-rw-r--r--Client/Source/GUI/UIMesh.cpp15
-rw-r--r--Client/Source/GUI/UIMesh.h42
-rw-r--r--Client/Source/GUI/UIQuad.cpp64
-rw-r--r--Client/Source/GUI/UIQuad.h21
-rw-r--r--Client/Source/GUI/UISquare.cpp53
-rw-r--r--Client/Source/GUI/UISquare.h22
-rw-r--r--Client/Source/GUI/UITextMesh.cpp278
-rw-r--r--Client/Source/GUI/UITextMesh.h69
-rw-r--r--Client/Source/GUI/freetype.h4
-rw-r--r--Client/Source/GUI/utf8.cpp415
-rw-r--r--Client/Source/GUI/utf8.h113
-rw-r--r--Client/Source/Graphics/Color.cpp4
-rw-r--r--Client/Source/Graphics/Color.h48
-rw-r--r--Client/Source/Graphics/CustomVertexLayout.cpp23
-rw-r--r--Client/Source/Graphics/CustomVertexLayout.h33
-rw-r--r--Client/Source/Graphics/DefaultVertexLayout.cpp119
-rw-r--r--Client/Source/Graphics/DefaultVertexLayout.h55
-rw-r--r--Client/Source/Graphics/DeviceDefine.h69
-rw-r--r--Client/Source/Graphics/DynamicMesh.h13
-rw-r--r--Client/Source/Graphics/DynamicVertexBuffer.cpp260
-rw-r--r--Client/Source/Graphics/DynamicVertexBuffer.h54
-rw-r--r--Client/Source/Graphics/FrameBuffer.cpp12
-rw-r--r--Client/Source/Graphics/FrameBuffer.h35
-rw-r--r--Client/Source/Graphics/GPUDataBuffer.cpp207
-rw-r--r--Client/Source/Graphics/GPUDataBuffer.h75
-rw-r--r--Client/Source/Graphics/GfxDevice.cpp169
-rw-r--r--Client/Source/Graphics/GfxDevice.h109
-rw-r--r--Client/Source/Graphics/GlyphAtlas.cpp0
-rw-r--r--Client/Source/Graphics/GlyphAtlas.h6
-rw-r--r--Client/Source/Graphics/ImageData.cpp2
-rw-r--r--Client/Source/Graphics/ImageData.h39
-rw-r--r--Client/Source/Graphics/OpenGL.cpp7
-rw-r--r--Client/Source/Graphics/OpenGL.h41
-rw-r--r--Client/Source/Graphics/Point.cpp0
-rw-r--r--Client/Source/Graphics/Point.h0
-rw-r--r--Client/Source/Graphics/PolyLine.cpp9
-rw-r--r--Client/Source/Graphics/PolyLine.h18
-rw-r--r--Client/Source/Graphics/Primitive.h8
-rw-r--r--Client/Source/Graphics/Quad.cpp62
-rw-r--r--Client/Source/Graphics/Quad.h26
-rw-r--r--Client/Source/Graphics/RenderCommands.cpp9
-rw-r--r--Client/Source/Graphics/RenderCommands.h164
-rw-r--r--Client/Source/Graphics/RenderTexture.cpp0
-rw-r--r--Client/Source/Graphics/RenderTexture.h16
-rw-r--r--Client/Source/Graphics/Shader.cpp154
-rw-r--r--Client/Source/Graphics/Shader.h47
-rw-r--r--Client/Source/Graphics/ShaderCompiler.cpp347
-rw-r--r--Client/Source/Graphics/ShaderCompiler.h55
-rw-r--r--Client/Source/Graphics/Texture.cpp230
-rw-r--r--Client/Source/Graphics/Texture.h94
-rw-r--r--Client/Source/Graphics/VertexAttribute.cpp20
-rw-r--r--Client/Source/Graphics/VertexAttribute.h52
-rw-r--r--Client/Source/Graphics/VertexBuffer.cpp109
-rw-r--r--Client/Source/Graphics/VertexBuffer.h52
-rw-r--r--Client/Source/Math/Functions.cpp0
-rw-r--r--Client/Source/Math/Functions.h30
-rw-r--r--Client/Source/Math/Math.h6
-rw-r--r--Client/Source/Math/Matrix22.h10
-rw-r--r--Client/Source/Math/Matrix44.h8
-rw-r--r--Client/Source/Math/Rect.h13
-rw-r--r--Client/Source/Math/Vector2.hpp67
-rw-r--r--Client/Source/Math/Vector3.hpp32
-rw-r--r--Client/Source/Math/Vector4.hpp33
-rw-r--r--Client/Source/RagdollMain.cpp89
-rw-r--r--Client/Source/Threading/Job.cpp13
-rw-r--r--Client/Source/Threading/Job.h17
-rw-r--r--Client/Source/Threading/JobSystem.cpp56
-rw-r--r--Client/Source/Threading/JobSystem.h25
-rw-r--r--Client/Source/Threading/Mutex.cpp27
-rw-r--r--Client/Source/Threading/Mutex.h39
-rw-r--r--Client/Source/Threading/Semaphore.cpp0
-rw-r--r--Client/Source/Threading/Semaphore.h0
-rw-r--r--Client/Source/Threading/Thread.cpp70
-rw-r--r--Client/Source/Threading/Thread.h71
-rw-r--r--Client/Source/Threading/WorkThread.cpp50
-rw-r--r--Client/Source/Utilities/Assert.h11
-rw-r--r--Client/Source/Utilities/AutoInvoke.h27
-rw-r--r--Client/Source/Utilities/Exception.h12
-rw-r--r--Client/Source/Utilities/Singleton.h43
-rw-r--r--Client/Source/Utilities/StaticInitiator.h30
-rw-r--r--Client/Source/Utilities/Type.h25
-rw-r--r--Client/Source/Utilities/UIDGenerator.h20
-rw-r--r--Client/Source/Utilities/UtilMacros.h19
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