#pragma once #include #include #include #include namespace plot::render { /// CPU-side staging for one instanced-AA line draw call. /// /// `begin()` starts a frame and `clear()` empties the batch; neither releases /// the store, and both leave capacity alone so a steady-state frame re-uses the /// previous allocation instead of reallocating per frame. The renderer re-uses /// one batch for the whole run and uploads `data()` once per frame. /// /// The payload is interleaved `x0,y0,x1,y1` per segment — exactly the /// per-instance attribute layout the line shader declares (see buffer.hpp and /// shaders.hpp), so the intersection of `data()` with an `f32` span can be /// uploaded verbatim. class LineBatch { public: /// Starts a frame: empties the batch, keeps its capacity. void begin() noexcept; /// Same as `begin()`, so the render loop can read as begin/stage/draw. void clear() noexcept; /// Stages one segment: one instance, two quad corners per end. void add_segment(Vec2 a, Vec2 b); /// Stages `n - 1` segments joining consecutive points. A null pointer or /// fewer than two points stages nothing. void add_polyline(const Vec2* pts, u32 n); /// Segments staged so far, one instance each. u32 segment_count() const noexcept { return static_cast(v_.size() / 4u); } /// Vertices the pipeline will rasterise: two per staged segment. u32 vertex_count() const noexcept { return segment_count() * 2u; } /// Interleaved `x0,y0,x1,y1`, four floats per staged segment. const std::vector& data() const noexcept { return v_; } private: std::vector v_; }; /// CPU-side staging for one instanced text draw call. /// /// Like `LineBatch` it keeps its capacity across frames. Each staged glyph is /// one instance: the glyph quad's top-left corner in pixels, top-left origin /// like `plot::Rect`, plus the glyph's atlas texel origin, interleaved as /// `x,y,u,v` — the layout the text shader declares (see buffer.hpp). class TextBatch { public: /// Starts a frame: empties the batch, keeps its capacity. void begin() noexcept; /// Same as `begin()`. void clear() noexcept; /// Stages one glyph cell at `pos_px` (its top-left corner), reading the cell /// from `slot` in the font atlas (see `BitmapFont::atlas_origin`). `slot` /// indexes the atlas, not a codepoint. void add_glyph(Vec2 pos_px, u32 slot); /// Stages every codepoint of `utf8` and returns the pen past the last glyph, /// so the run is exactly `font.measure(utf8) * px_per_cell / /// BitmapFont::kGlyphPx` pixels wide — the width the draw covers when it is /// told the same cell size as `u_px_range`. /// /// `px_per_cell` is the on-screen size of one glyph cell: pass /// `BitmapFont::kGlyphPx` for 1:1 text, and anything else to scale the run. /// The glyph quad itself is sized by the shader's `u_px_range`, which must be /// the same value. A null or empty string stages nothing and returns `pen_px`. Vec2 add_text(Vec2 pen_px, const char* utf8, const BitmapFont& font, f32 px_per_cell = static_cast(BitmapFont::kGlyphPx)); /// Glyphs staged so far, one instance each. u32 glyph_count() const noexcept { return static_cast(v_.size() / 4u); } /// Interleaved `x,y,u,v`, four floats per staged glyph. const std::vector& data() const noexcept { return v_; } private: std::vector v_; }; } // namespace plot::render