prog
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
# Display Refactor Progress
|
||||||
|
|
||||||
|
## Immediate Goal
|
||||||
|
Render a 32x32 white square through the new render opcode stack on Linux.
|
||||||
|
|
||||||
|
## Architecture (summary)
|
||||||
|
See `.claude/display-refactor.md` for the full design.
|
||||||
|
|
||||||
|
- `src/dusk/render/` -- opcode format + buffer + submission API (the *contract*).
|
||||||
|
- Platform backends (e.g. `src/duskgl/`) consume the buffer and translate to native API calls.
|
||||||
|
- `src/dusk/display/` -- orchestration shell only: `displayInit`, `displayUpdate`, `displayDispose`.
|
||||||
|
- Scenes call `renderSprite(...)`, `renderClear(...)`. The backend executes the intent.
|
||||||
|
|
||||||
|
## Opcode format (32 bytes)
|
||||||
|
Every command starts with a 4-byte `ropheader_t` (opcode, flags, depth). Two commands defined:
|
||||||
|
- `ROP_CLEAR` (32 bytes) -- clear with a color.
|
||||||
|
- `ROP_DRAW_SPRITE` (32 bytes) -- screen-space int16 x/y/w/h + tint color.
|
||||||
|
|
||||||
|
## Milestone 1 -- Archive + strip existing display deps ✓
|
||||||
|
- [x] Old `src/dusk/display/` archived (now deleted from working tree via git).
|
||||||
|
- [x] Old `src/duskgl/display/` removed (new GL renderer replaces it).
|
||||||
|
- [x] `engine.c` stripped to minimal subsystems, set to `SCENE_TYPE_TEST`.
|
||||||
|
- [x] `scene.c` stripped of old display/shader/screen references.
|
||||||
|
- [x] `console.c` stripped of display deps.
|
||||||
|
- [x] `ui/CMakeLists.txt` gutted (re-implementation deferred).
|
||||||
|
- [x] `asset/loader/CMakeLists.txt` -- display loaders disabled.
|
||||||
|
- [x] `asset/loader/assetloader.h` -- display loader types removed.
|
||||||
|
- [x] `rpg/overworld/chunk.h` -- mesh_t / meshvertex_t removed.
|
||||||
|
- [x] `rpg/overworld/map.c` -- mesh/spritebatch calls removed.
|
||||||
|
- [x] `scene/overworld/sceneoverworld.c` -- stubbed to empty callbacks.
|
||||||
|
- [x] Test suite display tests disabled.
|
||||||
|
|
||||||
|
## Milestone 2 -- Render opcode system ✓
|
||||||
|
- [x] `src/dusk/render/rop.h` -- `ropheader_t`, `ropclear_t`, `ropsprite_t`.
|
||||||
|
- [x] `src/dusk/render/ropbuffer.h/.c` -- `ROPBUFFER` global, reset, alloc.
|
||||||
|
- [x] `src/dusk/render/render.h/.c` -- `renderClear()`, `renderSprite()`.
|
||||||
|
- [x] `src/dusk/render/CMakeLists.txt`.
|
||||||
|
|
||||||
|
## Milestone 3 -- New minimal display shell ✓
|
||||||
|
- [x] `src/dusk/display/display.h/.c` -- init/update/dispose, calls platform hooks.
|
||||||
|
- [x] `src/dusk/display/displaystate.h` -- cull/depth/blend flags.
|
||||||
|
- [x] `src/dusk/display/color.csv` + `CMakeLists.txt` -- color generation kept.
|
||||||
|
|
||||||
|
## Milestone 4 -- GL backend ✓
|
||||||
|
- [x] `src/duskgl/render/rendergl.h/.c`:
|
||||||
|
- GL 3.3 core shader (ortho projection, solid color, no texture yet).
|
||||||
|
- `renderGLInit` -- creates VAO/VBO/shader.
|
||||||
|
- `renderGLFlush(buf, w, h)` -- walks ROPBUFFER, GL calls per opcode.
|
||||||
|
- `ROP_CLEAR` → `glClearColor` + `glClear`.
|
||||||
|
- `ROP_DRAW_SPRITE` → 6-vertex quad, `glDrawArrays`.
|
||||||
|
- [x] `src/duskgl/error/errorgl.h/.c` -- `errorGLCheck`.
|
||||||
|
- [x] `src/duskgl/CMakeLists.txt`.
|
||||||
|
- [x] `src/dusksdl2/display/displaysdl2.h/.c` updated:
|
||||||
|
- `displaySDL2Init` -- SDL2 window + GL 3.3 context + `renderGLInit`.
|
||||||
|
- `displaySDL2Flush(ropbuffer_t *)` -- MakeCurrent + `renderGLFlush`.
|
||||||
|
- `displaySDL2Swap` -- SDL_GL_SwapWindow.
|
||||||
|
- [x] `src/dusklinux/display/displayplatform.h` updated with new macros.
|
||||||
|
|
||||||
|
## Milestone 5 -- Test scene ✓
|
||||||
|
- [x] `SCENE_TYPE_TEST` added to `scenetype.h/.c`.
|
||||||
|
- [x] `src/dusk/scene/test/scenetest.h/.c`:
|
||||||
|
- `renderClear(color(32, 32, 48, 255))` -- dark blue-grey background.
|
||||||
|
- `renderSprite(100, 100, 32, 32, COLOR_WHITE)` -- 32x32 white square.
|
||||||
|
- [x] `engine.c` starts with `SCENE_TYPE_TEST`.
|
||||||
|
|
||||||
|
## Milestone 6 -- Verified ✓
|
||||||
|
- [x] Build succeeds with no errors (2026-06-18).
|
||||||
|
- [x] Engine initializes: SDL window + GL context + shader + test scene.
|
||||||
|
- [x] No crashes running for 5+ seconds.
|
||||||
|
- [ ] 32x32 white square visually confirmed on screen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status: BUILD PASSING -- awaiting visual confirmation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Decisions log
|
||||||
|
|
||||||
|
**2026-06-18** -- `color_t = color4b_t` (from generated `display/color.h`). The color generation pipeline (color.csv + Python tool) is kept in the new minimal `src/dusk/display/CMakeLists.txt`.
|
||||||
|
|
||||||
|
**2026-06-18** -- `ROP_SIZE = 32`. All opcodes fixed 32 bytes. 3D quads will be 64 bytes when added later.
|
||||||
|
|
||||||
|
**2026-06-18** -- Depth sort deferred. Buffer stores unsorted commands; painter platforms sort on flush. GL uses Z-buffer.
|
||||||
|
|
||||||
|
**2026-06-18** -- Texture system not yet wired into the opcode pipeline. `ROP_DRAW_SPRITE` with `texture=0` uses solid tint color only (no sampler). Texture handle system comes next.
|
||||||
|
|
||||||
|
**2026-06-18** -- GL backend uses GL 3.3 Core profile. Shader takes screen-space pixel coordinates and converts to clip space using window size queried from SDL each frame.
|
||||||
|
|
||||||
|
**2026-06-18** -- `ROPBUFFER` is a global (4096 slots × 32 bytes = 128 KB). Reset at start of each frame in `displayUpdate`.
|
||||||
|
|
||||||
|
**2026-06-18** -- `ui/`, `rpg/overworld` display code, asset display loaders all temporarily stubbed/disabled. Will be rewritten against the new render API.
|
||||||
@@ -1,714 +0,0 @@
|
|||||||
# Display Layer Refactor
|
|
||||||
|
|
||||||
## Vision
|
|
||||||
|
|
||||||
The goal is to remove the implicit assumption that all platforms render
|
|
||||||
through a GL-like API, and replace it with a system where each platform
|
|
||||||
owns its rendering stack completely. The scene describes *what* to draw
|
|
||||||
in platform-neutral terms; the platform decides *how* to draw it.
|
|
||||||
|
|
||||||
This unlocks:
|
|
||||||
- Saturn (VDP1/VDP2 command-list, no Z-buffer, affine-only)
|
|
||||||
- PlayStation 1 (ordering table, affine textures, GTE fixed-point, CMake SDK)
|
|
||||||
- Nintendo 64 (RSP display list, hardware Z-buffer, perspective-correct,
|
|
||||||
real FPU -- closer to modern GL than to Saturn)
|
|
||||||
- SNES (PPU tile engine, Mode7 for overworld, no real 3D)
|
|
||||||
- Vulkan (explicit, modern, no legacy GL baggage)
|
|
||||||
- Native PSP GU (drop PSPGL which is just a compatibility shim)
|
|
||||||
- Legacy fixed-function GL as its own standalone target
|
|
||||||
- A real first-class 2D UI system not bolted onto 3D space
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why
|
|
||||||
|
|
||||||
### The current abstraction assumes GPU-style rendering
|
|
||||||
|
|
||||||
The current display layer was designed around a GL-like mental model:
|
|
||||||
vertex buffers, shaders, Z-buffered triangle rasterization, and texture
|
|
||||||
objects. `duskgl` implements this with real OpenGL. `duskdolphin` does its
|
|
||||||
own GX thing but still matches the same interface (mesh, shader, texture,
|
|
||||||
framebuffer). PSP uses PSPGL -- a library that *emulates* GL on top of
|
|
||||||
the PSP's native GE/GU hardware, which is entirely different underneath.
|
|
||||||
|
|
||||||
Problems this creates:
|
|
||||||
|
|
||||||
**PSPGL is a lie.** The PSP has a native graphics engine (GE/GU) with its
|
|
||||||
own command list, its own vertex formats, and its own display list model.
|
|
||||||
PSPGL translates GL calls into GU calls, but imperfectly -- and we end up
|
|
||||||
paying the abstraction cost without getting GL correctness. Writing directly
|
|
||||||
to GU gives better performance, access to native formats, and correct
|
|
||||||
behavior on edge cases that PSPGL gets wrong.
|
|
||||||
|
|
||||||
**Legacy GL should not share code with modern GL.** The fixed-function
|
|
||||||
pipeline (no shaders, matrix stacks via glMatrixMode, glTexEnv) is
|
|
||||||
meaningfully different from modern GL (VAO/VBO, GLSL, explicit uniform
|
|
||||||
locations). Treating them as "the same thing with a flag" creates a tangle
|
|
||||||
of `#ifdef DUSK_OPENGL_LEGACY` guards throughout the rendering code.
|
|
||||||
They are separate targets and should be separate platform directories.
|
|
||||||
|
|
||||||
**Saturn cannot fit the model at all.** VDP1 is a command-list processor:
|
|
||||||
you write 32-byte command structs (sprites, quads, lines) into VRAM, then
|
|
||||||
poke a register to trigger execution. There are no vertex buffers, no
|
|
||||||
shaders, no Z-buffer. Depth is pure painter's algorithm -- command order
|
|
||||||
IS the depth. VDP2 composites up to 6 background planes at scanline time;
|
|
||||||
these are tile maps and rotation parameter tables, not meshes. Nothing
|
|
||||||
about the current API maps onto this hardware.
|
|
||||||
|
|
||||||
**SNES is even further removed.** The PPU renders tiles. VRAM holds 8x8
|
|
||||||
or 16x16 pixel tiles and tile maps; the PPU references these during
|
|
||||||
scanline rendering. There are no draw calls. Mode7 is an affine transform
|
|
||||||
applied to a single background layer (the basis for the overworld map and
|
|
||||||
road perspective effects). Sprites are entries in OAM (Object Attribute
|
|
||||||
Memory). The 65816 CPU writes to memory-mapped registers and VRAM; the
|
|
||||||
PPU does the rest. The concept of "mesh" or "shader" is meaningless here.
|
|
||||||
|
|
||||||
**Textures loaded as RGBA waste memory and exclude platforms.** Loading
|
|
||||||
every texture as 32-bit RGBA and converting at runtime is expensive on
|
|
||||||
memory-constrained platforms (Saturn has ~1 MB total RAM; SNES has 64 KB
|
|
||||||
VRAM) and simply wrong for platforms that have native formats incompatible
|
|
||||||
with RGBA (e.g., PSP's ABGR8888 / BGR5650, Saturn's RGB555 / CI4 / CI8,
|
|
||||||
SNES's 2bpp/4bpp/8bpp indexed). The asset pipeline must compile textures
|
|
||||||
to platform-native formats at build time.
|
|
||||||
|
|
||||||
**UI in 3D space is wasteful and limiting.** Currently UI elements are
|
|
||||||
rendered as geometry projected into screen space, going through the full
|
|
||||||
3D pipeline. On platforms with dedicated 2D hardware (Saturn VDP2,
|
|
||||||
SNES BG layers), this is actively wrong -- UI should map to a hardware
|
|
||||||
plane, not a 3D draw call. On modern platforms it should be a clean
|
|
||||||
screen-space pass that never touches the 3D depth buffer.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current Model (Summary)
|
|
||||||
|
|
||||||
```
|
|
||||||
Scene
|
|
||||||
-> shaderBind(shader)
|
|
||||||
-> textureBind(texture)
|
|
||||||
-> meshDraw(mesh) <-- immediate draw call per object
|
|
||||||
-> meshDraw(mesh)
|
|
||||||
-> ...
|
|
||||||
Platform receives each draw call immediately.
|
|
||||||
Depth is handled by Z-buffer hardware.
|
|
||||||
All textures live in GPU memory as RGBA (or Dolphin's tiled RGBA).
|
|
||||||
UI is rendered as 3D geometry with an orthographic projection.
|
|
||||||
```
|
|
||||||
|
|
||||||
Key current concepts:
|
|
||||||
- `mesh_t` -- vertex array (triangles/quads), in GPU VBO (GL) or CPU
|
|
||||||
memory (Dolphin)
|
|
||||||
- `shader_t` -- GLSL program (modern GL), GL fixed-function state
|
|
||||||
(legacy GL), or GX matrix + TEV config (Dolphin)
|
|
||||||
- `texture_t` -- GPU texture handle (GL) or tiled CPU buffer (Dolphin);
|
|
||||||
always RGBA at the engine level
|
|
||||||
- `framebuffer_t` -- FBO (GL) or fixed hardware XFB (Dolphin)
|
|
||||||
- `spritebatch_t` -- accumulates 2D quads and flushes in batches of 32;
|
|
||||||
the only existing deferred-submission system in the engine
|
|
||||||
|
|
||||||
The spritebatch hints at the right model. Everything needs to work this way.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The Core Shift: Platform-Native Rendering
|
|
||||||
|
|
||||||
### Before
|
|
||||||
|
|
||||||
```
|
|
||||||
src/dusk/ Core engine + GL-like rendering API definition
|
|
||||||
src/duskgl/ OpenGL implementation
|
|
||||||
src/dusksdl2/ SDL2 window/input (shared)
|
|
||||||
src/duskpsp/ PSP via PSPGL (shim over GU)
|
|
||||||
src/duskvita/ Vita via GL ES (similar path to duskgl)
|
|
||||||
src/duskdolphin/ GameCube/Wii via GX (already custom)
|
|
||||||
src/dusklinux/ Linux (uses dusksdl2 + duskgl)
|
|
||||||
```
|
|
||||||
|
|
||||||
### After
|
|
||||||
|
|
||||||
```
|
|
||||||
src/dusk/ Core engine logic + render intent API ONLY
|
|
||||||
src/dusksdl2/ SDL2 window/input (unchanged)
|
|
||||||
src/duskgl/ Modern OpenGL (Linux, Vita modern path)
|
|
||||||
src/duskgllegacy/ Fixed-function OpenGL (older hardware, PSP with PSPGL
|
|
||||||
as a last resort)
|
|
||||||
src/duskvulkan/ Vulkan (Linux modern, future)
|
|
||||||
src/duskpsp/ PSP native GU (no PSPGL, direct command lists)
|
|
||||||
src/duskvita/ Vita native GXM (TBD)
|
|
||||||
src/duskdolphin/ GameCube/Wii GX (already custom, mostly kept)
|
|
||||||
src/dusksaturn/ Saturn VDP1/VDP2 (new)
|
|
||||||
src/duskps1/ PlayStation 1 ordering table + GTE (new)
|
|
||||||
src/duskn64/ Nintendo 64 RSP/RDP display list (new)
|
|
||||||
src/dusksnes/ SNES PPU/Mode7 (new, extremely constrained)
|
|
||||||
```
|
|
||||||
|
|
||||||
`src/dusk/` no longer knows about meshes, shaders, or framebuffers.
|
|
||||||
It defines the *render intent* system: what the scene wants to draw.
|
|
||||||
Each platform directory is entirely self-contained and responsible for
|
|
||||||
translating intents to its native API.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Render Intent System (new)
|
|
||||||
|
|
||||||
Instead of the scene calling `meshDraw()` or `shaderBind()`, it submits
|
|
||||||
render intents into a `renderqueue_t`. An intent describes what should
|
|
||||||
appear on screen without prescribing how to draw it.
|
|
||||||
|
|
||||||
### Primitive intents (3D world)
|
|
||||||
|
|
||||||
```
|
|
||||||
RENDER_INTENT_QUAD -- textured quad, 4 vertices or transform + size
|
|
||||||
RENDER_INTENT_POLYGON -- filled polygon (convex, up to N vertices)
|
|
||||||
RENDER_INTENT_LINE -- line segment or polyline
|
|
||||||
RENDER_INTENT_SPRITE -- 2D billboard (always faces camera)
|
|
||||||
RENDER_INTENT_MESH -- arbitrary vertex array (GL/GX only; degraded
|
|
||||||
on command-list platforms)
|
|
||||||
```
|
|
||||||
|
|
||||||
Each intent carries: texture reference, color/tint, depth hint (for
|
|
||||||
painter's algorithm sorting), blend mode, and cull flags.
|
|
||||||
|
|
||||||
### Background plane intents (2D layers)
|
|
||||||
|
|
||||||
```
|
|
||||||
RENDER_INTENT_BGPLANE -- configure a background/tilemap layer
|
|
||||||
```
|
|
||||||
|
|
||||||
Carries: layer index, tile map data reference, scroll offset, palette,
|
|
||||||
and transform (for Mode7-style affine).
|
|
||||||
|
|
||||||
### UI intents (screen space)
|
|
||||||
|
|
||||||
```
|
|
||||||
RENDER_INTENT_UI_RECT -- solid colored rectangle
|
|
||||||
RENDER_INTENT_UI_SPRITE -- textured rectangle (UI image)
|
|
||||||
RENDER_INTENT_UI_TEXT -- text string at screen position
|
|
||||||
```
|
|
||||||
|
|
||||||
UI intents are always screen-space. They are never mixed into the 3D
|
|
||||||
world queue. See UI System section below.
|
|
||||||
|
|
||||||
### Platform translation
|
|
||||||
|
|
||||||
| Intent | Modern GL | PSP GU | Saturn VDP1 | PS1 OT | N64 RSP | SNES PPU |
|
|
||||||
|---|---|---|---|---|---|---|
|
|
||||||
| QUAD | VAO + glDraw | GU display list | distorted-sprite cmd | GPU quad packet | RSP display list | OAM + BG tile |
|
|
||||||
| POLYGON | VAO + glDraw | GU display list | polygon cmd | GPU poly packet | RSP display list | OAM |
|
|
||||||
| BGPLANE | fullscreen quad | fullscreen quad | VDP2 config | fullscreen quad | fullscreen quad | BG layer config |
|
|
||||||
| UI_SPRITE | 2D ortho quad | 2D GU quad | VDP2 BG plane | GPU rect packet | RDP rectangle | BG layer tile |
|
|
||||||
| MESH | VAO/VBO | GU buffers | (degrade: quads) | (degrade: tris/quads) | RSP display list | (not supported) |
|
|
||||||
|
|
||||||
Note: N64 supports both triangles and axis-aligned rectangles natively via
|
|
||||||
RDP. PS1 supports triangles and quads (4-vertex) natively, so neither needs
|
|
||||||
the dead-vertex trick that Saturn requires.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Asset Pipeline: Platform-Native Formats
|
|
||||||
|
|
||||||
### The problem
|
|
||||||
|
|
||||||
All textures currently enter the engine as RGBA and are converted at
|
|
||||||
runtime by each platform (Dolphin retiles to 4x4 blocks; GL uploads as-is).
|
|
||||||
This wastes memory and CPU time, and is impossible for platforms where RGBA
|
|
||||||
is not a valid intermediate format at all.
|
|
||||||
|
|
||||||
### The solution
|
|
||||||
|
|
||||||
The asset compiler (offline, run at build time) produces platform-specific
|
|
||||||
binary bundles. A texture asset has one source (PNG or similar) but N
|
|
||||||
compiled outputs, one per target.
|
|
||||||
|
|
||||||
### Texture formats by platform
|
|
||||||
|
|
||||||
| Platform | Native Formats | Notes |
|
|
||||||
|---|---|---|
|
|
||||||
| Modern GL | RGBA8, RGB8, BC1-BC7 (compressed) | Upload directly, GPU handles |
|
|
||||||
| Legacy GL | RGBA8, RGB8, CI8 (palette via extension) | No compressed formats |
|
|
||||||
| Vulkan | VkFormat variants (RGBA8, BC, ASTC) | Chosen at compile time |
|
|
||||||
| PSP GU | ABGR8888, BGR5650, ABGR1555, ABGR4444, CI4, CI8 | Native swizzled format |
|
|
||||||
| Saturn VDP1/VDP2 | RGB555, CI4, CI8 (15-bit palette in CRAM) | Big-endian, packed |
|
|
||||||
| PlayStation 1 | RGB555 / CI4 / CI8 (CLUT in VRAM) | Little-endian; VRAM flat; CLUT at coord |
|
|
||||||
| Nintendo 64 | RGBA16, RGBA32, IA4-IA16, I4-I8, CI4, CI8 | 4 KB TMEM; tiles must fit in TMEM banks |
|
|
||||||
| GameCube/Wii GX | I4, I8, IA4, IA8, RGB565, RGB5A3, RGBA8, CMPR | 4x4 tiled, big-endian |
|
|
||||||
| SNES PPU | 2bpp, 4bpp, 8bpp indexed (CGRAM palette) | Tile-packed, no direct access |
|
|
||||||
|
|
||||||
### Asset bundle structure
|
|
||||||
|
|
||||||
The `.dsk` bundle gains a platform tag. The loader picks the right section
|
|
||||||
at runtime (or the build produces a single-platform bundle for constrained
|
|
||||||
targets like SNES/Saturn where there is no spare storage for unused data).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## UI System (first-class)
|
|
||||||
|
|
||||||
### Current problem
|
|
||||||
|
|
||||||
UI elements go through the 3D pipeline: they are meshes with an orthographic
|
|
||||||
shader, rendered in the same pass as the world. This means:
|
|
||||||
- UI competes for Z-buffer depth with world geometry
|
|
||||||
- On Saturn/SNES, UI cannot use dedicated hardware planes
|
|
||||||
- Text rendering is tied to the sprite batch which is tied to the 3D pass
|
|
||||||
- No separation between "draw the world" and "draw the HUD"
|
|
||||||
|
|
||||||
### New model
|
|
||||||
|
|
||||||
UI is a completely separate rendering context. The world renders first,
|
|
||||||
then the UI renders on top. They share no state.
|
|
||||||
|
|
||||||
UI coordinates are always in screen space (pixels or a logical resolution
|
|
||||||
that the platform scales to its native display size). No camera matrix,
|
|
||||||
no projection, no depth buffer involvement.
|
|
||||||
|
|
||||||
### Platform mapping
|
|
||||||
|
|
||||||
| Platform | UI implementation |
|
|
||||||
|---|---|
|
|
||||||
| Modern GL | Separate 2D ortho pass, screen-space quads, no depth test |
|
|
||||||
| Legacy GL | Same, using fixed-function |
|
|
||||||
| PSP GU | Separate GU display list, 2D mode |
|
|
||||||
| Saturn | VDP2 background plane(s) dedicated to UI |
|
|
||||||
| PlayStation 1 | Separate GPU packet chain, no Z; ordered after world OT |
|
|
||||||
| Nintendo 64 | RDP rectangle commands in a separate display list segment |
|
|
||||||
| GameCube/Wii | GX 2D mode or dedicated GX pass |
|
|
||||||
| SNES | Dedicated BG layer(s) for HUD tiles |
|
|
||||||
|
|
||||||
On Saturn, the UI occupying VDP2 planes is a genuine hardware win -- the
|
|
||||||
PPU composites it for free at scanline time, costing zero VDP1 commands.
|
|
||||||
On SNES, the HUD must live in a BG layer because there is no alternative.
|
|
||||||
|
|
||||||
### UI API (proposed)
|
|
||||||
|
|
||||||
```c
|
|
||||||
uiBegin();
|
|
||||||
uiDrawRect(x, y, w, h, color);
|
|
||||||
uiDrawSprite(x, y, w, h, texture, uvMin, uvMax);
|
|
||||||
uiDrawText(x, y, font, string);
|
|
||||||
uiEnd(); // platform flushes UI to hardware
|
|
||||||
```
|
|
||||||
|
|
||||||
The `uiBegin`/`uiEnd` block collects intents; the platform submits them
|
|
||||||
at frame end in whatever way is appropriate.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## SNES / Mode7
|
|
||||||
|
|
||||||
SNES is the most constrained platform the engine will ever support and
|
|
||||||
needs its own section because it breaks assumptions that even Saturn keeps.
|
|
||||||
|
|
||||||
### Hardware
|
|
||||||
|
|
||||||
- **CPU**: 65816 @ ~3.58 MHz (16-bit, no FPU, no cache)
|
|
||||||
- **PPU**: Tile-based scanline renderer. VRAM holds tile graphics and
|
|
||||||
tile maps. BG layers reference tiles by index.
|
|
||||||
- **Mode7**: A single BG layer with a 2D affine matrix applied per
|
|
||||||
scanline. Used for overworld maps, road perspective (F-Zero), rotation
|
|
||||||
effects. The matrix is set via HDMA (scanline DMA) for per-scanline
|
|
||||||
variation, enabling horizon-perspective effects.
|
|
||||||
- **Sprites/OAM**: Up to 128 sprites (8x8, 16x16, 32x32, 64x64 pixels),
|
|
||||||
4bpp indexed, up to 8 per scanline.
|
|
||||||
- **Palette**: CGRAM holds 256 entries of 15-bit RGB (512 bytes total).
|
|
||||||
BG layers use sub-palettes of 4/16/256 colors depending on bit depth.
|
|
||||||
- **VRAM**: 64 KB (tiles + tile maps)
|
|
||||||
- **WRAM**: 128 KB work RAM + usually 8 KB SRAM on cart for saves
|
|
||||||
- **No frame buffer.** The PPU renders scanlines directly. You cannot
|
|
||||||
read back what was drawn.
|
|
||||||
- **No general-purpose draw calls.** You configure registers and VRAM
|
|
||||||
before the frame and the PPU does the rest.
|
|
||||||
|
|
||||||
### What "3D" means on SNES
|
|
||||||
|
|
||||||
True 3D is not possible. What can be approximated:
|
|
||||||
- **Overworld map**: Mode7 with a flat texture and HDMA scroll gives a
|
|
||||||
top-down perspective with a horizon line (the classic JRPG overworld).
|
|
||||||
- **Depth illusion**: Mode7 matrix manipulation can simulate a moving
|
|
||||||
camera over flat terrain. Objects are sprites placed at screen positions
|
|
||||||
calculated by software perspective projection.
|
|
||||||
- **Sprite scaling**: Software-scaled sprites using pre-rendered frames
|
|
||||||
or the RSP-style tricks used in Super FX games (Star Fox). Super FX
|
|
||||||
is a co-processor on the cartridge -- base SNES cannot do this.
|
|
||||||
- **Basic 3D effects**: Some games use HDMA color gradient + Mode7 floor
|
|
||||||
with overlaid sprites to create a pseudo-3D look.
|
|
||||||
|
|
||||||
The engine plan for SNES: Mode7 overworld (confirmed), sprite-based world
|
|
||||||
objects, BG layer UI. "Basic 3D effects" (pseudo-perspective with sprites)
|
|
||||||
is aspirational -- implementation complexity TBD.
|
|
||||||
|
|
||||||
### SNES constraints on the engine
|
|
||||||
|
|
||||||
- **No dynamic allocation.** With 128 KB WRAM, a general-purpose allocator
|
|
||||||
is risky. The engine memory system may need a static pool mode for SNES.
|
|
||||||
- **No floating point.** `float_t` must resolve to integer or fixed-point.
|
|
||||||
- **No scripting (JerryScript).** The JS engine requires far more than
|
|
||||||
128 KB RAM. SNES scenes must be compiled C.
|
|
||||||
- **Asset data in ROM, not a .dsk bundle.** SNES loads from cartridge ROM
|
|
||||||
mapped into the address space. The asset system needs a ROM-mapped loader.
|
|
||||||
- **Tile pipeline.** Textures must be pre-converted to SNES tile format
|
|
||||||
(2bpp/4bpp/8bpp, 8x8 pixel tiles, CGRAM palette) at build time. This
|
|
||||||
is a completely different asset output from every other platform.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Platform Inventory
|
|
||||||
|
|
||||||
A summary of what each platform's native rendering looks like after the
|
|
||||||
refactor, for reference when designing the intent API.
|
|
||||||
|
|
||||||
### Modern OpenGL (duskgl)
|
|
||||||
|
|
||||||
VAO + VBO mesh storage, GLSL shaders, FBO render targets, Z-buffer.
|
|
||||||
No fixed-function. Targets: Linux, possibly Vita (GXM is preferred).
|
|
||||||
|
|
||||||
### Legacy OpenGL (duskgllegacy)
|
|
||||||
|
|
||||||
Fixed-function pipeline: `glMatrixMode`, `glTexEnv`, client-side vertex
|
|
||||||
arrays. No VAO/VBO. Used for: very old desktop hardware, maybe PSP as
|
|
||||||
last resort (PSPGL is this). Targets: legacy desktop, embedded Linux.
|
|
||||||
|
|
||||||
### Vulkan (duskvulkan)
|
|
||||||
|
|
||||||
Explicit pipeline state objects, render passes, descriptor sets, command
|
|
||||||
buffers. Highest ceiling for performance and control. Targets: Linux
|
|
||||||
(modern), future platforms. Not immediate priority but the architecture
|
|
||||||
should not block it.
|
|
||||||
|
|
||||||
### PSP native GU (duskpsp)
|
|
||||||
|
|
||||||
The GE/GU is a display-list GPU. You build a command list in memory and
|
|
||||||
the GU DMA engine processes it asynchronously. Native vertex formats are
|
|
||||||
PSP-specific (ABGR byte order, swizzled textures for cache efficiency).
|
|
||||||
No PSPGL. Targets: PSP hardware and emulators.
|
|
||||||
|
|
||||||
### Vita (duskvita)
|
|
||||||
|
|
||||||
GXM is Sony's Vita GPU API -- closer to modern GL than GU, with explicit
|
|
||||||
shader binaries (.gxp), ring buffers, and GPU sync primitives.
|
|
||||||
|
|
||||||
### GameCube/Wii GX (duskdolphin)
|
|
||||||
|
|
||||||
Already a custom renderer. GX uses immediate-mode vertex submission
|
|
||||||
(`GX_Begin` / `GX_Position1x16` loops), TEV for texture compositing, and
|
|
||||||
hardware XFB double-buffering. Big-endian. Mostly kept as-is; may benefit
|
|
||||||
from being expressed in terms of render intents for consistency.
|
|
||||||
|
|
||||||
### Saturn VDP1/VDP2 (dusksaturn)
|
|
||||||
|
|
||||||
VDP1: command-list (32-byte structs), quad-based, affine texture mapping,
|
|
||||||
no Z-buffer (painter's algorithm). VDP2: up to 6 background planes
|
|
||||||
composited at scanline time. Big-endian dual SH-2, no FPU. Fixed-point
|
|
||||||
math required throughout.
|
|
||||||
|
|
||||||
### PlayStation 1 (duskps1)
|
|
||||||
|
|
||||||
MIPS R3000A @ 33.87 MHz, little-endian, no FPU. GTE (coprocessor 2)
|
|
||||||
handles fixed-point matrix math, perspective divide, and lighting.
|
|
||||||
GPU receives packets via DMA linked-list (the Ordering Table). Primitives:
|
|
||||||
triangles and quads natively (no dead-vertex needed). Texture mapping:
|
|
||||||
affine, same limitation as Saturn. No Z-buffer; depth is OT slot order.
|
|
||||||
VRAM is 1 MB flat (frame buffers + textures + CLUTs share it). SDK:
|
|
||||||
PSn00bSDK, which is CMake-native -- a direct fit for the dusk build system.
|
|
||||||
|
|
||||||
### Nintendo 64 (duskn64)
|
|
||||||
|
|
||||||
VR4300 @ 93.75 MHz, big-endian, real IEEE 754 FPU. Rendering is split
|
|
||||||
between the RSP (geometry: programmable MIPS SIMD, runs microcode up to
|
|
||||||
~1000 instructions in 4 KB IMEM) and the RDP (rasterization: fixed
|
|
||||||
hardware). RSP produces triangle commands from a CPU-built display list
|
|
||||||
in RDRAM. RDP features: perspective-correct texture mapping, bilinear
|
|
||||||
filtering, hardware Z-buffer. Primitives: triangles and axis-aligned rects.
|
|
||||||
TMEM is 4 KB on-chip texture cache; textures must be loaded into tiles
|
|
||||||
before drawing -- a significant memory management constraint.
|
|
||||||
SDK: libdragon (Unlicense, GCC 14, Makefile-based -- not CMake; this
|
|
||||||
requires a wrapper toolchain file for dusk's build system).
|
|
||||||
|
|
||||||
### SNES PPU/Mode7 (dusksnes)
|
|
||||||
|
|
||||||
Tile-based. VRAM holds tiles and tile maps. Mode7 provides affine transform
|
|
||||||
for one BG layer. Sprites via OAM. No frame buffer. All configuration is
|
|
||||||
memory-mapped registers. 65816 CPU, no FPU, extremely limited RAM.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Threading Model
|
|
||||||
|
|
||||||
### Current model
|
|
||||||
|
|
||||||
The engine uses OS threads for async asset loading (`assetXxxLoaderAsync`).
|
|
||||||
Platforms that have pthreads or an equivalent RTOS (Linux, PSP, Vita) run
|
|
||||||
worker threads that load data in the background while the game loop runs.
|
|
||||||
The main thread polls or blocks on completion.
|
|
||||||
|
|
||||||
### The problem
|
|
||||||
|
|
||||||
Several target platforms have no OS threading whatsoever, and others have
|
|
||||||
hardware-specific async mechanisms that are nothing like pthreads.
|
|
||||||
|
|
||||||
### Per-platform reality
|
|
||||||
|
|
||||||
| Platform | Threading | Async mechanism |
|
|
||||||
|---|---|---|
|
|
||||||
| Linux | pthreads | Worker threads (current) |
|
|
||||||
| Vita | SceKernelThread | Per-SDK threads |
|
|
||||||
| PSP | SceKernelThread | Per-SDK threads |
|
|
||||||
| GameCube/Wii | libogc LWP | Lightweight processes |
|
|
||||||
| Saturn | None (OS) | Slave SH-2 for fixed jobs; CD-ROM via interrupt/callback |
|
|
||||||
| PlayStation 1 | None (OS) | V-blank ISR, 7 DMA channels, CD-ROM callbacks |
|
|
||||||
| Nintendo 64 | libdragon preview only | PI DMA for cartridge; RSP for parallel compute |
|
|
||||||
| SNES | None | DMA (GPDMA/HDMA); NMI V-blank; SPC700 audio is a separate CPU |
|
|
||||||
|
|
||||||
**Saturn slave SH-2**: The second SH-2 is not a general-purpose thread.
|
|
||||||
It runs a fixed subroutine you hand-load. The typical use is offloading
|
|
||||||
heavy per-frame computation (geometry transforms, depth sort) while the
|
|
||||||
master SH-2 handles game logic. Communication is via shared WRAM with
|
|
||||||
cache-through addresses to avoid coherency bugs. There is no scheduler
|
|
||||||
and no yield -- it runs to completion.
|
|
||||||
|
|
||||||
**SNES DMA**: GPDMA copies blocks of data (ROM to WRAM, WRAM to VRAM)
|
|
||||||
and halts the CPU for the duration -- it is synchronous from the game's
|
|
||||||
perspective. HDMA runs per-scanline during H-blank, writing to PPU
|
|
||||||
registers without CPU involvement; this is how Mode7 perspective is
|
|
||||||
achieved. Neither is "async" in the programming sense.
|
|
||||||
|
|
||||||
**SNES NMI**: The V-blank NMI fires at the start of every V-blank period.
|
|
||||||
This is the only safe window to write to VRAM and PPU registers. All
|
|
||||||
critical PPU updates must complete within ~1.2ms (the V-blank window).
|
|
||||||
|
|
||||||
### Proposed model
|
|
||||||
|
|
||||||
Introduce a compile-time threading capability flag:
|
|
||||||
|
|
||||||
```
|
|
||||||
DUSK_THREAD_PTHREAD -- Linux, maybe Vita
|
|
||||||
DUSK_THREAD_SCEKERNEL -- PSP, Vita SDK
|
|
||||||
DUSK_THREAD_LWP -- GameCube/Wii libogc
|
|
||||||
DUSK_THREAD_SLAVE_SH2 -- Saturn slave CPU (job dispatch only)
|
|
||||||
DUSK_THREAD_NONE -- SNES (and Saturn master thread view)
|
|
||||||
```
|
|
||||||
|
|
||||||
The asset loader's async path is gated on having a threading capability.
|
|
||||||
When `DUSK_THREAD_NONE` is defined, `assetXxxLoaderAsync` either does not
|
|
||||||
exist or is an alias for the synchronous version. On Saturn, the slave SH-2
|
|
||||||
is exposed as a distinct API (`sh2JobDispatch`, `sh2JobWait`) used only for
|
|
||||||
compute-heavy work, not for I/O.
|
|
||||||
|
|
||||||
### Asset loading without threads
|
|
||||||
|
|
||||||
**Saturn**: CD-ROM access is initiated via SBL/CDC routines and completes
|
|
||||||
via interrupt callback. The engine's asset loading loop can poll the
|
|
||||||
callback flag in the main loop rather than blocking a thread. This is
|
|
||||||
interrupt-driven cooperative async, not preemptive.
|
|
||||||
|
|
||||||
**SNES**: There is no loading. Assets live in ROM, mapped directly into the
|
|
||||||
65816 address space. "Loading a texture" means computing a pointer into ROM
|
|
||||||
and copying the tile data to VRAM during V-blank via GPDMA. The asset system
|
|
||||||
on SNES is essentially a VRAM/CGRAM allocator and a DMA scheduler, not a
|
|
||||||
file loader.
|
|
||||||
|
|
||||||
### Asset system changes
|
|
||||||
|
|
||||||
The asset pipeline needs to accommodate three loading models:
|
|
||||||
|
|
||||||
1. **File-based** (Linux, PSP, Vita, Saturn CD): open file, read bytes,
|
|
||||||
close. Can be sync or thread-async.
|
|
||||||
2. **DMA/interrupt** (Saturn CD-ROM, GC DVD): initiate transfer, poll or
|
|
||||||
callback on completion, no thread blocked.
|
|
||||||
3. **ROM-mapped** (SNES): data is already in the address space; "loading"
|
|
||||||
is a VRAM DMA copy scheduled for V-blank, not file I/O.
|
|
||||||
|
|
||||||
The `assetstream_t` abstraction that currently wraps file I/O needs a third
|
|
||||||
backend for ROM-mapped data, and the async path needs to support
|
|
||||||
callback-based completion as an alternative to thread-based blocking.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What Needs to Change
|
|
||||||
|
|
||||||
### 1. Render intent API (new, in src/dusk/)
|
|
||||||
|
|
||||||
Replace `mesh_t` / `shader_t` / `meshDraw()` as scene-facing APIs with
|
|
||||||
`renderqueue_t` and intent submission functions. `src/dusk/` defines the
|
|
||||||
intent types and submission API; platforms implement the flush.
|
|
||||||
|
|
||||||
### 2. Platform renderer directories
|
|
||||||
|
|
||||||
Move rendering implementations out of `duskgl/` as a shared layer and
|
|
||||||
into fully self-contained platform directories. `duskgl/` becomes the
|
|
||||||
*modern GL* platform only. Add `duskgllegacy/`, `duskvulkan/` as peers.
|
|
||||||
|
|
||||||
### 3. Asset pipeline: platform-native texture formats
|
|
||||||
|
|
||||||
The offline asset compiler must produce per-platform texture bundles in
|
|
||||||
native formats. The runtime texture loader expects pre-converted data,
|
|
||||||
not RGBA. `textureformat_t` grows to cover all platform formats but each
|
|
||||||
platform only ever sees the formats it natively supports.
|
|
||||||
|
|
||||||
### 4. UI system (first-class, separate from 3D)
|
|
||||||
|
|
||||||
New `src/dusk/ui/` subsystem with `uiBegin` / `uiEnd` and intent types
|
|
||||||
for rects, sprites, and text. Platforms implement the flush independently.
|
|
||||||
The 3D spritebatch is retired or scoped to world-space billboards only.
|
|
||||||
|
|
||||||
### 5. Fixed-point / no-FPU math
|
|
||||||
|
|
||||||
`float_t` needs a fixed-point mode. Proposed: define `fixed_t` as a
|
|
||||||
16.16 signed integer; define `DUSK_MATH_FIXED` for platforms that require
|
|
||||||
it (Saturn, SNES). Engine math utilities (`mathSin`, `mathCos`, etc.)
|
|
||||||
have fixed-point implementations selected by this flag. `float_t` on
|
|
||||||
FPU-less platforms becomes a typedef for `fixed_t`.
|
|
||||||
|
|
||||||
### 6. Background plane abstraction (bgplane_t)
|
|
||||||
|
|
||||||
New concept in `src/dusk/display/bgplane/`. A BG plane has a tile map or
|
|
||||||
bitmap source, scroll offsets, a palette reference, and optional affine
|
|
||||||
parameters (for Mode7-style use). On GL platforms: rendered as a
|
|
||||||
fullscreen textured quad or shader pass. On Saturn: VDP2 config. On SNES:
|
|
||||||
PPU BG layer config.
|
|
||||||
|
|
||||||
### 7. Memory system: static pool mode
|
|
||||||
|
|
||||||
For SNES (and possibly Saturn), the general-purpose allocator may be
|
|
||||||
unviable. A compile-time static pool mode (`DUSK_MEMORY_STATIC`) that uses
|
|
||||||
a fixed-size arena instead of dynamic allocation. All `memoryAllocate`
|
|
||||||
calls hit the pool; `memoryFree` is a no-op or a stack pop.
|
|
||||||
|
|
||||||
### 8. Script runtime: optional
|
|
||||||
|
|
||||||
JerryScript requires too much RAM for SNES and is marginal on Saturn.
|
|
||||||
The scripting system should be compile-time optional (`DUSK_SCRIPTING`),
|
|
||||||
not assumed present. SNES/Saturn scenes would be compiled C.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What to Keep
|
|
||||||
|
|
||||||
- Platform macro abstraction pattern (`displayplatform.h`, etc.) -- works,
|
|
||||||
no reason to change.
|
|
||||||
- Directory structure convention for platform directories.
|
|
||||||
- Entity-component system -- platform-agnostic, unaffected.
|
|
||||||
- Asset loading + `.dsk` bundle concept (extended for platform formats).
|
|
||||||
- The broad subsystem layout: asset, input, display, log, network, save,
|
|
||||||
system, time.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Open Questions
|
|
||||||
|
|
||||||
1. **Render intent granularity**: How much does the intent API need to
|
|
||||||
express? A MESH intent works on GL/N64 but degrades poorly on Saturn
|
|
||||||
(must split into quads) and is impossible on SNES. Should MESH be a
|
|
||||||
valid intent with a "best effort" contract, or excluded from the portable
|
|
||||||
API entirely?
|
|
||||||
|
|
||||||
2. **Threading abstraction depth**: Should `DUSK_THREAD_SLAVE_SH2` be a
|
|
||||||
first-class concept in the engine's job system, or a Saturn-internal
|
|
||||||
implementation detail the core never sees? Same question applies to N64's
|
|
||||||
RSP as a compute co-processor.
|
|
||||||
|
|
||||||
3. **Asset loading async contract**: When a platform has no threads, should
|
|
||||||
`assetLoadAsync` be a no-op alias for `assetLoadSync`, or return
|
|
||||||
immediately with a completion flag to poll? The polling model is more
|
|
||||||
honest but requires all call sites to handle it.
|
|
||||||
|
|
||||||
4. **N64 build system**: libdragon uses GNU Make, not CMake. Options are:
|
|
||||||
(a) write a CMake toolchain file that wraps n64.mk, (b) maintain a
|
|
||||||
parallel Makefile just for N64, or (c) wait for upstream CMake support.
|
|
||||||
Which is acceptable long-term?
|
|
||||||
|
|
||||||
5. **N64 RSP microcode**: Standard libdragon microcodes (Fast3D/F3DEX2) or
|
|
||||||
Tiny3D (community microcode with full T&L + skinning)? Writing custom
|
|
||||||
microcode is powerful but limited to ~1000 MIPS SIMD instructions.
|
|
||||||
This decision gates what 3D features the N64 port can support.
|
|
||||||
|
|
||||||
6. **PSPGL fate**: Drop immediately in favor of native GU, or keep as a
|
|
||||||
fallback (`duskgllegacy`) while native GU is built? The two can coexist
|
|
||||||
during transition.
|
|
||||||
|
|
||||||
7. **Vulkan priority**: Design the intent API with Vulkan in mind from the
|
|
||||||
start, or add it later? Vulkan's explicit pipeline state model may
|
|
||||||
conflict with how stateful platforms (Saturn, SNES) expect things to work.
|
|
||||||
|
|
||||||
8. **Background planes on modern platforms**: Does `bgplane_t` degrade to a
|
|
||||||
fullscreen textured quad on GL/Vulkan/N64, or should modern platforms
|
|
||||||
support actual background scene rendering (3D world behind the foreground)?
|
|
||||||
|
|
||||||
9. **PS1 ordering table depth**: The OT is a fixed-size array (e.g. 4096
|
|
||||||
slots). Depth precision = number of slots. How deep should the engine's
|
|
||||||
default OT be, and should this be configurable per-scene?
|
|
||||||
|
|
||||||
10. **Fixed-point strategy**: Does `float_t` transparently become `fixed_t`
|
|
||||||
on FPU-less platforms (Saturn, PS1, SNES), or do we require explicit
|
|
||||||
`fixed_t` in math-heavy paths? Transparent is easiest to port; explicit
|
|
||||||
is faster.
|
|
||||||
|
|
||||||
11. **SNES V-blank budget**: All VRAM writes must finish within ~1.2ms.
|
|
||||||
Does the engine need a V-blank work queue with a budget checker, or is
|
|
||||||
this left to the game to manage manually?
|
|
||||||
|
|
||||||
12. **SNES scripting**: JerryScript is out. Pure compiled C, or a lighter
|
|
||||||
scripting layer (Lua is ~100 KB -- tight but possible)?
|
|
||||||
|
|
||||||
13. **Asset compiler**: New standalone tool, or an extension of the existing
|
|
||||||
asset pipeline? Part of the CMake build or a separate pre-build step?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Proposed Sequence (Draft)
|
|
||||||
|
|
||||||
### Phase 1 -- Intent API (no behavior change)
|
|
||||||
1. Design and stabilize `renderqueue_t` and intent types
|
|
||||||
2. Refactor modern GL path to submit through render intents (same output,
|
|
||||||
new plumbing)
|
|
||||||
3. Refactor Dolphin path the same way
|
|
||||||
4. Validate no regressions on Linux + GameCube
|
|
||||||
|
|
||||||
### Phase 2 -- UI system
|
|
||||||
5. Extract UI rendering from the 3D path into `src/dusk/ui/`
|
|
||||||
6. Implement UI flush for GL and Dolphin
|
|
||||||
7. Wire existing UI elements through the new system
|
|
||||||
|
|
||||||
### Phase 3 -- Platform splits
|
|
||||||
8. Split `duskgl/` into `duskgl/` (modern) and `duskgllegacy/` (fixed-func)
|
|
||||||
9. Port PSP to native GU (`duskpsp/display/` rewrite, drop PSPGL dependency)
|
|
||||||
10. Stub `duskvulkan/` structure for future implementation
|
|
||||||
|
|
||||||
### Phase 4 -- Asset pipeline
|
|
||||||
11. Design platform-native texture format system
|
|
||||||
12. Extend asset compiler for per-platform output
|
|
||||||
13. Update texture loader to expect pre-converted data
|
|
||||||
|
|
||||||
### Phase 5 -- Saturn
|
|
||||||
14. CMake toolchain for SH-2 cross-compile (yaul / libyaul toolchain)
|
|
||||||
15. `src/dusksaturn/` -- input (SMPC), asset (CD-ROM), log, system
|
|
||||||
16. VDP1 backend for render queue (quads, polygons, painter's sort)
|
|
||||||
17. VDP2 backend for bgplane_t (tile maps, scroll, palette)
|
|
||||||
18. Fixed-point math mode (`DUSK_MATH_FIXED`)
|
|
||||||
19. UI backend (VDP2 plane(s))
|
|
||||||
|
|
||||||
### Phase 6 -- PlayStation 1
|
|
||||||
20. CMake toolchain wrapping PSn00bSDK (already CMake-native)
|
|
||||||
21. `src/duskps1/` -- input (BIOS pad), asset (CD-ROM libpsxcd), log, system
|
|
||||||
22. GTE integration for fixed-point math (reuse `DUSK_MATH_FIXED` path)
|
|
||||||
23. Ordering table builder for render queue (painter's sort, DMA linked-list)
|
|
||||||
24. GPU packet backend for intents (tris, quads, rects)
|
|
||||||
25. UI backend (separate GPU packet chain after world OT)
|
|
||||||
|
|
||||||
### Phase 7 -- Nintendo 64
|
|
||||||
26. CMake toolchain wrapping libdragon (n64.mk wrapper or toolchain file)
|
|
||||||
27. `src/duskn64/` -- input (N64 controller via PIF), asset (PI DMA /
|
|
||||||
DragonFS), log, system
|
|
||||||
28. RSP display list builder for render queue (Z-buffer path, no sorting)
|
|
||||||
29. TMEM tile management for textures
|
|
||||||
30. RDP rectangle backend for UI
|
|
||||||
31. Decide on RSP microcode (Tiny3D vs standard F3DEX2)
|
|
||||||
|
|
||||||
### Phase 8 -- SNES
|
|
||||||
32. SNES toolchain (cc65 or llvm-mos 65816 target)
|
|
||||||
33. Static memory pool mode (`DUSK_MEMORY_STATIC`)
|
|
||||||
34. PPU tile pipeline + VRAM management
|
|
||||||
35. Mode7 overworld implementation
|
|
||||||
36. OAM sprite system
|
|
||||||
37. BG layer UI
|
|
||||||
38. Scripting-optional build (`DUSK_SCRIPTING` off)
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
display.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Subdirectories
|
||||||
|
add_subdirectory(framebuffer)
|
||||||
|
add_subdirectory(mesh)
|
||||||
|
add_subdirectory(screen)
|
||||||
|
add_subdirectory(shader)
|
||||||
|
add_subdirectory(spritebatch)
|
||||||
|
add_subdirectory(text)
|
||||||
|
add_subdirectory(texture)
|
||||||
|
|
||||||
|
# Color definitions
|
||||||
|
dusk_run_python(
|
||||||
|
dusk_color_defs
|
||||||
|
tools.color.csv
|
||||||
|
--csv ${CMAKE_CURRENT_SOURCE_DIR}/color.csv
|
||||||
|
--output ${DUSK_GENERATED_HEADERS_DIR}/display/color.h
|
||||||
|
)
|
||||||
|
add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_color_defs)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name,r,g,b,a
|
||||||
|
black,0,0,0,1
|
||||||
|
white,1,1,1,1
|
||||||
|
red,1,0,0,1
|
||||||
|
green,0,1,0,1
|
||||||
|
blue,0,0,1,1
|
||||||
|
yellow,1,1,0,1
|
||||||
|
cyan,0,1,1,1
|
||||||
|
magenta,1,0,1,1
|
||||||
|
transparent,0,0,0,0
|
||||||
|
transparent_white,1,1,1,0
|
||||||
|
transparent_black,0,0,0,0
|
||||||
|
gray,0.5,0.5,0.5,1
|
||||||
|
light_gray,0.75,0.75,0.75,1
|
||||||
|
dark_gray,0.25,0.25,0.25,1
|
||||||
|
orange,1,0.65,0,1
|
||||||
|
purple,0.5,0,0.5,1
|
||||||
|
brown,0.6,0.4,0.2,1
|
||||||
|
pink,1,0.75,0.8,1
|
||||||
|
lime,0.75,1,0,1
|
||||||
|
navy,0,0,0.5,1
|
||||||
|
teal,0,0.5,0.5,1
|
||||||
|
cornflower_blue,0.39,0.58,0.93,1
|
||||||
|
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "display/display.h"
|
||||||
|
#include "display/framebuffer/framebuffer.h"
|
||||||
|
#include "scene/scene.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/mesh/quad.h"
|
||||||
|
#include "display/mesh/cube.h"
|
||||||
|
#include "display/mesh/sphere.h"
|
||||||
|
#include "display/mesh/plane.h"
|
||||||
|
#include "display/mesh/capsule.h"
|
||||||
|
#include "display/mesh/triprism.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
|
#include "ui/ui.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "asset/asset.h"
|
||||||
|
#include "display/shader/shaderlist.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
|
||||||
|
display_t DISPLAY = { 0 };
|
||||||
|
|
||||||
|
errorret_t displayInit(void) {
|
||||||
|
memoryZero(&DISPLAY, sizeof(DISPLAY));
|
||||||
|
|
||||||
|
#ifdef displayPlatformInit
|
||||||
|
errorChain(displayPlatformInit());
|
||||||
|
#endif
|
||||||
|
errorChain(displaySetState((displaystate_t){ .flags = 0 }));
|
||||||
|
errorChain(textureInit(
|
||||||
|
&TEXTURE_WHITE, 4, 4,
|
||||||
|
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_WHITE_PIXELS }
|
||||||
|
));
|
||||||
|
errorChain(textureInit(
|
||||||
|
&TEXTURE_TEST, 4, 4,
|
||||||
|
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_TEST_PIXELS }
|
||||||
|
));
|
||||||
|
|
||||||
|
// Standard meshes
|
||||||
|
errorChain(quadInit());
|
||||||
|
errorChain(cubeInit());
|
||||||
|
errorChain(sphereInit());
|
||||||
|
errorChain(planeInit());
|
||||||
|
errorChain(capsuleInit());
|
||||||
|
errorChain(triPrismInit());
|
||||||
|
|
||||||
|
errorChain(frameBufferInitBackBuffer());
|
||||||
|
errorChain(spriteBatchInit());
|
||||||
|
errorChain(textInit());
|
||||||
|
errorChain(screenInit());
|
||||||
|
|
||||||
|
// Setup initial shader with default values
|
||||||
|
|
||||||
|
errorChain(shaderListInit());
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t displayUpdate(void) {
|
||||||
|
#ifdef displayPlatformUpdate
|
||||||
|
errorChain(displayPlatformUpdate());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Reset state
|
||||||
|
spriteBatchClear();
|
||||||
|
errorChain(frameBufferBind(NULL));
|
||||||
|
|
||||||
|
// Bind screen and render scene
|
||||||
|
errorChain(screenBind());
|
||||||
|
frameBufferClear(
|
||||||
|
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
|
||||||
|
SCREEN.background
|
||||||
|
);
|
||||||
|
|
||||||
|
errorChain(sceneRender());
|
||||||
|
|
||||||
|
// Finish up
|
||||||
|
screenUnbind();
|
||||||
|
screenRender();
|
||||||
|
|
||||||
|
// Swap and return.
|
||||||
|
#ifdef displayPlatformSwap
|
||||||
|
errorChain(displayPlatformSwap());
|
||||||
|
#endif
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t displaySetState(displaystate_t state) {
|
||||||
|
#ifdef displayPlatformSetState
|
||||||
|
errorChain(displayPlatformSetState(state));
|
||||||
|
#endif
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t displayDispose(void) {
|
||||||
|
errorChain(shaderListDispose());
|
||||||
|
errorChain(spriteBatchDispose());
|
||||||
|
screenDispose();
|
||||||
|
errorChain(textDispose());
|
||||||
|
errorChain(textureDispose(&TEXTURE_WHITE));
|
||||||
|
errorChain(textureDispose(&TEXTURE_TEST));
|
||||||
|
|
||||||
|
#ifdef displayPlatformDispose
|
||||||
|
displayPlatformDispose();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For now, we just return an OK error.
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "display/displayplatform.h"
|
||||||
|
|
||||||
|
// Expecting some definitions to be provided
|
||||||
|
#ifndef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||||
|
#ifndef DUSK_DISPLAY_WIDTH
|
||||||
|
#error "DUSK_DISPLAY_WIDTH must be defined."
|
||||||
|
#endif
|
||||||
|
#ifndef DUSK_DISPLAY_HEIGHT
|
||||||
|
#error "DUSK_DISPLAY_HEIGHT must be defined"
|
||||||
|
#endif
|
||||||
|
#define DUSK_DISPLAY_WIDTH_DEFAULT DUSK_DISPLAY_WIDTH
|
||||||
|
#define DUSK_DISPLAY_HEIGHT_DEFAULT DUSK_DISPLAY_HEIGHT
|
||||||
|
#else
|
||||||
|
#ifndef DUSK_DISPLAY_WIDTH_DEFAULT
|
||||||
|
#error "DUSK_DISPLAY_WIDTH_DEFAULT must be defined."
|
||||||
|
#endif
|
||||||
|
#ifndef DUSK_DISPLAY_HEIGHT_DEFAULT
|
||||||
|
#error "DUSK_DISPLAY_HEIGHT_DEFAULT must be defined."
|
||||||
|
#endif
|
||||||
|
#ifdef DUSK_DISPLAY_WIDTH
|
||||||
|
#error "DUSK_DISPLAY_WIDTH should not be defined."
|
||||||
|
#endif
|
||||||
|
#ifdef DUSK_DISPLAY_HEIGHT
|
||||||
|
#error "DUSK_DISPLAY_HEIGHT should not be defined."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Main Display Struct, platform-speicifc
|
||||||
|
typedef displayplatform_t display_t;
|
||||||
|
|
||||||
|
extern display_t DISPLAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the display system.
|
||||||
|
* @return An errorret_t indicating success or failure.
|
||||||
|
*/
|
||||||
|
errorret_t displayInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the display system to actually draw the frame.
|
||||||
|
* @return An errorret_t indicating success or failure.
|
||||||
|
*/
|
||||||
|
errorret_t displayUpdate(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the display state.
|
||||||
|
*
|
||||||
|
* @param state The state to set.
|
||||||
|
* @return An errorret_t indicating success or failure.
|
||||||
|
*/
|
||||||
|
errorret_t displaySetState(displaystate_t state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the display system.
|
||||||
|
* @return An errorret_t indicating success or failure.
|
||||||
|
*/
|
||||||
|
errorret_t displayDispose(void);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#define DISPLAY_STATE_FLAG_CULL (1 << 0)
|
||||||
|
#define DISPLAY_STATE_FLAG_DEPTH_TEST (1 << 1)
|
||||||
|
#define DISPLAY_STATE_FLAG_BLEND (1 << 2)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t flags;
|
||||||
|
} displaystate_t;
|
||||||
@@ -56,9 +56,9 @@ add_subdirectory(animation)
|
|||||||
add_subdirectory(event)
|
add_subdirectory(event)
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
add_subdirectory(cutscene)
|
|
||||||
add_subdirectory(console)
|
add_subdirectory(console)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
|
add_subdirectory(render)
|
||||||
add_subdirectory(log)
|
add_subdirectory(log)
|
||||||
add_subdirectory(engine)
|
add_subdirectory(engine)
|
||||||
add_subdirectory(error)
|
add_subdirectory(error)
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
|
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(display)
|
# add_subdirectory(display) # disabled: pending rop-based asset loader rewrite
|
||||||
add_subdirectory(locale)
|
add_subdirectory(locale)
|
||||||
add_subdirectory(json)
|
add_subdirectory(json)
|
||||||
@@ -10,33 +10,15 @@
|
|||||||
assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
||||||
[ASSET_LOADER_TYPE_NULL] = { 0 },
|
[ASSET_LOADER_TYPE_NULL] = { 0 },
|
||||||
|
|
||||||
[ASSET_LOADER_TYPE_MESH] = {
|
|
||||||
.loadSync = assetMeshLoaderSync,
|
|
||||||
.loadAsync = assetMeshLoaderAsync,
|
|
||||||
.dispose = assetMeshDispose
|
|
||||||
},
|
|
||||||
|
|
||||||
[ASSET_LOADER_TYPE_TEXTURE] = {
|
|
||||||
.loadSync = assetTextureLoaderSync,
|
|
||||||
.loadAsync = assetTextureLoaderAsync,
|
|
||||||
.dispose = assetTextureDispose
|
|
||||||
},
|
|
||||||
|
|
||||||
[ASSET_LOADER_TYPE_TILESET] = {
|
|
||||||
.loadSync = assetTilesetLoaderSync,
|
|
||||||
.loadAsync = assetTilesetLoaderAsync,
|
|
||||||
.dispose = assetTilesetDispose
|
|
||||||
},
|
|
||||||
|
|
||||||
[ASSET_LOADER_TYPE_LOCALE] = {
|
[ASSET_LOADER_TYPE_LOCALE] = {
|
||||||
.loadSync = assetLocaleLoaderSync,
|
.loadSync = assetLocaleLoaderSync,
|
||||||
.loadAsync = assetLocaleLoaderAsync,
|
.loadAsync = assetLocaleLoaderAsync,
|
||||||
.dispose = assetLocaleDispose
|
.dispose = assetLocaleDispose
|
||||||
},
|
},
|
||||||
|
|
||||||
[ASSET_LOADER_TYPE_JSON] = {
|
[ASSET_LOADER_TYPE_JSON] = {
|
||||||
.loadSync = assetJsonLoaderSync,
|
.loadSync = assetJsonLoaderSync,
|
||||||
.loadAsync = assetJsonLoaderAsync,
|
.loadAsync = assetJsonLoaderAsync,
|
||||||
.dispose = assetJsonDispose
|
.dispose = assetJsonDispose
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,46 +6,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "asset/loader/display/assetmeshloader.h"
|
|
||||||
#include "asset/loader/display/assettextureloader.h"
|
|
||||||
#include "asset/loader/display/assettilesetloader.h"
|
|
||||||
#include "asset/loader/locale/assetlocaleloader.h"
|
#include "asset/loader/locale/assetlocaleloader.h"
|
||||||
#include "asset/loader/json/assetjsonloader.h"
|
#include "asset/loader/json/assetjsonloader.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ASSET_LOADER_TYPE_NULL,
|
ASSET_LOADER_TYPE_NULL,
|
||||||
|
|
||||||
ASSET_LOADER_TYPE_MESH,
|
|
||||||
ASSET_LOADER_TYPE_TEXTURE,
|
|
||||||
ASSET_LOADER_TYPE_TILESET,
|
|
||||||
ASSET_LOADER_TYPE_LOCALE,
|
ASSET_LOADER_TYPE_LOCALE,
|
||||||
ASSET_LOADER_TYPE_JSON,
|
ASSET_LOADER_TYPE_JSON,
|
||||||
|
|
||||||
ASSET_LOADER_TYPE_COUNT
|
ASSET_LOADER_TYPE_COUNT
|
||||||
} assetloadertype_t;
|
} assetloadertype_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
assetmeshloaderinput_t mesh;
|
|
||||||
assettextureloaderinput_t texture;
|
|
||||||
assettilesetloaderinput_t tileset;
|
|
||||||
assetlocaleloaderinput_t locale;
|
assetlocaleloaderinput_t locale;
|
||||||
assetjsonloaderinput_t json;
|
assetjsonloaderinput_t json;
|
||||||
} assetloaderinput_t;
|
} assetloaderinput_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
assetmeshloaderloading_t mesh;
|
|
||||||
assettextureloaderloading_t texture;
|
|
||||||
assettilesetloaderloading_t tileset;
|
|
||||||
assetlocaleloaderloading_t locale;
|
assetlocaleloaderloading_t locale;
|
||||||
assetjsonloaderloading_t json;
|
assetjsonloaderloading_t json;
|
||||||
} assetloaderloading_t;
|
} assetloaderloading_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
assetmeshoutput_t mesh;
|
|
||||||
assettextureoutput_t texture;
|
|
||||||
assettilesetoutput_t tileset;
|
|
||||||
assetlocaleoutput_t locale;
|
assetlocaleoutput_t locale;
|
||||||
assetjsonoutput_t json;
|
assetjsonoutput_t json;
|
||||||
} assetloaderoutput_t;
|
} assetloaderoutput_t;
|
||||||
|
|
||||||
typedef struct assetloading_s assetloading_t;
|
typedef struct assetloading_s assetloading_t;
|
||||||
|
|||||||
+16
-29
@@ -12,9 +12,6 @@
|
|||||||
#include "input/input.h"
|
#include "input/input.h"
|
||||||
#include "log/log.h"
|
#include "log/log.h"
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
|
|
||||||
console_t CONSOLE;
|
console_t CONSOLE;
|
||||||
|
|
||||||
@@ -22,9 +19,9 @@ void consoleInit(void) {
|
|||||||
memoryZero(&CONSOLE, sizeof(console_t));
|
memoryZero(&CONSOLE, sizeof(console_t));
|
||||||
CONSOLE.visible = true;
|
CONSOLE.visible = true;
|
||||||
|
|
||||||
#ifdef DUSK_CONSOLE_POSIX
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
threadMutexInit(&CONSOLE.printMutex);
|
threadMutexInit(&CONSOLE.printMutex);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void consolePrint(const char_t *message, ...) {
|
void consolePrint(const char_t *message, ...) {
|
||||||
@@ -35,9 +32,9 @@ void consolePrint(const char_t *message, ...) {
|
|||||||
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
|
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
#ifdef DUSK_CONSOLE_POSIX
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
threadMutexLock(&CONSOLE.printMutex);
|
threadMutexLock(&CONSOLE.printMutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memoryMove(
|
memoryMove(
|
||||||
CONSOLE.line[0],
|
CONSOLE.line[0],
|
||||||
@@ -46,17 +43,17 @@ void consolePrint(const char_t *message, ...) {
|
|||||||
);
|
);
|
||||||
memoryCopy(CONSOLE.line[CONSOLE_HISTORY_MAX - 1], buffer, len + 1);
|
memoryCopy(CONSOLE.line[CONSOLE_HISTORY_MAX - 1], buffer, len + 1);
|
||||||
|
|
||||||
#ifdef DUSK_CONSOLE_POSIX
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
threadMutexUnlock(&CONSOLE.printMutex);
|
threadMutexUnlock(&CONSOLE.printMutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
logDebug("%s\n", buffer);
|
logDebug("%s\n", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void consoleUpdate(void) {
|
void consoleUpdate(void) {
|
||||||
#ifdef DUSK_TIME_DYNAMIC
|
#ifdef DUSK_TIME_DYNAMIC
|
||||||
if(TIME.dynamicUpdate) return;
|
if(TIME.dynamicUpdate) return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(inputPressed(INPUT_ACTION_CONSOLE)) {
|
if(inputPressed(INPUT_ACTION_CONSOLE)) {
|
||||||
CONSOLE.visible = !CONSOLE.visible;
|
CONSOLE.visible = !CONSOLE.visible;
|
||||||
@@ -64,21 +61,11 @@ void consoleUpdate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t consoleDraw(void) {
|
errorret_t consoleDraw(void) {
|
||||||
if(!CONSOLE.visible) errorOk();
|
errorOk();
|
||||||
|
|
||||||
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
|
|
||||||
errorChain(textDraw(
|
|
||||||
0, FONT_DEFAULT.tileset->tileHeight * i,
|
|
||||||
CONSOLE.line[i],
|
|
||||||
COLOR_RED,
|
|
||||||
&FONT_DEFAULT
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return spriteBatchFlush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void consoleDispose(void) {
|
void consoleDispose(void) {
|
||||||
#ifdef DUSK_CONSOLE_POSIX
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
threadMutexDispose(&CONSOLE.printMutex);
|
threadMutexDispose(&CONSOLE.printMutex);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
// Copyright (c) 2026 Dominic Masters
|
|
||||||
//
|
|
||||||
// This software is released under the MIT License.
|
|
||||||
// https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
#include "cutscene.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
cutscene_t CUTSCENE;
|
|
||||||
|
|
||||||
errorret_t cutsceneInit(void) {
|
|
||||||
memoryZero(&CUTSCENE, sizeof(cutscene_t));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneUpdate(void) {
|
|
||||||
#ifdef DUSK_TIME_DYNAMIC
|
|
||||||
if(TIME.dynamicUpdate) {
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(!CUTSCENE.active) errorOk();
|
|
||||||
|
|
||||||
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(event->onUpdate) errorChain(event->onUpdate());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutscenePlay(
|
|
||||||
const cutsceneevent_t *events,
|
|
||||||
const uint8_t eventCount
|
|
||||||
) {
|
|
||||||
assertNotNull(events, "Events cannot be null");
|
|
||||||
assertTrue(eventCount > 0, "Event count must be greater than zero");
|
|
||||||
assertTrue(
|
|
||||||
eventCount <= CUTSCENE_EVENT_COUNT_MAX,
|
|
||||||
"Event count exceeds CUTSCENE_EVENT_COUNT_MAX"
|
|
||||||
);
|
|
||||||
|
|
||||||
if(CUTSCENE.active) {
|
|
||||||
errorChain(cutsceneStop());
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryCopy(CUTSCENE.events, events, sizeof(cutsceneevent_t) * eventCount);
|
|
||||||
CUTSCENE.eventCount = eventCount;
|
|
||||||
CUTSCENE.eventCurrent = 0;
|
|
||||||
CUTSCENE.active = true;
|
|
||||||
|
|
||||||
cutsceneevent_t *firstEvent = &CUTSCENE.events[0];
|
|
||||||
if(firstEvent->onStart) errorChain(firstEvent->onStart());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneAdvance(void) {
|
|
||||||
if(!CUTSCENE.active) errorOk();
|
|
||||||
|
|
||||||
cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(currentEvent->onEnd) errorChain(currentEvent->onEnd());
|
|
||||||
CUTSCENE.eventCurrent++;
|
|
||||||
|
|
||||||
if(CUTSCENE.eventCurrent >= CUTSCENE.eventCount) {
|
|
||||||
if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop());
|
|
||||||
CUTSCENE.active = false;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
cutsceneevent_t *nextEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(nextEvent->onStart) errorChain(nextEvent->onStart());
|
|
||||||
consolePrint("Cutscene advance");
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneStop(void) {
|
|
||||||
if(!CUTSCENE.active) errorOk();
|
|
||||||
|
|
||||||
cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(currentEvent->onEnd) errorChain(currentEvent->onEnd());
|
|
||||||
|
|
||||||
if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop());
|
|
||||||
|
|
||||||
CUTSCENE.active = false;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneDispose(void) {
|
|
||||||
errorChain(cutsceneStop());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t cutsceneIsActive(void) {
|
|
||||||
return CUTSCENE.active;
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
// Copyright (c) 2026 Dominic Masters
|
|
||||||
//
|
|
||||||
// This software is released under the MIT License.
|
|
||||||
// https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
|
|
||||||
#define CUTSCENE_EVENT_COUNT_MAX 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
errorret_t (*onStart)(void);
|
|
||||||
errorret_t (*onEnd)(void);
|
|
||||||
errorret_t (*onUpdate)(void);
|
|
||||||
} cutsceneevent_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
cutsceneevent_t events[CUTSCENE_EVENT_COUNT_MAX];
|
|
||||||
uint8_t eventCount;
|
|
||||||
uint8_t eventCurrent;
|
|
||||||
errorret_t (*onStop)(void);
|
|
||||||
bool_t active;
|
|
||||||
} cutscene_t;
|
|
||||||
|
|
||||||
extern cutscene_t CUTSCENE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the cutscene manager.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ticks the active cutscene event, calling its onUpdate callback.
|
|
||||||
* Does nothing when no cutscene is playing.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the given event array and begins playing from the first
|
|
||||||
* event. If a cutscene is already playing it is stopped first.
|
|
||||||
*
|
|
||||||
* @param events Array of events to copy.
|
|
||||||
* @param eventCount Number of events. Must be > 0 and
|
|
||||||
* <= CUTSCENE_EVENT_COUNT_MAX.
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutscenePlay(
|
|
||||||
const cutsceneevent_t *events,
|
|
||||||
const uint8_t eventCount
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the current event and starts the next one.
|
|
||||||
* Marks the cutscene as inactive after the last event ends.
|
|
||||||
* Does nothing when no cutscene is playing.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneAdvance(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the current event and stops the cutscene immediately.
|
|
||||||
* Does nothing when no cutscene is playing.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneStop(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the cutscene manager, stopping any active cutscene.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneDispose(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a cutscene is currently playing.
|
|
||||||
*
|
|
||||||
* @return true if a cutscene is active.
|
|
||||||
*/
|
|
||||||
bool_t cutsceneIsActive(void);
|
|
||||||
@@ -1,24 +1,13 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
# Copyright (c) 2026 Dominic Masters
|
||||||
#
|
#
|
||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
display.c
|
display.c
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirectories
|
|
||||||
add_subdirectory(framebuffer)
|
|
||||||
add_subdirectory(mesh)
|
|
||||||
add_subdirectory(screen)
|
|
||||||
add_subdirectory(shader)
|
|
||||||
add_subdirectory(spritebatch)
|
|
||||||
add_subdirectory(text)
|
|
||||||
add_subdirectory(texture)
|
|
||||||
|
|
||||||
# Color definitions
|
|
||||||
dusk_run_python(
|
dusk_run_python(
|
||||||
dusk_color_defs
|
dusk_color_defs
|
||||||
tools.color.csv
|
tools.color.csv
|
||||||
|
|||||||
+14
-90
@@ -6,111 +6,35 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "display/framebuffer/framebuffer.h"
|
#include "render/ropbuffer.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "display/mesh/quad.h"
|
|
||||||
#include "display/mesh/cube.h"
|
|
||||||
#include "display/mesh/sphere.h"
|
|
||||||
#include "display/mesh/plane.h"
|
|
||||||
#include "display/mesh/capsule.h"
|
|
||||||
#include "display/mesh/triprism.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "ui/ui.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
|
||||||
#include "asset/asset.h"
|
|
||||||
#include "display/shader/shaderlist.h"
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
display_t DISPLAY = { 0 };
|
display_t DISPLAY = { 0 };
|
||||||
|
|
||||||
errorret_t displayInit(void) {
|
errorret_t displayInit(void) {
|
||||||
memoryZero(&DISPLAY, sizeof(DISPLAY));
|
memoryZero(&DISPLAY, sizeof(DISPLAY));
|
||||||
|
#ifdef displayPlatformInit
|
||||||
#ifdef displayPlatformInit
|
errorChain(displayPlatformInit());
|
||||||
errorChain(displayPlatformInit());
|
#endif
|
||||||
#endif
|
|
||||||
errorChain(displaySetState((displaystate_t){ .flags = 0 }));
|
|
||||||
errorChain(textureInit(
|
|
||||||
&TEXTURE_WHITE, 4, 4,
|
|
||||||
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_WHITE_PIXELS }
|
|
||||||
));
|
|
||||||
errorChain(textureInit(
|
|
||||||
&TEXTURE_TEST, 4, 4,
|
|
||||||
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_TEST_PIXELS }
|
|
||||||
));
|
|
||||||
|
|
||||||
// Standard meshes
|
|
||||||
errorChain(quadInit());
|
|
||||||
errorChain(cubeInit());
|
|
||||||
errorChain(sphereInit());
|
|
||||||
errorChain(planeInit());
|
|
||||||
errorChain(capsuleInit());
|
|
||||||
errorChain(triPrismInit());
|
|
||||||
|
|
||||||
errorChain(frameBufferInitBackBuffer());
|
|
||||||
errorChain(spriteBatchInit());
|
|
||||||
errorChain(textInit());
|
|
||||||
errorChain(screenInit());
|
|
||||||
|
|
||||||
// Setup initial shader with default values
|
|
||||||
|
|
||||||
errorChain(shaderListInit());
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t displayUpdate(void) {
|
errorret_t displayUpdate(void) {
|
||||||
#ifdef displayPlatformUpdate
|
ropBufferReset(&ROPBUFFER);
|
||||||
errorChain(displayPlatformUpdate());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Reset state
|
|
||||||
spriteBatchClear();
|
|
||||||
errorChain(frameBufferBind(NULL));
|
|
||||||
|
|
||||||
// Bind screen and render scene
|
|
||||||
errorChain(screenBind());
|
|
||||||
frameBufferClear(
|
|
||||||
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
|
|
||||||
SCREEN.background
|
|
||||||
);
|
|
||||||
|
|
||||||
errorChain(sceneRender());
|
errorChain(sceneRender());
|
||||||
|
#ifdef displayPlatformFlush
|
||||||
// Finish up
|
errorChain(displayPlatformFlush(&ROPBUFFER));
|
||||||
screenUnbind();
|
#endif
|
||||||
screenRender();
|
#ifdef displayPlatformSwap
|
||||||
|
errorChain(displayPlatformSwap());
|
||||||
// Swap and return.
|
#endif
|
||||||
#ifdef displayPlatformSwap
|
|
||||||
errorChain(displayPlatformSwap());
|
|
||||||
#endif
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t displaySetState(displaystate_t state) {
|
|
||||||
#ifdef displayPlatformSetState
|
|
||||||
errorChain(displayPlatformSetState(state));
|
|
||||||
#endif
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t displayDispose(void) {
|
errorret_t displayDispose(void) {
|
||||||
errorChain(shaderListDispose());
|
#ifdef displayPlatformDispose
|
||||||
errorChain(spriteBatchDispose());
|
displayPlatformDispose();
|
||||||
screenDispose();
|
#endif
|
||||||
errorChain(textDispose());
|
|
||||||
errorChain(textureDispose(&TEXTURE_WHITE));
|
|
||||||
errorChain(textureDispose(&TEXTURE_TEST));
|
|
||||||
|
|
||||||
#ifdef displayPlatformDispose
|
|
||||||
displayPlatformDispose();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For now, we just return an OK error.
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -7,59 +7,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "display/displayplatform.h"
|
#include "display/displayplatform.h"
|
||||||
|
#include "display/displaystate.h"
|
||||||
|
|
||||||
// Expecting some definitions to be provided
|
|
||||||
#ifndef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
#ifndef DUSK_DISPLAY_WIDTH
|
|
||||||
#error "DUSK_DISPLAY_WIDTH must be defined."
|
|
||||||
#endif
|
|
||||||
#ifndef DUSK_DISPLAY_HEIGHT
|
|
||||||
#error "DUSK_DISPLAY_HEIGHT must be defined"
|
|
||||||
#endif
|
|
||||||
#define DUSK_DISPLAY_WIDTH_DEFAULT DUSK_DISPLAY_WIDTH
|
|
||||||
#define DUSK_DISPLAY_HEIGHT_DEFAULT DUSK_DISPLAY_HEIGHT
|
|
||||||
#else
|
|
||||||
#ifndef DUSK_DISPLAY_WIDTH_DEFAULT
|
|
||||||
#error "DUSK_DISPLAY_WIDTH_DEFAULT must be defined."
|
|
||||||
#endif
|
|
||||||
#ifndef DUSK_DISPLAY_HEIGHT_DEFAULT
|
|
||||||
#error "DUSK_DISPLAY_HEIGHT_DEFAULT must be defined."
|
|
||||||
#endif
|
|
||||||
#ifdef DUSK_DISPLAY_WIDTH
|
|
||||||
#error "DUSK_DISPLAY_WIDTH should not be defined."
|
|
||||||
#endif
|
|
||||||
#ifdef DUSK_DISPLAY_HEIGHT
|
|
||||||
#error "DUSK_DISPLAY_HEIGHT should not be defined."
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Main Display Struct, platform-speicifc
|
|
||||||
typedef displayplatform_t display_t;
|
typedef displayplatform_t display_t;
|
||||||
|
|
||||||
extern display_t DISPLAY;
|
extern display_t DISPLAY;
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the display system.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t displayInit(void);
|
errorret_t displayInit(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the display system to actually draw the frame.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t displayUpdate(void);
|
errorret_t displayUpdate(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the display state.
|
|
||||||
*
|
|
||||||
* @param state The state to set.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t displaySetState(displaystate_t state);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the display system.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t displayDispose(void);
|
errorret_t displayDispose(void);
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
|
||||||
#define DISPLAY_STATE_FLAG_CULL (1 << 0)
|
#define DISPLAY_STATE_FLAG_CULL (1 << 0)
|
||||||
#define DISPLAY_STATE_FLAG_DEPTH_TEST (1 << 1)
|
#define DISPLAY_STATE_FLAG_DEPTH_TEST (1 << 1)
|
||||||
#define DISPLAY_STATE_FLAG_BLEND (1 << 2)
|
#define DISPLAY_STATE_FLAG_BLEND (1 << 2)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
|||||||
@@ -9,19 +9,12 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
#include "input/input.h"
|
#include "input/input.h"
|
||||||
#include "locale/localemanager.h"
|
|
||||||
#include "rpg/rpg.h"
|
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "cutscene/cutscene.h"
|
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "ui/ui.h"
|
|
||||||
#include "ui/uitextbox.h"
|
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "network/network.h"
|
|
||||||
#include "system/system.h"
|
#include "system/system.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "save/save.h"
|
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
|
|
||||||
@@ -29,48 +22,32 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
assertInit();
|
assertInit();
|
||||||
memoryZero(&ENGINE, sizeof(engine_t));
|
memoryZero(&ENGINE, sizeof(engine_t));
|
||||||
ENGINE.running = true;
|
ENGINE.running = true;
|
||||||
ENGINE.argc = argc;
|
ENGINE.argc = argc;
|
||||||
ENGINE.argv = argv;
|
ENGINE.argv = argv;
|
||||||
ENGINE.version = DUSK_VERSION;
|
ENGINE.version = DUSK_VERSION;
|
||||||
|
|
||||||
// Init systems. Order is important.
|
|
||||||
errorChain(systemInit());
|
errorChain(systemInit());
|
||||||
timeInit();
|
timeInit();
|
||||||
consoleInit();
|
consoleInit();
|
||||||
errorChain(inputInit());
|
errorChain(inputInit());
|
||||||
errorChain(assetInit());
|
errorChain(assetInit());
|
||||||
// errorChain(saveInit());
|
|
||||||
errorChain(localeManagerInit());
|
|
||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
errorChain(uiInit());
|
|
||||||
errorChain(uiTextboxInit());
|
|
||||||
errorChain(cutsceneInit());
|
|
||||||
errorChain(rpgInit());
|
|
||||||
errorChain(networkInit());
|
|
||||||
errorChain(sceneInit());
|
errorChain(sceneInit());
|
||||||
|
|
||||||
consolePrint("Engine initialized");
|
consolePrint("Engine initialized");
|
||||||
sceneSet(SCENE_TYPE_OVERWORLD);
|
sceneSet(SCENE_TYPE_TEST);
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineUpdate(void) {
|
errorret_t engineUpdate(void) {
|
||||||
// Order here is important.
|
|
||||||
errorChain(networkUpdate());
|
|
||||||
timeUpdate();
|
timeUpdate();
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
consoleUpdate();
|
consoleUpdate();
|
||||||
errorChain(rpgUpdate());
|
|
||||||
uiUpdate();
|
|
||||||
errorChain(uiTextboxUpdate());
|
|
||||||
errorChain(cutsceneUpdate());
|
|
||||||
errorChain(sceneUpdate());
|
errorChain(sceneUpdate());
|
||||||
errorChain(assetUpdate());
|
errorChain(assetUpdate());
|
||||||
|
|
||||||
// Render
|
|
||||||
errorChain(displayUpdate());
|
errorChain(displayUpdate());
|
||||||
|
|
||||||
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -80,17 +57,9 @@ void engineExit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
uiTextboxDispose();
|
|
||||||
cutsceneDispose();
|
|
||||||
errorChain(sceneDispose());
|
errorChain(sceneDispose());
|
||||||
errorChain(networkDispose());
|
|
||||||
errorChain(rpgDispose());
|
|
||||||
localeManagerDispose();
|
|
||||||
uiDispose();
|
|
||||||
consoleDispose();
|
consoleDispose();
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
// errorChain(saveDispose());
|
|
||||||
errorChain(assetDispose());
|
errorChain(assetDispose());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,35 +6,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Important to be included first:
|
|
||||||
#include "display/display.h"
|
|
||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool_t running;
|
bool_t running;
|
||||||
int32_t argc;
|
int32_t argc;
|
||||||
const char_t **argv;
|
const char_t **argv;
|
||||||
const char_t *version;
|
const char_t *version;
|
||||||
} engine_t;
|
} engine_t;
|
||||||
|
|
||||||
extern engine_t ENGINE;
|
extern engine_t ENGINE;
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the engine.
|
|
||||||
*
|
|
||||||
* @param argc The argument count from main().
|
|
||||||
* @param argv The argument vector from main().
|
|
||||||
*/
|
|
||||||
errorret_t engineInit(const int32_t argc, const char_t **argv);
|
errorret_t engineInit(const int32_t argc, const char_t **argv);
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the engine.
|
|
||||||
*/
|
|
||||||
errorret_t engineUpdate(void);
|
errorret_t engineUpdate(void);
|
||||||
|
void engineExit(void);
|
||||||
/**
|
|
||||||
* Shuts down the engine.
|
|
||||||
*/
|
|
||||||
errorret_t engineDispose(void);
|
errorret_t engineDispose(void);
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
texturegl.c
|
ropbuffer.c
|
||||||
|
render.c
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "render/render.h"
|
||||||
|
|
||||||
|
void renderClear(color_t color) {
|
||||||
|
ropclear_t *cmd = ropBufferAlloc(&ROPBUFFER, ROP_CLEAR);
|
||||||
|
cmd->color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderSprite(
|
||||||
|
int16_t x, int16_t y,
|
||||||
|
int16_t w, int16_t h,
|
||||||
|
color_t tint
|
||||||
|
) {
|
||||||
|
ropsprite_t *cmd = ropBufferAlloc(&ROPBUFFER, ROP_DRAW_SPRITE);
|
||||||
|
cmd->x = x;
|
||||||
|
cmd->y = y;
|
||||||
|
cmd->w = w;
|
||||||
|
cmd->h = h;
|
||||||
|
cmd->uvX = 0;
|
||||||
|
cmd->uvY = 0;
|
||||||
|
cmd->uvW = 255;
|
||||||
|
cmd->uvH = 255;
|
||||||
|
cmd->tint = tint;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "render/rop.h"
|
||||||
|
#include "render/ropbuffer.h"
|
||||||
|
|
||||||
|
void renderClear(color_t color);
|
||||||
|
|
||||||
|
void renderSprite(
|
||||||
|
int16_t x, int16_t y,
|
||||||
|
int16_t w, int16_t h,
|
||||||
|
color_t tint
|
||||||
|
);
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "display/color.h"
|
||||||
|
|
||||||
|
#define ROP_SIZE 32
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ROP_NOP = 0,
|
||||||
|
ROP_CLEAR,
|
||||||
|
ROP_DRAW_SPRITE,
|
||||||
|
ROP_COUNT
|
||||||
|
} ropop_t;
|
||||||
|
|
||||||
|
#define ROP_FLAG_BLEND ((uint8_t)(1 << 0))
|
||||||
|
|
||||||
|
/* 4 bytes, every opcode starts with this */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t op;
|
||||||
|
uint8_t flags;
|
||||||
|
int16_t depth;
|
||||||
|
} ropheader_t;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(ropheader_t) == 4, "ropheader_t must be 4 bytes");
|
||||||
|
|
||||||
|
/* ROP_CLEAR — 32 bytes */
|
||||||
|
typedef struct {
|
||||||
|
ropheader_t header; /* 4 */
|
||||||
|
color_t color; /* 4 */
|
||||||
|
uint8_t pad[24];/* 24 */
|
||||||
|
} ropclear_t;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(ropclear_t) == ROP_SIZE, "ropclear_t must be ROP_SIZE bytes");
|
||||||
|
|
||||||
|
/* ROP_DRAW_SPRITE — 32 bytes, screen-space pixel coordinates */
|
||||||
|
typedef struct {
|
||||||
|
ropheader_t header; /* 4 */
|
||||||
|
int16_t x, y; /* 4 */
|
||||||
|
int16_t w, h; /* 4 */
|
||||||
|
uint8_t uvX, uvY; /* 2 */
|
||||||
|
uint8_t uvW, uvH; /* 2 */
|
||||||
|
color_t tint; /* 4 */
|
||||||
|
uint16_t texture; /* 2 handle, 0 = white */
|
||||||
|
uint16_t palette; /* 2 */
|
||||||
|
uint8_t pad[8]; /* 8 */
|
||||||
|
} ropsprite_t;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(ropsprite_t) == ROP_SIZE, "ropsprite_t must be ROP_SIZE bytes");
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "render/ropbuffer.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
ropbuffer_t ROPBUFFER;
|
||||||
|
|
||||||
|
void ropBufferReset(ropbuffer_t *buf) {
|
||||||
|
buf->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ropBufferAlloc(ropbuffer_t *buf, ropop_t op) {
|
||||||
|
assertTrue(buf->count < ROPBUFFER_MAX_COMMANDS, "ROP buffer is full");
|
||||||
|
uint8_t *ptr = buf->data + (buf->count * ROP_SIZE);
|
||||||
|
memoryZero(ptr, ROP_SIZE);
|
||||||
|
((ropheader_t *)ptr)->op = (uint8_t)op;
|
||||||
|
buf->count++;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "render/rop.h"
|
||||||
|
|
||||||
|
#define ROPBUFFER_MAX_COMMANDS 4096
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[ROPBUFFER_MAX_COMMANDS * ROP_SIZE];
|
||||||
|
uint32_t count;
|
||||||
|
} ropbuffer_t;
|
||||||
|
|
||||||
|
extern ropbuffer_t ROPBUFFER;
|
||||||
|
|
||||||
|
void ropBufferReset(ropbuffer_t *buf);
|
||||||
|
void *ropBufferAlloc(ropbuffer_t *buf, ropop_t op);
|
||||||
@@ -15,6 +15,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
add_subdirectory(cutscene)
|
add_subdirectory(cutscene)
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(overworld)
|
add_subdirectory(overworld)
|
||||||
|
|
||||||
add_subdirectory(story)
|
add_subdirectory(story)
|
||||||
add_subdirectory(item)
|
add_subdirectory(item)
|
||||||
@@ -8,26 +8,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "rpg/overworld/tile.h"
|
#include "rpg/overworld/tile.h"
|
||||||
#include "worldpos.h"
|
#include "worldpos.h"
|
||||||
#include "display/mesh/quad.h"
|
#include "display/color.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
|
|
||||||
// #define CHUNK_MESH_COUNT_MAX 3
|
|
||||||
#define CHUNK_VERTEX_COUNT (QUAD_VERTEX_COUNT * CHUNK_TILE_COUNT)
|
|
||||||
#define CHUNK_ENTITY_COUNT_MAX 10
|
#define CHUNK_ENTITY_COUNT_MAX 10
|
||||||
|
|
||||||
typedef struct chunk_s {
|
typedef struct chunk_s {
|
||||||
chunkpos_t position;
|
chunkpos_t position;
|
||||||
tile_t tiles[CHUNK_TILE_COUNT];
|
tile_t tiles[CHUNK_TILE_COUNT];
|
||||||
|
color_t testColor;
|
||||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
uint32_t vertCount;
|
||||||
uint32_t vertCount;
|
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
||||||
mesh_t mesh;
|
|
||||||
color_t testColor;
|
|
||||||
|
|
||||||
// uint8_t meshCount;
|
|
||||||
// meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
|
|
||||||
// mesh_t meshes[CHUNK_MESH_COUNT_MAX];
|
|
||||||
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
|
||||||
} chunk_t;
|
} chunk_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+10
-151
@@ -17,18 +17,6 @@ map_t MAP;
|
|||||||
errorret_t mapInit() {
|
errorret_t mapInit() {
|
||||||
memoryZero(&MAP, sizeof(map_t));
|
memoryZero(&MAP, sizeof(map_t));
|
||||||
|
|
||||||
// Setup chunk meshes
|
|
||||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
|
||||||
chunk_t *chunk = &MAP.chunks[i];
|
|
||||||
errorChain(meshInit(
|
|
||||||
&chunk->mesh,
|
|
||||||
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
|
||||||
CHUNK_VERTEX_COUNT,
|
|
||||||
chunk->vertices
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform "initial load"
|
|
||||||
MAP.loaded = true;
|
MAP.loaded = true;
|
||||||
int32_t i = 0;
|
int32_t i = 0;
|
||||||
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
||||||
@@ -52,61 +40,6 @@ bool_t mapIsLoaded() {
|
|||||||
return MAP.loaded;
|
return MAP.loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
|
|
||||||
// assertStrLenMin(path, 1, "Map file path cannot be empty");
|
|
||||||
// assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long");
|
|
||||||
|
|
||||||
// if(stringCompare(MAP.filePath, path) == 0) {
|
|
||||||
// // Same map, no need to reload
|
|
||||||
// errorOk();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// chunkindex_t i;
|
|
||||||
|
|
||||||
// // Unload all loaded chunks
|
|
||||||
// if(mapIsLoaded()) {
|
|
||||||
// for(i = 0; i < MAP_CHUNK_COUNT; i++) {
|
|
||||||
// mapChunkUnload(&MAP.chunks[i]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Store the map file path
|
|
||||||
// stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX);
|
|
||||||
|
|
||||||
// // Determine directory path (it is dirname)
|
|
||||||
// stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX);
|
|
||||||
// char_t *last = stringFindLastChar(MAP.dirPath, '/');
|
|
||||||
// if(last == NULL) errorThrow("Invalid map file path");
|
|
||||||
|
|
||||||
// // Store filename, sans extension
|
|
||||||
// stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX);
|
|
||||||
// *last = '\0'; // Terminate to get directory path
|
|
||||||
|
|
||||||
// last = stringFindLastChar(MAP.fileName, '.');
|
|
||||||
// if(last == NULL) errorThrow("Map file name has no extension");
|
|
||||||
// *last = '\0'; // Terminate to remove extension
|
|
||||||
|
|
||||||
// // Reset map position
|
|
||||||
// MAP.chunkPosition = position;
|
|
||||||
|
|
||||||
// // Perform "initial load"
|
|
||||||
// i = 0;
|
|
||||||
// for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
|
||||||
// for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
|
||||||
// for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
|
|
||||||
// chunk_t *chunk = &MAP.chunks[i];
|
|
||||||
// chunk->position.x = x + position.x;
|
|
||||||
// chunk->position.y = y + position.y;
|
|
||||||
// chunk->position.z = z + position.z;
|
|
||||||
// MAP.chunkOrder[i] = chunk;
|
|
||||||
// errorChain(mapChunkLoad(chunk));
|
|
||||||
// i++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// errorOk();
|
|
||||||
// }
|
|
||||||
|
|
||||||
errorret_t mapPositionSet(const chunkpos_t newPos) {
|
errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||||
|
|
||||||
@@ -115,43 +48,31 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
|||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine which chunks remain loaded
|
|
||||||
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
|
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
|
||||||
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
|
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
|
||||||
|
|
||||||
uint32_t remainingCount = 0;
|
uint32_t remainingCount = 0;
|
||||||
uint32_t freedCount = 0;
|
uint32_t freedCount = 0;
|
||||||
|
|
||||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||||
// Will this chunk remain loaded?
|
|
||||||
chunk_t *chunk = &MAP.chunks[i];
|
chunk_t *chunk = &MAP.chunks[i];
|
||||||
if(
|
if(
|
||||||
chunk->position.x >= newPos.x &&
|
chunk->position.x >= newPos.x &&
|
||||||
chunk->position.x < newPos.x + MAP_CHUNK_WIDTH &&
|
chunk->position.x < newPos.x + MAP_CHUNK_WIDTH &&
|
||||||
|
|
||||||
chunk->position.y >= newPos.y &&
|
chunk->position.y >= newPos.y &&
|
||||||
chunk->position.y < newPos.y + MAP_CHUNK_HEIGHT &&
|
chunk->position.y < newPos.y + MAP_CHUNK_HEIGHT &&
|
||||||
|
|
||||||
chunk->position.z >= newPos.z &&
|
chunk->position.z >= newPos.z &&
|
||||||
chunk->position.z < newPos.z + MAP_CHUNK_DEPTH
|
chunk->position.z < newPos.z + MAP_CHUNK_DEPTH
|
||||||
) {
|
) {
|
||||||
// Stays loaded
|
|
||||||
chunksRemaining[remainingCount++] = i;
|
chunksRemaining[remainingCount++] = i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not remaining loaded
|
|
||||||
chunksFreed[freedCount++] = i;
|
chunksFreed[freedCount++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unload the freed chunks
|
|
||||||
for(chunkindex_t i = 0; i < freedCount; i++) {
|
for(chunkindex_t i = 0; i < freedCount; i++) {
|
||||||
chunk_t *chunk = &MAP.chunks[chunksFreed[i]];
|
mapChunkUnload(&MAP.chunks[chunksFreed[i]]);
|
||||||
mapChunkUnload(chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can probably be optimized later, for now we check each chunk and see
|
|
||||||
// if it needs loading or not, and update the chunk order
|
|
||||||
chunkindex_t orderIndex = 0;
|
chunkindex_t orderIndex = 0;
|
||||||
for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) {
|
for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) {
|
||||||
for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) {
|
for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) {
|
||||||
@@ -160,7 +81,6 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
|||||||
newPos.x + xOff, newPos.y + yOff, newPos.z + zOff
|
newPos.x + xOff, newPos.y + yOff, newPos.z + zOff
|
||||||
};
|
};
|
||||||
|
|
||||||
// Is this chunk already loaded (was not unloaded earlier)?
|
|
||||||
chunkindex_t chunkIndex = -1;
|
chunkindex_t chunkIndex = -1;
|
||||||
for(chunkindex_t i = 0; i < remainingCount; i++) {
|
for(chunkindex_t i = 0; i < remainingCount; i++) {
|
||||||
chunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
|
chunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
|
||||||
@@ -169,9 +89,7 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to load this chunk
|
|
||||||
if(chunkIndex == -1) {
|
if(chunkIndex == -1) {
|
||||||
// Find a freed chunk to reuse
|
|
||||||
chunkIndex = chunksFreed[--freedCount];
|
chunkIndex = chunksFreed[--freedCount];
|
||||||
chunk_t *chunk = &MAP.chunks[chunkIndex];
|
chunk_t *chunk = &MAP.chunks[chunkIndex];
|
||||||
chunk->position = newChunkPos;
|
chunk->position = newChunkPos;
|
||||||
@@ -183,25 +101,21 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update map position
|
|
||||||
MAP.chunkPosition = newPos;
|
MAP.chunkPosition = newPos;
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mapUpdate() {
|
void mapUpdate() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t mapDispose() {
|
errorret_t mapDispose() {
|
||||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||||
mapChunkUnload(&MAP.chunks[i]);
|
mapChunkUnload(&MAP.chunks[i]);
|
||||||
errorChain(meshDispose(&MAP.chunks[i].mesh));
|
|
||||||
}
|
}
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mapChunkUnload(chunk_t* chunk) {
|
void mapChunkUnload(chunk_t *chunk) {
|
||||||
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||||
if(chunk->entities[i] == 0xFF) break;
|
if(chunk->entities[i] == 0xFF) break;
|
||||||
entity_t *entity = &ENTITIES[chunk->entities[i]];
|
entity_t *entity = &ENTITIES[chunk->entities[i]];
|
||||||
@@ -210,76 +124,21 @@ void mapChunkUnload(chunk_t* chunk) {
|
|||||||
chunk->vertCount = 0;
|
chunk->vertCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t mapChunkLoad(chunk_t* chunk) {
|
errorret_t mapChunkLoad(chunk_t *chunk) {
|
||||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||||
|
|
||||||
color_t color = COLOR_WHITE;
|
color_t color = COLOR_WHITE;
|
||||||
if(chunk->position.y % 2 == 0) {
|
if(chunk->position.y % 2 == 0) {
|
||||||
if(chunk->position.x % 2 == 0) {
|
color = (chunk->position.x % 2 == 0) ? COLOR_BLACK : COLOR_WHITE;
|
||||||
color = COLOR_BLACK;
|
|
||||||
} else {
|
|
||||||
color = COLOR_WHITE;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if(chunk->position.x % 2 == 0) {
|
color = (chunk->position.x % 2 == 0) ? COLOR_WHITE : COLOR_BLACK;
|
||||||
color = COLOR_WHITE;
|
|
||||||
} else {
|
|
||||||
color = COLOR_BLACK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// if(chunk->position.x == 0 && chunk->position.y == 0 && chunk->position.z == 0) {
|
|
||||||
// color = COLOR_RED;
|
|
||||||
// }
|
|
||||||
chunk->testColor = color;
|
chunk->testColor = color;
|
||||||
|
|
||||||
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
|
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
|
||||||
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
||||||
chunk->vertCount = 0;
|
chunk->vertCount = 0;
|
||||||
|
|
||||||
if(chunk->position.z != 0) {
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set Chunk sprites.
|
|
||||||
vec3 spriteMin = {
|
|
||||||
chunk->position.x * CHUNK_WIDTH,
|
|
||||||
chunk->position.y * CHUNK_HEIGHT,
|
|
||||||
chunk->position.z * CHUNK_DEPTH
|
|
||||||
};
|
|
||||||
|
|
||||||
spritebatchsprite_t sprites[CHUNK_TILE_COUNT];
|
|
||||||
uint32_t i = 0;
|
|
||||||
for(uint8_t x = 0; x < CHUNK_WIDTH; x++) {
|
|
||||||
for(uint8_t y = 0; y < CHUNK_HEIGHT; y++) {
|
|
||||||
glm_vec3_copy(spriteMin, sprites[i].min);
|
|
||||||
glm_vec3_add(
|
|
||||||
sprites[i].min,
|
|
||||||
(vec3){ x, y, 0 },
|
|
||||||
sprites[i].min
|
|
||||||
);
|
|
||||||
|
|
||||||
glm_vec3_copy(sprites[i].min, sprites[i].max);
|
|
||||||
glm_vec3_add(
|
|
||||||
sprites[i].max,
|
|
||||||
(vec3){ 1, 1, 0 },
|
|
||||||
sprites[i].max
|
|
||||||
);
|
|
||||||
|
|
||||||
glm_vec2_copy((vec2){ 0, 0 }, sprites[i].uvMin);
|
|
||||||
glm_vec2_copy((vec2){ 1, 1 }, sprites[i].uvMax);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk->vertCount = i * QUAD_VERTEX_COUNT;
|
|
||||||
spriteBatchBufferToMesh(
|
|
||||||
sprites,
|
|
||||||
i,
|
|
||||||
chunk->vertices,
|
|
||||||
chunk->vertCount
|
|
||||||
);
|
|
||||||
errorChain(meshFlush(&chunk->mesh, 0, chunk->vertCount));
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +163,7 @@ chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {
|
|||||||
return chunkPosToIndex(&relPos);
|
return chunkPosToIndex(&relPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_t* mapGetChunk(const uint8_t index) {
|
chunk_t *mapGetChunk(const uint8_t index) {
|
||||||
if(index >= MAP_CHUNK_COUNT) return NULL;
|
if(index >= MAP_CHUNK_COUNT) return NULL;
|
||||||
if(!mapIsLoaded()) return NULL;
|
if(!mapIsLoaded()) return NULL;
|
||||||
return MAP.chunkOrder[index];
|
return MAP.chunkOrder[index];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
#include "rpg/overworld/chunk.h"
|
#include "rpg/overworld/chunk.h"
|
||||||
|
|
||||||
#define MAP_FILE_PATH_MAX 128
|
#define MAP_FILE_PATH_MAX 128
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(overworld)
|
add_subdirectory(overworld)
|
||||||
|
add_subdirectory(test)
|
||||||
@@ -5,199 +5,24 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scene/scene.h"
|
#include "scene/overworld/sceneoverworld.h"
|
||||||
#include "console/console.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
#include "display/shader/shader.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
|
|
||||||
#include "rpg/overworld/map.h"
|
|
||||||
#include "rpg/entity/entity.h"
|
|
||||||
#include "rpg/rpgcamera.h"
|
|
||||||
#include "util/math.h"
|
|
||||||
|
|
||||||
errorret_t sceneOverworldInit(scenedata_t *sceneData) {
|
errorret_t sceneOverworldInit(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
(void)sceneData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneOverworldUpdate(scenedata_t *sceneData) {
|
errorret_t sceneOverworldUpdate(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
(void)sceneData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
(void)sceneData;
|
||||||
|
|
||||||
mat4 proj, model, eye;
|
|
||||||
|
|
||||||
|
|
||||||
errorChain(shaderBind(&SHADER_UNLIT));
|
|
||||||
|
|
||||||
// Model
|
|
||||||
glm_mat4_identity(model);
|
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
|
||||||
|
|
||||||
// Camera projection
|
|
||||||
float_t fov = glm_rad(45.0f);
|
|
||||||
glm_perspective(
|
|
||||||
fov,
|
|
||||||
(float_t)SCREEN.width / (float_t)SCREEN.height,
|
|
||||||
0.1f,
|
|
||||||
100.0f,
|
|
||||||
proj
|
|
||||||
);
|
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
|
||||||
|
|
||||||
// Camera view
|
|
||||||
{
|
|
||||||
vec3 target = { 0.5f, 0.5f, 0.5f };
|
|
||||||
if(RPG_CAMERA.mode == RPG_CAMERA_MODE_FOLLOW_ENTITY) {
|
|
||||||
entity_t *followed = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
|
|
||||||
if(followed->type != ENTITY_TYPE_NULL) {
|
|
||||||
float_t walkT = followed->animation == ENTITY_ANIM_WALK
|
|
||||||
? fixedToFloat(
|
|
||||||
fixedDiv(followed->animTime, ENTITY_ANIM_WALK_DURATION)
|
|
||||||
)
|
|
||||||
: 0.0f;
|
|
||||||
target[0] = mathLerp(
|
|
||||||
fixedToFloat(followed->position[0]),
|
|
||||||
fixedToFloat(followed->lastPosition[0]),
|
|
||||||
walkT
|
|
||||||
) + 0.5f;
|
|
||||||
target[1] = mathLerp(
|
|
||||||
fixedToFloat(followed->position[1]),
|
|
||||||
fixedToFloat(followed->lastPosition[1]),
|
|
||||||
walkT
|
|
||||||
) + 0.5f;
|
|
||||||
target[2] = mathLerp(
|
|
||||||
fixedToFloat(followed->position[2]),
|
|
||||||
fixedToFloat(followed->lastPosition[2]),
|
|
||||||
walkT
|
|
||||||
) + 0.5f;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
worldpos_t camPos = rpgCameraGetPosition();
|
|
||||||
target[0] = (float_t)camPos.x + 0.5f;
|
|
||||||
target[1] = (float_t)camPos.y + 0.5f;
|
|
||||||
target[2] = (float_t)camPos.z + 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float_t pixelsPerUnit = 16.0f;
|
|
||||||
float_t worldH = (float_t)SCREEN.height / pixelsPerUnit;
|
|
||||||
float_t eyeZ = (worldH * 0.5f) / tanf(fov * 0.5f);
|
|
||||||
float_t offset = -16.0f;
|
|
||||||
|
|
||||||
glm_lookat(
|
|
||||||
(vec3){ target[0], target[1] + offset, target[2] + eyeZ },
|
|
||||||
target,
|
|
||||||
(vec3){ 0, 1, 0 },
|
|
||||||
eye
|
|
||||||
);
|
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chunks
|
|
||||||
{
|
|
||||||
shadermaterial_t chunkMaterial = {
|
|
||||||
.unlit = {
|
|
||||||
.color = COLOR_WHITE,
|
|
||||||
.texture = NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
|
||||||
for(uint8_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
|
|
||||||
for(uint8_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
|
||||||
for(uint8_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
|
||||||
chunk_t *chunk = &MAP.chunks[i];
|
|
||||||
if(chunk->vertCount == 0) {
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkMaterial.unlit.color = chunk->testColor;
|
|
||||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
|
||||||
errorChain(meshDraw(&chunk->mesh, 0, chunk->vertCount));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entities
|
|
||||||
{
|
|
||||||
uint8_t spriteCount = 0;
|
|
||||||
spritebatchsprite_t sprites[ENTITY_COUNT];
|
|
||||||
for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
|
|
||||||
entity_t *ent = &ENTITIES[i];
|
|
||||||
if(ent->type == ENTITY_TYPE_NULL) continue;
|
|
||||||
|
|
||||||
float_t walkT = ent->animation == ENTITY_ANIM_WALK
|
|
||||||
? fixedToFloat(fixedDiv(ent->animTime, ENTITY_ANIM_WALK_DURATION))
|
|
||||||
: 0.0f;
|
|
||||||
vec3 position = {
|
|
||||||
mathLerp(
|
|
||||||
fixedToFloat(ent->position[0]),
|
|
||||||
fixedToFloat(ent->lastPosition[0]),
|
|
||||||
walkT
|
|
||||||
),
|
|
||||||
mathLerp(
|
|
||||||
fixedToFloat(ent->position[1]),
|
|
||||||
fixedToFloat(ent->lastPosition[1]),
|
|
||||||
walkT
|
|
||||||
),
|
|
||||||
mathLerp(
|
|
||||||
fixedToFloat(ent->position[2]),
|
|
||||||
fixedToFloat(ent->lastPosition[2]),
|
|
||||||
walkT
|
|
||||||
) + 0.01f
|
|
||||||
};
|
|
||||||
|
|
||||||
glm_vec3_copy(position, sprites[spriteCount].min);
|
|
||||||
glm_vec3_copy(position, sprites[spriteCount].max);
|
|
||||||
glm_vec3_add(
|
|
||||||
sprites[spriteCount].max,
|
|
||||||
(vec3){ 1, 1, 0 },
|
|
||||||
sprites[spriteCount].max
|
|
||||||
);
|
|
||||||
|
|
||||||
glm_vec2_copy((vec2){ 0, 0 }, sprites[spriteCount].uvMin);
|
|
||||||
glm_vec2_copy((vec2){ 1, 1 }, sprites[spriteCount].uvMax);
|
|
||||||
|
|
||||||
spriteCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(spriteCount) {
|
|
||||||
shadermaterial_t material = {
|
|
||||||
.unlit = {
|
|
||||||
.color = COLOR_CYAN,
|
|
||||||
.texture = NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// material.unlit.texture = &TEXTURE_TEST;
|
|
||||||
spriteBatchBuffer(sprites, spriteCount, &SHADER_UNLIT, material);
|
|
||||||
spriteBatchFlush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
(void)sceneData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-42
@@ -7,12 +7,7 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
#include "display/display.h"
|
|
||||||
#include "ui/ui.h"
|
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "asset/loader/assetloader.h"
|
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
|
|
||||||
scene_t SCENE;
|
scene_t SCENE;
|
||||||
@@ -23,7 +18,6 @@ errorret_t sceneInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneUpdate(void) {
|
errorret_t sceneUpdate(void) {
|
||||||
// Handle scene change.
|
|
||||||
if(SCENE.next != SCENE_TYPE_NULL) {
|
if(SCENE.next != SCENE_TYPE_NULL) {
|
||||||
if(
|
if(
|
||||||
SCENE.current != SCENE_TYPE_NULL &&
|
SCENE.current != SCENE_TYPE_NULL &&
|
||||||
@@ -33,7 +27,7 @@ errorret_t sceneUpdate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SCENE.current = SCENE.next;
|
SCENE.current = SCENE.next;
|
||||||
SCENE.next = SCENE_TYPE_NULL;
|
SCENE.next = SCENE_TYPE_NULL;
|
||||||
|
|
||||||
if(
|
if(
|
||||||
SCENE.current != SCENE_TYPE_NULL &&
|
SCENE.current != SCENE_TYPE_NULL &&
|
||||||
@@ -43,11 +37,11 @@ errorret_t sceneUpdate(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DUSK_TIME_DYNAMIC
|
#if DUSK_TIME_DYNAMIC
|
||||||
if(TIME.dynamicUpdate) {
|
if(TIME.dynamicUpdate) {
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(
|
if(
|
||||||
SCENE.current != SCENE_TYPE_NULL &&
|
SCENE.current != SCENE_TYPE_NULL &&
|
||||||
@@ -60,41 +54,12 @@ errorret_t sceneUpdate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneRender(void) {
|
errorret_t sceneRender(void) {
|
||||||
// Scene rendering
|
|
||||||
if(
|
if(
|
||||||
SCENE.current != SCENE_TYPE_NULL &&
|
SCENE.current != SCENE_TYPE_NULL &&
|
||||||
SCENE_TYPES[SCENE.current].render != NULL
|
SCENE_TYPES[SCENE.current].render != NULL
|
||||||
) {
|
) {
|
||||||
errorChain(SCENE_TYPES[SCENE.current].render(&SCENE.data));
|
errorChain(SCENE_TYPES[SCENE.current].render(&SCENE.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI Rendering
|
|
||||||
mat4 proj, view, ident;
|
|
||||||
glm_mat4_identity(ident);
|
|
||||||
errorChain(shaderBind(&SHADER_UNLIT));
|
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident));
|
|
||||||
|
|
||||||
glm_ortho(
|
|
||||||
0.0f, SCREEN.width,
|
|
||||||
SCREEN.height, 0.0f,
|
|
||||||
0.1f, 100.0f,
|
|
||||||
proj
|
|
||||||
);
|
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
|
||||||
|
|
||||||
glm_lookat(
|
|
||||||
(vec3){ 0.0f, 0.0f, 1.0f },
|
|
||||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
view
|
|
||||||
);
|
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
|
||||||
|
|
||||||
errorChain(displaySetState((displaystate_t){
|
|
||||||
.flags = DISPLAY_STATE_FLAG_BLEND
|
|
||||||
}));
|
|
||||||
errorChain(uiRender());
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +75,5 @@ errorret_t sceneDispose(void) {
|
|||||||
) {
|
) {
|
||||||
errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data));
|
errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,16 @@ scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
|||||||
[SCENE_TYPE_NULL] = { 0 },
|
[SCENE_TYPE_NULL] = { 0 },
|
||||||
|
|
||||||
[SCENE_TYPE_OVERWORLD] = {
|
[SCENE_TYPE_OVERWORLD] = {
|
||||||
.init = sceneOverworldInit,
|
.init = sceneOverworldInit,
|
||||||
.update = sceneOverworldUpdate,
|
.update = sceneOverworldUpdate,
|
||||||
.render = sceneOverworldRender,
|
.render = sceneOverworldRender,
|
||||||
.dispose = sceneOverworldDispose
|
.dispose = sceneOverworldDispose
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
|
[SCENE_TYPE_TEST] = {
|
||||||
|
.init = sceneTestInit,
|
||||||
|
.update = sceneTestUpdate,
|
||||||
|
.render = sceneTestRender,
|
||||||
|
.dispose = sceneTestDispose
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "scene/scenebase.h"
|
#include "scene/scenebase.h"
|
||||||
#include "scene/overworld/sceneoverworld.h"
|
#include "scene/overworld/sceneoverworld.h"
|
||||||
|
#include "scene/test/scenetest.h"
|
||||||
|
|
||||||
typedef union scenedata_u {
|
typedef union scenedata_u {
|
||||||
sceneoverworld_t overworld;
|
sceneoverworld_t overworld;
|
||||||
|
scenetest_t test;
|
||||||
} scenedata_t;
|
} scenedata_t;
|
||||||
|
|
||||||
typedef errorret_t (*scenecallback_t)(scenedata_t *);
|
typedef errorret_t (*scenecallback_t)(scenedata_t *);
|
||||||
@@ -24,9 +26,8 @@ typedef struct {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SCENE_TYPE_NULL,
|
SCENE_TYPE_NULL,
|
||||||
|
|
||||||
SCENE_TYPE_OVERWORLD,
|
SCENE_TYPE_OVERWORLD,
|
||||||
|
SCENE_TYPE_TEST,
|
||||||
SCENE_TYPE_COUNT
|
SCENE_TYPE_COUNT
|
||||||
} scenetype_t;
|
} scenetype_t;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
meshgl.c
|
scenetest.c
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scene/test/scenetest.h"
|
||||||
|
#include "render/render.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
|
||||||
|
errorret_t sceneTestInit(scenedata_t *data) {
|
||||||
|
(void)data;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneTestUpdate(scenedata_t *data) {
|
||||||
|
(void)data;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneTestRender(scenedata_t *data) {
|
||||||
|
(void)data;
|
||||||
|
renderClear(color(32, 32, 48, 255));
|
||||||
|
renderSprite(100, 100, 32, 32, COLOR_WHITE);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneTestDispose(scenedata_t *data) {
|
||||||
|
(void)data;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "scene/scenebase.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t unused;
|
||||||
|
} scenetest_t;
|
||||||
|
|
||||||
|
errorret_t sceneTestInit(scenedata_t *data);
|
||||||
|
errorret_t sceneTestUpdate(scenedata_t *data);
|
||||||
|
errorret_t sceneTestRender(scenedata_t *data);
|
||||||
|
errorret_t sceneTestDispose(scenedata_t *data);
|
||||||
@@ -3,15 +3,4 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Sources
|
# Display-dependent UI sources are temporarily disabled pending rop-based rewrite.
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
ui.c
|
|
||||||
uifps.c
|
|
||||||
uielement.c
|
|
||||||
uiframe.c
|
|
||||||
uifullbox.c
|
|
||||||
uiloading.c
|
|
||||||
uitextbox.c
|
|
||||||
uiplayerpos.c
|
|
||||||
)
|
|
||||||
@@ -3,12 +3,10 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Includes
|
|
||||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
add_subdirectory(display)
|
|
||||||
add_subdirectory(error)
|
add_subdirectory(error)
|
||||||
|
add_subdirectory(render)
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
displaygl.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
add_subdirectory(framebuffer)
|
|
||||||
add_subdirectory(texture)
|
|
||||||
add_subdirectory(mesh)
|
|
||||||
add_subdirectory(shader)
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "displaygl.h"
|
|
||||||
|
|
||||||
errorret_t displayOpenGLInit(void) {
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
#if DUSK_OPENGL_LEGACY
|
|
||||||
glDisable(GL_LIGHTING);// PSP defaults this on?
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/errorgl.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the OpenGL specific contexts for rendering.
|
|
||||||
*
|
|
||||||
* @return An errorret_t indicating success or failure of the initialization.
|
|
||||||
*/
|
|
||||||
errorret_t displayOpenGLInit(void);
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
framebuffergl.c
|
|
||||||
)
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "display/display.h"
|
|
||||||
#include "display/framebuffer/framebuffer.h"
|
|
||||||
#include "assert/assertgl.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
errorret_t frameBufferGLInitBackBuffer(void) {
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t frameBufferGLGetWidth(const framebuffer_t *framebuffer) {
|
|
||||||
if(framebuffer == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
|
|
||||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
int32_t windowWidth, windowHeight;
|
|
||||||
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
|
|
||||||
return windowWidth;
|
|
||||||
#else
|
|
||||||
return DUSK_DISPLAY_WIDTH;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return framebuffer->texture.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t frameBufferGLGetHeight(const framebuffer_t *framebuffer) {
|
|
||||||
if(framebuffer == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
|
|
||||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
int32_t windowWidth, windowHeight;
|
|
||||||
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
|
|
||||||
return windowHeight;
|
|
||||||
#else
|
|
||||||
return DUSK_DISPLAY_HEIGHT;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return framebuffer->texture.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t frameBufferGLBind(framebuffer_t *framebuffer) {
|
|
||||||
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
|
|
||||||
|
|
||||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
||||||
} else {
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
glViewport(
|
|
||||||
0, 0,
|
|
||||||
frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer)
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
glViewport(
|
|
||||||
0, 0,
|
|
||||||
DUSK_DISPLAY_WIDTH, DUSK_DISPLAY_HEIGHT
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
void frameBufferGLClear(const uint8_t flags, const color_t color) {
|
|
||||||
GLbitfield glFlags = 0;
|
|
||||||
|
|
||||||
if(flags & FRAMEBUFFER_CLEAR_COLOR) {
|
|
||||||
glFlags |= GL_COLOR_BUFFER_BIT;
|
|
||||||
glClearColor(
|
|
||||||
color.r / 255.0f,
|
|
||||||
color.g / 255.0f,
|
|
||||||
color.b / 255.0f,
|
|
||||||
color.a / 255.0f
|
|
||||||
);
|
|
||||||
assertNoGLError("Failed to set clear color");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(flags & FRAMEBUFFER_CLEAR_DEPTH) {
|
|
||||||
glFlags |= GL_DEPTH_BUFFER_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
glClear(glFlags);
|
|
||||||
assertNoGLError("Failed to clear framebuffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
errorret_t frameBufferGLInit(
|
|
||||||
framebuffer_t *fb,
|
|
||||||
const uint32_t width,
|
|
||||||
const uint32_t height
|
|
||||||
) {
|
|
||||||
assertNotNull(fb, "Framebuffer cannot be NULL");
|
|
||||||
assertTrue(width > 0 && height > 0, "W/H must be greater than 0");
|
|
||||||
|
|
||||||
memoryZero(fb, sizeof(framebuffer_t));
|
|
||||||
textureInit(&fb->texture, width, height, TEXTURE_FORMAT_RGBA,
|
|
||||||
(texturedata_t){ .rgbaColors = NULL }
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glGenFramebuffersEXT(1, &fb->id);
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glFramebufferTexture2DEXT(
|
|
||||||
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
||||||
GL_TEXTURE_2D, fb->texture.id, 0
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
if(
|
|
||||||
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
|
|
||||||
GL_FRAMEBUFFER_COMPLETE_EXT
|
|
||||||
) {
|
|
||||||
assertUnreachable("Framebuffer is not complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t frameBufferGLDispose(framebuffer_t *framebuffer) {
|
|
||||||
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
|
|
||||||
|
|
||||||
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
|
|
||||||
assertUnreachable("Cannot dispose of backbuffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
errorChain(textureDispose(&framebuffer->texture));
|
|
||||||
glDeleteFramebuffersEXT(1, &framebuffer->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
#include "error/errorgl.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
GLuint id;
|
|
||||||
texture_t texture;
|
|
||||||
} framebuffergl_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the backbuffer framebuffer. (OpenGL implementation).
|
|
||||||
*/
|
|
||||||
errorret_t frameBufferGLInitBackBuffer(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the height of the framebuffer. (OpenGL implementation).
|
|
||||||
*
|
|
||||||
* @param framebuffer The framebuffer to get the height of.
|
|
||||||
* @return The height of the framebuffer, or 0 if the framebuffer is NULL.
|
|
||||||
*/
|
|
||||||
uint32_t frameBufferGLGetWidth(const framebuffergl_t *framebuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an OpenGL style framebuffer.
|
|
||||||
*
|
|
||||||
* @param fb The framebuffer to initialize.
|
|
||||||
* @param width The width of the framebuffer.
|
|
||||||
* @param height The height of the framebuffer.
|
|
||||||
* @return Either error or not.
|
|
||||||
*/
|
|
||||||
uint32_t frameBufferGLGetHeight(const framebuffergl_t *framebuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the width of the framebuffer. (OpenGL implementation).
|
|
||||||
*
|
|
||||||
* @param framebuffer The framebuffer to get the width of.
|
|
||||||
* @return The width of the framebuffer, or 0 if the framebuffer is NULL.
|
|
||||||
*/
|
|
||||||
errorret_t frameBufferGLBind(framebuffergl_t *framebuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the framebuffer with the specified flags and color.
|
|
||||||
*
|
|
||||||
* @param flags The clear flags.
|
|
||||||
* @param color The clear color.
|
|
||||||
*/
|
|
||||||
void frameBufferGLClear(const uint8_t flags, const color_t color);
|
|
||||||
|
|
||||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
/**
|
|
||||||
* Initializes an OpenGL style framebuffer.
|
|
||||||
*
|
|
||||||
* @param fb The framebuffer to initialize.
|
|
||||||
* @param width The width of the framebuffer.
|
|
||||||
* @param height The height of the framebuffer.
|
|
||||||
* @return Either error or not.
|
|
||||||
*/
|
|
||||||
errorret_t frameBufferGLInit(
|
|
||||||
framebuffergl_t *fb,
|
|
||||||
const uint32_t width,
|
|
||||||
const uint32_t height
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the framebuffer. Will also be used for request disposing of the
|
|
||||||
* backbuffer.
|
|
||||||
*
|
|
||||||
* @param framebuffer The framebuffer to dispose of.
|
|
||||||
*/
|
|
||||||
errorret_t frameBufferGLDispose(framebuffergl_t *framebuffer);
|
|
||||||
#endif
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "display/framebuffer/framebuffergl.h"
|
|
||||||
typedef framebuffergl_t framebufferplatform_t;
|
|
||||||
|
|
||||||
#define frameBufferPlatformInitBackBuffer frameBufferGLInitBackBuffer
|
|
||||||
#define frameBufferPlatformGetWidth frameBufferGLGetWidth
|
|
||||||
#define frameBufferPlatformGetHeight frameBufferGLGetHeight
|
|
||||||
#define frameBufferPlatformBind frameBufferGLBind
|
|
||||||
#define frameBufferPlatformClear frameBufferGLClear
|
|
||||||
|
|
||||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
|
||||||
#define frameBufferPlatformInit frameBufferGLInit
|
|
||||||
#define frameBufferPlatformDispose frameBufferGLDispose
|
|
||||||
#endif
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "display/mesh/mesh.h"
|
|
||||||
#include "assert/assertgl.h"
|
|
||||||
#include "error/errorgl.h"
|
|
||||||
#include "display/shader/shadergl.h"
|
|
||||||
|
|
||||||
errorret_t meshInitGL(
|
|
||||||
meshgl_t *mesh,
|
|
||||||
const meshprimitivetypegl_t primitiveType,
|
|
||||||
const int32_t vertexCount,
|
|
||||||
const meshvertex_t *vertices
|
|
||||||
) {
|
|
||||||
assertNotNull(mesh, "Mesh cannot be NULL");
|
|
||||||
assertNotNull(vertices, "Vertices cannot be NULL");
|
|
||||||
assertTrue(vertexCount > 0, "Vertex count must be greater than 0");
|
|
||||||
|
|
||||||
mesh->primitiveType = primitiveType;
|
|
||||||
mesh->vertexCount = vertexCount;
|
|
||||||
mesh->vertices = vertices;
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
// Nothing needed.
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
glEnableClientState(GL_COLOR_ARRAY);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#else
|
|
||||||
// Generate Vertex Buffer Object
|
|
||||||
glGenBuffers(1, &mesh->vboId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBufferData(
|
|
||||||
GL_ARRAY_BUFFER,
|
|
||||||
vertexCount * sizeof(meshvertex_t),
|
|
||||||
vertices,
|
|
||||||
GL_DYNAMIC_DRAW
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
// Generate Vertex Array Object
|
|
||||||
glGenVertexArrays(1, &mesh->vaoId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindVertexArray(mesh->vaoId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
// Set up vertex attribute pointers
|
|
||||||
glVertexAttribPointer(
|
|
||||||
0,
|
|
||||||
MESH_VERTEX_POS_SIZE,
|
|
||||||
GL_FLOAT,
|
|
||||||
GL_FALSE,
|
|
||||||
sizeof(meshvertex_t),
|
|
||||||
(const GLvoid*)offsetof(meshvertex_t, pos)
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glVertexAttribPointer(
|
|
||||||
1,
|
|
||||||
MESH_VERTEX_UV_SIZE,
|
|
||||||
GL_FLOAT,
|
|
||||||
GL_FALSE,
|
|
||||||
sizeof(meshvertex_t),
|
|
||||||
(const GLvoid*)offsetof(meshvertex_t, uv)
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
glVertexAttribPointer(
|
|
||||||
2,
|
|
||||||
sizeof(color_t) / sizeof(GLubyte),
|
|
||||||
GL_UNSIGNED_BYTE,
|
|
||||||
GL_TRUE,
|
|
||||||
sizeof(meshvertex_t),
|
|
||||||
(const GLvoid*)offsetof(meshvertex_t, color)
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glEnableVertexAttribArray(2);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Unbind VAO and VBO to prevent accidental modification
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindVertexArray(0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t meshFlushGL(
|
|
||||||
meshgl_t *mesh,
|
|
||||||
const int32_t vertOffset,
|
|
||||||
const int32_t vertCount
|
|
||||||
) {
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
// Nothing doing, we use the glClientState stuff.
|
|
||||||
#else
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBufferData(
|
|
||||||
GL_ARRAY_BUFFER,
|
|
||||||
mesh->vertexCount * sizeof(meshvertex_t),
|
|
||||||
mesh->vertices,
|
|
||||||
// vertCount * sizeof(meshvertex_t),
|
|
||||||
// &mesh->vertices[vertOffset],
|
|
||||||
GL_DYNAMIC_DRAW
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t meshDrawGL(
|
|
||||||
const meshgl_t *mesh,
|
|
||||||
const int32_t offset,
|
|
||||||
const int32_t count
|
|
||||||
) {
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
// Legacy pointer style rendering
|
|
||||||
const GLsizei stride = sizeof(meshvertex_t);
|
|
||||||
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
glColorPointer(
|
|
||||||
sizeof(color4b_t),
|
|
||||||
GL_UNSIGNED_BYTE,
|
|
||||||
stride,
|
|
||||||
(const GLvoid*)&mesh->vertices[offset].color
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
glTexCoordPointer(
|
|
||||||
MESH_VERTEX_UV_SIZE,
|
|
||||||
GL_FLOAT,
|
|
||||||
stride,
|
|
||||||
(const GLvoid*)&mesh->vertices[offset].uv[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
glVertexPointer(
|
|
||||||
MESH_VERTEX_POS_SIZE,
|
|
||||||
GL_FLOAT,
|
|
||||||
stride,
|
|
||||||
(const GLvoid*)&mesh->vertices[offset].pos[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Shader may have model matrix here
|
|
||||||
errorChain(shaderLegacyMatrixUpdate());
|
|
||||||
glDrawArrays(mesh->primitiveType, 0, count);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#else
|
|
||||||
// Modern VAO/VBO rendering
|
|
||||||
glBindVertexArray(mesh->vaoId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glDrawArrays(mesh->primitiveType, offset, count);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindVertexArray(0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t meshGetVertexCountGL(const meshgl_t *mesh) {
|
|
||||||
return mesh->vertexCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t meshDisposeGL(meshgl_t *mesh) {
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
// No dynamic resources to free for this mesh implementation
|
|
||||||
#else
|
|
||||||
glDeleteBuffers(1, &mesh->vboId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glDeleteVertexArrays(1, &mesh->vaoId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
#include "display/mesh/meshvertex.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
MESH_PRIMITIVE_TYPE_TRIANGLES = GL_TRIANGLES,
|
|
||||||
MESH_PRIMITIVE_TYPE_LINES = GL_LINES,
|
|
||||||
MESH_PRIMITIVE_TYPE_POINTS = GL_POINTS,
|
|
||||||
} meshprimitivetypegl_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t vertexCount;
|
|
||||||
meshprimitivetypegl_t primitiveType;
|
|
||||||
const meshvertex_t *vertices;
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
// Nothing needed
|
|
||||||
#else
|
|
||||||
GLuint vaoId;
|
|
||||||
GLuint vboId;
|
|
||||||
#endif
|
|
||||||
} meshgl_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a mesh for OpenGL.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to initialize.
|
|
||||||
* @param primitiveType The OpenGL primitive type (e.g., GL_TRIANGLES).
|
|
||||||
* @param vertexCount The number of vertices in the mesh.
|
|
||||||
* @param vertices The vertex data for the mesh.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t meshInitGL(
|
|
||||||
meshgl_t *mesh,
|
|
||||||
const meshprimitivetypegl_t primitiveType,
|
|
||||||
const int32_t vertexCount,
|
|
||||||
const meshvertex_t *vertices
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flushes the vertices (stored in memory) to the GPU.
|
|
||||||
*
|
|
||||||
* @param mesh Mesh to flush vertices for.
|
|
||||||
* @param vertOffset First vertice index to flush.
|
|
||||||
* @param vertCount Count of vertices to flush.
|
|
||||||
* @return Error state.
|
|
||||||
*/
|
|
||||||
errorret_t meshFlushGL(
|
|
||||||
meshgl_t *mesh,
|
|
||||||
const int32_t vertOffset,
|
|
||||||
const int32_t vertCount
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a mesh using OpenGL.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to draw.
|
|
||||||
* @param vertexOffset The offset in the vertex array to start drawing from.
|
|
||||||
* @param vertexCount The number of vertices to draw. If -1, draws all vertices.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t meshDrawGL(
|
|
||||||
const meshgl_t *mesh,
|
|
||||||
const int32_t vertexOffset,
|
|
||||||
const int32_t vertexCount
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the vertex count of a mesh used for OpenGL.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to get the vertex count from.
|
|
||||||
* @return The vertex count of the mesh.
|
|
||||||
*/
|
|
||||||
int32_t meshGetVertexCountGL(const meshgl_t *mesh);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes a mesh used for OpenGL.
|
|
||||||
*
|
|
||||||
* @param mesh The mesh to dispose.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t meshDisposeGL(meshgl_t *mesh);
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "meshgl.h"
|
|
||||||
|
|
||||||
typedef meshprimitivetypegl_t meshprimitivetypeplatform_t;
|
|
||||||
typedef meshgl_t meshplatform_t;
|
|
||||||
|
|
||||||
#define meshInitPlatform meshInitGL
|
|
||||||
#define meshFlushPlatform meshFlushGL
|
|
||||||
#define meshDrawPlatform meshDrawGL
|
|
||||||
#define meshGetVertexCountPlatform meshGetVertexCountGL
|
|
||||||
#define meshDisposePlatform meshDisposeGL
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
shadergl.c
|
|
||||||
shaderunlitgl.c
|
|
||||||
)
|
|
||||||
@@ -1,415 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "shadergl.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "assert/assertgl.h"
|
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
shaderlegacygl_t SHADER_LEGACY = { 0 };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
assertNotNull(def, "Shader definition cannot be null");
|
|
||||||
memoryZero(shader, sizeof(shadergl_t));
|
|
||||||
|
|
||||||
shader->definition = def;
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
glm_mat4_identity(shader->view);
|
|
||||||
glm_mat4_identity(shader->proj);
|
|
||||||
glm_mat4_identity(shader->model);
|
|
||||||
|
|
||||||
SHADER_LEGACY.boundShader = NULL;
|
|
||||||
errorOk();
|
|
||||||
#else
|
|
||||||
assertNotNull(def->vert, "Vertex shader source cannot be null");
|
|
||||||
assertNotNull(def->frag, "Fragment shader source cannot be null");
|
|
||||||
|
|
||||||
// Create vertex shader
|
|
||||||
shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
errorret_t err = errorGLCheck();
|
|
||||||
errorChain(err);
|
|
||||||
|
|
||||||
glShaderSource(shader->vertexShaderId, 1, &def->vert, NULL);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glCompileShader(shader->vertexShaderId);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLint ok = 0;
|
|
||||||
glGetShaderiv(shader->vertexShaderId, GL_COMPILE_STATUS, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
GLchar log[1024];
|
|
||||||
glGetShaderInfoLog(shader->vertexShaderId, sizeof(log), NULL, log);
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
errorThrow("Vertex shader compilation failed: %s", log);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create fragment shader
|
|
||||||
shader->fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glShaderSource(shader->fragmentShaderId, 1, &def->frag, NULL);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glCompileShader(shader->fragmentShaderId);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glGetShaderiv(shader->fragmentShaderId, GL_COMPILE_STATUS, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
GLchar log[1024];
|
|
||||||
glGetShaderInfoLog(shader->fragmentShaderId, sizeof(log), NULL, log);
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorThrow("Fragment shader compilation failed: %s", log);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create shader program
|
|
||||||
shader->shaderProgramId = glCreateProgram();
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glAttachShader(shader->shaderProgramId, shader->vertexShaderId);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteProgram(shader->shaderProgramId);
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glAttachShader(shader->shaderProgramId, shader->fragmentShaderId);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteProgram(shader->shaderProgramId);
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
glLinkProgram(shader->shaderProgramId);
|
|
||||||
err = errorGLCheck();
|
|
||||||
if(errorIsNotOk(err)) {
|
|
||||||
glDeleteProgram(shader->shaderProgramId);
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorChain(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = 0;
|
|
||||||
glGetProgramiv(shader->shaderProgramId, GL_LINK_STATUS, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
GLchar log[1024];
|
|
||||||
glGetProgramInfoLog(shader->shaderProgramId, sizeof(log), NULL, log);
|
|
||||||
glDeleteProgram(shader->shaderProgramId);
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
errorThrow("Shader program linking failed: %s", log);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t shaderParamGetLocationGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
GLint *location
|
|
||||||
) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
||||||
assertNotNull(location, "Location cannot be null");
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
assertUnreachable("Cannot get uniform locations on legacy opengl.");
|
|
||||||
#else
|
|
||||||
*location = glGetUniformLocation(shader->shaderProgramId, name);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
if(*location == -1) {
|
|
||||||
errorThrow("Uniform '%s' not found in shader.", name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t shaderSetMatrixGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
mat4 mat
|
|
||||||
) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
||||||
assertNotNull(mat, "Matrix data cannot be null");
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
assertTrue(
|
|
||||||
SHADER_LEGACY.boundShader == shader,
|
|
||||||
"Shader must be bound to set legacy matrices."
|
|
||||||
);
|
|
||||||
// Use unaligned copy to safely handle possibly unaligned input matrices
|
|
||||||
if(stringCompare(name, SHADER_UNLIT_PROJECTION) == 0) {
|
|
||||||
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_PROJ;
|
|
||||||
glm_mat4_ucopy(mat, shader->proj);
|
|
||||||
|
|
||||||
} else if(stringCompare(name, SHADER_UNLIT_VIEW) == 0) {
|
|
||||||
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_VIEW;
|
|
||||||
glm_mat4_ucopy(mat, shader->view);
|
|
||||||
|
|
||||||
} else if(stringCompare(name, SHADER_UNLIT_MODEL) == 0) {
|
|
||||||
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_MODEL;
|
|
||||||
glm_mat4_ucopy(mat, shader->model);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
assertUnreachable("Cannot use a custom matrix on legacy opengl.");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
GLint location;
|
|
||||||
errorChain(shaderParamGetLocationGL(shader, name, &location));
|
|
||||||
|
|
||||||
glUniformMatrix4fv(location, 1, GL_FALSE, (const GLfloat *)mat);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t shaderSetTextureGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
texture_t *texture
|
|
||||||
) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
assertStringEqual(
|
|
||||||
name,
|
|
||||||
SHADER_UNLIT_TEXTURE,
|
|
||||||
"Only one texture supported in legacy opengl."
|
|
||||||
);
|
|
||||||
|
|
||||||
// glActiveTexture(GL_TEXTURE0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
if(texture == NULL) {
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
#else
|
|
||||||
assertNotNull(shader->definition, "Shader definition cannot be null");
|
|
||||||
assertNotNull(shader->definition->setTexture, "Shader cannot do textures.");
|
|
||||||
errorChain(shader->definition->setTexture(shader, name, texture));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t shaderSetColorGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
color_t color
|
|
||||||
) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
// if(color.a == 0) {
|
|
||||||
// glDisable(GL_TEXTURE_2D);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// errorOk();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// glActiveTexture(GL_TEXTURE1);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
// if(color.r == 255 && color.g == 255 && color.b == 255) {
|
|
||||||
// glDisable(GL_TEXTURE_2D);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// errorOk();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// glEnable(GL_TEXTURE_2D);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glBindTexture(GL_TEXTURE_2D, TEXTURE_WHITE.id);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
// GLfloat tint[4] = {
|
|
||||||
// ((float_t)color.r) / 255.0f,
|
|
||||||
// ((float_t)color.g) / 255.0f,
|
|
||||||
// ((float_t)color.b) / 255.0f,
|
|
||||||
// ((float_t)color.a) / 255.0f
|
|
||||||
// };
|
|
||||||
// glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, tint);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
|
|
||||||
// errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glColor4f(
|
|
||||||
(float_t)color.r / 255.0f,
|
|
||||||
(float_t)color.g / 255.0f,
|
|
||||||
(float_t)color.b / 255.0f,
|
|
||||||
(float_t)color.a / 255.0f
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
#else
|
|
||||||
GLint location;
|
|
||||||
errorChain(shaderParamGetLocationGL(shader, name, &location));
|
|
||||||
glUniform4f(
|
|
||||||
location,
|
|
||||||
color.r / 255.0f,
|
|
||||||
color.g / 255.0f,
|
|
||||||
color.b / 255.0f,
|
|
||||||
color.a / 255.0f
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t shaderBindGL(shadergl_t *shader) {
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
assertNotNull(shader, "Cannot bind a null shader.");
|
|
||||||
SHADER_LEGACY.boundShader = shader;
|
|
||||||
SHADER_LEGACY.dirty = (
|
|
||||||
SHADER_LEGACY_DIRTY_MODEL |
|
|
||||||
SHADER_LEGACY_DIRTY_PROJ |
|
|
||||||
SHADER_LEGACY_DIRTY_VIEW
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
glUseProgram(shader->shaderProgramId);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t shaderDisposeGL(shadergl_t *shader) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
SHADER_LEGACY.boundShader = NULL;
|
|
||||||
#else
|
|
||||||
if(shader->shaderProgramId != 0) {
|
|
||||||
glDeleteProgram(shader->shaderProgramId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(shader->vertexShaderId != 0) {
|
|
||||||
glDeleteShader(shader->vertexShaderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(shader->fragmentShaderId != 0) {
|
|
||||||
glDeleteShader(shader->fragmentShaderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNoGLError("Failed disposing shader");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memoryZero(shader, sizeof(shadergl_t));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
errorret_t shaderLegacyMatrixUpdate() {
|
|
||||||
assertNotNull(SHADER_LEGACY.boundShader, "No shader is currently bound.");
|
|
||||||
|
|
||||||
if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_PROJ) != 0) {
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glLoadIdentity();
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->proj);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(
|
|
||||||
(SHADER_LEGACY.dirty &
|
|
||||||
(SHADER_LEGACY_DIRTY_VIEW | SHADER_LEGACY_DIRTY_MODEL)) != 0
|
|
||||||
) {
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
mat4 viewModel;
|
|
||||||
glm_mat4_mul(
|
|
||||||
SHADER_LEGACY.boundShader->view,
|
|
||||||
SHADER_LEGACY.boundShader->model,
|
|
||||||
viewModel
|
|
||||||
);
|
|
||||||
glLoadMatrixf((const GLfloat *)viewModel);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
}
|
|
||||||
|
|
||||||
SHADER_LEGACY.dirty = 0;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/errorgl.h"
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
|
|
||||||
typedef struct shadergl_s shadergl_t;
|
|
||||||
typedef union shadermaterial_u shadermaterial_t;
|
|
||||||
|
|
||||||
typedef errorret_t (*shadersettexturefn_t)(
|
|
||||||
shadergl_t *,
|
|
||||||
const char_t *,
|
|
||||||
texture_t *
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
errorret_t (*setMaterial)(shadergl_t *, const shadermaterial_t *);
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
#else
|
|
||||||
errorret_t (*setTexture)(shadergl_t *, const char_t *, texture_t *);
|
|
||||||
|
|
||||||
const char_t *vert;
|
|
||||||
const char_t *frag;
|
|
||||||
#endif
|
|
||||||
} shaderdefinitiongl_t;
|
|
||||||
|
|
||||||
typedef struct shadergl_s {
|
|
||||||
const shaderdefinitiongl_t *definition;
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
mat4 view;
|
|
||||||
mat4 proj;
|
|
||||||
mat4 model;
|
|
||||||
#else
|
|
||||||
GLuint shaderProgramId;
|
|
||||||
GLuint vertexShaderId;
|
|
||||||
GLuint fragmentShaderId;
|
|
||||||
#endif
|
|
||||||
} shadergl_t;
|
|
||||||
|
|
||||||
#if DUSK_OPENGL_LEGACY
|
|
||||||
typedef struct {
|
|
||||||
shadergl_t *boundShader;
|
|
||||||
uint_fast8_t dirty;
|
|
||||||
} shaderlegacygl_t;
|
|
||||||
|
|
||||||
extern shaderlegacygl_t SHADER_LEGACY;
|
|
||||||
|
|
||||||
#define SHADER_LEGACY_DIRTY_PROJ (1 << 0)
|
|
||||||
#define SHADER_LEGACY_DIRTY_VIEW (1 << 1)
|
|
||||||
#define SHADER_LEGACY_DIRTY_MODEL (1 << 2)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a shader.
|
|
||||||
*
|
|
||||||
* @param shader The shader to initialize.
|
|
||||||
* @param def The definition of the shader to initialize with.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds a shader for use in rendering.
|
|
||||||
*
|
|
||||||
* @param shader The shader to bind.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t shaderBindGL(shadergl_t *shader);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the location of a shader uniform parameter.
|
|
||||||
*
|
|
||||||
* @param shader The shader to query.
|
|
||||||
* @param name The name of the uniform parameter.
|
|
||||||
* @param location Output parameter to receive the location of the uniform.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t shaderParamGetLocationGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
GLint *location
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a mat4 uniform parameter in the shader.
|
|
||||||
*
|
|
||||||
* @param shader The shader to update.
|
|
||||||
* @param name The name of the uniform parameter.
|
|
||||||
* @param mat The 4x4 matrix data to set.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t shaderSetMatrixGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
mat4 matrix
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a color uniform parameter in the shader.
|
|
||||||
*
|
|
||||||
* @param shader The shader to update.
|
|
||||||
* @param name The name of the uniform parameter.
|
|
||||||
* @param color The color data to set.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t shaderSetTextureGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
texture_t *texture
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a color uniform parameter in the shader.
|
|
||||||
*
|
|
||||||
* @param shader The shader to update.
|
|
||||||
* @param name The name of the uniform parameter.
|
|
||||||
* @param color The color data to set.
|
|
||||||
* @return An errorret_t indicating success or failure.
|
|
||||||
*/
|
|
||||||
errorret_t shaderSetColorGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
color_t color
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of a shader, freeing any associated resources.
|
|
||||||
*
|
|
||||||
* @param shader The shader to dispose.
|
|
||||||
*/
|
|
||||||
errorret_t shaderDisposeGL(shadergl_t *shader);
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
/**
|
|
||||||
* During mesh rendering, this is requesting the legacy system to push all
|
|
||||||
* shaders necessary to render the currently bound shader's matrices.
|
|
||||||
*
|
|
||||||
* @return Any error state.
|
|
||||||
*/
|
|
||||||
errorret_t shaderLegacyMatrixUpdate();
|
|
||||||
#endif
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "shadergl.h"
|
|
||||||
|
|
||||||
typedef shadergl_t shaderplatform_t;
|
|
||||||
typedef shaderdefinitiongl_t shaderdefinitionplatform_t;
|
|
||||||
|
|
||||||
#define shaderInitPlatform shaderInitGL
|
|
||||||
#define shaderBindPlatform shaderBindGL
|
|
||||||
#define shaderSetMatrixPlatform shaderSetMatrixGL
|
|
||||||
#define shaderSetTexturePlatform shaderSetTextureGL
|
|
||||||
#define shaderSetColorPlatform shaderSetColorGL
|
|
||||||
#define shaderDisposePlatform shaderDisposeGL
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
#include "assert/assertgl.h"
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
|
|
||||||
.setMaterial = shaderUnlitSetMaterial,
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
errorret_t shaderUnlitSetTextureGL(
|
|
||||||
shadergl_t *shader,
|
|
||||||
const char_t *name,
|
|
||||||
texture_t *texture
|
|
||||||
) {
|
|
||||||
assertNotNull(shader, "Shader cannot be null");
|
|
||||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
||||||
assertStringEqual(
|
|
||||||
name,
|
|
||||||
SHADER_UNLIT_TEXTURE,
|
|
||||||
"Only one texture supported in unlit shader."
|
|
||||||
);
|
|
||||||
|
|
||||||
GLint locTexture, locType, locColorCount, locColors;
|
|
||||||
|
|
||||||
errorChain(shaderParamGetLocationGL(shader, "u_TextureType", &locType));
|
|
||||||
|
|
||||||
// NULL textures
|
|
||||||
if(texture == NULL) {
|
|
||||||
glUniform1i(locType, 0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set texture.
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
errorChain(shaderParamGetLocationGL(shader, name, &locTexture));
|
|
||||||
glUniform1i(locTexture, 0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
// Set texture type
|
|
||||||
if(texture->format == TEXTURE_FORMAT_PALETTE) {
|
|
||||||
glUniform1i(locType, 2);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
shaderParamGetLocationGL(shader, "u_ColorCount", &locColorCount);
|
|
||||||
glUniform1i(locColorCount, texture->palette->count);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
shaderParamGetLocationGL(shader, "u_Colors", &locColors);
|
|
||||||
GLuint paletteData[texture->palette->count];
|
|
||||||
for(size_t i = 0; i < texture->palette->count; i++) {
|
|
||||||
color_t color = texture->palette->colors[i];
|
|
||||||
paletteData[i] = (
|
|
||||||
((uint32_t)color.r << 24) |
|
|
||||||
((uint32_t)color.g << 16) |
|
|
||||||
((uint32_t)color.b << 8) |
|
|
||||||
((uint32_t)color.a << 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
glUniform1uiv(locColors, texture->palette->count, paletteData);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
} else {
|
|
||||||
glUniform1i(locType, 1);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
}
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
|
|
||||||
.setMaterial = shaderUnlitSetMaterial,
|
|
||||||
.setTexture = shaderUnlitSetTextureGL,
|
|
||||||
|
|
||||||
.vert =
|
|
||||||
#ifdef DUSK_OPENGL_ES
|
|
||||||
"#version 300 es\n"
|
|
||||||
"precision mediump float;\n"
|
|
||||||
// Attributes
|
|
||||||
"layout(location = 0) in vec3 a_Pos;\n"
|
|
||||||
"layout(location = 1) in vec2 a_TexCoord;\n"
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
"layout(location = 2) in vec4 a_Color;\n"
|
|
||||||
#endif
|
|
||||||
// Uniforms
|
|
||||||
"uniform mat4 u_Proj;\n"
|
|
||||||
"uniform mat4 u_View;\n"
|
|
||||||
"uniform mat4 u_Model;\n"
|
|
||||||
// Vertex shader outputs
|
|
||||||
"out vec4 v_Color;\n"
|
|
||||||
"out vec2 v_TexCoord;\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n"
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
" v_Color = a_Color;\n"
|
|
||||||
#else
|
|
||||||
" v_Color = vec4(1.0);\n"
|
|
||||||
#endif
|
|
||||||
" v_TexCoord = a_TexCoord;\n"
|
|
||||||
"}\n",
|
|
||||||
#else
|
|
||||||
"#version 330 core\n"
|
|
||||||
// Attributes
|
|
||||||
"layout(location = 0) in vec3 a_Pos;\n"
|
|
||||||
"layout(location = 1) in vec2 a_TexCoord;\n"
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
"layout(location = 2) in vec4 a_Color;\n"
|
|
||||||
#endif
|
|
||||||
// Uniforms
|
|
||||||
"uniform mat4 u_Proj;\n"
|
|
||||||
"uniform mat4 u_View;\n"
|
|
||||||
"uniform mat4 u_Model;\n"
|
|
||||||
// Vertex shader outputs
|
|
||||||
"out vec4 v_Color;\n"
|
|
||||||
"out vec2 v_TexCoord;\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n"
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
" v_Color = a_Color;\n"
|
|
||||||
#else
|
|
||||||
" v_Color = vec4(1.0);\n"
|
|
||||||
#endif
|
|
||||||
" v_TexCoord = a_TexCoord;\n"
|
|
||||||
"}\n",
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
.frag =
|
|
||||||
#ifdef DUSK_OPENGL_ES
|
|
||||||
"#version 300 es\n"
|
|
||||||
"precision mediump float;\n"
|
|
||||||
// Uniforms
|
|
||||||
"uniform sampler2D u_Texture;\n"
|
|
||||||
"uniform int u_TextureType;\n"
|
|
||||||
"uniform uint u_Colors[256];\n"// For paletted textures.
|
|
||||||
"uniform int u_ColorCount;\n"
|
|
||||||
"uniform vec4 u_Color;\n"
|
|
||||||
// Fragment shader inputs
|
|
||||||
"in vec4 v_Color;\n"
|
|
||||||
"in vec2 v_TexCoord;\n"
|
|
||||||
// Fragment shader output
|
|
||||||
"out vec4 FragColor;\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" if(u_TextureType == 0) {\n"// No texture
|
|
||||||
" FragColor = v_Color * u_Color;\n"
|
|
||||||
" return;\n"
|
|
||||||
" }\n"
|
|
||||||
" if(u_TextureType == 1) {\n"// Regular texture
|
|
||||||
" FragColor = texture(u_Texture, v_TexCoord) * v_Color * u_Color;\n"
|
|
||||||
" return;\n"
|
|
||||||
" }\n"
|
|
||||||
" if(u_TextureType == 2) {\n"// Paletted texture
|
|
||||||
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
|
|
||||||
" uint index = uint(floor(texColor.r * 255.0));\n"
|
|
||||||
" uint palColor = u_Colors[index];\n"
|
|
||||||
" float r = float((palColor >> 24) & 0xFFu) / 255.0;\n"
|
|
||||||
" float g = float((palColor >> 16) & 0xFFu) / 255.0;\n"
|
|
||||||
" float b = float((palColor >> 8) & 0xFFu) / 255.0;\n"
|
|
||||||
" float a = float((palColor >> 0) & 0xFFu) / 255.0;\n"
|
|
||||||
" FragColor = vec4(r, g, b, a) * u_Color;\n"
|
|
||||||
" return;\n"
|
|
||||||
" }\n"
|
|
||||||
" FragColor = v_Color * u_Color;\n"// Unknown texture type?
|
|
||||||
"}\n",
|
|
||||||
#else
|
|
||||||
"#version 330 core\n"
|
|
||||||
// Uniforms
|
|
||||||
"uniform sampler2D u_Texture;\n"
|
|
||||||
"uniform int u_TextureType;\n"
|
|
||||||
"uniform uint u_Colors[256];\n"// For paletted textures.
|
|
||||||
"uniform int u_ColorCount;\n"
|
|
||||||
"uniform vec4 u_Color;\n"
|
|
||||||
// Fragment shader inputs
|
|
||||||
"in vec4 v_Color;\n"
|
|
||||||
"in vec2 v_TexCoord;\n"
|
|
||||||
// Fragment shader output
|
|
||||||
"out vec4 FragColor;\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" if(u_TextureType == 0) {\n"// No texture
|
|
||||||
" FragColor = v_Color * u_Color;\n"
|
|
||||||
" return;\n"
|
|
||||||
" }\n"
|
|
||||||
" if(u_TextureType == 1) {\n"// Regular texture
|
|
||||||
" FragColor = texture(u_Texture, v_TexCoord) * v_Color * u_Color;\n"
|
|
||||||
" return;\n"
|
|
||||||
" }\n"
|
|
||||||
" if(u_TextureType == 2) {\n"// Paletted texture
|
|
||||||
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
|
|
||||||
" uint index = uint(floor(texColor.r * 255.0));\n"
|
|
||||||
" uint palColor = u_Colors[index];\n"
|
|
||||||
" float r = float((palColor >> 24) & 0xFFu) / 255.0;\n"
|
|
||||||
" float g = float((palColor >> 16) & 0xFFu) / 255.0;\n"
|
|
||||||
" float b = float((palColor >> 8) & 0xFFu) / 255.0;\n"
|
|
||||||
" float a = float((palColor >> 0) & 0xFFu) / 255.0;\n"
|
|
||||||
" FragColor = vec4(r, g, b, a) * u_Color;\n"
|
|
||||||
" return;\n"
|
|
||||||
" }\n"
|
|
||||||
" FragColor = v_Color * u_Color;\n"// Unknown texture type?
|
|
||||||
"}\n",
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "error/errorgl.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
errorret_t textureInitGL(
|
|
||||||
texturegl_t *texture,
|
|
||||||
const int32_t width,
|
|
||||||
const int32_t height,
|
|
||||||
const textureformatgl_t format,
|
|
||||||
const texturedata_t data
|
|
||||||
) {
|
|
||||||
glGenTextures(1, &texture->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
switch(format) {
|
|
||||||
case TEXTURE_FORMAT_RGBA:
|
|
||||||
glTexImage2D(
|
|
||||||
GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE, (void*)data.rgbaColors
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEXTURE_FORMAT_PALETTE:
|
|
||||||
texture->palette = data.paletted.palette;
|
|
||||||
assertTrue(
|
|
||||||
texture->palette == &PALETTES[0],
|
|
||||||
"Only the first palette is supported in legacy opengl."
|
|
||||||
);
|
|
||||||
|
|
||||||
#ifdef DUSK_OPENGL_LEGACY
|
|
||||||
glColorTableEXT(
|
|
||||||
GL_TEXTURE_2D, GL_RGBA, texture->palette->count, GL_RGBA,
|
|
||||||
GL_UNSIGNED_BYTE, (const void*)texture->palette->colors
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glTexImage2D(
|
|
||||||
GL_TEXTURE_2D,
|
|
||||||
0, GL_COLOR_INDEX8_EXT,
|
|
||||||
width, height,
|
|
||||||
0, GL_COLOR_INDEX8_EXT,
|
|
||||||
GL_UNSIGNED_BYTE, (void*)data.paletted.indices
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#else
|
|
||||||
// For modern systems we send to only the R channel and the shader does
|
|
||||||
// the rest.
|
|
||||||
glTexImage2D(
|
|
||||||
GL_TEXTURE_2D, 0, GL_RED, width, height, 0,
|
|
||||||
GL_RED, GL_UNSIGNED_BYTE, (void*)data.paletted.indices
|
|
||||||
);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Unknown texture format");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t textureDisposeGL(texturegl_t *texture) {
|
|
||||||
assertNotNull(texture, "Texture cannot be NULL");
|
|
||||||
assertTrue(texture->id != 0, "Texture ID must be valid");
|
|
||||||
|
|
||||||
glDeleteTextures(1, &texture->id);
|
|
||||||
errorChain(errorGLCheck());
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user