Working on portrait mode
This commit is contained in:
parent
b8d79c7b7b
commit
6b39cfca3b
10
src/Game.cpp
10
src/Game.cpp
@ -694,9 +694,9 @@ namespace ZL
|
|||||||
if (!loadingCompleted) {
|
if (!loadingCompleted) {
|
||||||
drawLoading();
|
drawLoading();
|
||||||
}
|
}
|
||||||
else if (Environment::width < Environment::height) {
|
/*else if (Environment::width < Environment::height) {
|
||||||
drawMobilePortraitOverlay();
|
drawMobilePortraitOverlay();
|
||||||
}
|
}*/
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Location* loc = currentLocation();
|
Location* loc = currentLocation();
|
||||||
@ -733,7 +733,7 @@ namespace ZL
|
|||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::drawMobilePortraitOverlay()
|
/*void Game::drawMobilePortraitOverlay()
|
||||||
{
|
{
|
||||||
if (!mobileRotateTexture) return;
|
if (!mobileRotateTexture) return;
|
||||||
|
|
||||||
@ -762,7 +762,7 @@ namespace ZL
|
|||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
void Game::drawLoading()
|
void Game::drawLoading()
|
||||||
{
|
{
|
||||||
@ -944,7 +944,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suppress all input while portrait overlay is shown
|
// Suppress all input while portrait overlay is shown
|
||||||
if (mobileIsPortrait) continue;
|
//if (mobileIsPortrait) continue;
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
||||||
|
|||||||
@ -153,7 +153,7 @@ namespace ZL {
|
|||||||
float mobileRotateMeshLastW = -1.0f;
|
float mobileRotateMeshLastW = -1.0f;
|
||||||
float mobileRotateMeshLastH = -1.0f;
|
float mobileRotateMeshLastH = -1.0f;
|
||||||
|
|
||||||
void drawMobilePortraitOverlay();
|
//void drawMobilePortraitOverlay();
|
||||||
|
|
||||||
int64_t newTickCount;
|
int64_t newTickCount;
|
||||||
int64_t lastTickCount;
|
int64_t lastTickCount;
|
||||||
|
|||||||
@ -56,6 +56,13 @@ void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) {
|
|||||||
return;
|
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 W = Environment::projectionWidth;
|
||||||
|
|
||||||
const float nameX = 312 + 8;
|
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 bodyY = 232 - 78.0f;
|
||||||
|
|
||||||
const float bodyMaxWidthPx = 1280.0 - nameX - 48.f - 60.f;
|
const float bodyMaxWidthPx = 1280.0 - nameX - 48.f - 60.f;
|
||||||
|
|
||||||
|
|
||||||
float centeredShiftX = ((W - nameX - 48.f - 60.f) - bodyMaxWidthPx)*0.5;
|
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);
|
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 });
|
bodyRenderer->drawText(wrappedBody, bodyX + centeredShiftX, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f });
|
||||||
|
|
||||||
|
|
||||||
if (model.mode == PresentationMode::Choice) {
|
if (model.mode == PresentationMode::Choice) {
|
||||||
const float choiceStartY = 270.f-35;
|
const float choiceStartY = 270.f-35;
|
||||||
const float choiceHeight = 121.f*0.9;
|
const float choiceHeight = 121.f*0.9;
|
||||||
const float choiceSpacing = -30.f;
|
const float choiceSpacing = -30.f;
|
||||||
const float choiceWidth = 913.f * 0.9;
|
const float choiceWidth = 913.f * 0.9;
|
||||||
|
|
||||||
//UiRect textboxRect{ 30.f, -48.f, 1222.f, 340.0f };
|
|
||||||
|
|
||||||
|
|
||||||
if (choiceQuads.size() < model.choices.size()) {
|
if (choiceQuads.size() < model.choices.size()) {
|
||||||
choiceQuads.resize(model.choices.size());
|
choiceQuads.resize(model.choices.size());
|
||||||
}
|
}
|
||||||
@ -204,6 +208,141 @@ void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) {
|
|||||||
glDisable(GL_BLEND);
|
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) {
|
void DialogueOverlay::handlePointerDown(float x, float y, const PresentationModel& model) {
|
||||||
if (model.mode == PresentationMode::Choice) {
|
if (model.mode == PresentationMode::Choice) {
|
||||||
handlePointerMoved(x, y, model);
|
handlePointerMoved(x, y, model);
|
||||||
@ -241,8 +380,8 @@ bool DialogueOverlay::handlePointerReleased(float x, float y, const Presentation
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (model.mode == PresentationMode::Dialogue) {
|
if (model.mode == PresentationMode::Dialogue) {
|
||||||
outAdvanceDialogue = rectContains(lastDialogueAdvanceRect, x, y);
|
outAdvanceDialogue = true;
|
||||||
return outAdvanceDialogue;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -47,11 +47,18 @@ private:
|
|||||||
UiQuad textboxChoiceQuad;
|
UiQuad textboxChoiceQuad;
|
||||||
mutable std::vector<UiQuad> choiceQuads;
|
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);
|
std::shared_ptr<Texture> loadTextureCached(const std::string& path);
|
||||||
|
|
||||||
static std::string wrapTextToWidth(const std::string& input, const TextRenderer& textRenderer,
|
static std::string wrapTextToWidth(const std::string& input, const TextRenderer& textRenderer,
|
||||||
float maxWidthPx, float scale);
|
float maxWidthPx, float scale);
|
||||||
static bool rectContains(const UiRect& rect, float x, float y);
|
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
|
} // namespace ZL::Dialogue
|
||||||
|
|||||||
17
src/main.cpp
17
src/main.cpp
@ -38,14 +38,10 @@ void MainLoop() {
|
|||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
|
|
||||||
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
||||||
//delete g_game;
|
|
||||||
//g_game = nullptr;
|
|
||||||
return EM_TRUE;
|
return EM_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
||||||
//g_game = new ZL::Game();
|
|
||||||
//g_game->setup();
|
|
||||||
return EM_TRUE;
|
return EM_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +231,8 @@ int main(int argc, char *argv[]) {
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
constexpr int CONST_WIDTH = 1280;
|
constexpr int CONST_WIDTH = ZL::Environment::CONST_DEFAULT_WIDTH;
|
||||||
constexpr int CONST_HEIGHT = 720;
|
constexpr int CONST_HEIGHT = ZL::Environment::CONST_DEFAULT_HEIGHT;
|
||||||
|
|
||||||
ZL::Environment::width = CONST_WIDTH;
|
ZL::Environment::width = CONST_WIDTH;
|
||||||
ZL::Environment::height = CONST_HEIGHT;
|
ZL::Environment::height = CONST_HEIGHT;
|
||||||
@ -250,16 +246,9 @@ int main(int argc, char *argv[]) {
|
|||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
|
|
||||||
#ifdef FULLSCREEN
|
#ifdef FULLSCREEN
|
||||||
// Match the desktop's current resolution so the framebuffer matches
|
|
||||||
// the screen and we don't force a video mode change.
|
|
||||||
/*SDL_DisplayMode dm;
|
|
||||||
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
|
|
||||||
ZL::Environment::width = dm.w;
|
|
||||||
ZL::Environment::height = dm.h;
|
|
||||||
}*/
|
|
||||||
const Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP;
|
const Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
#else
|
#else
|
||||||
const Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI;
|
const Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ZL::Environment::window = SDL_CreateWindow(
|
ZL::Environment::window = SDL_CreateWindow(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user