Working on portrait mode

This commit is contained in:
Vladislav Khorev committed 2026-06-25 12:13:02 +03:00
1 parent b8d79c7b7b
commit 6b39cfca3b
5 files changed
+162 -27

No files matched your search

+146 -7
View File
@@ -56,6 +56,13 @@ void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) {
return;
}
if (Environment::projectionWidth < Environment::projectionHeight)
drawPortrait(renderer, model);
else
drawLandscape(renderer, model);
}
void DialogueOverlay::drawLandscape(Renderer& renderer, const PresentationModel& model) {
const float W = Environment::projectionWidth;
const float nameX = 312 + 8;
@@ -64,7 +71,7 @@ void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) {
const float bodyY = 232 - 78.0f;
const float bodyMaxWidthPx = 1280.0 - nameX - 48.f - 60.f;
float centeredShiftX = ((W - nameX - 48.f - 60.f) - bodyMaxWidthPx)*0.5;
@@ -147,16 +154,13 @@ void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) {
const std::string wrappedBody = wrapTextToWidth(model.visibleText, *bodyRenderer, bodyMaxWidthPx, 1.0f);
bodyRenderer->drawText(wrappedBody, bodyX + centeredShiftX, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f });
if (model.mode == PresentationMode::Choice) {
const float choiceStartY = 270.f-35;
const float choiceHeight = 121.f*0.9;
const float choiceSpacing = -30.f;
const float choiceWidth = 913.f * 0.9;
//UiRect textboxRect{ 30.f, -48.f, 1222.f, 340.0f };
if (choiceQuads.size() < model.choices.size()) {
choiceQuads.resize(model.choices.size());
}
@@ -204,6 +208,141 @@ void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) {
glDisable(GL_BLEND);
}
void DialogueOverlay::drawPortrait(Renderer& renderer, const PresentationModel& model) {
const float W = Environment::projectionWidth;
const float H = Environment::projectionHeight;
// Panel: DialogChoice001.png scaled to full screen width, aspect ratio preserved.
const float texAspect = static_cast<float>(textboxChoiceTexture->getWidth())
/ static_cast<float>(textboxChoiceTexture->getHeight());
const float panelW = W;
const float panelH = panelW / texAspect;
const float clipAtBottom = 18.f;
const float panelTopY = panelH - clipAtBottom;
// Character portrait sits above the panel, shifted right and slightly overlapping.
const float portraitSize = 200.f;
const float portraitX = 56.f;
const float portraitY = panelTopY + 6.f - 40.f;
// Text sits inside the panel near the top.
const float leftPad = 60.f;
const float rightPad = 60.f;
const float textMaxWidth = W - leftPad - rightPad;
const float nameY = panelTopY - 32.f - 50.f;
const float bodyY = nameY - 38.f;
// --- Rebuild quads only when geometry changes ---
{
UiRect panelRect{ 0.f, -clipAtBottom, panelW, panelH };
if (!portraitPanelQuad.initialized
|| portraitPanelQuad.rect.x != panelRect.x || portraitPanelQuad.rect.y != panelRect.y
|| portraitPanelQuad.rect.w != panelRect.w || portraitPanelQuad.rect.h != panelRect.h) {
portraitPanelQuad.rebuild(panelRect);
}
}
{
UiRect portraitRect{ portraitX, portraitY, portraitSize, portraitSize };
if (!portraitAbovePortraitQuad.initialized
|| portraitAbovePortraitQuad.rect.x != portraitRect.x || portraitAbovePortraitQuad.rect.y != portraitRect.y
|| portraitAbovePortraitQuad.rect.w != portraitRect.w || portraitAbovePortraitQuad.rect.h != portraitRect.h) {
portraitAbovePortraitQuad.rebuild(portraitRect);
}
}
// --- Draw panel and portrait image ---
glEnable(GL_BLEND);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.PushProjectionMatrix(0.f, W, 0.f, H, -10.f, 10.f);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.RenderUniform1f("uAlpha", 1.0f);
glBindTexture(GL_TEXTURE_2D, textboxChoiceTexture->getTexID());
renderer.DrawVertexRenderStruct(portraitPanelQuad.mesh);
auto portrait = loadTextureCached(model.portraitPath);
if (portrait) {
glBindTexture(GL_TEXTURE_2D, portrait->getTexID());
renderer.DrawVertexRenderStruct(portraitAbovePortraitQuad.mesh);
}
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
// --- Name and body text ---
lastChoiceRects.clear();
if (!model.speaker.empty()) {
nameRenderer->drawText(model.speaker, leftPad, nameY, 1.0f, false, { 1.0f, 0.88f, 0.45f, 1.0f });
}
const std::string wrappedBody = wrapTextToWidth(model.visibleText, *bodyRenderer, textMaxWidth, 1.0f);
bodyRenderer->drawText(wrappedBody, leftPad, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f });
// Hit rect for tap-to-advance.
lastDialogueAdvanceRect = { 0.f, 0.f, W, panelTopY };
// --- Choice mode: stack choices inside the panel ---
if (model.mode == PresentationMode::Choice) {
const float choiceTexAspect = static_cast<float>(choiceMainTexture->getWidth())
/ static_cast<float>(choiceMainTexture->getHeight());
const float choiceW = W - leftPad - rightPad;
const float choiceH = choiceW / choiceTexAspect;
const float choiceSpacing = 8.f;
const float totalChoicesH = (choiceH + choiceSpacing) * static_cast<float>(model.choices.size()) - choiceSpacing;
const float choiceStartY = panelTopY - 34.f - totalChoicesH; // below name row
if (choiceQuads.size() < model.choices.size()) {
choiceQuads.resize(model.choices.size());
}
glEnable(GL_BLEND);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.PushProjectionMatrix(0.f, W, 0.f, H, -10.f, 10.f);
renderer.PushMatrix();
renderer.LoadIdentity();
for (size_t i = 0; i < model.choices.size(); ++i) {
const float cy = choiceStartY + (choiceH + choiceSpacing) * static_cast<float>(model.choices.size() - 1 - i);
UiRect rect{ leftPad, cy, choiceW, choiceH };
lastChoiceRects.push_back(rect);
choiceQuads[i].rebuild(rect);
const bool isHighlighted = static_cast<int>(i) == hoveredChoiceIndex || static_cast<int>(i) == model.selectedChoice;
std::shared_ptr<Texture> choiceTexture =
(model.choices[i].kind == ChoiceKind::Optional) ? choiceOptionalTexture : choiceMainTexture;
if (isHighlighted) choiceTexture = choiceSelectedTexture;
glBindTexture(GL_TEXTURE_2D, choiceTexture->getTexID());
renderer.DrawVertexRenderStruct(choiceQuads[i].mesh);
}
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
for (size_t i = 0; i < model.choices.size(); ++i) {
const UiRect& rect = lastChoiceRects[i];
const bool isHighlighted = static_cast<int>(i) == hoveredChoiceIndex || static_cast<int>(i) == model.selectedChoice;
const std::array<float, 4> color = (model.choices[i].kind == ChoiceKind::Optional)
? std::array<float, 4>{0.82f, 0.82f, 0.82f, 1.0f}
: std::array<float, 4>{ 1.0f, 0.93f, 0.65f, 1.0f };
const std::string wrappedChoice = wrapTextToWidth(model.choices[i].text, *choiceRenderer, rect.w - 20.0f, 1.0f);
choiceRenderer->drawText(
wrappedChoice, rect.x + 20.f, rect.y + choiceH * 0.35f, 1.5f, false,
isHighlighted ? std::array<float, 4>{1.0f, 1.0f, 1.0f, 1.0f} : color
);
}
}
glDisable(GL_BLEND);
}
void DialogueOverlay::handlePointerDown(float x, float y, const PresentationModel& model) {
if (model.mode == PresentationMode::Choice) {
handlePointerMoved(x, y, model);
@@ -241,8 +380,8 @@ bool DialogueOverlay::handlePointerReleased(float x, float y, const Presentation
}
if (model.mode == PresentationMode::Dialogue) {
outAdvanceDialogue = rectContains(lastDialogueAdvanceRect, x, y);
return outAdvanceDialogue;
outAdvanceDialogue = true;
return true;
}
return false;
+7
View File
@@ -47,11 +47,18 @@ private:
UiQuad textboxChoiceQuad;
mutable std::vector<UiQuad> choiceQuads;
// Portrait-mode quads (rebuilt when geometry changes)
UiQuad portraitPanelQuad;
UiQuad portraitAbovePortraitQuad;
std::shared_ptr<Texture> loadTextureCached(const std::string& path);
static std::string wrapTextToWidth(const std::string& input, const TextRenderer& textRenderer,
float maxWidthPx, float scale);
static bool rectContains(const UiRect& rect, float x, float y);
void drawLandscape(Renderer& renderer, const PresentationModel& model);
void drawPortrait(Renderer& renderer, const PresentationModel& model);
};
} // namespace ZL::Dialogue