From 0da1ae124c260c5809cff88ed90bc0952a12a34f Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Tue, 26 May 2026 22:21:05 +0300 Subject: [PATCH] Working on UI part --- resources/dialogue/dorm_dialogues.json | 12 +-- resources/dialogue/portrait_hero_neutral.png | 3 + resources/dialogue/textbox_bg.png | 4 +- resources/dialogue/textbox_bg_old.png | 3 + resources/w/ui/hud_step0.json | 36 +++++++++ resources/w/ui/hud_step1.json | 46 ++++++++++++ resources/w/ui/img/Hint1.png | 3 + resources/w/ui/img/Hint2.png | 3 + resources/w/ui/img/hint_arrow_down.png | 3 + src/Game.cpp | 16 +++- src/MenuManager.cpp | 3 +- src/SparkEmitter.cpp | 1 + src/UiManager.cpp | 13 ++-- src/dialogue/DialogueOverlay.cpp | 28 ++++--- src/render/TextRenderer.cpp | 2 + src/render/TextureManager.cpp | 77 ++++++++++++++++++-- src/render/TextureManager.h | 10 ++- 17 files changed, 221 insertions(+), 42 deletions(-) create mode 100644 resources/dialogue/portrait_hero_neutral.png create mode 100644 resources/dialogue/textbox_bg_old.png create mode 100644 resources/w/ui/hud_step0.json create mode 100644 resources/w/ui/hud_step1.json create mode 100644 resources/w/ui/img/Hint1.png create mode 100644 resources/w/ui/img/Hint2.png create mode 100644 resources/w/ui/img/hint_arrow_down.png diff --git a/resources/dialogue/dorm_dialogues.json b/resources/dialogue/dorm_dialogues.json index a7a68a1..b9be6b8 100644 --- a/resources/dialogue/dorm_dialogues.json +++ b/resources/dialogue/dorm_dialogues.json @@ -8,16 +8,8 @@ "id": "line_1", "type": "Line", "speaker": "Бекзат", - "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", - "text": "Новый день! Я проснулся, позавтракал и готов поехать в универ!", - "next": "line_2" - }, - { - "id": "line_2", - "type": "Line", - "speaker": "Бекзат", - "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", - "text": "Надо проверить телефон, и не забыть взять свою записную книжку.", + "portrait": "resources/dialogue/portrait_hero_neutral.png", + "text": "Новый день! Я проснулся, позавтракал и готов поехать в универ! Надо проверить телефон, и не забыть взять свою записную книжку.", "next": "end_1" }, { diff --git a/resources/dialogue/portrait_hero_neutral.png b/resources/dialogue/portrait_hero_neutral.png new file mode 100644 index 0000000..05b9229 --- /dev/null +++ b/resources/dialogue/portrait_hero_neutral.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:825576cce699c3127f26170e083493cfa5d9f5a0c4c58fa16addc865a3ff1ed0 +size 108117 diff --git a/resources/dialogue/textbox_bg.png b/resources/dialogue/textbox_bg.png index e02bef2..e0ec5dd 100644 --- a/resources/dialogue/textbox_bg.png +++ b/resources/dialogue/textbox_bg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36fc69bd2e67933be9b040ad4b5d5390a516ae1859ef1c865b1031801f7a22e1 -size 1880 +oid sha256:f86a735db9c2129076a518606ac409a7ab38f4f205169ed3275e246de3249520 +size 1308486 diff --git a/resources/dialogue/textbox_bg_old.png b/resources/dialogue/textbox_bg_old.png new file mode 100644 index 0000000..e02bef2 --- /dev/null +++ b/resources/dialogue/textbox_bg_old.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36fc69bd2e67933be9b040ad4b5d5390a516ae1859ef1c865b1031801f7a22e1 +size 1880 diff --git a/resources/w/ui/hud_step0.json b/resources/w/ui/hud_step0.json new file mode 100644 index 0000000..0e32cab --- /dev/null +++ b/resources/w/ui/hud_step0.json @@ -0,0 +1,36 @@ +{ + "root": { + "type": "FrameLayout", + "name": "hud_root", + "width": "match_parent", + "height": "match_parent", + "children": [ + { + "type": "LinearLayout", + "orientation": "vertical", + "vertical_align": "center", + "horizontal_align": "center", + "spacing": 0, + "width": "match_parent", + "height": 600, + "children": [ + { + "type": "StaticImage", + "name": "hint1", + "width": 440, + "height": 134, + "horizontal_gravity": "center", + "texture": "resources/w/ui/img/Hint2.png" + }, + { + "type": "StaticImage", + "name": "hint2", + "width": 24, + "height": 118, + "horizontal_gravity": "center", + "texture": "resources/w/ui/img/hint_arrow_down.png" + } + ] + }] + } +} diff --git a/resources/w/ui/hud_step1.json b/resources/w/ui/hud_step1.json new file mode 100644 index 0000000..bcbd9db --- /dev/null +++ b/resources/w/ui/hud_step1.json @@ -0,0 +1,46 @@ +{ + "root": { + "type": "FrameLayout", + "name": "hud_root", + "width": "match_parent", + "height": "match_parent", + "children": [ + { + "type": "TextButton", + "name": "inventory_button", + "x": 50.0, + "y": 50.0, + "width": 150.0, + "height": 60.0, + "text": "Inventory", + "fontSize": 24, + "fontPath": "resources/fonts/DroidSans.ttf", + "textCentered": true, + "color": [1.0, 1.0, 1.0, 1.0], + "textures": { + "normal": "resources/w/red.png", + "hover": "resources/w/red.png", + "pressed": "resources/w/red.png" + } + }, + { + "type": "TextButton", + "name": "quest_journal_button", + "x": 220.0, + "y": 50.0, + "width": 170.0, + "height": 60.0, + "text": "Quests", + "fontSize": 24, + "fontPath": "resources/fonts/DroidSans.ttf", + "textCentered": true, + "color": [1.0, 1.0, 1.0, 1.0], + "textures": { + "normal": "resources/w/red.png", + "hover": "resources/w/red.png", + "pressed": "resources/w/red.png" + } + } + ] + } +} diff --git a/resources/w/ui/img/Hint1.png b/resources/w/ui/img/Hint1.png new file mode 100644 index 0000000..54e7624 --- /dev/null +++ b/resources/w/ui/img/Hint1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9df93a2d9772131198ba870d21ec58f2cdad6c8cfddd8d7651d1eb060dc86de2 +size 94084 diff --git a/resources/w/ui/img/Hint2.png b/resources/w/ui/img/Hint2.png new file mode 100644 index 0000000..5eff0ad --- /dev/null +++ b/resources/w/ui/img/Hint2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58b0cbfdee7f1a9833c2373138fb57b294cd74241eee76d8d6d486bbc1900bba +size 92577 diff --git a/resources/w/ui/img/hint_arrow_down.png b/resources/w/ui/img/hint_arrow_down.png new file mode 100644 index 0000000..eb8c9c4 --- /dev/null +++ b/resources/w/ui/img/hint_arrow_down.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f1346a192fb72f69ba8778c56cf5db7c269b45abb7f3db2aa57eac3166a9c30 +size 2019 diff --git a/src/Game.cpp b/src/Game.cpp index 4431ab5..603c46d 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -286,7 +286,6 @@ namespace ZL if (locations["uni_exterior"]->player) locations["uni_exterior"]->player->onDeathAnimComplete = [this]() { startDarklandsTransition(); }; - LocationSetup params_dorm; //params_dorm.gameObjectsJsonPath = "resources/config2/gameobjects_dorm.json"; //params_dorm.gameObjectsJsonPath = "resources/config2/gameobjects_dorm_trees001.json"; @@ -436,6 +435,7 @@ namespace ZL glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); + renderer.shaderManager.PushShader(defaultShaderName); renderer.RenderUniform1i(textureUniformName, 0); glEnable(GL_BLEND); @@ -697,6 +697,8 @@ namespace ZL break; case SDLK_o: + x = x + 1; + std::cout << "current x: " << x << std::endl; //y = y + 0.002; //currentLocation->player->hp = 200; //currentLocation->npcs[0]->walkSpeed += 0.01f; @@ -704,8 +706,10 @@ namespace ZL break; case SDLK_k: + y = y + 1; + std::cout << "current y: " << y << std::endl; //y = y - 0.002; - std::cout << "Player pos: " << currentLocation->player->position.transpose() << std::endl; + //std::cout << "Player pos: " << currentLocation->player->position.transpose() << std::endl; //currentLocation->npcs[0]->walkSpeed -= 0.01f; //std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl; break; @@ -718,7 +722,8 @@ namespace ZL break; case SDLK_l: - x = x - 0.002; + x = x - 1; + std::cout << "current x: " << x << std::endl; break; case SDLK_c: @@ -726,6 +731,11 @@ namespace ZL activateSlowMoEffect(); break; + case SDLK_i: + y = y - 1; + std::cout << "current y: " << y << std::endl; + break; + case SDLK_b: if (navigationEditorMode && currentLocation) { currentLocation->navigationEditorSave(); diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index ab19bfc..69167fd 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -33,7 +33,8 @@ namespace ZL { void MenuManager::setup(Inventory& inv, const std::string& zipFile) { inventory = &inv; - hudRoot = loadUiFromFile("resources/config2/hud.json", renderer, zipFile); + //hudRoot = loadUiFromFile("resources/config2/hud.json", renderer, zipFile); + hudRoot = loadUiFromFile("resources/w/ui/hud_step0.json", renderer, zipFile); inventoryRoot = loadUiFromFile("resources/config2/ui_inventory.json", renderer, zipFile); questJournalRoot = loadUiFromFile("resources/config2/ui_quest_journal.json", renderer, zipFile); diff --git a/src/SparkEmitter.cpp b/src/SparkEmitter.cpp index fe59905..4fde628 100644 --- a/src/SparkEmitter.cpp +++ b/src/SparkEmitter.cpp @@ -231,6 +231,7 @@ namespace ZL { renderer.DrawVertexRenderStruct(sparkQuad); //renderer.PopMatrix(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); renderer.shaderManager.PopShader(); } diff --git a/src/UiManager.cpp b/src/UiManager.cpp index e2043e5..fc169a3 100644 --- a/src/UiManager.cpp +++ b/src/UiManager.cpp @@ -490,7 +490,7 @@ namespace ZL { std::string path = t[key].get(); try { std::cout << "UiManager: loading texture for button '" << btn->name << "' : " << path << " Zip file: " << zipFile << std::endl; - return renderer.textureManager.LoadFromPng(path, zipFile); + return renderer.textureManager.LoadFromPng(path, zipFile, true); } catch (const std::exception& e) { std::cerr << "UiManager: failed load texture " << path << " : " << e.what() << std::endl; @@ -522,7 +522,7 @@ namespace ZL { std::string path = t[key].get(); try { std::cout << "UiManager: --loading texture for slider '" << s->name << "' : " << path << " Zip file: " << zipFile << std::endl; - return renderer.textureManager.LoadFromPng(path, zipFile); + return renderer.textureManager.LoadFromPng(path, zipFile, true); } catch (const std::exception& e) { std::cerr << "UiManager: failed load texture " << path << " : " << e.what() << std::endl; @@ -594,8 +594,7 @@ namespace ZL { if (!t.contains(key) || !t[key].is_string()) return nullptr; std::string path = t[key].get(); try { - auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str()); - return std::make_shared(data); + return renderer.textureManager.LoadFromPng(path, zipFile, true); } catch (const std::exception& e) { std::cerr << "UiManager: TextButton '" << tb->name << "' failed to load texture " << path << ": " << e.what() << std::endl; @@ -672,7 +671,7 @@ namespace ZL { if (!texPath.empty()) { try { - img->texture = renderer.textureManager.LoadFromPng(texPath, zipFile); + img->texture = renderer.textureManager.LoadFromPng(texPath, zipFile, true); } catch (const std::exception& e) { std::cerr << "UiManager: failed load texture for StaticImage '" << img->name << "' : " << e.what() << std::endl; @@ -1056,9 +1055,9 @@ namespace ZL { try { if (!trackPath.empty()) - s->texTrack = renderer.textureManager.LoadFromPng(trackPath, zipFile); + s->texTrack = renderer.textureManager.LoadFromPng(trackPath, zipFile, true); if (!knobPath.empty()) - s->texKnob = renderer.textureManager.LoadFromPng(knobPath, zipFile); + s->texKnob = renderer.textureManager.LoadFromPng(knobPath, zipFile, true); } catch (const std::exception& e) { std::cerr << "UiManager: addSlider failed to load textures: " << e.what() << std::endl; diff --git a/src/dialogue/DialogueOverlay.cpp b/src/dialogue/DialogueOverlay.cpp index 7587a51..3a64ac2 100644 --- a/src/dialogue/DialogueOverlay.cpp +++ b/src/dialogue/DialogueOverlay.cpp @@ -7,6 +7,12 @@ #include #include +namespace ZL +{ + extern float x; + extern float y; +} + namespace ZL::Dialogue { void DialogueOverlay::TexturedQuad::rebuild(const UiRect& newRect) { @@ -161,8 +167,10 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel& const float W = Environment::projectionWidth; // const float H = Environment::projectionHeight; - const UiRect portraitRect{ 24.0f, 24.0f, 182.0f, 182.0f }; - const UiRect textboxRect{ 220.0f, 24.0f, max(200.0f, W - 244.0f), 182.0f }; + UiRect portraitRect{ 24.0f+90, 24.0f+16, 176.0f, 176.0f }; + //const UiRect textboxRect{ 220.0f, 24.0f, max(200.0f, W - 244.0f), 182.0f }; + UiRect textboxRect{ 30.f, -48.f, 1222.f, 340.0f }; + lastDialogueAdvanceRect = { portraitRect.x, portraitRect.y, textboxRect.x + textboxRect.w - portraitRect.x, textboxRect.h }; lastCutsceneAdvanceRect = {}; @@ -187,27 +195,27 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel& renderer.PushProjectionMatrix(0.0f, W, 0.0f, Environment::projectionHeight, -10.0f, 10.0f); renderer.PushMatrix(); renderer.LoadIdentity(); + renderer.RenderUniform1f("uAlpha", 1.0f); drawQuad(renderer, textboxQuad, textboxTexture); - drawQuad(renderer, portraitQuad, model.portraitPath.empty() ? portraitFrameTexture : loadTextureCached(model.portraitPath)); - drawQuad(renderer, portraitQuad, portraitFrameTexture); + //drawQuad(renderer, portraitQuad, model.portraitPath.empty() ? portraitFrameTexture : loadTextureCached(model.portraitPath)); + drawQuad(renderer, portraitQuad, loadTextureCached(model.portraitPath)); renderer.PopMatrix(); renderer.PopProjectionMatrix(); renderer.shaderManager.PopShader(); - const float nameX = textboxRect.x + 24.0f; - const float nameY = textboxRect.y + textboxRect.h - 38.0f; - const float bodyX = textboxRect.x + 24.0f; - const float bodyY = textboxRect.y + textboxRect.h - 78.0f; + const float nameX = 312; + const float nameY = 232 - 38.0f; + const float bodyX = 312; + const float bodyY = 232 - 78.0f; if (!model.speaker.empty()) { nameRenderer->drawText(model.speaker, nameX, nameY, 1.0f, false, { 1.0f, 0.88f, 0.45f, 1.0f }); } - const float bodyTextScale = 1.0f; - const float bodyMaxWidthPx = textboxRect.w - 48.0f; + const float bodyMaxWidthPx = W - nameX - 48.f-x; const std::string wrappedBody = wrapTextToWidth(model.visibleText, *bodyRenderer, bodyMaxWidthPx, bodyTextScale); bodyRenderer->drawText(wrappedBody, bodyX, bodyY, bodyTextScale, false, { 1.0f, 1.0f, 1.0f, 1.0f }); diff --git a/src/render/TextRenderer.cpp b/src/render/TextRenderer.cpp index e5758e7..64e2906 100644 --- a/src/render/TextRenderer.cpp +++ b/src/render/TextRenderer.cpp @@ -459,6 +459,8 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca r->RenderUniform1i("uText", 0); r->RenderUniform4fv("uColor", color.data()); + r->RenderUniform1f("uAlpha", 1.0f); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, atlasTexture->getTexID()); diff --git a/src/render/TextureManager.cpp b/src/render/TextureManager.cpp index 61fdc69..f280b9b 100644 --- a/src/render/TextureManager.cpp +++ b/src/render/TextureManager.cpp @@ -8,9 +8,68 @@ namespace ZL { + bool IsPowerOf2(size_t n) { + return n > 0 && (n & (n - 1)) == 0; + } + + size_t NextPowerOf2(size_t n) { + if (n == 0) return 1; + --n; + n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; +#if SIZE_MAX > 0xFFFFFFFF + n |= n >> 32; +#endif + return n + 1; + } + + TextureDataStruct ResizeTextureDataToPOT(const TextureDataStruct& src) { + size_t newW = IsPowerOf2(src.width) ? src.width : NextPowerOf2(src.width); + size_t newH = IsPowerOf2(src.height) ? src.height : NextPowerOf2(src.height); + if (newW == src.width && newH == src.height) return src; + + int channels = (src.format == TextureDataStruct::R8) ? 1 + : (src.format == TextureDataStruct::RGB) ? 3 : 4; + + TextureDataStruct dst = src; + dst.width = newW; + dst.height = newH; + dst.data.resize(newW * newH * channels); + + for (size_t y = 0; y < newH; ++y) { + for (size_t x = 0; x < newW; ++x) { + float fx = (newW > 1) ? (float)x * (float)(src.width - 1) / (float)(newW - 1) : 0.f; + float fy = (newH > 1) ? (float)y * (float)(src.height - 1) / (float)(newH - 1) : 0.f; + size_t x0 = (size_t)fx, y0 = (size_t)fy; + size_t x1 = min(x0 + 1, src.width - 1); + size_t y1 = min(y0 + 1, src.height - 1); + float tx = fx - (float)x0, ty = fy - (float)y0; + for (int c = 0; c < channels; ++c) { + float v00 = (float)(unsigned char)src.data[(y0 * src.width + x0) * channels + c]; + float v10 = (float)(unsigned char)src.data[(y0 * src.width + x1) * channels + c]; + float v01 = (float)(unsigned char)src.data[(y1 * src.width + x0) * channels + c]; + float v11 = (float)(unsigned char)src.data[(y1 * src.width + x1) * channels + c]; + float val = v00 * (1.f - tx) * (1.f - ty) + v10 * tx * (1.f - ty) + + v01 * (1.f - tx) * ty + v11 * tx * ty; + dst.data[(y * newW + x) * channels + c] = (char)(unsigned char)(val + 0.5f); + } + } + } + std::cout << "TextureManager: resized texture from " + << src.width << "x" << src.height + << " to " << newW << "x" << newH << " (POT)" << std::endl; + return dst; + } + Texture::Texture(const TextureDataStruct& texData) { width = texData.width; height = texData.height; + + if (!IsPowerOf2(width) || !IsPowerOf2(height)) { + std::cerr << "TextureManager WARNING: non-POT texture " + << width << "x" << height + << " — upload may fail on some platforms." << std::endl; + } + glGenTextures(1, &texID); glBindTexture(GL_TEXTURE_2D, texID); @@ -422,36 +481,42 @@ namespace ZL return zipFile.empty() ? fileName : fileName + "|" + zipFile; } - std::shared_ptr TextureManager::LoadFromBmp24(const std::string& fileName, const std::string& zipFile) + std::shared_ptr TextureManager::LoadFromBmp24(const std::string& fileName, const std::string& zipFile, bool resizeToPOT) { std::string key = MakeKey(fileName, zipFile); auto it = textureMap.find(key); if (it != textureMap.end()) return it->second; - auto tex = std::make_shared(CreateTextureDataFromBmp24(fileName, zipFile)); + auto data = CreateTextureDataFromBmp24(fileName, zipFile); + if (resizeToPOT) data = ResizeTextureDataToPOT(data); + auto tex = std::make_shared(data); textureMap[key] = tex; return tex; } - std::shared_ptr TextureManager::LoadFromBmp32(const std::string& fileName, const std::string& zipFile) + std::shared_ptr TextureManager::LoadFromBmp32(const std::string& fileName, const std::string& zipFile, bool resizeToPOT) { std::string key = MakeKey(fileName, zipFile); auto it = textureMap.find(key); if (it != textureMap.end()) return it->second; - auto tex = std::make_shared(CreateTextureDataFromBmp32(fileName, zipFile)); + auto data = CreateTextureDataFromBmp32(fileName, zipFile); + if (resizeToPOT) data = ResizeTextureDataToPOT(data); + auto tex = std::make_shared(data); textureMap[key] = tex; return tex; } #ifdef PNG_ENABLED - std::shared_ptr TextureManager::LoadFromPng(const std::string& fileName, const std::string& zipFile) + std::shared_ptr TextureManager::LoadFromPng(const std::string& fileName, const std::string& zipFile, bool resizeToPOT) { std::string key = MakeKey(fileName, zipFile); auto it = textureMap.find(key); if (it != textureMap.end()) return it->second; - auto tex = std::make_shared(CreateTextureDataFromPng(fileName, zipFile)); + auto data = CreateTextureDataFromPng(fileName, zipFile); + if (resizeToPOT) data = ResizeTextureDataToPOT(data); + auto tex = std::make_shared(data); textureMap[key] = tex; return tex; } diff --git a/src/render/TextureManager.h b/src/render/TextureManager.h index eb53ec4..3bfe4ca 100644 --- a/src/render/TextureManager.h +++ b/src/render/TextureManager.h @@ -62,13 +62,17 @@ namespace ZL TextureDataStruct CreateTextureDataFromPng(const std::string& fullFileName, const std::string& ZIPFileName = ""); #endif + bool IsPowerOf2(size_t n); + size_t NextPowerOf2(size_t n); + TextureDataStruct ResizeTextureDataToPOT(const TextureDataStruct& texData); + class TextureManager { public: - std::shared_ptr LoadFromBmp24(const std::string& fileName, const std::string& zipFile = ""); - std::shared_ptr LoadFromBmp32(const std::string& fileName, const std::string& zipFile = ""); + std::shared_ptr LoadFromBmp24(const std::string& fileName, const std::string& zipFile = "", bool resizeToPOT = false); + std::shared_ptr LoadFromBmp32(const std::string& fileName, const std::string& zipFile = "", bool resizeToPOT = false); #ifdef PNG_ENABLED - std::shared_ptr LoadFromPng(const std::string& fileName, const std::string& zipFile = ""); + std::shared_ptr LoadFromPng(const std::string& fileName, const std::string& zipFile = "", bool resizeToPOT = false); #endif void Unload(const std::string& fileName, const std::string& zipFile = "");