- BillboardInstance: per-instance uv sub-rect, feet anchor, turn, dithered alpha, flash; BillboardBatch::sharedTexture; Texture createEmpty/update/cellUV/cellAnchor/nearest - SpriteAtlas: fixed-cell runtime atlas with gutters - hd2d::ui: Octopath-style panels, fonts (Cinzel, EB Garamond), dialogue box with typewriter + portraits + choices, menus, nameplates, toasts, ImGui theme - DayCycle (dawn/day/dusk/night keyframes, sun + moon arcs), Weather overcast/mist/ lightning, night-aware presets, stars and moon in the sky - Hidden-window offscreen captures (Window visible=false, Renderer output FBO, OffscreenTarget); --screenshot never opens a window - Camera::worldToScreen; CMake usable via add_subdirectory (HD2D_BUILD_DEMO, HD2D_ENGINE_ASSET_DIR)
163 lines
7.0 KiB
C++
163 lines
7.0 KiB
C++
#pragma once
|
|
// HD-2D style game UI (Octopath Traveler / Triangle Strategy look) drawn with
|
|
// Dear ImGui draw lists: translucent navy panels with a gold double border and
|
|
// corner gems, serif text with a soft shadow, name plates, menus, progress
|
|
// bars, a typewriter dialogue box with choices, and toast notifications.
|
|
//
|
|
// Everything draws into an ImDrawList (usually ImGui::GetForegroundDrawList()),
|
|
// so no ImGui windows are needed. Sizes are in pixels at a 1080 px tall
|
|
// viewport; multiply by scaleFor(viewport) (the widgets taking `scale` or a
|
|
// viewport do this for you).
|
|
//
|
|
// Setup: DebugUI::init(), then ui::loadFonts(dir, ui::scaleFor(viewport))
|
|
// before the first ImGui frame (or between frames to rebake after a resize).
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
namespace hd2d::ui {
|
|
|
|
// Game fonts. `body` dialogue/menu text (serif), `title` large display face,
|
|
// `name` speaker / name plates (small caps serif), `small` captions and toasts.
|
|
// ImGui's default font stays the default so debug panels are unaffected.
|
|
struct Fonts {
|
|
ImFont* body = nullptr;
|
|
ImFont* title = nullptr;
|
|
ImFont* name = nullptr;
|
|
ImFont* small = nullptr;
|
|
};
|
|
|
|
// Nominal font sizes (px at 1080p). Fonts are baked at size * pixelScale so
|
|
// text drawn at size * scaleFor(viewport) is pixel-exact.
|
|
inline constexpr float kBodySize = 34.0f;
|
|
inline constexpr float kTitleSize = 96.0f;
|
|
inline constexpr float kNameSize = 28.0f;
|
|
inline constexpr float kSmallSize = 26.0f;
|
|
|
|
// Loads the fonts from `fontDir` (assets/fonts) baked for `pixelScale` and
|
|
// rebuilds the ImGui font atlas. Requires an ImGui context (DebugUI::init)
|
|
// and must be called outside an ImGui frame. Returns false if a font file is
|
|
// missing (the missing slots fall back to the default font).
|
|
bool loadFonts(const std::string& fontDir, float pixelScale = 1.0f);
|
|
const Fonts& fonts();
|
|
|
|
// Styles standard ImGui widgets (windows, buttons, input text, sliders, combo,
|
|
// checkboxes, tabs, scrollbars) in the same navy + gold palette.
|
|
void applyTheme();
|
|
|
|
// UI scale for a viewport in pixels: 1.0 at 1080 px tall.
|
|
float scaleFor(ImVec2 viewport);
|
|
|
|
// Shared palette.
|
|
inline constexpr ImU32 kGold = IM_COL32(222, 189, 120, 255);
|
|
inline constexpr ImU32 kGoldDim = IM_COL32(150, 122, 74, 255);
|
|
inline constexpr ImU32 kTextColor = IM_COL32(246, 240, 226, 255);
|
|
inline constexpr ImU32 kTextMuted = IM_COL32(184, 178, 164, 255);
|
|
|
|
// `alpha` is the background opacity; `tint` modulates the border/ornament
|
|
// colour and its alpha fades the whole panel (shadow, background, border).
|
|
// `rounding` is in 1080p pixels; `scale` 0 = scaleFor(ImGui display size).
|
|
struct PanelStyle {
|
|
float alpha = 0.88f;
|
|
bool ornate = true;
|
|
float rounding = 6.0f;
|
|
ImU32 tint = IM_COL32(255, 255, 255, 255);
|
|
float scale = 0.0f;
|
|
};
|
|
void panel(ImDrawList* dl, ImVec2 min, ImVec2 max, const PanelStyle& style = {});
|
|
|
|
// Text with a 1px dark drop shadow. `font` may be null (current ImGui font).
|
|
void text(ImDrawList* dl, ImFont* font, float size, ImVec2 pos, ImU32 color, std::string_view s, bool shadow = true);
|
|
// Word-wrapped variant of text(); wrapWidth <= 0 disables wrapping.
|
|
void textWrapped(ImDrawList* dl, ImFont* font, float size, ImVec2 pos, ImU32 color, std::string_view s,
|
|
float wrapWidth, bool shadow = true);
|
|
ImVec2 measure(ImFont* font, float size, std::string_view s, float wrapWidth = 0.0f);
|
|
void textCentered(ImDrawList* dl, ImFont* font, float size, ImVec2 center, ImU32 color, std::string_view s,
|
|
bool shadow = true);
|
|
|
|
// Big glowing gold title with flourish rules underneath (title screens).
|
|
void titleText(ImDrawList* dl, ImVec2 center, std::string_view s, float size);
|
|
|
|
// Small pill label whose pointer tip sits on `anchorPx` (e.g. a projected
|
|
// head position). `accent` colours the border and pointer.
|
|
void nameplate(ImDrawList* dl, ImVec2 anchorPx, std::string_view s, ImU32 accent, float scale = 1.0f);
|
|
|
|
// Vertical option list in a panel with an animated cursor on `selected`.
|
|
void menu(ImDrawList* dl, ImVec2 center, const std::vector<std::string>& items, int selected,
|
|
std::string_view title = {}, float scale = 1.0f);
|
|
// Same as menu(), placed so that `pivot` (0..1 of the panel size) lands on `pos`.
|
|
void menuAt(ImDrawList* dl, ImVec2 pos, ImVec2 pivot, const std::vector<std::string>& items, int selected,
|
|
std::string_view title = {}, float scale = 1.0f);
|
|
// Panel size menu()/menuAt() will use.
|
|
ImVec2 menuSize(const std::vector<std::string>& items, std::string_view title = {}, float scale = 1.0f);
|
|
|
|
// Recessed bar filled to t (0..1) with a vertical gradient of `color`.
|
|
void progressBar(ImDrawList* dl, ImVec2 min, ImVec2 max, float t, ImU32 color);
|
|
|
|
// Small blinking/bobbing gem pointing down ("more text" indicator); `time` drives the animation.
|
|
void advanceIndicator(ImDrawList* dl, ImVec2 center, float scale, float time);
|
|
|
|
struct DialogueLine {
|
|
std::string speaker; // empty = no name plate
|
|
std::string text; // UTF-8, word-wrapped to the box
|
|
ImTextureID portrait = 0; // optional, drawn square on the left inside the box
|
|
ImVec2 uv0{0, 0}, uv1{1, 1};
|
|
};
|
|
|
|
// Bottom-centre typewriter dialogue box. Typical loop:
|
|
// if (confirmPressed && box.advance()) handle(box.choice());
|
|
// box.update(dt); box.draw(viewport);
|
|
class DialogueBox {
|
|
public:
|
|
void start(std::vector<DialogueLine> lines, std::vector<std::string> choices = {}); // choices shown with the last line
|
|
bool isOpen() const { return open_; }
|
|
void update(float dt);
|
|
bool advance(); // reveal the rest of the line, else next line; returns true when the conversation closed
|
|
void moveChoice(int delta);
|
|
int choice() const { return chosen_; } // chosen index once closed with choices, else -1
|
|
void close();
|
|
void draw(ImVec2 viewport); // draws into ImGui::GetForegroundDrawList()
|
|
|
|
bool lineRevealed() const; // current line fully shown
|
|
bool choicesVisible() const; // last line revealed and choices pending
|
|
|
|
float charsPerSecond = 48.0f;
|
|
float sentencePause = 0.22f; // extra seconds after . ! ? and an ellipsis
|
|
float clausePause = 0.09f; // extra seconds after , ; : and dashes
|
|
|
|
private:
|
|
void stepChar();
|
|
|
|
std::vector<DialogueLine> lines_;
|
|
std::vector<std::string> choices_;
|
|
size_t line_ = 0;
|
|
size_t revealed_ = 0; // bytes of the current line shown (always on a UTF-8 boundary)
|
|
float budget_ = 0.0f; // seconds banked for the typewriter
|
|
float time_ = 0.0f;
|
|
int selected_ = 0;
|
|
int chosen_ = -1;
|
|
bool open_ = false;
|
|
};
|
|
|
|
// Stacked notification banners at the top centre that slide in and fade out.
|
|
class Toasts {
|
|
public:
|
|
void push(std::string text, ImU32 accent = IM_COL32(230, 190, 90, 255), float seconds = 3.5f);
|
|
void update(float dt);
|
|
void draw(ImVec2 viewport); // draws into ImGui::GetForegroundDrawList()
|
|
void clear() { items_.clear(); }
|
|
|
|
private:
|
|
struct Item {
|
|
std::string text;
|
|
ImU32 accent;
|
|
float seconds, age = 0.0f;
|
|
};
|
|
std::vector<Item> items_;
|
|
};
|
|
|
|
} // namespace hd2d::ui
|