diff --git a/.gitmodules b/.gitmodules index f4c6a38..0c391ab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,3 +26,9 @@ [submodule "Hazel/vendor/box2d"] path = Hazel/vendor/box2d url = https://github.com/erincatto/box2d.git +[submodule "Hazel/vendor/msdf-atlas-gen"] + path = Hazel/vendor/msdf-atlas-gen + url = https://github.com/Chlumsky/msdf-atlas-gen.git +[submodule "Hazel/vendor/freetype"] + path = Hazel/vendor/freetype + url = https://github.com/freetype/freetype.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 49baba7..97ea569 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ endif () add_subdirectory(Hazel) add_subdirectory(Editor) +add_subdirectory(Sandbox) add_subdirectory(Hazel-ScriptCore) add_subdirectory(Editor/SandboxProject) diff --git a/Editor/assets/shaders/Renderer2D_text.glsl b/Editor/assets/shaders/Renderer2D_text.glsl new file mode 100644 index 0000000..24c3e1e --- /dev/null +++ b/Editor/assets/shaders/Renderer2D_text.glsl @@ -0,0 +1,69 @@ +// Basic MSDF Text Texture Shader + +#type vertex +#version 450 core + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec4 a_Color; +layout(location = 2) in vec2 a_TexCoord; +layout(location = 3) in int a_EntityID; + +layout(std140, binding = 0) uniform Camera +{ + mat4 u_ViewProjection; +}; + +layout (location = 0) out vec4 o_Color; +layout (location = 1) out vec2 o_TexCoord; +layout (location = 2) out flat int v_EntityID; + +void main() +{ + o_Color = a_Color; + o_TexCoord = a_TexCoord; + v_EntityID = a_EntityID; + + gl_Position = u_ViewProjection * vec4(a_Position, 1.0); +} + +#type fragment +#version 450 core + +layout(location = 0) out vec4 o_Color; +layout(location = 1) out int o_EntityID; + +layout (location = 0) in vec4 i_Color; +layout (location = 1) in vec2 i_TexCoord; +layout (location = 2) in flat int v_EntityID; + +layout (binding = 0) uniform sampler2D u_FontAtlas; + +float screenPxRange() { + const float pxRange = 2.0; // set to distance field's pixel range + vec2 unitRange = vec2(pxRange)/vec2(textureSize(u_FontAtlas, 0)); + vec2 screenTexSize = vec2(1.0)/fwidth(i_TexCoord); // 使用 i_TexCoord + return max(0.5*dot(unitRange, screenTexSize), 1.0); +} + +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +void main() +{ + vec4 texColor = i_Color * texture(u_FontAtlas, i_TexCoord); // 使用 i_Color 和 i_TexCoord + + vec3 msd = texture(u_FontAtlas, i_TexCoord).rgb; // 使用 i_TexCoord + float sd = median(msd.r, msd.g, msd.b); + float screenPxDistance = screenPxRange()*(sd - 0.5); + float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); + if (opacity == 0.0) + discard; + + vec4 bgColor = vec4(0.0); + o_Color = mix(bgColor, i_Color, opacity); // 使用 i_Color + if (o_Color.a == 0.0) + discard; + + o_EntityID = v_EntityID; +} \ No newline at end of file diff --git a/Editor/src/Editor/EditorLayer.cpp b/Editor/src/Editor/EditorLayer.cpp index 9e65cd1..ae0a73e 100644 --- a/Editor/src/Editor/EditorLayer.cpp +++ b/Editor/src/Editor/EditorLayer.cpp @@ -15,16 +15,18 @@ #include "imgui_internal.h" #include "Hazel/Project/Project.h" +#include "Hazel/Renderer/Font.h" #include "Hazel/Scripting/ScriptEngine.h" namespace Hazel { + static Ref s_Font; EditorLayer::EditorLayer() : Layer("HazelEditor"), m_CameraController((float)Application::Get().GetWindow().GetWidth() / (float)Application::Get().GetWindow().GetHeight()) { - + s_Font = Font::GetDefault(); } void EditorLayer::OnAttach() diff --git a/Hazel/CMakeLists.txt b/Hazel/CMakeLists.txt index 56e72fc..8f61cf3 100644 --- a/Hazel/CMakeLists.txt +++ b/Hazel/CMakeLists.txt @@ -8,6 +8,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) +# set +set(VCPKG_TARGET_TRIPLET "x64-windows") + add_subdirectory(vendor/spdlog EXCLUDE_FROM_ALL) add_subdirectory(vendor/GLAD EXCLUDE_FROM_ALL) add_subdirectory(vendor/glm EXCLUDE_FROM_ALL) @@ -16,6 +19,7 @@ add_subdirectory(vendor/shaderc EXCLUDE_FROM_ALL) add_subdirectory(vendor/SPIRV-Cross EXCLUDE_FROM_ALL) add_subdirectory(vendor/box2d EXCLUDE_FROM_ALL) add_subdirectory(vendor/SDL EXCLUDE_FROM_ALL) +add_subdirectory(vendor/msdf-atlas-gen) file(GLOB_RECURSE SOURCES "src/**.cpp") @@ -88,6 +92,7 @@ target_link_libraries(${PROJECT_NAME}-static PUBLIC ws2_32 bcrypt assimp::assimp + msdf-atlas-gen ) @@ -121,10 +126,19 @@ target_link_libraries(${PROJECT_NAME}-shared PUBLIC ws2_32 bcrypt assimp::assimp - + msdf-atlas-gen ) + +if(WIN32) + target_compile_definitions(${PROJECT_NAME}-static PUBLIC HZ_PLATFORM_WINDOWS) + target_compile_definitions(${PROJECT_NAME}-shared PUBLIC HZ_PLATFORM_WINDOWS) + set(CMAKE_SHARED_LIBRARY_PREFIX "") +endif () + target_compile_definitions(${PROJECT_NAME}-static PRIVATE HZ_BUILD_STATIC) -target_compile_definitions(${PROJECT_NAME}-shared PRIVATE HZ_BUILD_DLL IMGUI_API=__declspec\(dllexport\))# 编译DLL时定义 +target_compile_definitions(${PROJECT_NAME}-static INTERFACE HZ_BUILD_STATIC) +target_compile_definitions(${PROJECT_NAME}-shared PRIVATE HZ_BUILD_SHARED HZ_BUILD_DLL IMGUI_API=__declspec\(dllexport\))# 编译DLL时定义 + set_target_properties(${PROJECT_NAME}-static PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-static) set_target_properties(${PROJECT_NAME}-shared PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) @@ -133,11 +147,6 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(${PROJECT_NAME}-static PUBLIC HZ_DEBUG) target_compile_definitions(${PROJECT_NAME}-shared PUBLIC HZ_DEBUG) endif () -if(WIN32) - target_compile_definitions(${PROJECT_NAME}-static PUBLIC HZ_PLATFORM_WINDOWS) - target_compile_definitions(${PROJECT_NAME}-shared PUBLIC HZ_PLATFORM_WINDOWS) - set(CMAKE_SHARED_LIBRARY_PREFIX "") -endif () # ------------------------------------------- diff --git a/Hazel/src/Hazel/Core/Core.h b/Hazel/src/Hazel/Core/Core.h index cc264f3..c271501 100644 --- a/Hazel/src/Hazel/Core/Core.h +++ b/Hazel/src/Hazel/Core/Core.h @@ -1,15 +1,25 @@ #pragma once #ifdef HZ_PLATFORM_WINDOWS + #if defined(HZ_BUILD_STATIC) + #define HAZEL_API + #elif defined(HZ_BUILD_DLL) + #define HAZEL_API __declspec(dllexport) + #else + #define HAZEL_API __declspec(dllimport) + #endif + /* #ifdef HZ_BUILD_DLL #define HAZEL_API __declspec(dllexport) #else #define HAZEL_API __declspec(dllimport) #endif - #ifdef HZ_HAZEL_STATIC + #ifdef HZ_BUILD_STATIC #define HAZEL_API #endif + */ + #else #define HAZEL_API // #error Hazel only support Windows! diff --git a/Hazel/src/Hazel/Core/Input.h b/Hazel/src/Hazel/Core/Input.h index 271196f..b5504e8 100644 --- a/Hazel/src/Hazel/Core/Input.h +++ b/Hazel/src/Hazel/Core/Input.h @@ -19,6 +19,7 @@ namespace Hazel static bool IsMouseButtonPressed(MouseButton button); static std::pair GetMousePosition(); + static uint32_t GetMouseState(float* x = nullptr, float* y = nullptr); static float GetMouseX(); static float GetMouseY(); diff --git a/Hazel/src/Hazel/Renderer/Font.cpp b/Hazel/src/Hazel/Renderer/Font.cpp new file mode 100644 index 0000000..607d151 --- /dev/null +++ b/Hazel/src/Hazel/Renderer/Font.cpp @@ -0,0 +1,158 @@ +// +// Created by sfd on 25-10-30. +// + +#include "Font.h" + +#include "Hazel/Core/assert.h" +#include "MSDFData.h" + + +namespace Hazel +{ + + template GenFun> + static Ref CreateAndCacheAtlas(const std::string& fontName, float fontSize, const std::vector& glyphs, const msdf_atlas::FontGeometry& font, uint32_t width, uint32_t height) + { + msdf_atlas::GeneratorAttributes attributes; + attributes.config.overlapSupport = true; + attributes.scanlinePass = true; + + msdf_atlas::ImmediateAtlasGenerator> generator(width, height); + generator.setAttributes(attributes); + generator.setThreadCount(8); + generator.generate(glyphs.data(), (int)glyphs.size()); + + msdfgen::BitmapConstRef bitmap = (msdfgen::BitmapConstRef)generator.atlasStorage(); + + TextureSpecification spec; + spec.Width = width; + spec.Height = height; + spec.Format = ImageFormat::RGB8; + spec.GenerateMips = false; + + Ref texture = Texture2D::Create(spec); + texture->SetData((void*)bitmap.pixels, bitmap.width * bitmap.height * 3); + return texture; + } + + Font::Font(const std::filesystem::path& filePath) + : m_Data(new MSDFData()) + { + msdfgen::FreetypeHandle* ft = msdfgen::initializeFreetype(); + HZ_CORE_ASSERT(ft); + + std::string fileString = filePath.string(); + msdfgen::FontHandle* font = msdfgen::loadFont(ft, fileString.c_str()); + + if (!font) + { + HZ_CORE_ASSERT("failed to load font: {}", filePath.string()); + return; + } + + struct CharsetRange + { + uint32_t Begin, End; + }; + + // from imgui_draw.cpp + struct CharsetRange charsetranges[] = { + {0x0020, 0x00FF} + }; + + msdf_atlas::Charset charset; + for (CharsetRange& range : charsetranges) + { + for (uint32_t c = range.Begin; c < range.End; ++c) + { + charset.add(c); + } + } + + double fontScale = 1.0f; + m_Data->FontGeometry = msdf_atlas::FontGeometry(&m_Data->Glyphs); + int gyphsloaded = m_Data->FontGeometry.loadCharset(font, fontScale, charset); + HZ_CORE_INFO("loaded {0} glyhps from font (out of {1})", gyphsloaded, charset.size()); + + double emSize = 40.f; + + msdf_atlas::TightAtlasPacker atlasPacker; + + atlasPacker.setPixelRange(2.0f); + atlasPacker.setMiterLimit(1.0f); + atlasPacker.setPadding(0); + atlasPacker.setScale(emSize); + int remaining = atlasPacker.pack(m_Data->Glyphs.data(), (int)m_Data->Glyphs.size()); + HZ_CORE_ASSERT(remaining == 0); + + int width, height; + atlasPacker.getDimensions(width, height); + emSize = atlasPacker.getScale(); + + // TODO: reading this +#define DEFAULT_ANGLE_THRESHOLD 3.0 +#define LCG_MULTIPLIER 6364136223846793005ull +#define LCG_INCREMENT 1442695040888963407ull +#define THREAD_COUNT 8 + + // if MSTF || MTSDF + + uint64_t coloringSeed = 0; + bool expensiveColoring = false; + + if (expensiveColoring) + { + msdf_atlas::Workload([&glyphs = m_Data->Glyphs, &coloringSeed] (int i , int threadNo) -> bool + { + unsigned long long glyphSeed = (LCG_MULTIPLIER * (coloringSeed ^ i) + LCG_INCREMENT) * !!coloringSeed; + glyphs[i].edgeColoring(msdfgen::edgeColoringInkTrap, DEFAULT_ANGLE_THRESHOLD, glyphSeed); + return true; + }, m_Data->Glyphs.size()).finish(THREAD_COUNT); + }else + { + unsigned long long glyphSeed = coloringSeed; + + for (auto& glyph : m_Data->Glyphs) + { + glyphSeed *= LCG_MULTIPLIER; + glyph.edgeColoring(msdfgen::edgeColoringInkTrap, DEFAULT_ANGLE_THRESHOLD, glyphSeed); + } + } + + + m_AtlasTexture = CreateAndCacheAtlas("Test", (int)emSize, m_Data->Glyphs, m_Data->FontGeometry, width, height); + +#if 0 + msdfgen::Shape shape; + if (msdfgen::loadGlyph(shape, font, 'C')) + { + shape.normalize(); + + // max. angle + msdfgen::edgeColoringSimple(shape, 3.0f); + msdfgen::Bitmap msdf(32, 32); + + msdfgen::generateMSDF(msdf, shape, 4.0f, 1.0f, msdfgen::Vector2(4.0f, 4.0f)); + + msdfgen::savePng(msdf, "output.png"); + } +#endif + + msdfgen::destroyFont(font); + msdfgen::deinitializeFreetype(ft); + } + + Font::~Font() + { + delete m_Data; + } + + Ref Font::GetDefault() + { + static Ref defaultFont; + if (!defaultFont) + defaultFont = CreateRef("assets/fonts/OpenSans/OpenSans-Bold.ttf"); + return defaultFont; + } +} diff --git a/Hazel/src/Hazel/Renderer/Font.h b/Hazel/src/Hazel/Renderer/Font.h new file mode 100644 index 0000000..596c703 --- /dev/null +++ b/Hazel/src/Hazel/Renderer/Font.h @@ -0,0 +1,35 @@ +// +// Created by sfd on 25-10-30. +// + +#ifndef FONT_H +#define FONT_H + +#include + +#include "Texture.h" +#include "Hazel/Core/Core.h" + + +namespace Hazel +{ + + struct MSDFData; + + class HAZEL_API Font { + public: + Font(const std::filesystem::path& filePath); + ~Font(); + const MSDFData* GetMSDFData() const { return m_Data; } + Ref GetAtlasTexture() const { return m_AtlasTexture; } + static Ref GetDefault(); + + private: + MSDFData* m_Data; + Ref m_AtlasTexture; + }; + +} + + +#endif //FONT_H diff --git a/Hazel/src/Hazel/Renderer/MSDFData.h b/Hazel/src/Hazel/Renderer/MSDFData.h new file mode 100644 index 0000000..cc9fce6 --- /dev/null +++ b/Hazel/src/Hazel/Renderer/MSDFData.h @@ -0,0 +1,20 @@ +// +// Created by sfd on 25-10-30. +// + +#ifndef MSDFDATA_H +#define MSDFDATA_H +#include + +#include "msdf-atlas-gen.h" + +namespace Hazel +{ + struct MSDFData + { + std::vector Glyphs; + msdf_atlas::FontGeometry FontGeometry; + }; +} + +#endif //MSDFDATA_H diff --git a/Hazel/src/Hazel/Renderer/Renderer2D.cpp b/Hazel/src/Hazel/Renderer/Renderer2D.cpp index 8469362..e54d24e 100644 --- a/Hazel/src/Hazel/Renderer/Renderer2D.cpp +++ b/Hazel/src/Hazel/Renderer/Renderer2D.cpp @@ -7,6 +7,8 @@ #include #include #include + +#include "MSDFData.h" #include "RendererCommand.h" #include "Shader.h" #include "UniformBuffer.h" @@ -51,6 +53,15 @@ namespace Hazel int EntityID; }; + struct TextVertex + { + glm::vec3 Position; + glm::vec4 Color; + glm::vec2 TexCoord; + + int EntityID; + }; + struct Renderer2DStorage { const uint32_t MaxQuad = 10000; @@ -71,6 +82,10 @@ namespace Hazel Ref LineVertexBuffer; Ref LineShader; + Ref TextVertexArray; + Ref TextVertexBuffer; + Ref TextShader; + uint32_t QuadIndexCount = 0; QuadVertex* QuadVertexBufferBase = nullptr; @@ -84,11 +99,17 @@ namespace Hazel LineVertex* LineVertexBufferBase = nullptr; LineVertex* LineVertexBufferPtr = nullptr; + uint32_t TextIndexCount = 0; + TextVertex* TextVertexBufferBase = nullptr; + TextVertex* TextVertexBufferPtr = nullptr; + float LineWidth = 2.0f; std::array, MaxTextureSlots> TextureSlots; uint32_t TextureSlotIndex = 1; // 0 use white texture + Ref FontAtlasTexture; + glm::vec4 QuadVertexPosition[4]; Renderer2D::Statistic Stats; @@ -183,7 +204,25 @@ namespace Hazel s_Data.LineVertexBufferBase = new LineVertex[s_Data.MaxVertices]; /////////////////////////////////////// - s_Data.WhiteTexture = Texture2D::Create(1, 1); + + /////////////// text ////////////////// + s_Data.TextVertexArray = VertexArray::Create(); + s_Data.TextVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(TextVertex)); + s_Data.TextVertexBuffer->SetLayout({ + {ShaderDataType::Float3, "a_Position"}, + {ShaderDataType::Float4, "a_Color"}, + {ShaderDataType::Float2, "a_TexCoord"}, + {ShaderDataType::Int, "a_EntityID"} + }); + + s_Data.TextVertexArray->AddVertexBuffer(s_Data.TextVertexBuffer); + s_Data.TextVertexArray->SetIndexBuffer(quadIB); + s_Data.TextVertexBufferBase = new TextVertex[s_Data.MaxVertices]; + + /////////////////////////////////////// + + + s_Data.WhiteTexture = Texture2D::Create(TextureSpecification()); uint32_t whiteTextureData = 0xffffffff; s_Data.WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t)); @@ -197,6 +236,7 @@ namespace Hazel s_Data.QuadShader = Shader::Create("assets/shaders/Renderer2D_quad.glsl"); s_Data.CircleShader = Shader::Create("assets/shaders/Renderer2D_circle.glsl"); s_Data.LineShader = Shader::Create("assets/shaders/Renderer2D_line.glsl"); + s_Data.TextShader = Shader::Create("assets/shaders/Renderer2D_text.glsl"); // set all texture slot to 0 s_Data.TextureSlots[0] = s_Data.WhiteTexture; @@ -284,7 +324,6 @@ namespace Hazel { const uint32_t datasize = reinterpret_cast(s_Data.CircleVertexBufferPtr) - reinterpret_cast(s_Data.CircleVertexBufferBase); s_Data.CircleVertexBuffer->SetData(s_Data.CircleVertexBufferBase, datasize); - auto& a = s_Data; s_Data.CircleShader->Bind(); RendererCommand::DrawIndexed(s_Data.CircleVertexArray, s_Data.CircleIndexCount); s_Data.Stats.DrawCalls++; @@ -294,12 +333,23 @@ namespace Hazel { const uint32_t datasize = reinterpret_cast(s_Data.LineVertexBufferPtr) - reinterpret_cast(s_Data.LineVertexBufferBase); s_Data.LineVertexBuffer->SetData(s_Data.LineVertexBufferBase, datasize); - auto& a = s_Data; s_Data.LineShader->Bind(); RendererCommand::SetLineWidth(s_Data.LineWidth); RendererCommand::DrawLines(s_Data.LineVertexArray, s_Data.LineVertexCount); s_Data.Stats.DrawCalls++; } + + if (s_Data.TextIndexCount) + { + const uint32_t datasize = reinterpret_cast(s_Data.TextVertexBufferPtr) - reinterpret_cast(s_Data.TextVertexBufferBase); + s_Data.TextVertexBuffer->SetData(s_Data.TextVertexBufferBase, datasize); + + s_Data.FontAtlasTexture->Bind(0); + s_Data.TextShader->Bind(); + + RendererCommand::DrawIndexed(s_Data.TextVertexArray, s_Data.TextIndexCount); + s_Data.Stats.DrawCalls++; + } } void Renderer2D::StartBatch() @@ -313,6 +363,9 @@ namespace Hazel s_Data.LineVertexCount = 0; s_Data.LineVertexBufferPtr = s_Data.LineVertexBufferBase; + s_Data.TextIndexCount = 0; + s_Data.TextVertexBufferPtr = s_Data.TextVertexBufferBase; + s_Data.TextureSlotIndex = 1; } @@ -706,6 +759,107 @@ namespace Hazel DrawLine(lineVectices[3], lineVectices[0], color, entityID); } + void Renderer2D::DrawString(const std::string& string, const Ref& font, const glm::mat4& transform, + const glm::vec4& color, int entityID) + { + const auto& fontGeomatry = font->GetMSDFData()->FontGeometry; + const auto& metrics = fontGeomatry.getMetrics(); + + const Ref fontAtlas = font->GetAtlasTexture(); + + s_Data.FontAtlasTexture = fontAtlas; + + double x = 0.0; + double fsScale = 1.0 / (metrics.ascenderY - metrics.descenderY); + double y = 0.0; + float lineHeightOffset = 0.0f; + + for (size_t i = 0; i < string.size(); i++) + { + char character = string[i]; + if (character == '\r') + continue; + + if (character == '\n') + { + x = 0; + y -= fsScale * metrics.lineHeight + lineHeightOffset; + continue; + } + auto glphy = fontGeomatry.getGlyph(character); + if (!glphy) + glphy = fontGeomatry.getGlyph('?'); + if (!glphy) + return; + + if (character == '\t') + glphy = fontGeomatry.getGlyph(' '); + + // Atlas bound + double aLeft, aButtom, aRight, aTop; + glphy->getQuadAtlasBounds(aLeft, aButtom, aRight, aTop); + + glm::vec2 texCoordMin((float)aLeft, (float)aButtom); + glm::vec2 texCoordMax((float)aRight, (float)aTop); + + // PlaneBound + double pLeft, pButtom, pRight, pTop; + glphy->getQuadPlaneBounds(pLeft, pButtom, pRight, pTop); + + glm::vec2 quadMin((float)pLeft, (float)pButtom); + glm::vec2 quadMax((float)pRight, (float)pTop); + + quadMin *= fsScale, quadMax *= fsScale; + + quadMin += glm::vec2(x, y); + quadMax += glm::vec2(x, y); + + float texelWidth = 1.0f / fontAtlas->GetWidth(); + float texelHeight = 1.0f / fontAtlas->GetHeight(); + + texCoordMin *= glm::vec2(texelWidth, texelHeight); + texCoordMax *= glm::vec2(texelWidth, texelHeight); + + // Render + s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMin, 0.0f, 1.0f); + s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->TexCoord = texCoordMin; + s_Data.TextVertexBufferPtr->EntityID = entityID; + s_Data.TextVertexBufferPtr++; + + s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMin.x, quadMax.y, 0.0f, 1.0f); + s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->TexCoord = { texCoordMin.x, texCoordMax.y }; + s_Data.TextVertexBufferPtr->EntityID = entityID; + s_Data.TextVertexBufferPtr++; + + s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMax, 0.0f, 1.0f); + s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->TexCoord = texCoordMax; + s_Data.TextVertexBufferPtr->EntityID = entityID; + s_Data.TextVertexBufferPtr++; + + s_Data.TextVertexBufferPtr->Position = transform * glm::vec4(quadMax.x, quadMin.y, 0.0f, 1.0f); + s_Data.TextVertexBufferPtr->Color = color; + s_Data.TextVertexBufferPtr->TexCoord = {texCoordMax.x, texCoordMin.y}; + s_Data.TextVertexBufferPtr->EntityID = entityID; + s_Data.TextVertexBufferPtr++; + + s_Data.TextIndexCount += 6; + s_Data.Stats.QuadCount++; + + if (i < string.size() - 1) + { + double advance = glphy->getAdvance(); + const char nextCharacter = string[i + 1]; + fontGeomatry.getAdvance(advance, character, nextCharacter); + + float kerningOffset = 0.0f; + x += fsScale * advance + kerningOffset; + } + } + } + float Renderer2D::GetLineWidth() { return s_Data.LineWidth; diff --git a/Hazel/src/Hazel/Renderer/Renderer2D.h b/Hazel/src/Hazel/Renderer/Renderer2D.h index 2b61ad6..7d593d6 100644 --- a/Hazel/src/Hazel/Renderer/Renderer2D.h +++ b/Hazel/src/Hazel/Renderer/Renderer2D.h @@ -9,6 +9,7 @@ #include "Camera.h" #include "EditorCamera.h" +#include "Font.h" #include "OrthographicCamera.h" #include "SubTexture2D.h" #include "Texture.h" @@ -60,6 +61,9 @@ namespace Hazel static void DrawRect(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color, int entityID = -1); static void DrawRect(const glm::mat4& transform, const glm::vec4& color, int entityID = -1); + + static void DrawString(const std::string& string, const Ref& font, const glm::mat4& transform, const glm::vec4& color, int entityID = -1); + static float GetLineWidth(); static void SetLineWidth(float width); diff --git a/Hazel/src/Hazel/Renderer/Texture.cpp b/Hazel/src/Hazel/Renderer/Texture.cpp index 527a66f..1384ec3 100644 --- a/Hazel/src/Hazel/Renderer/Texture.cpp +++ b/Hazel/src/Hazel/Renderer/Texture.cpp @@ -8,6 +8,25 @@ namespace Hazel { + Ref Texture2D::Create(const TextureSpecification& spec) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::NONE: + HZ_CORE_ERROR("NONE is not Support!"); + return nullptr; + case RendererAPI::API::OPENGL: + // return std::make_shared(OpenGLVertexBuffer(vertices, size)); + return std::make_shared(spec); + break; + default: + break; + } + + HZ_CORE_ERROR("Unknown RendererAPI!"); + return nullptr; + } + Ref Texture2D::Create(uint32_t width, uint32_t height) { switch (Renderer::GetAPI()) diff --git a/Hazel/src/Hazel/Renderer/Texture.h b/Hazel/src/Hazel/Renderer/Texture.h index 23066e0..a11a7f4 100644 --- a/Hazel/src/Hazel/Renderer/Texture.h +++ b/Hazel/src/Hazel/Renderer/Texture.h @@ -11,10 +11,32 @@ namespace Hazel { + + enum class ImageFormat + { + None = 0, + R8, + RGB8, + RGBA8, + RGBA32F + }; + + struct TextureSpecification + { + uint32_t Width = 1; + uint32_t Height = 1; + + ImageFormat Format = ImageFormat::RGBA8; + bool GenerateMips = true; + }; + class HAZEL_API Texture { public: virtual ~Texture() = default; + + virtual const TextureSpecification& GetSpecification() const = 0; + virtual const uint32_t GetWidth() const = 0; virtual const uint32_t GetHeight() const = 0; virtual const uint32_t GetRendererID() const = 0; @@ -34,6 +56,7 @@ namespace Hazel public: virtual ~Texture2D() = default; + static Ref Create(const TextureSpecification& spec); static Ref Create(uint32_t width, uint32_t height); static Ref Create(const std::string& path); diff --git a/Hazel/src/Hazel/Scene/Scene.cpp b/Hazel/src/Hazel/Scene/Scene.cpp index 042e00d..6c83fde 100644 --- a/Hazel/src/Hazel/Scene/Scene.cpp +++ b/Hazel/src/Hazel/Scene/Scene.cpp @@ -223,6 +223,12 @@ namespace Hazel Renderer2D::DrawCircle(transform.GetTransform(), circle.Color, circle.Thickness, circle.Fade, (int)entity); } + // Renderer2D::DrawString("hello", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(1.0f), 0); + + Renderer2D::DrawString(R"( + Hello World! + )", Font::GetDefault(), glm::mat4(1.0f), glm::vec4(0.2f, 0.5f, 0.3f, 1.0f)); + Renderer2D::EndScene(); } diff --git a/Hazel/src/Hazel/Scene/Scene.h b/Hazel/src/Hazel/Scene/Scene.h index ff47337..267b6f2 100644 --- a/Hazel/src/Hazel/Scene/Scene.h +++ b/Hazel/src/Hazel/Scene/Scene.h @@ -6,6 +6,7 @@ #define SCENE_H +#include #include #include #include @@ -14,7 +15,6 @@ #include "entt.hpp" #include "Hazel/Core/Core.h" - namespace Hazel { class Entity; diff --git a/Hazel/src/Hazel/Scripting/ScriptEngine.cpp b/Hazel/src/Hazel/Scripting/ScriptEngine.cpp index 848ea17..62a542c 100644 --- a/Hazel/src/Hazel/Scripting/ScriptEngine.cpp +++ b/Hazel/src/Hazel/Scripting/ScriptEngine.cpp @@ -180,6 +180,7 @@ namespace Hazel // runtime Scene* SceneContext = nullptr; + bool IsMonoUsed = false; }; static ScriptEngineData* s_Data = nullptr; @@ -226,6 +227,7 @@ namespace Hazel // 1.create an object (and run constructor) s_Data->EntityClass = ScriptClass("Hazel", "Entity", true); + s_Data->IsMonoUsed = true; #if 0 // 1.create an object (and run constructor) // MonoClass* monoClass = mono_class_from_name(s_Data->CoreAssemblyImage, "Hazel", "Entity"); @@ -261,7 +263,9 @@ namespace Hazel void ScriptEngine::Shutdown() { - MonoShutdown(); + if (s_Data != nullptr) + if (s_Data->IsMonoUsed) + MonoShutdown(); delete s_Data; } @@ -427,7 +431,10 @@ namespace Hazel const Ref instance = s_Data->EntityInstances[entityUUID]; - instance->InvokeOnUpdate(ts); + if (instance) + instance->InvokeOnUpdate(ts); + else + HZ_CORE_ERROR("could not find ScriptInstance for entity {}", (uint64_t)entityUUID); } Scene* ScriptEngine::GetSceneContext() diff --git a/Hazel/src/Platform/OpenGL/OpenGLTexture.cpp b/Hazel/src/Platform/OpenGL/OpenGLTexture.cpp index 367f3d9..0338bf7 100644 --- a/Hazel/src/Platform/OpenGL/OpenGLTexture.cpp +++ b/Hazel/src/Platform/OpenGL/OpenGLTexture.cpp @@ -8,10 +8,58 @@ #include #include "stb_image.h" +#include "Hazel/Core/assert.h" namespace Hazel { - OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height) : m_Width(width), m_Height(height) + namespace Utils + { + static GLenum HazelImageFormatToDataFormat(ImageFormat format) + { + switch (format) + { + case ImageFormat::RGB8: return GL_RGB; + case ImageFormat::RGBA8: return GL_RGBA; + } + + HZ_CORE_ASSERT(false); + return 0; + } + + static GLenum HazelImageFormatToGLInternalFormal(ImageFormat format) + { + switch (format) + { + case ImageFormat::RGB8: return GL_RGB8; + case ImageFormat::RGBA8: return GL_RGBA8; + } + + HZ_CORE_ASSERT(false); + return 0; + } + } + + + + OpenGLTexture2D::OpenGLTexture2D(const TextureSpecification& spec) + :m_Specification(spec), m_Width(m_Specification.Width), m_Height(m_Specification.Height) + { + HZ_PROFILE_FUNCTION(); + m_InternalFormat = Utils::HazelImageFormatToGLInternalFormal(m_Specification.Format); + m_DataFormat = Utils::HazelImageFormatToDataFormat(m_Specification.Format); + + glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height) + : m_Width(width), m_Height(height) { HZ_PROFILE_FUNCTION(); m_InternalFormat = GL_RGBA8; diff --git a/Hazel/src/Platform/OpenGL/OpenGLTexture.h b/Hazel/src/Platform/OpenGL/OpenGLTexture.h index c82d5a0..3fe2183 100644 --- a/Hazel/src/Platform/OpenGL/OpenGLTexture.h +++ b/Hazel/src/Platform/OpenGL/OpenGLTexture.h @@ -12,10 +12,13 @@ namespace Hazel class OpenGLTexture2D : public Hazel::Texture2D { public: + OpenGLTexture2D(const TextureSpecification& spec); OpenGLTexture2D(uint32_t width, uint32_t height); OpenGLTexture2D(const std::string& path); virtual ~OpenGLTexture2D(); + virtual const TextureSpecification& GetSpecification() const override { return m_Specification; } + virtual const uint32_t GetWidth() const override {return m_Width; } virtual const uint32_t GetHeight() const override { return m_Height; } virtual const std::string& GetPath() const override {return m_Path; } @@ -31,6 +34,8 @@ namespace Hazel return m_RendererID == other.GetRendererID(); } private: + TextureSpecification m_Specification; + std::string m_Path; uint32_t m_Width, m_Height; uint32_t m_RendererID; diff --git a/Hazel/src/Platform/Windows/WindowInput.cpp b/Hazel/src/Platform/Windows/WindowInput.cpp index 51b53ba..b591519 100644 --- a/Hazel/src/Platform/Windows/WindowInput.cpp +++ b/Hazel/src/Platform/Windows/WindowInput.cpp @@ -28,6 +28,16 @@ namespace Hazel return {xpos, ypos}; } + uint32_t Input::GetMouseState(float* x, float* y) + { + float xpos, ypos; + const uint32_t state = SDL_GetMouseState(&xpos, &ypos); + if (x) *x = xpos; + if (y) *y = ypos; + return state; + } + + float Input::GetMouseX() { return GetMousePosition().first; diff --git a/Hazel/vendor/msdf-atlas-gen b/Hazel/vendor/msdf-atlas-gen new file mode 160000 index 0000000..77804c2 --- /dev/null +++ b/Hazel/vendor/msdf-atlas-gen @@ -0,0 +1 @@ +Subproject commit 77804c2f4108f49eb73491cda9296a2afb714d4c diff --git a/Sandbox/CMakeLists.txt b/Sandbox/CMakeLists.txt index 58a41b1..edb5a36 100644 --- a/Sandbox/CMakeLists.txt +++ b/Sandbox/CMakeLists.txt @@ -1,6 +1,19 @@ -set(PROJECT_NAME "Sandbox") +set(PROJECT_NAME "Sandbox-Example") -# SandBox + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin-int) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin-int) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# set MSVC output directory +if(MSVC) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif () + +# SandBox-Example project(${PROJECT_NAME}) file(GLOB_RECURSE SOURCES src/**.cpp @@ -8,4 +21,4 @@ file(GLOB_RECURSE SOURCES add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} PRIVATE Hazel) +target_link_libraries(${PROJECT_NAME} PRIVATE Hazel-shared) diff --git a/Sandbox/src/SandBox2D/SandBox2D.cpp b/Sandbox/src/SandBox2D/SandBox2D.cpp index 139b49a..5ab81d2 100644 --- a/Sandbox/src/SandBox2D/SandBox2D.cpp +++ b/Sandbox/src/SandBox2D/SandBox2D.cpp @@ -7,6 +7,7 @@ #include #include "glm/gtc/type_ptr.hpp" +#include "Hazel/Core/Input.h" static constexpr uint32_t s_MapWidth = 24; static const char* s_MapTiles = @@ -46,9 +47,11 @@ void SandBox2D::OnAttach() m_TextureMap['W'] = Hazel::SubTexture2D::CreateFromCoords(m_Texture, {0, 11}, {128, 128}); // m_SubTexture = Hazel::SubTexture2D::CreateFromCoords(m_Texture, {8, 5}, {128, 128}, {1, 2}); Hazel::FrameBufferSpecification specification; - specification.Width = 1280; - specification.Height = 720; - m_FrameBuffer = Hazel::FrameBuffer::Create(specification); + specification.Width = 1280; + specification.Height = 720; + specification.Attachments = {Hazel::FrameBufferTextureFormat::RGBA8, Hazel::FrameBufferTextureFormat::RED_INTEGER, Hazel::FrameBufferTextureFormat::DEPTH }; + m_FrameBuffer = Hazel::FrameBuffer::Create(specification); + m_ViewPortSize = {specification.Width, m_ViewPortSize.y}; m_Particle.ColorBegin = {0 / 255.f, 212 / 255.f, 123 / 255.f, 1.0f}; m_Particle.ColorEnd = {254 / 255.f, 109 / 255.f, 41 / 255.f, 1.0f}; @@ -66,13 +69,19 @@ void SandBox2D::OnDetech() void SandBox2D::OnUpdate(Hazel::TimeStep& ts) { - auto mouseState = SDL_GetMouseState(NULL, NULL); + const auto mouseState = Hazel::Input::GetMouseState(); // PROFILE_SCOPE("SandBox2D::OnUpdate"); HZ_PROFILE_FUNCTION(); Hazel::Renderer2D::ResetStats(); m_CameraController.OnUpdate(ts); + if (const auto& spec = m_FrameBuffer->GetSpecification(); + spec.Width != m_ViewPortSize.x || spec.Height != m_ViewPortSize.y) + { + m_FrameBuffer->Resize((uint32_t)m_ViewPortSize.x, (uint32_t)m_ViewPortSize.y); + } + { HZ_PROFILE_SCOPE("Renderer Prep"); @@ -112,7 +121,7 @@ void SandBox2D::OnUpdate(Hazel::TimeStep& ts) auto height = Hazel::Application::Get().GetWindow().GetHeight(); float x, y; - SDL_GetMouseState(&x, &y); + Hazel::Input::GetMouseState(&x, &y); auto bounds = m_CameraController.GetBounds(); auto cameraPos = m_CameraController.GetCamera().GetPosition(); @@ -135,11 +144,7 @@ void SandBox2D::OnUpdate(Hazel::TimeStep& ts) void SandBox2D::OnImGuiRender() { - ImGui::Begin("Settings"); - ImGui::Image(m_FrameBuffer->GetColorAttachmentID(), {1280.f, 720.f}); - ImGui::End(); - - static bool showDockspace = false; + static bool showDockspace = true; if (showDockspace) { // const auto cameraRotation = m_CameraController.GetCamera().GetRotation(); @@ -220,6 +225,15 @@ void SandBox2D::OnImGuiRender() ImGui::EndMenuBar(); } + { + ImVec2 viewPortPanelSize = ImGui::GetContentRegionAvail(); + if (m_ViewPortSize != *reinterpret_cast(&viewPortPanelSize)) + { + m_ViewPortSize = {viewPortPanelSize.x, viewPortPanelSize.y}; + } + + } + { ImGui::Begin("Hazel Layer"); diff --git a/Sandbox/src/SandBox2D/SandBox2D.h b/Sandbox/src/SandBox2D/SandBox2D.h index 84fa583..26621b6 100644 --- a/Sandbox/src/SandBox2D/SandBox2D.h +++ b/Sandbox/src/SandBox2D/SandBox2D.h @@ -27,6 +27,7 @@ private: Hazel::Ref m_defaultTexture; Hazel::Ref m_FrameBuffer; + glm::vec2 m_ViewPortSize = {0.0f, 0.0f}; glm::vec4 m_BackgroundColor = {0.2f, 0.2f, 0.2f, 1.0f}; struct ProfileResult