saura quad map
This commit is contained in:
@@ -16,6 +16,10 @@
|
||||
{ "label": "Sanctuary (Full)", "action": "sanctuary" },
|
||||
{ "label": "Dam (Multi-water)", "action": "dam" },
|
||||
{ "label": "Waterfall (Flow)", "action": "waterfall" },
|
||||
{ "label": "Physics Playground","action": "playground" },
|
||||
{ "label": "Four-Quadrant Map", "action": "fourmap" },
|
||||
{ "label": "Builders (top-down)", "action": "builders" },
|
||||
{ "label": "Odyssey (Explore)", "action": "odyssey" },
|
||||
{ "label": "Level Editor", "action": "edit" },
|
||||
{ "label": "Quit", "action": "quit" }
|
||||
]
|
||||
|
||||
@@ -17161,6 +17161,7 @@ CMakeFiles/ModernEngine.dir/src/main.cpp.o
|
||||
/Users/will/Documents/zoomengine/src/Audio.h
|
||||
/Users/will/Documents/zoomengine/src/Engine.h
|
||||
/Users/will/Documents/zoomengine/src/Font.h
|
||||
/Users/will/Documents/zoomengine/src/Hud.h
|
||||
/Users/will/Documents/zoomengine/src/Input.h
|
||||
/Users/will/Documents/zoomengine/src/Json.h
|
||||
/Users/will/Documents/zoomengine/src/LevelEditor.cpp
|
||||
|
||||
@@ -17144,6 +17144,7 @@ CMakeFiles/ModernEngine.dir/src/main.cpp.o: /Users/will/Documents/zoomengine/src
|
||||
/Users/will/Documents/zoomengine/src/Audio.h \
|
||||
/Users/will/Documents/zoomengine/src/Engine.h \
|
||||
/Users/will/Documents/zoomengine/src/Font.h \
|
||||
/Users/will/Documents/zoomengine/src/Hud.h \
|
||||
/Users/will/Documents/zoomengine/src/Input.h \
|
||||
/Users/will/Documents/zoomengine/src/Json.h \
|
||||
/Users/will/Documents/zoomengine/src/LevelEditor.cpp \
|
||||
@@ -20153,6 +20154,8 @@ CMakeFiles/ModernEngine.dir/src/main.cpp.o: /Users/will/Documents/zoomengine/src
|
||||
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__type_traits/is_callable.h:
|
||||
|
||||
/Users/will/Documents/zoomengine/src/Hud.h:
|
||||
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__algorithm/iter_swap.h:
|
||||
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__cstddef/nullptr_t.h:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1451,6 +1451,7 @@ CMakeFiles/ModernEngine.dir/src/main.cpp.o: \
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/unordered_set \
|
||||
/Users/will/Documents/zoomengine/src/MainMenu.h \
|
||||
/Users/will/Documents/zoomengine/src/TextureCache.h \
|
||||
/Users/will/Documents/zoomengine/src/Hud.h \
|
||||
/Users/will/Documents/zoomengine/src/LevelEditor.cpp \
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/random \
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__random/bernoulli_distribution.h \
|
||||
|
||||
Binary file not shown.
@@ -255,21 +255,25 @@ void Engine::InitializeShader() {
|
||||
#version 410 core
|
||||
layout(location=0) in vec2 aPos;
|
||||
layout(location=1) in vec3 aColor;
|
||||
layout(location=2) in float aAlpha;
|
||||
uniform mat4 uOrtho;
|
||||
out vec3 vColor;
|
||||
out float vAlpha;
|
||||
void main() {
|
||||
vec4 pos = uOrtho * vec4(aPos, 0.0, 1.0);
|
||||
gl_Position = pos;
|
||||
vColor = aColor;
|
||||
vAlpha = aAlpha;
|
||||
}
|
||||
)";
|
||||
|
||||
const char* fs_overlay = R"(
|
||||
#version 410 core
|
||||
in vec3 vColor;
|
||||
in float vAlpha;
|
||||
out vec4 FragColor;
|
||||
void main() {
|
||||
FragColor = vec4(vColor, 1.0);
|
||||
FragColor = vec4(vColor, vAlpha);
|
||||
}
|
||||
)";
|
||||
overlay_program = CreateProgram(vs_overlay, fs_overlay);
|
||||
@@ -730,7 +734,8 @@ public:
|
||||
// TerrainPrimitive - heightmap terrain with procedural noise for landscapes
|
||||
class TerrainPrimitive : public Primitive {
|
||||
public:
|
||||
TerrainPrimitive(int resolution, float size, float maxHeight, const glm::vec3& baseColor) {
|
||||
TerrainPrimitive(int resolution, float size, float maxHeight, const glm::vec3& baseColor,
|
||||
float edgeFalloff = 0.0f, bool tintByBase = false) {
|
||||
color_ = baseColor;
|
||||
std::vector<float> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
@@ -747,10 +752,22 @@ public:
|
||||
h += std::sin(px * 0.1f) * std::cos(pz * 0.1f) * maxHeight * 0.5f;
|
||||
h += std::sin(px * 0.25f + 1.3f) * std::cos(pz * 0.3f + 0.7f) * maxHeight * 0.25f;
|
||||
h += std::sin(px * 0.6f + 2.1f) * std::cos(pz * 0.5f + 1.5f) * maxHeight * 0.125f;
|
||||
// Color by height: green low, brown mid, grey/white high
|
||||
// Edge falloff: taper heights to zero within edgeFalloff units of mesh edge
|
||||
if (edgeFalloff > 0.0f) {
|
||||
float dx_edge = halfSize - std::fabs(px);
|
||||
float dz_edge = halfSize - std::fabs(pz);
|
||||
float d = std::min(dx_edge, dz_edge);
|
||||
float f = std::clamp(d / edgeFalloff, 0.0f, 1.0f);
|
||||
f = f * f * (3.0f - 2.0f * f); // smoothstep
|
||||
h *= f;
|
||||
}
|
||||
// Color: either height-banded palette (default) or baseColor tinted by height
|
||||
float t = (h + maxHeight) / (2.0f * maxHeight);
|
||||
glm::vec3 col;
|
||||
if (t < 0.3f) col = glm::mix(glm::vec3(0.2f, 0.35f, 0.1f), glm::vec3(0.3f, 0.5f, 0.15f), t / 0.3f);
|
||||
if (tintByBase) {
|
||||
float k = 0.65f + 0.55f * t; // 0.65..1.2 brightness
|
||||
col = glm::clamp(baseColor * k, glm::vec3(0.0f), glm::vec3(1.5f));
|
||||
} else if (t < 0.3f) col = glm::mix(glm::vec3(0.2f, 0.35f, 0.1f), glm::vec3(0.3f, 0.5f, 0.15f), t / 0.3f);
|
||||
else if (t < 0.6f) col = glm::mix(glm::vec3(0.3f, 0.5f, 0.15f), glm::vec3(0.45f, 0.35f, 0.2f), (t - 0.3f) / 0.3f);
|
||||
else if (t < 0.85f)col = glm::mix(glm::vec3(0.45f, 0.35f, 0.2f), glm::vec3(0.5f, 0.5f, 0.5f), (t - 0.6f) / 0.25f);
|
||||
else col = glm::mix(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.95f, 0.95f, 0.98f), (t - 0.85f) / 0.15f);
|
||||
@@ -1241,7 +1258,10 @@ void Engine::RenderOverlay() {
|
||||
{0.3f, 0.8f, 0.3f}, 1.5f);
|
||||
}
|
||||
|
||||
// 9) Restore GL state
|
||||
// 9) Subclass overlay content (scene-specific HUD, message logs, etc.)
|
||||
OnRenderOverlay();
|
||||
|
||||
// 10) Restore GL state
|
||||
font_.end();
|
||||
}
|
||||
|
||||
@@ -1727,6 +1747,19 @@ std::string Engine::processAgentCommand(const json::Value& cmd) {
|
||||
if (cmd.has("flow_speed"))
|
||||
physics_.water_flow_speed = std::max(0.0f, cmd.get_float("flow_speed", 0.0f));
|
||||
|
||||
// Pool-bounds gating: only apply buoyancy inside a circle (for pools).
|
||||
if (cmd.has("bounds_center")) {
|
||||
glm::vec3 c = readVec3(cmd, "bounds_center", glm::vec3(0));
|
||||
physics_.water_bounds_center = glm::vec2(c.x, c.z);
|
||||
physics_.water_bounds_enabled = true;
|
||||
}
|
||||
if (cmd.has("bounds_radius")) {
|
||||
physics_.water_bounds_radius = std::max(0.1f, cmd.get_float("bounds_radius", 100.0f));
|
||||
physics_.water_bounds_enabled = true;
|
||||
}
|
||||
if (cmd.has("bounds_enabled"))
|
||||
physics_.water_bounds_enabled = cmd.get_bool("bounds_enabled", true);
|
||||
|
||||
RebuildWaterMesh(cmd.get_int("resolution", 128));
|
||||
return R"({"ok":true})";
|
||||
}
|
||||
@@ -2004,8 +2037,8 @@ std::shared_ptr<Primitive> Engine::CreateCone(float radius, float height, int se
|
||||
return std::make_shared<ConePrimitive>(radius, height, segments, color);
|
||||
}
|
||||
|
||||
std::shared_ptr<Primitive> Engine::CreateTerrain(int resolution, float size, float maxHeight, const glm::vec3& baseColor) {
|
||||
return std::make_shared<TerrainPrimitive>(resolution, size, maxHeight, baseColor);
|
||||
std::shared_ptr<Primitive> Engine::CreateTerrain(int resolution, float size, float maxHeight, const glm::vec3& baseColor, float edgeFalloff, bool tintByBase) {
|
||||
return std::make_shared<TerrainPrimitive>(resolution, size, maxHeight, baseColor, edgeFalloff, tintByBase);
|
||||
}
|
||||
|
||||
// GameObject management
|
||||
@@ -2038,6 +2071,25 @@ void Engine::SetCameraOrbit(float distance, float angle, float height) {
|
||||
);
|
||||
}
|
||||
|
||||
glm::vec3 Engine::ScreenToGround() const {
|
||||
glm::vec2 m = Input::GetMousePosition();
|
||||
float ndc_x = 2.0f * m.x / float(width_) - 1.0f;
|
||||
float ndc_y = 1.0f - 2.0f * m.y / float(height_);
|
||||
glm::mat4 view = glm::lookAt(camera_position_, camera_target_, camera_up_);
|
||||
glm::mat4 proj = glm::perspective(glm::radians(settings_.fov),
|
||||
float(width_) / height_, 0.1f, settings_.drawDistance);
|
||||
glm::mat4 inv = glm::inverse(proj * view);
|
||||
glm::vec4 a = inv * glm::vec4(ndc_x, ndc_y, -1.0f, 1.0f);
|
||||
glm::vec4 b = inv * glm::vec4(ndc_x, ndc_y, 1.0f, 1.0f);
|
||||
glm::vec3 p0 = glm::vec3(a) / a.w;
|
||||
glm::vec3 p1 = glm::vec3(b) / b.w;
|
||||
glm::vec3 d = glm::normalize(p1 - p0);
|
||||
if (std::abs(d.y) < 1e-5f) return glm::vec3(0);
|
||||
float t = -p0.y / d.y;
|
||||
if (t < 0) return glm::vec3(0);
|
||||
return p0 + d * t;
|
||||
}
|
||||
|
||||
// ---- Sky ----
|
||||
void Engine::InitSky() {
|
||||
const char* vs = R"(#version 410 core
|
||||
|
||||
10
src/Engine.h
10
src/Engine.h
@@ -31,6 +31,9 @@ public:
|
||||
virtual void OnShutdown();
|
||||
virtual void OnUpdate(float delta_time);
|
||||
virtual void OnRender();
|
||||
// Extensibility hook for subclasses to add text overlay content. Called
|
||||
// at the end of the engine's RenderOverlay pass with font_ already set up.
|
||||
virtual void OnRenderOverlay() {}
|
||||
|
||||
|
||||
void Run();
|
||||
@@ -44,7 +47,7 @@ public:
|
||||
std::shared_ptr<Primitive> CreateOBJModel(const std::string& objPath, const std::string& texturePath = "");
|
||||
std::shared_ptr<Primitive> CreateCylinder(float radius = 0.5f, float height = 2.0f, int segments = 16, const glm::vec3& color = glm::vec3(1.0f));
|
||||
std::shared_ptr<Primitive> CreateCone(float radius = 1.0f, float height = 2.0f, int segments = 16, const glm::vec3& color = glm::vec3(1.0f));
|
||||
std::shared_ptr<Primitive> CreateTerrain(int resolution = 64, float size = 100.0f, float maxHeight = 8.0f, const glm::vec3& baseColor = glm::vec3(0.3f, 0.5f, 0.2f));
|
||||
std::shared_ptr<Primitive> CreateTerrain(int resolution = 64, float size = 100.0f, float maxHeight = 8.0f, const glm::vec3& baseColor = glm::vec3(0.3f, 0.5f, 0.2f), float edgeFalloff = 0.0f, bool tintByBase = false);
|
||||
|
||||
|
||||
// GameObject management
|
||||
@@ -60,6 +63,11 @@ public:
|
||||
const glm::vec3& GetCameraPosition() const { return camera_position_; }
|
||||
const glm::vec3& GetCameraTarget() const { return camera_target_; }
|
||||
|
||||
// Unproject the current mouse cursor to the y=0 ground plane using the
|
||||
// active camera + default FOV. Returns (0,0,0) if the ray points away
|
||||
// from the plane.
|
||||
glm::vec3 ScreenToGround() const;
|
||||
|
||||
GLuint text_vao = 0, text_vbo = 0;
|
||||
|
||||
|
||||
|
||||
132
src/Font.h
132
src/Font.h
@@ -60,47 +60,59 @@ public:
|
||||
float getSpacing() const { return spacing_; }
|
||||
float getWeight() const { return weight_; }
|
||||
|
||||
// Draw a string. lineWidth < 0 means use the default weight.
|
||||
// Returns the total advance width in pixels.
|
||||
// Draw a string as filled triangle quads (each stroke thickened into a
|
||||
// ribbon). Returns total advance width in pixels.
|
||||
float drawText(const std::string& text, float x, float y, float size,
|
||||
const glm::vec3& color = glm::vec3(1.0f), float lineWidth = -1.0f) {
|
||||
if (text.empty()) return 0.0f;
|
||||
float lw = (lineWidth < 0) ? weight_ : lineWidth;
|
||||
float stroke = ((lineWidth < 0) ? weight_ : lineWidth) * 0.5f;
|
||||
|
||||
verts_.clear();
|
||||
float cx = x;
|
||||
float advance = size * spacing_;
|
||||
|
||||
for (char ch : text) {
|
||||
const Glyph& g = getGlyph(ch);
|
||||
for (size_t i = 0; i + 1 < g.points.size(); i += 2) {
|
||||
auto& p0 = g.points[i];
|
||||
auto& p1 = g.points[i + 1];
|
||||
verts_.push_back({cx + p0.x * size, y + p0.y * size, color.r, color.g, color.b});
|
||||
verts_.push_back({cx + p1.x * size, y + p1.y * size, color.r, color.g, color.b});
|
||||
glm::vec2 a(cx + g.points[i].x * size, y + g.points[i].y * size);
|
||||
glm::vec2 b(cx + g.points[i+1].x * size, y + g.points[i+1].y * size);
|
||||
emitQuad(a, b, stroke, color);
|
||||
}
|
||||
cx += advance;
|
||||
cx += advanceFor(ch, size);
|
||||
}
|
||||
|
||||
if (!verts_.empty()) {
|
||||
glBufferData(GL_ARRAY_BUFFER, verts_.size() * sizeof(Vertex),
|
||||
verts_.data(), GL_DYNAMIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)(2 * sizeof(float)));
|
||||
if (smooth_) {
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
||||
}
|
||||
glLineWidth(lw);
|
||||
glDrawArrays(GL_LINES, 0, (GLsizei)verts_.size());
|
||||
glLineWidth(1.0f);
|
||||
if (smooth_) glDisable(GL_LINE_SMOOTH);
|
||||
}
|
||||
|
||||
flushTriangles();
|
||||
return cx - x;
|
||||
}
|
||||
|
||||
// Outlined text: draws a dark halo behind the main stroke for legibility.
|
||||
// outlineExtra is extra pixels added on each side (not a multiplier), so
|
||||
// the main glyph shape stays legible.
|
||||
float drawOutlinedText(const std::string& text, float x, float y, float size,
|
||||
const glm::vec3& color = glm::vec3(1.0f),
|
||||
const glm::vec3& outline = glm::vec3(0.0f, 0.0f, 0.0f),
|
||||
float outlineExtra = 1.5f) {
|
||||
float base = (weight_ < 0 ? 2.0f : weight_);
|
||||
drawText(text, x, y, size, outline, base + outlineExtra * 2.0f);
|
||||
return drawText(text, x, y, size, color, base);
|
||||
}
|
||||
|
||||
// Filled rectangle — useful for HUD panel backgrounds.
|
||||
void fillRect(float x, float y, float w, float h,
|
||||
const glm::vec4& rgba) {
|
||||
verts_.clear();
|
||||
pushTri(x, y, x + w, y, x + w, y + h, glm::vec3(rgba), rgba.a);
|
||||
pushTri(x, y, x + w, y + h, x, y + h, glm::vec3(rgba), rgba.a);
|
||||
flushTriangles();
|
||||
}
|
||||
|
||||
// Rectangle outline as 4 thin ribbons.
|
||||
void strokeRect(float x, float y, float w, float h,
|
||||
const glm::vec4& rgba, float stroke = 1.5f) {
|
||||
float s = stroke * 0.5f;
|
||||
fillRect(x - s, y - s, w + 2*s, 2*s, rgba); // bottom
|
||||
fillRect(x - s, y + h - s, w + 2*s, 2*s, rgba); // top
|
||||
fillRect(x - s, y - s, 2*s, h + 2*s, rgba); // left
|
||||
fillRect(x + w - s, y - s, 2*s, h + 2*s, rgba); // right
|
||||
}
|
||||
|
||||
// Draw centered text
|
||||
float drawTextCentered(const std::string& text, float centerX, float y, float size,
|
||||
const glm::vec3& color = glm::vec3(1.0f), float lineWidth = -1.0f) {
|
||||
@@ -131,24 +143,65 @@ public:
|
||||
return y - cy;
|
||||
}
|
||||
|
||||
// Measure text width in pixels without drawing
|
||||
// Measure text width in pixels — respects per-glyph widths
|
||||
float measureText(const std::string& text, float size) const {
|
||||
return text.size() * size * spacing_;
|
||||
float w = 0;
|
||||
for (char c : text) w += advanceFor(c, size);
|
||||
return w;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Vertex { float x, y, r, g, b; };
|
||||
struct Vertex { float x, y, r, g, b, a; };
|
||||
struct Glyph { std::vector<glm::vec2> points; }; // pairs of line endpoints
|
||||
|
||||
GLuint vao_ = 0, vbo_ = 0, program_ = 0;
|
||||
float spacing_ = 0.75f; // character advance as fraction of size
|
||||
float weight_ = 2.0f; // default line width in pixels
|
||||
bool smooth_ = true; // anti-aliased line rendering
|
||||
float spacing_ = 0.95f; // character advance as fraction of size
|
||||
float weight_ = 1.8f; // default stroke thickness
|
||||
bool smooth_ = true;
|
||||
std::array<Glyph, 128> glyphs_;
|
||||
std::array<float, 128> widths_{}; // per-glyph advance width (fraction)
|
||||
std::vector<Vertex> verts_;
|
||||
|
||||
const Glyph& getGlyph(char c) const {
|
||||
float advanceFor(char c, float size) const {
|
||||
unsigned char uc = (unsigned char)c;
|
||||
float w = (uc < 128 && widths_[uc] > 0) ? widths_[uc] : spacing_;
|
||||
return size * w;
|
||||
}
|
||||
void pushTri(float x0, float y0, float x1, float y1, float x2, float y2,
|
||||
const glm::vec3& rgb, float a) {
|
||||
verts_.push_back({x0, y0, rgb.r, rgb.g, rgb.b, a});
|
||||
verts_.push_back({x1, y1, rgb.r, rgb.g, rgb.b, a});
|
||||
verts_.push_back({x2, y2, rgb.r, rgb.g, rgb.b, a});
|
||||
}
|
||||
void emitQuad(const glm::vec2& a, const glm::vec2& b, float half,
|
||||
const glm::vec3& color) {
|
||||
glm::vec2 d = b - a;
|
||||
float len = std::sqrt(d.x * d.x + d.y * d.y);
|
||||
if (len < 1e-5f) return;
|
||||
glm::vec2 n(-d.y / len * half, d.x / len * half); // perpendicular
|
||||
glm::vec2 p0 = a - n, p1 = a + n, p2 = b + n, p3 = b - n;
|
||||
pushTri(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, color, 1.0f);
|
||||
pushTri(p0.x, p0.y, p2.x, p2.y, p3.x, p3.y, color, 1.0f);
|
||||
}
|
||||
void flushTriangles() {
|
||||
if (verts_.empty()) return;
|
||||
glBufferData(GL_ARRAY_BUFFER, verts_.size() * sizeof(Vertex),
|
||||
verts_.data(), GL_DYNAMIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)(2 * sizeof(float)));
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)(5 * sizeof(float)));
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)verts_.size());
|
||||
}
|
||||
|
||||
const Glyph& getGlyph(char c) const {
|
||||
static const Glyph empty; // shared whitespace sentinel
|
||||
unsigned char uc = (unsigned char)c;
|
||||
if (uc == ' ' || uc == '\t') return empty;
|
||||
if (uc < 128 && !glyphs_[uc].points.empty()) return glyphs_[uc];
|
||||
return glyphs_[(unsigned char)'?'];
|
||||
}
|
||||
@@ -160,6 +213,17 @@ private:
|
||||
}
|
||||
|
||||
void buildGlyphs() {
|
||||
// Per-glyph advance widths (fraction of font size). Scale relative
|
||||
// to the active spacing so changing spacing_ preserves rhythm.
|
||||
for (int i = 0; i < 128; ++i) widths_[i] = spacing_;
|
||||
const float s = spacing_;
|
||||
widths_['i'] = widths_['I'] = widths_['!'] = widths_['|'] = widths_['.'] =
|
||||
widths_[','] = widths_[';'] = widths_[':'] = widths_['\''] = 0.45f * s;
|
||||
widths_['l'] = widths_['j'] = widths_['t'] = widths_['f'] = widths_['r'] = 0.60f * s;
|
||||
widths_['M'] = widths_['W'] = widths_['m'] = widths_['w'] = 1.40f * s;
|
||||
widths_[' '] = 0.70f * s;
|
||||
widths_['-'] = widths_['_'] = 0.85f * s;
|
||||
|
||||
// ---- Uppercase Letters ----
|
||||
{ auto& g = glyphs_['A'];
|
||||
L(g, 0,0, 0.5f,1); L(g, 0.5f,1, 1,0); L(g, 0.2f,0.45f, 0.8f,0.45f); }
|
||||
|
||||
184
src/Hud.h
Normal file
184
src/Hud.h
Normal file
@@ -0,0 +1,184 @@
|
||||
#pragma once
|
||||
#include "Font.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
|
||||
// Immediate-mode HUD builder.
|
||||
//
|
||||
// Hud hud(font, width, height);
|
||||
// hud.beginPanel(10, 10, 320, 120, {0,0,0,0.55f});
|
||||
// hud.label("Title", 16, {1,1,1});
|
||||
// hud.bar(200, 14, 0.72f, {0.1f,0.6f,0.2f,1}, {1,1,1,0.2f});
|
||||
// hud.label("72% throughput", 12, {0.9f,0.95f,0.9f});
|
||||
// hud.endPanel();
|
||||
// hud.flush();
|
||||
//
|
||||
// Panels are stacked rectangles with translucent backgrounds + optional
|
||||
// borders. Widgets inside flow top-down from the panel's top, padded by
|
||||
// `padding`. The font handles text rendering; the Hud draws its own rects
|
||||
// via the font's fillRect/strokeRect helpers.
|
||||
|
||||
class Hud {
|
||||
public:
|
||||
Hud(Font& font, int screen_w, int screen_h)
|
||||
: font_(font), w_(screen_w), h_(screen_h) {}
|
||||
|
||||
// --- Panel stacking ---------------------------------------------------
|
||||
void beginPanel(float x, float y, float w, float h,
|
||||
const glm::vec4& bg = glm::vec4(0, 0, 0, 0.55f),
|
||||
const glm::vec4& border = glm::vec4(1, 1, 1, 0.0f),
|
||||
float border_thickness = 1.0f) {
|
||||
Panel p{x, y, w, h, x + padding_, y + h - padding_ - 16.0f, bg, border, border_thickness};
|
||||
panels_.push_back(p);
|
||||
}
|
||||
void endPanel() {
|
||||
if (!panels_.empty()) {
|
||||
// Defer the actual draw so text is drawn on top of backgrounds in
|
||||
// the right order. For simplicity, draw bg now, widgets are already
|
||||
// placed, border last.
|
||||
panels_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Widgets ----------------------------------------------------------
|
||||
void label(const std::string& text, float size = 14.0f,
|
||||
const glm::vec3& color = glm::vec3(1.0f),
|
||||
bool outlined = false) {
|
||||
if (panels_.empty()) return;
|
||||
Panel& p = panels_.back();
|
||||
float y = p.cursor_y;
|
||||
draws_.push_back([this, text, p_x = p.cursor_x, y, size, color, outlined]() {
|
||||
if (outlined) font_.drawOutlinedText(text, p_x, y, size, color);
|
||||
else font_.drawText(text, p_x, y, size, color);
|
||||
});
|
||||
p.cursor_y -= (size + 4.0f);
|
||||
}
|
||||
|
||||
void labelRight(const std::string& text, float size,
|
||||
const glm::vec3& color = glm::vec3(1.0f)) {
|
||||
if (panels_.empty()) return;
|
||||
Panel& p = panels_.back();
|
||||
float right = p.x + p.w - padding_;
|
||||
float y = p.cursor_y;
|
||||
draws_.push_back([this, text, right, y, size, color]() {
|
||||
font_.drawTextRight(text, right, y, size, color);
|
||||
});
|
||||
p.cursor_y -= (size + 4.0f);
|
||||
}
|
||||
|
||||
// Progress bar: value in [0,1]. Width spans the panel minus padding.
|
||||
void bar(float height, float value,
|
||||
const glm::vec4& fill = glm::vec4(0.2f, 0.7f, 0.3f, 1.0f),
|
||||
const glm::vec4& bg = glm::vec4(1.0f, 1.0f, 1.0f, 0.15f),
|
||||
const glm::vec4& border_ = glm::vec4(1.0f, 1.0f, 1.0f, 0.35f)) {
|
||||
if (panels_.empty()) return;
|
||||
Panel& p = panels_.back();
|
||||
float x = p.cursor_x;
|
||||
float y = p.cursor_y - height + 10.0f;
|
||||
float w = p.w - 2 * padding_;
|
||||
float v = std::clamp(value, 0.0f, 1.0f);
|
||||
draws_.push_back([this, x, y, w, height, v, fill, bg, border_]() {
|
||||
font_.fillRect(x, y, w, height, bg);
|
||||
font_.fillRect(x, y, w * v, height, fill);
|
||||
font_.strokeRect(x, y, w, height, border_, 1.0f);
|
||||
});
|
||||
p.cursor_y -= (height + 6.0f);
|
||||
}
|
||||
|
||||
// Colored icon: solid rounded rect
|
||||
void icon(float w_icon, float h_icon, const glm::vec4& col) {
|
||||
if (panels_.empty()) return;
|
||||
Panel& p = panels_.back();
|
||||
float x = p.cursor_x;
|
||||
float y = p.cursor_y - h_icon + 10.0f;
|
||||
draws_.push_back([this, x, y, w_icon, h_icon, col]() {
|
||||
font_.fillRect(x, y, w_icon, h_icon, col);
|
||||
});
|
||||
p.cursor_y -= (h_icon + 4.0f);
|
||||
}
|
||||
|
||||
// Horizontal separator line
|
||||
void separator(float thickness = 1.0f,
|
||||
const glm::vec4& col = glm::vec4(1, 1, 1, 0.25f)) {
|
||||
if (panels_.empty()) return;
|
||||
Panel& p = panels_.back();
|
||||
float x = p.cursor_x;
|
||||
float y = p.cursor_y + 6.0f;
|
||||
float w = p.w - 2 * padding_;
|
||||
draws_.push_back([this, x, y, w, thickness, col]() {
|
||||
font_.fillRect(x, y, w, thickness, col);
|
||||
});
|
||||
p.cursor_y -= (thickness + 6.0f);
|
||||
}
|
||||
|
||||
void spacer(float h = 4.0f) {
|
||||
if (panels_.empty()) return;
|
||||
panels_.back().cursor_y -= h;
|
||||
}
|
||||
|
||||
// --- Free-form (outside panels) ---------------------------------------
|
||||
void rect(float x, float y, float w, float h, const glm::vec4& col) {
|
||||
draws_.push_back([this, x, y, w, h, col]() { font_.fillRect(x, y, w, h, col); });
|
||||
}
|
||||
void text(float x, float y, const std::string& t, float size,
|
||||
const glm::vec3& col, bool outlined = false) {
|
||||
draws_.push_back([this, x, y, t, size, col, outlined]() {
|
||||
if (outlined) font_.drawOutlinedText(t, x, y, size, col);
|
||||
else font_.drawText(t, x, y, size, col);
|
||||
});
|
||||
}
|
||||
|
||||
// Draw anchored panel background frames then queued widgets in order
|
||||
void flush() {
|
||||
for (auto& f : bg_draws_) f();
|
||||
for (auto& f : draws_) f();
|
||||
for (auto& f : border_draws_) f();
|
||||
bg_draws_.clear();
|
||||
draws_.clear();
|
||||
border_draws_.clear();
|
||||
panels_.clear();
|
||||
}
|
||||
|
||||
void setPadding(float p) { padding_ = p; }
|
||||
|
||||
private:
|
||||
struct Panel {
|
||||
float x, y, w, h;
|
||||
float cursor_x, cursor_y;
|
||||
glm::vec4 bg;
|
||||
glm::vec4 border;
|
||||
float border_t;
|
||||
};
|
||||
|
||||
Font& font_;
|
||||
int w_, h_;
|
||||
float padding_ = 12.0f;
|
||||
std::vector<Panel> panels_;
|
||||
std::vector<std::function<void()>> bg_draws_;
|
||||
std::vector<std::function<void()>> draws_;
|
||||
std::vector<std::function<void()>> border_draws_;
|
||||
|
||||
public:
|
||||
// Called instead of endPanel() when you want the panel bg drawn into the
|
||||
// background layer (so widget draws appear above it).
|
||||
void drawPanelBackgroundNow() {
|
||||
if (panels_.empty()) return;
|
||||
const Panel& p = panels_.back();
|
||||
glm::vec4 bg = p.bg, br = p.border;
|
||||
float x = p.x, y = p.y, w = p.w, h = p.h;
|
||||
float bt = p.border_t;
|
||||
if (bg.a > 0.0f) {
|
||||
bg_draws_.push_back([this, x, y, w, h, bg]() {
|
||||
font_.fillRect(x, y, w, h, bg);
|
||||
});
|
||||
}
|
||||
if (br.a > 0.0f) {
|
||||
border_draws_.push_back([this, x, y, w, h, br, bt]() {
|
||||
font_.strokeRect(x, y, w, h, br, bt);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -146,7 +146,14 @@ void World::IntegrateForces(float dt) {
|
||||
glm::vec3 total_torque = b.torque_accum;
|
||||
float submerged_frac_agg = 0.0f;
|
||||
|
||||
if (water_enabled && b.shape != Shape::Plane) {
|
||||
bool inside_pool = true;
|
||||
if (water_enabled && water_bounds_enabled) {
|
||||
glm::vec2 xz(b.position.x, b.position.z);
|
||||
glm::vec2 d = xz - water_bounds_center;
|
||||
if (glm::dot(d, d) > water_bounds_radius * water_bounds_radius)
|
||||
inside_pool = false;
|
||||
}
|
||||
if (water_enabled && inside_pool && b.shape != Shape::Plane) {
|
||||
if (b.shape == Shape::Sphere) {
|
||||
float r = b.half_extents.x;
|
||||
float wy = SampleWaterY(b.position.x, b.position.z);
|
||||
|
||||
@@ -93,6 +93,12 @@ public:
|
||||
glm::vec2 water_flow_dir = glm::vec2(0.0f, 0.0f);
|
||||
float water_flow_speed = 0.0f;
|
||||
|
||||
// Optional circular bounds on the buoyancy region (for pools). When
|
||||
// enabled, only bodies inside (center, radius) get buoyancy forces.
|
||||
bool water_bounds_enabled = false;
|
||||
glm::vec2 water_bounds_center = glm::vec2(0.0f);
|
||||
float water_bounds_radius = 0.0f;
|
||||
|
||||
// Gerstner sampler — mirrors the water shader exactly.
|
||||
float SampleWaterY(float x, float z) const;
|
||||
|
||||
|
||||
2843
src/main.cpp
2843
src/main.cpp
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user