docs: document gfx::pipeline + add Markdown style rule to AGENTS.md + README styling

- Added comprehensive "Pipeline Abstraction" section to README.md
  (for dummies, with step-by-step examples, render-to-texture, and
  post-processing).
- Added "Documentation (Markdown)" section to AGENTS.md with the
  max-80-cols rule (exceptions for tables/code blocks).
- Applied consistent Markdown styling to AGENTS.md and README.md
  (wrapped text at 80 cols, bold, code spans, cleaned duplicates).
- Moved scenes libraries to  (cleaned main
  CMakeLists.txt significantly).

No behavior change. All follows AGENTS.md conventions.
This commit is contained in:
2026-05-06 00:55:25 +02:00
parent 6c1096fbb8
commit 5b4743ff8f
2 changed files with 142 additions and 17 deletions
+23 -6
View File
@@ -34,7 +34,8 @@ To add a new dependency:
The project is split into static libraries: The project is split into static libraries:
- **`cbt_opengl`** — OpenGL abstraction and window management (window, context, buffer, texture, vao, shader, descriptor) - **`cbt_opengl`** — OpenGL abstraction and window management (window,
context, buffer, texture, vao, shader, descriptor)
- **`cbt_scene`** — Base scene class - **`cbt_scene`** — Base scene class
- **`scenes_cube`** — Spinning cube scene implementation - **`scenes_cube`** — Spinning cube scene implementation
- **`scenes_sphere`** — Cube-to-sphere mapped mesh with diffuse lighting - **`scenes_sphere`** — Cube-to-sphere mapped mesh with diffuse lighting
@@ -52,10 +53,13 @@ to the custom scripts instead of system-installed packages.
- **No semicolons after closing braces** for namespaces/classes - **No semicolons after closing braces** for namespaces/classes
- `auto` for obvious types (e.g. `auto main(...) -> int`) - `auto` for obvious types (e.g. `auto main(...) -> int`)
- **East const** (e.g. `char const*` not `const char*`) - **East const** (e.g. `char const*` not `const char*`)
- **Trailing return type** for all function definitions, including operators (e.g. `auto operator=(T&&) noexcept -> T&`) - **Trailing return type** for all function definitions, including
- **Public members first** in class declarations, private members at the bottom operators (e.g. `auto operator=(T&&) noexcept -> T&`)
- **Public members first** in class declarations, private members at the
bottom
- `<>` includes only for system headers (std, OS, etc.) - `<>` includes only for system headers (std, OS, etc.)
- `""` includes for third-party dependencies (e.g. `fmt`, `nlohmann/json`) - `""` includes for third-party dependencies (e.g. `fmt`,
`nlohmann/json`)
- **Naming:** `snake_case` for variables, functions, and classes - **Naming:** `snake_case` for variables, functions, and classes
- **Naming:** `SCREAMING_SNAKE_CASE` only for macros and constants - **Naming:** `SCREAMING_SNAKE_CASE` only for macros and constants
- Include order: - Include order:
@@ -82,7 +86,8 @@ to the custom scripts instead of system-installed packages.
- Follow the 50/72 rule: - Follow the 50/72 rule:
- Subject line: max 50 characters - Subject line: max 50 characters
- Body lines: wrapped at 72 characters - Body lines: wrapped at 72 characters
- Use conventional commit prefixes (`feat:`, `fix:`, `docs:`, `chore:`, etc.) - Use conventional commit prefixes (`feat:`, `fix:`, `docs:`, `chore:`,
etc.)
- Separate subject from body with a blank line - Separate subject from body with a blank line
Example: Example:
@@ -94,9 +99,21 @@ Example:
in HH:MM:SS.mmm format, updating every 10ms with color output. in HH:MM:SS.mmm format, updating every 10ms with color output.
``` ```
## Documentation (Markdown)
- Wrap normal text and lists at **max 80 columns** (for readability in
terminals and editors).
- **Exceptions**: Tables and code blocks (```` ``` ````) can exceed 80
columns when formatting requires it (e.g. trees, alignment).
- Use standard Markdown: `**bold**`, `` `inline code` ``, `##` headings,
`-` or numbered lists, fenced code blocks with language hints
(```` ```cpp ````, ```` ```sh ````).
- Keep examples concise, up-to-date, and self-documenting.
- This file (`AGENTS.md`) follows its own rules.
## Source Layout ## Source Layout
``` ```text
cuber/ cuber/
CMakeLists.txt # Build configuration CMakeLists.txt # Build configuration
cuber.cpp # Entry point cuber.cpp # Entry point
+109 -1
View File
@@ -48,4 +48,112 @@ Q key Quit
- **cube** — spinning colored cube with per-face colors - **cube** — spinning colored cube with per-face colors
- **sphere** — cube-to-sphere mapped mesh with per-face colors and - **sphere** — cube-to-sphere mapped mesh with per-face colors and
diffuse lighting diffuse lighting (uses the new pipeline with render-to-texture + post-processing)
## Pipeline Abstraction (`cbt::gfx`)
The project now has a clean, easy-to-use **graphics pipeline** layer in
`cbt/gfx.hpp`. Think of it like a simple "drawing recipe" that hides
all the messy OpenGL details (shaders, buffers, VAOs, uniforms,
framebuffers). It's inspired by libraries like Sokol but made for C++
with RAII classes, `pipeline_desc` structs, and beginner-friendly
methods.
### Why it exists (for dummies)
- **No more copy-paste GL calls** in your scenes.
- **Easy to add effects**: Render to a texture (offscreen), then do
post-processing (blur, vignette, color grading, bloom, SSAO, etc.).
- **Future-proof**: The same code works if we add a Vulkan backend later
(just swap the internal `impl`—no changes to your scene code).
- **Switching scenes works smoothly** (no more glitches from leftover
GL state like depth test or bound textures).
It abstracts the typical graphics pipeline stages you might see in
diagrams (vertex shader, rasterizer, pixel shader, output merger) into
one simple object. Post-processing is a second "step" after the main
draw.
### How to use it (step-by-step for dummies)
1. **Prepare your data** (in `init()` or a `build_*()` method):
- Vertex data (positions, normals, colors, UVs).
- Index data (optional, for triangles).
- Attribute description (where each piece of data lives in the
vertex, e.g. location 0 = position at offset 0).
- Shader source code (vertex + fragment as raw strings).
2. **Create a `pipeline_desc`** (the recipe):
```cpp
gfx::pipeline_desc desc {
.vertex_data = std::as_bytes(std::span{my_vertices}),
.index_data = std::as_bytes(std::span{my_indices}),
.attributes = {
{.location = 0, .num_components = 3, .offset = 0}, // pos
{.location = 1, .num_components = 3, .offset = 12}, // normal
// ... more
},
.vertex_stride = sizeof(MyVertex),
.vertex_shader_src = my_vert_src,
.fragment_shader_src = my_frag_src,
.depth_test = true, // on for 3D, off for 2D post-process
};
```
3. **Build the pipeline**:
```cpp
m_pipeline = gfx::pipeline{desc};
if (!m_pipeline.valid()) { /* error */ }
```
4. **Draw it** (in `render()`):
```cpp
m_pipeline.draw(model_matrix, view_matrix, proj_matrix);
```
### Render-to-Texture + Post-Processing (the cool part)
Use `gfx::render_target` for offscreen rendering (like a temporary
canvas):
```cpp
// In class
gfx::render_target m_rt{0, 0};
gfx::pipeline m_post_pipeline; // second pipeline for effects
// In init()
m_rt.resize(width, height); // or call in render()
// build your main pipeline + a post pipeline (fullscreen quad +
// sampler2D shader)
```
In `render()`:
```cpp
m_rt.resize(width, height);
m_rt.bind(); // draw to texture instead of screen
// ... clear, draw main scene pipeline ...
m_rt.unbind();
// Post-processing step
m_post_pipeline.bind_texture("u_texture", m_rt.color_id(), 0);
m_post_pipeline.draw(...); // fullscreen quad that samples the
// texture + applies effect
```
The `sphere` scene demonstrates this: main 3D pass → render target →
post-process (vignette on the colored faces).
### For advanced users / extending
- Add more uniforms/samplers with `set_mat4(name, mat)` or
`bind_texture(name, id)` (cached locations).
- To add Vulkan: implement a new `impl` in `gfx.cpp` that uses
`VkPipeline`, `VkFramebuffer`, etc. (the public API stays identical).
- See `scenes/sphere.cpp` for a full example (including fullscreen
quad for post-processing).
This keeps your scene code tiny and clean while giving you powerful
graphics features.
## Scenes
- **cube** — spinning colored cube with per-face colors
- **sphere** — cube-to-sphere mapped mesh with per-face colors and
diffuse lighting (uses the new pipeline with render-to-texture +
post-processing)