You've already forked flecs_tests
Add shapes and basic selective actions
This commit is contained in:
29
CLAUDE.md
29
CLAUDE.md
@@ -1,5 +1,34 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
|
## Project: Cartograph
|
||||||
|
|
||||||
|
A browser-based world map creation tool (like Wonderdraft/Inkarnate). C99 compiled to WebAssembly via Emscripten.
|
||||||
|
|
||||||
|
### Stack
|
||||||
|
- **Graphics:** Sokol (WebGPU backend, `SOKOL_WGPU`) — `lib/sokol/`
|
||||||
|
- **UI:** Dear ImGui via cimgui — `lib/imgui/`
|
||||||
|
- **Math:** cglm (types are C arrays: `vec2` = `float[2]`, `mat4` = `float[4][4]` column-major) — `lib/cglm/`
|
||||||
|
- **Shaders:** WGSL in `src/shaders/`, compiled to C headers via `xxd -i` into `src/generated/`
|
||||||
|
|
||||||
|
### Build
|
||||||
|
- `make` (release) / `make debug` — outputs `app.html`
|
||||||
|
- All includes go through `src/api.h` which defines `SOKOL_IMPL`, `SOKOL_WGPU`, and pulls in every library header
|
||||||
|
- Include paths: `lib/sokol`, `lib/imgui`, `lib/imgui/imgui`, `lib/util`, `lib/cglm/include`
|
||||||
|
|
||||||
|
### Key files
|
||||||
|
- `src/main.c` — entry point, sokol init, render loop, input (zoom/pan/drag)
|
||||||
|
- `src/api.h` — central include hub, backend defines
|
||||||
|
- `src/sprite.h` — sprite batching, texture manager, file import stubs
|
||||||
|
- `src/util.h` — `vector_t` (dynamic array) and `mem_pool_t` (free-list pool), both stripe-based
|
||||||
|
- `src/rand.h` — xorshift32 PRNG
|
||||||
|
|
||||||
|
### Conventions
|
||||||
|
- No malloc/free directly — use `ALLOC`/`FREE` macros (wired to smemtrack in main.c)
|
||||||
|
- Assert is encouraged for invariant checks
|
||||||
|
- Data structures use stripe-based allocation (byte stride per element, not sizeof)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||||
|
|
||||||
## 1. Think Before Coding
|
## 1. Think Before Coding
|
||||||
|
|||||||
63
README.md
Normal file
63
README.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Cartograph
|
||||||
|
|
||||||
|
A browser-based world map creation tool inspired by Wonderdraft and Inkarnate. Uses shape-based terrain generation with procedural detail.
|
||||||
|
|
||||||
|
## Features (planned)
|
||||||
|
|
||||||
|
- **Shapes** — the fundamental building block. Each shape has:
|
||||||
|
- **Height** — additive (raise terrain) or subtractive (lower terrain)
|
||||||
|
- **Biome** — determines the sampled texture applied to the shape
|
||||||
|
- **Intensity** — blending weight of the biome texture
|
||||||
|
- **Roughness** — edge noise amount (voronoi-like jagged edges at maximum)
|
||||||
|
- **Shape editing** — select, move, rotate, scale individual shapes
|
||||||
|
- **Groups** — combine shapes into groups for batch operations
|
||||||
|
- **Shake landmass** — procedurally decompose a shape into a more complex set of shapes
|
||||||
|
- **Viewport** — zoom and pan across the map canvas
|
||||||
|
|
||||||
|
## Tech stack
|
||||||
|
|
||||||
|
| Layer | Library |
|
||||||
|
|---|---|
|
||||||
|
| Language | C (C99) |
|
||||||
|
| Compiler | [Emscripten](https://emscripten.org/) (emcc) → WebAssembly |
|
||||||
|
| Graphics | [Sokol](https://github.com/floooh/sokol) with WebGPU backend |
|
||||||
|
| UI | [Dear ImGui](https://github.com/ocornut/imgui) via [cimgui](https://github.com/cimgui/cimgui) |
|
||||||
|
| Math | [cglm](https://github.com/recp/cglm) |
|
||||||
|
| Shaders | WGSL (compiled to C headers via `xxd`) |
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Install dependencies
|
||||||
|
./fetch_libs.sh
|
||||||
|
|
||||||
|
# Release build
|
||||||
|
make
|
||||||
|
|
||||||
|
# Debug build
|
||||||
|
make debug
|
||||||
|
```
|
||||||
|
|
||||||
|
Output is `app.html`, served by Emscripten's built-in web server or any static server.
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
main.c Entry point, sokol init, render loop, input handling
|
||||||
|
api.h Central include — all library headers and project-wide defines
|
||||||
|
sprite.h Sprite batching, texture management, file import
|
||||||
|
util.h Vector (dynamic array) and memory pool data structures
|
||||||
|
rand.h Xorshift32 PRNG utilities
|
||||||
|
shaders/ WGSL shader sources
|
||||||
|
generated/ xxd-generated C headers from shaders
|
||||||
|
lib/
|
||||||
|
sokol/ Sokol single-file headers (gfx, app, glue, log)
|
||||||
|
imgui/ Dear ImGui + cimgui
|
||||||
|
cglm/ C linear math library
|
||||||
|
util/ Sokol utility headers (memtrack, imgui integration)
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
@@ -9,6 +9,7 @@ echo "=== Fetching library dependencies ==="
|
|||||||
mkdir -p "$LIB_DIR/sokol"
|
mkdir -p "$LIB_DIR/sokol"
|
||||||
mkdir -p "$LIB_DIR/imgui"
|
mkdir -p "$LIB_DIR/imgui"
|
||||||
mkdir -p "$LIB_DIR/util"
|
mkdir -p "$LIB_DIR/util"
|
||||||
|
mkdir -p "$LIB_DIR/cglm"
|
||||||
|
|
||||||
if [ ! -f "$LIB_DIR/sokol/sokol_gfx.h" ]; then
|
if [ ! -f "$LIB_DIR/sokol/sokol_gfx.h" ]; then
|
||||||
echo " > Fetching sokol (pinned to emdawnwebgpu-compatible version)..."
|
echo " > Fetching sokol (pinned to emdawnwebgpu-compatible version)..."
|
||||||
@@ -47,4 +48,15 @@ else
|
|||||||
echo " > cimgui already present"
|
echo " > cimgui already present"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 4. cglm
|
||||||
|
if [ ! -f "$LIB_DIR/cglm/include/cglm/cglm.h" ]; then
|
||||||
|
echo " > Fetching cglm..."
|
||||||
|
git clone --depth 1 --branch v0.9.6 https://github.com/recp/cglm.git "$LIB_DIR/cglm_tmp"
|
||||||
|
cp -r "$LIB_DIR/cglm_tmp/include" "$LIB_DIR/cglm/"
|
||||||
|
cp -r "$LIB_DIR/cglm_tmp/src" "$LIB_DIR/cglm/"
|
||||||
|
rm -rf "$LIB_DIR/cglm_tmp"
|
||||||
|
else
|
||||||
|
echo " > cglm already present"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|||||||
11
makefile
11
makefile
@@ -19,6 +19,7 @@ IMGUI_SOURCES = $(LIB_DIR)/imgui/imgui/imgui.cpp \
|
|||||||
$(LIB_DIR)/imgui/imgui/imgui_tables.cpp \
|
$(LIB_DIR)/imgui/imgui/imgui_tables.cpp \
|
||||||
$(LIB_DIR)/imgui/imgui/imgui_widgets.cpp \
|
$(LIB_DIR)/imgui/imgui/imgui_widgets.cpp \
|
||||||
$(LIB_DIR)/imgui/cimgui.cpp
|
$(LIB_DIR)/imgui/cimgui.cpp
|
||||||
|
CGLM_SOURCES = $(wildcard $(LIB_DIR)/cglm/src/*.c)
|
||||||
|
|
||||||
# Dynamic shader processing
|
# Dynamic shader processing
|
||||||
SHADER_FILES = $(wildcard $(SHADER_DIR)/*.wgsl)
|
SHADER_FILES = $(wildcard $(SHADER_DIR)/*.wgsl)
|
||||||
@@ -38,8 +39,8 @@ SHELL_FILE = shell.html
|
|||||||
all: $(FETCH) $(TARGET)
|
all: $(FETCH) $(TARGET)
|
||||||
|
|
||||||
# Main build target
|
# Main build target
|
||||||
$(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE)
|
$(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) $(SHELL_FILE)
|
||||||
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) \
|
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) \
|
||||||
-o $(TARGET) \
|
-o $(TARGET) \
|
||||||
$(EMCC_FLAGS) \
|
$(EMCC_FLAGS) \
|
||||||
-O3 \
|
-O3 \
|
||||||
@@ -47,6 +48,7 @@ $(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE)
|
|||||||
-I$(LIB_DIR)/imgui \
|
-I$(LIB_DIR)/imgui \
|
||||||
-I$(LIB_DIR)/imgui/imgui \
|
-I$(LIB_DIR)/imgui/imgui \
|
||||||
-I$(LIB_DIR)/util \
|
-I$(LIB_DIR)/util \
|
||||||
|
-I$(LIB_DIR)/cglm/include \
|
||||||
--shell-file=$(SHELL_FILE)
|
--shell-file=$(SHELL_FILE)
|
||||||
|
|
||||||
# Shader header generation
|
# Shader header generation
|
||||||
@@ -58,8 +60,8 @@ $(GENERATED_DIR)/%.h: $(SHADER_DIR)/%.wgsl | $(GENERATED_DIR)
|
|||||||
$(GENERATED_DIR):
|
$(GENERATED_DIR):
|
||||||
mkdir -p $(GENERATED_DIR)
|
mkdir -p $(GENERATED_DIR)
|
||||||
|
|
||||||
debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE)
|
debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) $(SHELL_FILE)
|
||||||
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) \
|
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) \
|
||||||
-o $(TARGET) \
|
-o $(TARGET) \
|
||||||
$(EMCC_FLAGS) \
|
$(EMCC_FLAGS) \
|
||||||
-g -gsource-map=inline \
|
-g -gsource-map=inline \
|
||||||
@@ -67,6 +69,7 @@ debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE)
|
|||||||
-I$(LIB_DIR)/imgui \
|
-I$(LIB_DIR)/imgui \
|
||||||
-I$(LIB_DIR)/imgui/imgui \
|
-I$(LIB_DIR)/imgui/imgui \
|
||||||
-I$(LIB_DIR)/util \
|
-I$(LIB_DIR)/util \
|
||||||
|
-I$(LIB_DIR)/cglm/include \
|
||||||
--shell-file=$(SHELL_FILE)
|
--shell-file=$(SHELL_FILE)
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
|
|||||||
15
src/api.h
15
src/api.h
@@ -6,6 +6,7 @@
|
|||||||
#define SOKOL_IMPL
|
#define SOKOL_IMPL
|
||||||
#define SOKOL_WGPU
|
#define SOKOL_WGPU
|
||||||
#define SOKOL_IMGUI_IMPL
|
#define SOKOL_IMGUI_IMPL
|
||||||
|
#define SOKOL_VALIDATE_NON_FATAL
|
||||||
|
|
||||||
#include "sokol_gfx.h"
|
#include "sokol_gfx.h"
|
||||||
#include "sokol_app.h"
|
#include "sokol_app.h"
|
||||||
@@ -14,14 +15,22 @@
|
|||||||
#include "cimgui.h"
|
#include "cimgui.h"
|
||||||
#include "sokol_imgui.h"
|
#include "sokol_imgui.h"
|
||||||
#include "sokol_memtrack.h"
|
#include "sokol_memtrack.h"
|
||||||
|
#include "sokol_shape.h"
|
||||||
|
|
||||||
|
#define ALLOC(arg) smemtrack_alloc(arg, NULL)
|
||||||
|
#define FREE(arg) smemtrack_free(arg, NULL)
|
||||||
|
|
||||||
|
#include "cglm/cglm.h"
|
||||||
|
|
||||||
#include "math.h"
|
|
||||||
#include "rand.h"
|
#include "rand.h"
|
||||||
#include "util.h"
|
|
||||||
#include "sprite.h"
|
|
||||||
|
|
||||||
#include "generated/sprite.h"
|
#include "generated/sprite.h"
|
||||||
|
#include "generated/shape.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "shape.h"
|
||||||
|
|
||||||
|
#include <emscripten.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|||||||
95
src/generated/shape.h
Normal file
95
src/generated/shape.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
unsigned char src_shaders_shape_wgsl[] = {
|
||||||
|
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x55, 0x6e, 0x69,
|
||||||
|
0x66, 0x6f, 0x72, 0x6d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d,
|
||||||
|
0x76, 0x70, 0x3a, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x78, 0x34, 0x66, 0x2c,
|
||||||
|
0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20,
|
||||||
|
0x53, 0x68, 0x61, 0x70, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d,
|
||||||
|
0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||||
|
0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x78, 0x34,
|
||||||
|
0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f,
|
||||||
|
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66,
|
||||||
|
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a,
|
||||||
|
0x20, 0x75, 0x33, 0x32, 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74,
|
||||||
|
0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x49, 0x6e, 0x20, 0x7b, 0x0a,
|
||||||
|
0x20, 0x20, 0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x28, 0x30, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0a, 0x7d, 0x3b,
|
||||||
|
0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x32,
|
||||||
|
0x46, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, 0x62, 0x75,
|
||||||
|
0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69,
|
||||||
|
0x6f, 0x6e, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x3a, 0x20, 0x76, 0x65, 0x63,
|
||||||
|
0x34, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63,
|
||||||
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x30, 0x29, 0x20, 0x40, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x28, 0x6c, 0x69,
|
||||||
|
0x6e, 0x65, 0x61, 0x72, 0x29, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
|
||||||
|
0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a,
|
||||||
|
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x46, 0x73, 0x4f, 0x75, 0x74,
|
||||||
|
0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x28, 0x30, 0x29, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
|
||||||
|
0x72, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x2c, 0x0a, 0x7d, 0x3b,
|
||||||
|
0x0a, 0x0a, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30,
|
||||||
|
0x29, 0x20, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x30, 0x29, 0x20,
|
||||||
|
0x76, 0x61, 0x72, 0x3c, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x3e,
|
||||||
|
0x20, 0x76, 0x73, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73,
|
||||||
|
0x3a, 0x20, 0x56, 0x73, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x3b,
|
||||||
|
0x0a, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x31, 0x29,
|
||||||
|
0x20, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x30, 0x29, 0x20, 0x76,
|
||||||
|
0x61, 0x72, 0x3c, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x20,
|
||||||
|
0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72,
|
||||||
|
0x6d, 0x3a, 0x20, 0x53, 0x68, 0x61, 0x70, 0x65, 0x55, 0x6e, 0x69, 0x66,
|
||||||
|
0x6f, 0x72, 0x6d, 0x3b, 0x0a, 0x0a, 0x40, 0x76, 0x65, 0x72, 0x74, 0x65,
|
||||||
|
0x78, 0x20, 0x66, 0x6e, 0x20, 0x76, 0x73, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
|
0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, 0x49, 0x6e,
|
||||||
|
0x29, 0x20, 0x2d, 0x3e, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x20, 0x7b,
|
||||||
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x75, 0x74,
|
||||||
|
0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x3b, 0x0a,
|
||||||
|
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x6c,
|
||||||
|
0x64, 0x5f, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34,
|
||||||
|
0x66, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x70, 0x6f, 0x73, 0x69,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x70, 0x75,
|
||||||
|
0x74, 0x2e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79,
|
||||||
|
0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20,
|
||||||
|
0x2a, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66,
|
||||||
|
0x6f, 0x72, 0x6d, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72,
|
||||||
|
0x6d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75,
|
||||||
|
0x74, 0x2e, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x77, 0x6f, 0x72, 0x6c,
|
||||||
|
0x64, 0x5f, 0x70, 0x6f, 0x73, 0x20, 0x2a, 0x20, 0x76, 0x73, 0x5f, 0x75,
|
||||||
|
0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6d, 0x76, 0x70, 0x3b,
|
||||||
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x68, 0x61,
|
||||||
|
0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x73,
|
||||||
|
0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x75, 0x29, 0x20,
|
||||||
|
0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75,
|
||||||
|
0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d,
|
||||||
|
0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20,
|
||||||
|
0x30, 0x2e, 0x38, 0x34, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31,
|
||||||
|
0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65,
|
||||||
|
0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x68, 0x61, 0x70,
|
||||||
|
0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x73, 0x74,
|
||||||
|
0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x75, 0x29, 0x20, 0x7b,
|
||||||
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74,
|
||||||
|
0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20,
|
||||||
|
0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f,
|
||||||
|
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65,
|
||||||
|
0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x2e, 0x35,
|
||||||
|
0x2c, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x30, 0x2e, 0x30, 0x29,
|
||||||
|
0x2c, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x29,
|
||||||
|
0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73,
|
||||||
|
0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
|
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
|
||||||
|
0x20, 0x3d, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69,
|
||||||
|
0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
|
||||||
|
0x6c, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
|
||||||
|
0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75,
|
||||||
|
0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x40, 0x66, 0x72,
|
||||||
|
0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x6e, 0x20, 0x66, 0x73,
|
||||||
|
0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a,
|
||||||
|
0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x29, 0x20, 0x2d, 0x3e, 0x20, 0x46,
|
||||||
|
0x73, 0x4f, 0x75, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76,
|
||||||
|
0x61, 0x72, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x46,
|
||||||
|
0x73, 0x4f, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75,
|
||||||
|
0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d,
|
||||||
|
0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
|
||||||
|
0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
|
||||||
|
0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a
|
||||||
|
};
|
||||||
|
unsigned int src_shaders_shape_wgsl_len = 1103;
|
||||||
502
src/main.c
502
src/main.c
@@ -1,13 +1,16 @@
|
|||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
|
||||||
#define ALLOC(arg) smemtrack_alloc(arg, NULL)
|
|
||||||
#define FREE(arg) smemtrack_free(arg, NULL)
|
|
||||||
|
|
||||||
#define GRID_X 1000
|
#define GRID_X 1000
|
||||||
#define GRID_Y 1000
|
#define GRID_Y 1000
|
||||||
|
#define LOG_RING_SIZE 64
|
||||||
|
|
||||||
|
typedef struct log_entry_t {
|
||||||
|
char text[256];
|
||||||
|
uint32_t level;
|
||||||
|
} log_entry_t;
|
||||||
|
|
||||||
typedef struct vs_uniform_t {
|
typedef struct vs_uniform_t {
|
||||||
mat4x4f mvp;
|
mat4 mvp;
|
||||||
} uniform_t;
|
} uniform_t;
|
||||||
|
|
||||||
typedef struct renderer_t {
|
typedef struct renderer_t {
|
||||||
@@ -23,11 +26,26 @@ typedef struct dragger_t {
|
|||||||
|
|
||||||
typedef struct userdata_t {
|
typedef struct userdata_t {
|
||||||
int width, height;
|
int width, height;
|
||||||
vec2f pan;
|
vec2 pan;
|
||||||
float zoom;
|
float zoom;
|
||||||
dragger_t dragger;
|
dragger_t dragger;
|
||||||
renderer_t renderer;
|
renderer_t renderer;
|
||||||
manager_t manager;
|
vector_t shapes;
|
||||||
|
int selected_count;
|
||||||
|
int hovered_shape;
|
||||||
|
|
||||||
|
bool selecting;
|
||||||
|
float sel_sx, sel_sy;
|
||||||
|
float sel_cx, sel_cy;
|
||||||
|
bool sel_dragging;
|
||||||
|
int sel_clicked_shape;
|
||||||
|
|
||||||
|
sg_buffer rect_vbuf, rect_ibuf;
|
||||||
|
|
||||||
|
log_entry_t log_ring[LOG_RING_SIZE];
|
||||||
|
int log_head;
|
||||||
|
int log_count;
|
||||||
|
bool log_show;
|
||||||
} userdata_t;
|
} userdata_t;
|
||||||
|
|
||||||
const char* format(const char* format, ...)
|
const char* format(const char* format, ...)
|
||||||
@@ -45,10 +63,73 @@ const char* format(const char* format, ...)
|
|||||||
}
|
}
|
||||||
void compute_mvp(userdata_t *userdata)
|
void compute_mvp(userdata_t *userdata)
|
||||||
{
|
{
|
||||||
userdata->renderer.uniform.mvp.e[0][0] = (2.0f / userdata->width) * userdata->zoom;
|
const float w = (float)userdata->width;
|
||||||
userdata->renderer.uniform.mvp.e[1][1] = (2.0f / userdata->height) * userdata->zoom;
|
const float h = (float)userdata->height;
|
||||||
userdata->renderer.uniform.mvp.e[0][3] = (2.0f / userdata->width) * userdata->pan.x;
|
const float z = userdata->zoom;
|
||||||
userdata->renderer.uniform.mvp.e[1][3] = (2.0f / userdata->height) * userdata->pan.y;
|
const float px = userdata->pan[0];
|
||||||
|
const float py = userdata->pan[1];
|
||||||
|
mat4 *m = &userdata->renderer.uniform.mvp;
|
||||||
|
|
||||||
|
(*m)[0][0] = (2.0f / w) * z;
|
||||||
|
(*m)[0][1] = 0.0f;
|
||||||
|
(*m)[0][2] = 0.0f;
|
||||||
|
(*m)[0][3] = (2.0f / w) * px;
|
||||||
|
|
||||||
|
(*m)[1][0] = 0.0f;
|
||||||
|
(*m)[1][1] = (2.0f / h) * z;
|
||||||
|
(*m)[1][2] = 0.0f;
|
||||||
|
(*m)[1][3] = (2.0f / h) * py;
|
||||||
|
|
||||||
|
(*m)[2][0] = 0.0f;
|
||||||
|
(*m)[2][1] = 0.0f;
|
||||||
|
(*m)[2][2] = 0.0f;
|
||||||
|
(*m)[2][3] = 0.0f;
|
||||||
|
|
||||||
|
(*m)[3][0] = 0.0f;
|
||||||
|
(*m)[3][1] = 0.0f;
|
||||||
|
(*m)[3][2] = 0.0f;
|
||||||
|
(*m)[3][3] = 1.0f;
|
||||||
|
}
|
||||||
|
static void screen_to_world(userdata_t *ud, float mx, float my, float *wx, float *wy)
|
||||||
|
{
|
||||||
|
const float sx = mx - ud->width * 0.5f;
|
||||||
|
const float sy = ud->height * 0.5f - my;
|
||||||
|
*wx = (sx - ud->pan[0]) / ud->zoom;
|
||||||
|
*wy = (sy - ud->pan[1]) / ud->zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_capture(const char* tag, uint32_t log_level, uint32_t log_item,
|
||||||
|
const char* message, uint32_t line_nr, const char* filename,
|
||||||
|
void* user_data)
|
||||||
|
{
|
||||||
|
userdata_t* ud = (userdata_t*)user_data;
|
||||||
|
|
||||||
|
const char* level_str;
|
||||||
|
switch (log_level) {
|
||||||
|
case 0: level_str = "panic"; break;
|
||||||
|
case 1: level_str = "error"; break;
|
||||||
|
case 2: level_str = "warn"; break;
|
||||||
|
default:level_str = "info"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[256];
|
||||||
|
int n = snprintf(buf, sizeof(buf), "[%s][%s][id:%u]", tag, level_str, log_item);
|
||||||
|
if (filename) {
|
||||||
|
n += snprintf(buf + n, sizeof(buf) - n, " %s:%u:", filename, line_nr);
|
||||||
|
}
|
||||||
|
if (message && n < (int)sizeof(buf) - 2) {
|
||||||
|
snprintf(buf + n, sizeof(buf) - n, " %s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = ud->log_head;
|
||||||
|
strncpy(ud->log_ring[idx].text, buf, 255);
|
||||||
|
ud->log_ring[idx].text[255] = 0;
|
||||||
|
ud->log_ring[idx].level = log_level;
|
||||||
|
ud->log_head = (idx + 1) % LOG_RING_SIZE;
|
||||||
|
if (ud->log_count < LOG_RING_SIZE) ud->log_count++;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s\n", buf);
|
||||||
|
if (log_level <= 1) ud->log_show = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void frame(void* _userdata)
|
static void frame(void* _userdata)
|
||||||
@@ -60,30 +141,166 @@ static void frame(void* _userdata)
|
|||||||
.swapchain = sglue_swapchain(),
|
.swapchain = sglue_swapchain(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const uint32_t length = vector_length(&userdata->manager.textures);
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
if(length > 0)
|
shape_draw((shape_t*) vec_get(&userdata->shapes, i), &userdata->renderer.uniform.mvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw selection overlay (marquee during drag, AABB for multi-select)
|
||||||
{
|
{
|
||||||
sg_apply_pipeline(userdata->renderer.pipeline);
|
float omin[2], omax[2];
|
||||||
|
bool has_overlay = false;
|
||||||
|
|
||||||
for(uint32_t i = 0; i < length; i++)
|
if (userdata->selecting && userdata->sel_dragging) {
|
||||||
{
|
float wx1, wy1, wx2, wy2;
|
||||||
texture_t* texture = (texture_t*) vector_get(&userdata->manager.textures, i);
|
screen_to_world(userdata, userdata->sel_sx, userdata->sel_sy, &wx1, &wy1);
|
||||||
|
screen_to_world(userdata, userdata->sel_cx, userdata->sel_cy, &wx2, &wy2);
|
||||||
if(texture->dirty)
|
omin[0] = fminf(wx1, wx2); omin[1] = fminf(wy1, wy2);
|
||||||
{
|
omax[0] = fmaxf(wx1, wx2); omax[1] = fmaxf(wy1, wy2);
|
||||||
const sg_range range = vector_range(&texture->sprites);
|
has_overlay = true;
|
||||||
sg_buffer buf = sg_query_view_buffer(texture->binding.views[1]);
|
} else if (userdata->selected_count >= 2) {
|
||||||
sg_update_buffer(buf, &range);
|
bool first = true;
|
||||||
texture->dirty = false;
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
if (!s->selected) continue;
|
||||||
|
for (uint32_t v = 0; v < s->num_verts; v++) {
|
||||||
|
float vx = s->verts[v].x, vy = s->verts[v].y;
|
||||||
|
if (first) {
|
||||||
|
omin[0] = omax[0] = vx;
|
||||||
|
omin[1] = omax[1] = vy;
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
if (vx < omin[0]) omin[0] = vx;
|
||||||
|
if (vx > omax[0]) omax[0] = vx;
|
||||||
|
if (vy < omin[1]) omin[1] = vy;
|
||||||
|
if (vy > omax[1]) omax[1] = vy;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
float pad = 8.0f / userdata->zoom;
|
||||||
|
omin[0] -= pad; omin[1] -= pad;
|
||||||
|
omax[0] += pad; omax[1] += pad;
|
||||||
|
has_overlay = true;
|
||||||
|
}
|
||||||
|
|
||||||
sg_apply_bindings(&texture->binding);
|
if (has_overlay) {
|
||||||
sg_apply_uniforms(0, &SG_RANGE(userdata->renderer.uniform));
|
shape_vertex_t rect_verts[4] = {
|
||||||
|
{omin[0], omin[1]}, {omax[0], omin[1]}, {omax[0], omax[1]}, {omin[0], omax[1]},
|
||||||
|
};
|
||||||
|
sg_update_buffer(userdata->rect_vbuf, &(sg_range){rect_verts, sizeof(rect_verts)});
|
||||||
|
|
||||||
sg_draw(0, 6, vector_length(&texture->sprites));
|
shape_uniform_t u;
|
||||||
|
glm_mat4_identity(u.transform);
|
||||||
|
u.base_color[0] = 0.3f; u.base_color[1] = 0.5f;
|
||||||
|
u.base_color[2] = 1.0f; u.base_color[3] = 0.35f;
|
||||||
|
u.state = 0;
|
||||||
|
memset(u._pad, 0, sizeof(u._pad));
|
||||||
|
|
||||||
|
sg_apply_pipeline(overlay_pipeline);
|
||||||
|
sg_apply_uniforms(0, &SG_RANGE(userdata->renderer.uniform.mvp));
|
||||||
|
sg_apply_uniforms(1, &SG_RANGE(u));
|
||||||
|
sg_apply_bindings(&(sg_bindings){
|
||||||
|
.vertex_buffers[0] = userdata->rect_vbuf,
|
||||||
|
.index_buffer = userdata->rect_ibuf,
|
||||||
|
});
|
||||||
|
sg_draw(0, 6, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simgui_new_frame(&(simgui_frame_desc_t){
|
||||||
|
.width = sapp_width(),
|
||||||
|
.height = sapp_height(),
|
||||||
|
.delta_time = sapp_frame_duration(),
|
||||||
|
.dpi_scale = sapp_dpi_scale(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Properties panel
|
||||||
|
{
|
||||||
|
const float panel_w = 250.0f;
|
||||||
|
igSetNextWindowPos((ImVec2){userdata->width - panel_w, 20.0f}, ImGuiCond_Always, (ImVec2){0, 0});
|
||||||
|
igSetNextWindowSize((ImVec2){panel_w, userdata->height - 20.0f}, ImGuiCond_Always);
|
||||||
|
igBegin("Properties", NULL,
|
||||||
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||||
|
|
||||||
|
if (userdata->selected_count == 0) {
|
||||||
|
igText("No shape selected");
|
||||||
|
} else if (userdata->selected_count > 1) {
|
||||||
|
igText("%d shapes selected", userdata->selected_count);
|
||||||
|
} else {
|
||||||
|
int idx = 0;
|
||||||
|
while (idx < userdata->shapes.count) {
|
||||||
|
shape_t *tmp = (shape_t*) vec_get(&userdata->shapes, idx);
|
||||||
|
if (tmp->selected) break;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, idx);
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
changed |= igDragFloat2("Position", &s->cx, 1.0f, 0, 0, "%.1f", 0);
|
||||||
|
changed |= igDragFloat2("Scale", &s->sx, 1.0f, 0.1f, 0, "%.1f", 0);
|
||||||
|
changed |= igDragFloat("Rotation", &s->rotation, 0.01f, 0, 0, "%.3f", 0);
|
||||||
|
igColorEdit4("Color", s->uniform.base_color, 0);
|
||||||
|
|
||||||
|
if (changed) shape_regenerate(s);
|
||||||
|
|
||||||
|
igSeparator();
|
||||||
|
|
||||||
|
igBeginDisabled(true);
|
||||||
|
{
|
||||||
|
float dummy = 0;
|
||||||
|
igDragFloat("Height", &dummy, 1.0f, 0, 0, "%.1f", 0);
|
||||||
|
igDragFloat("Biome", &dummy, 1.0f, 0, 0, "%.1f", 0);
|
||||||
|
igDragFloat("Roughness", &dummy, 1.0f, 0, 0, "%.1f", 0);
|
||||||
|
igDragFloat("Intensity", &dummy, 1.0f, 0, 0, "%.0f", 0);
|
||||||
|
}
|
||||||
|
igEndDisabled();
|
||||||
|
|
||||||
|
igSeparator();
|
||||||
|
|
||||||
|
if (igButton("Shake Landmass", (ImVec2){0, 0})) {
|
||||||
|
// dummy for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
igEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log panel
|
||||||
|
if (userdata->log_show) {
|
||||||
|
igSetNextWindowPos((ImVec2){10.0f, userdata->height - 200.0f}, ImGuiCond_FirstUseEver, (ImVec2){0, 0});
|
||||||
|
igSetNextWindowSize((ImVec2){400.0f, 180.0f}, ImGuiCond_FirstUseEver);
|
||||||
|
igBegin("Log", &userdata->log_show, 0);
|
||||||
|
if (igButton("Clear", (ImVec2){0, 0})) {
|
||||||
|
userdata->log_head = 0;
|
||||||
|
userdata->log_count = 0;
|
||||||
|
}
|
||||||
|
igSameLine(0.0f, 10.0f);
|
||||||
|
igText("%d entries", userdata->log_count);
|
||||||
|
igSeparator();
|
||||||
|
|
||||||
|
igBeginChild_Str("LogScroll", (ImVec2){0, 0}, false, 0);
|
||||||
|
int total = userdata->log_count < LOG_RING_SIZE ? userdata->log_count : LOG_RING_SIZE;
|
||||||
|
int start = userdata->log_count < LOG_RING_SIZE ? 0 : userdata->log_head;
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
int idx = (start + i) % LOG_RING_SIZE;
|
||||||
|
log_entry_t *e = &userdata->log_ring[idx];
|
||||||
|
ImVec4 color;
|
||||||
|
switch (e->level) {
|
||||||
|
case 0: color = (ImVec4){1.0f, 0.3f, 0.3f, 1.0f}; break; // panic: red
|
||||||
|
case 1: color = (ImVec4){1.0f, 0.5f, 0.3f, 1.0f}; break; // error: orange
|
||||||
|
case 2: color = (ImVec4){1.0f, 0.9f, 0.3f, 1.0f}; break; // warn: yellow
|
||||||
|
default:color = (ImVec4){0.7f, 0.7f, 0.7f, 1.0f}; break; // info: gray
|
||||||
|
}
|
||||||
|
igPushStyleColor_Vec4(ImGuiCol_Text, color);
|
||||||
|
igTextUnformatted(e->text, NULL);
|
||||||
|
igPopStyleColor(1);
|
||||||
|
}
|
||||||
|
if (total > 0) igSetScrollHereY(1.0f);
|
||||||
|
igEndChild();
|
||||||
|
igEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
simgui_render();
|
||||||
|
|
||||||
sg_end_pass();
|
sg_end_pass();
|
||||||
sg_commit();
|
sg_commit();
|
||||||
}
|
}
|
||||||
@@ -96,21 +313,23 @@ static void init(void* _userdata)
|
|||||||
|
|
||||||
sg_desc sgdesc = {
|
sg_desc sgdesc = {
|
||||||
.environment = sglue_environment(),
|
.environment = sglue_environment(),
|
||||||
.logger.func = slog_func,
|
.logger.func = log_capture,
|
||||||
|
.logger.user_data = userdata,
|
||||||
};
|
};
|
||||||
sg_setup(&sgdesc);
|
sg_setup(&sgdesc);
|
||||||
if (!sg_isvalid()) {
|
if (!sg_isvalid()) {
|
||||||
fprintf(stderr, "Failed to create Sokol GFX context!\n");
|
fprintf(stderr, "Failed to create Sokol GFX context!\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
simgui_setup(&(simgui_desc_t){0});
|
||||||
|
|
||||||
const vec2f quad[4] = {
|
const vec2 quad[4] = {
|
||||||
{-2.0f, 2.0f}, // bottom left
|
{-2.0f, 2.0f}, // bottom left
|
||||||
{2.0f, 2.0f}, // bottom right
|
{2.0f, 2.0f}, // bottom right
|
||||||
{2.0f, -2.0f}, // top right
|
{2.0f, -2.0f}, // top right
|
||||||
{-2.0f, -2.0f}, // top left
|
{-2.0f, -2.0f}, // top left
|
||||||
};
|
};
|
||||||
const vec2f uv[4] = {
|
const vec2 uv[4] = {
|
||||||
{0.0f, 1.0f}, // bottom left
|
{0.0f, 1.0f}, // bottom left
|
||||||
{1.0f, 1.0f}, // bottom right
|
{1.0f, 1.0f}, // bottom right
|
||||||
{1.0f, 0.0f}, // top right
|
{1.0f, 0.0f}, // top right
|
||||||
@@ -122,7 +341,7 @@ static void init(void* _userdata)
|
|||||||
|
|
||||||
userdata->width = sapp_width();
|
userdata->width = sapp_width();
|
||||||
userdata->height = sapp_height();
|
userdata->height = sapp_height();
|
||||||
userdata->pan = (vec2f) { 0.0f, 0.0f };
|
glm_vec2_zero(userdata->pan);
|
||||||
userdata->zoom = 2;
|
userdata->zoom = 2;
|
||||||
|
|
||||||
sg_shader sprite_shader = sg_make_shader(&(sg_shader_desc) {
|
sg_shader sprite_shader = sg_make_shader(&(sg_shader_desc) {
|
||||||
@@ -201,7 +420,37 @@ static void init(void* _userdata)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
gs_init(&userdata->manager);
|
shape_init_pipeline();
|
||||||
|
|
||||||
|
vec_init(&userdata->shapes, sizeof(shape_t));
|
||||||
|
userdata->selected_count = 0;
|
||||||
|
userdata->hovered_shape = -1;
|
||||||
|
userdata->selecting = false;
|
||||||
|
userdata->sel_dragging = false;
|
||||||
|
userdata->log_head = 0;
|
||||||
|
userdata->log_count = 0;
|
||||||
|
userdata->log_show = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
shape_vertex_t dummy[4] = {0};
|
||||||
|
uint16_t indices[6] = {0, 1, 2, 0, 2, 3};
|
||||||
|
userdata->rect_vbuf = sg_make_buffer(&(sg_buffer_desc){
|
||||||
|
.usage = { .stream_update = true },
|
||||||
|
.data = {dummy, sizeof(dummy)},
|
||||||
|
.label = "Sel rect verts",
|
||||||
|
});
|
||||||
|
userdata->rect_ibuf = sg_make_buffer(&(sg_buffer_desc){
|
||||||
|
.usage = {.index_buffer = true},
|
||||||
|
.data = {indices, sizeof(indices)},
|
||||||
|
.label = "Sel rect indices",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
*((shape_t*) vec_push(&userdata->shapes)) = shape_star(0.0f, 0.0f, 200.0f, 80.0f, 7,
|
||||||
|
(float[4]){ 0.0f, 0.94f, 1.0f, 1.0f });
|
||||||
|
|
||||||
|
*((shape_t*) vec_push(&userdata->shapes)) = shape_circle(300.0f, 0.0f, 120.0f,
|
||||||
|
(float[4]){ 1.0f, 0.47f, 0.0f, 1.0f });
|
||||||
|
|
||||||
compute_mvp(userdata);
|
compute_mvp(userdata);
|
||||||
}
|
}
|
||||||
@@ -210,43 +459,27 @@ static void cleanup(void* _userdata)
|
|||||||
{
|
{
|
||||||
userdata_t* userdata = (userdata_t*) _userdata;
|
userdata_t* userdata = (userdata_t*) _userdata;
|
||||||
|
|
||||||
vector_free(&userdata->manager.textures);
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_shutdown((shape_t*) vec_get(&userdata->shapes, i));
|
||||||
|
}
|
||||||
|
vec_free(&userdata->shapes);
|
||||||
|
sg_destroy_buffer(userdata->rect_vbuf);
|
||||||
|
sg_destroy_buffer(userdata->rect_ibuf);
|
||||||
|
shape_shutdown_pipeline();
|
||||||
|
|
||||||
FREE(userdata);
|
FREE(userdata);
|
||||||
|
|
||||||
|
simgui_shutdown();
|
||||||
sg_shutdown();
|
sg_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void event(const sapp_event* event, void* _userdata)
|
static void event(const sapp_event* event, void* _userdata)
|
||||||
{
|
{
|
||||||
|
if (simgui_handle_event(event)) return;
|
||||||
userdata_t* userdata = (userdata_t*) _userdata;
|
userdata_t* userdata = (userdata_t*) _userdata;
|
||||||
|
|
||||||
switch(event->type)
|
switch(event->type)
|
||||||
{
|
{
|
||||||
case SAPP_EVENTTYPE_FILES_DROPPED:
|
|
||||||
uint32_t files = sapp_get_num_dropped_files();
|
|
||||||
|
|
||||||
for(uint32_t i = 0; i < files; i++)
|
|
||||||
{
|
|
||||||
const uint32_t size = sapp_html5_get_dropped_file_size(i);
|
|
||||||
|
|
||||||
if(size > MAX_FILE_SIZE)
|
|
||||||
{
|
|
||||||
//Toast error
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* buffer = malloc(size);
|
|
||||||
|
|
||||||
sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request) {
|
|
||||||
.buffer = (sapp_range) { .ptr = buffer, .size = size },
|
|
||||||
.dropped_file_index = i,
|
|
||||||
.callback = gs_import_file,
|
|
||||||
.user_data = userdata
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case SAPP_EVENTTYPE_RESIZED:
|
case SAPP_EVENTTYPE_RESIZED:
|
||||||
userdata->width = sapp_width();
|
userdata->width = sapp_width();
|
||||||
userdata->height = sapp_height();
|
userdata->height = sapp_height();
|
||||||
@@ -255,7 +488,44 @@ static void event(const sapp_event* event, void* _userdata)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case SAPP_EVENTTYPE_MOUSE_DOWN:
|
case SAPP_EVENTTYPE_MOUSE_DOWN:
|
||||||
if(event->modifiers & SAPP_MODIFIER_RMB)
|
if (event->mouse_button == SAPP_MOUSEBUTTON_LEFT)
|
||||||
|
{
|
||||||
|
float wx, wy;
|
||||||
|
screen_to_world(userdata, event->mouse_x, event->mouse_y, &wx, &wy);
|
||||||
|
const float tol = 4.0f / userdata->zoom;
|
||||||
|
|
||||||
|
if (event->modifiers & SAPP_MODIFIER_CTRL) {
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
if (shape_hit_test(s, wx, wy, tol)) {
|
||||||
|
s->selected = !s->selected;
|
||||||
|
userdata->selected_count += s->selected ? 1 : -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userdata->selecting = true;
|
||||||
|
userdata->sel_dragging = false;
|
||||||
|
userdata->sel_sx = event->mouse_x;
|
||||||
|
userdata->sel_sy = event->mouse_y;
|
||||||
|
userdata->sel_cx = event->mouse_x;
|
||||||
|
userdata->sel_cy = event->mouse_y;
|
||||||
|
|
||||||
|
userdata->sel_clicked_shape = -1;
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
if (shape_hit_test((shape_t*) vec_get(&userdata->shapes, i), wx, wy, tol)) {
|
||||||
|
userdata->sel_clicked_shape = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
shape_set_state(s, s->hovered, s->selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event->modifiers & SAPP_MODIFIER_RMB)
|
||||||
{
|
{
|
||||||
userdata->dragger.dragging = true;
|
userdata->dragger.dragging = true;
|
||||||
userdata->dragger.origin_x = event->mouse_x;
|
userdata->dragger.origin_x = event->mouse_x;
|
||||||
@@ -264,29 +534,133 @@ static void event(const sapp_event* event, void* _userdata)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case SAPP_EVENTTYPE_MOUSE_UP:
|
case SAPP_EVENTTYPE_MOUSE_UP:
|
||||||
|
if (userdata->selecting) {
|
||||||
|
if (!userdata->sel_dragging) {
|
||||||
|
if (userdata->sel_clicked_shape >= 0) {
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
s->selected = false;
|
||||||
|
}
|
||||||
|
((shape_t*) vec_get(&userdata->shapes, userdata->sel_clicked_shape))->selected = true;
|
||||||
|
userdata->selected_count = 1;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
s->selected = false;
|
||||||
|
}
|
||||||
|
userdata->selected_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userdata->selecting = false;
|
||||||
|
userdata->sel_dragging = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
shape_set_state(s, s->hovered, s->selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
userdata->dragger.dragging = false;
|
userdata->dragger.dragging = false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case SAPP_EVENTTYPE_MOUSE_MOVE:
|
case SAPP_EVENTTYPE_MOUSE_MOVE:
|
||||||
if(userdata->dragger.dragging)
|
if (userdata->dragger.dragging)
|
||||||
{
|
{
|
||||||
userdata->pan.x += event->mouse_dx;
|
userdata->pan[0] += event->mouse_dx;
|
||||||
userdata->pan.y -= event->mouse_dy;
|
userdata->pan[1] -= event->mouse_dy;
|
||||||
|
|
||||||
compute_mvp(userdata);
|
compute_mvp(userdata);
|
||||||
}
|
}
|
||||||
|
else if (userdata->selecting)
|
||||||
|
{
|
||||||
|
userdata->sel_cx = event->mouse_x;
|
||||||
|
userdata->sel_cy = event->mouse_y;
|
||||||
|
float dx = userdata->sel_cx - userdata->sel_sx;
|
||||||
|
float dy = userdata->sel_cy - userdata->sel_sy;
|
||||||
|
if (dx * dx + dy * dy > 9.0f) {
|
||||||
|
userdata->sel_dragging = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userdata->sel_dragging) {
|
||||||
|
float wx1, wy1, wx2, wy2;
|
||||||
|
screen_to_world(userdata, userdata->sel_sx, userdata->sel_sy, &wx1, &wy1);
|
||||||
|
screen_to_world(userdata, userdata->sel_cx, userdata->sel_cy, &wx2, &wy2);
|
||||||
|
float min_x = fminf(wx1, wx2), min_y = fminf(wy1, wy2);
|
||||||
|
float max_x = fmaxf(wx1, wx2), max_y = fmaxf(wy1, wy2);
|
||||||
|
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
s->selected = false;
|
||||||
|
}
|
||||||
|
userdata->selected_count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
bool hit = false;
|
||||||
|
if (s->cx >= min_x && s->cx <= max_x && s->cy >= min_y && s->cy <= max_y)
|
||||||
|
hit = true;
|
||||||
|
for (uint32_t v = 0; !hit && v < s->num_verts; v++) {
|
||||||
|
if (s->verts[v].x >= min_x && s->verts[v].x <= max_x &&
|
||||||
|
s->verts[v].y >= min_y && s->verts[v].y <= max_y)
|
||||||
|
hit = true;
|
||||||
|
}
|
||||||
|
if (hit) {
|
||||||
|
s->selected = true;
|
||||||
|
userdata->selected_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
shape_set_state(s, false, s->selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float wx, wy;
|
||||||
|
screen_to_world(userdata, event->mouse_x, event->mouse_y, &wx, &wy);
|
||||||
|
const float tol = SHAPE_HOVER_PX / userdata->zoom;
|
||||||
|
|
||||||
|
int hovered = -1;
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
if (shape_hit_test((shape_t*) vec_get(&userdata->shapes, i), wx, wy, tol)) {
|
||||||
|
hovered = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hovered != userdata->hovered_shape) {
|
||||||
|
userdata->hovered_shape = hovered;
|
||||||
|
emscripten_run_script(
|
||||||
|
hovered >= 0
|
||||||
|
? "document.querySelector('canvas').style.cursor = 'pointer'"
|
||||||
|
: "document.querySelector('canvas').style.cursor = 'default'");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < userdata->shapes.count; i++) {
|
||||||
|
shape_t *s = (shape_t*) vec_get(&userdata->shapes, i);
|
||||||
|
shape_set_state(s, i == hovered, s->selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case SAPP_EVENTTYPE_MOUSE_SCROLL:
|
case SAPP_EVENTTYPE_MOUSE_SCROLL: {
|
||||||
if((userdata->zoom >= 6.0f && event->scroll_y > 0.0f) || (userdata->zoom <= 0.1f && event->scroll_y < 0.0f))
|
if((userdata->zoom >= 6.0f && event->scroll_y > 0.0f) || (userdata->zoom <= 0.1f && event->scroll_y < 0.0f))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const float diff = expf(event->scroll_y * 0.01f);
|
float wx, wy;
|
||||||
|
screen_to_world(userdata, event->mouse_x, event->mouse_y, &wx, &wy);
|
||||||
|
|
||||||
|
const float diff = expf(event->scroll_y * 0.1f);
|
||||||
userdata->zoom = _sg_clamp(userdata->zoom * diff, 0.1f, 6.0f);
|
userdata->zoom = _sg_clamp(userdata->zoom * diff, 0.1f, 6.0f);
|
||||||
|
|
||||||
compute_mvp(userdata);
|
const float sx = event->mouse_x - userdata->width * 0.5f;
|
||||||
|
const float sy = userdata->height * 0.5f - event->mouse_y;
|
||||||
|
userdata->pan[0] = sx - wx * userdata->zoom;
|
||||||
|
userdata->pan[1] = sy - wy * userdata->zoom;
|
||||||
|
|
||||||
break;
|
compute_mvp(userdata);
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/math.h
49
src/math.h
@@ -1,49 +0,0 @@
|
|||||||
#ifndef MATH_IMPL_H
|
|
||||||
#define MATH_IMPL_H
|
|
||||||
|
|
||||||
#include "api.h"
|
|
||||||
|
|
||||||
#define PI 3.14159265358979323846
|
|
||||||
#define PI32 3.14159265359f
|
|
||||||
|
|
||||||
typedef struct vec2f {
|
|
||||||
float x, y;
|
|
||||||
} vec2f;
|
|
||||||
typedef struct vec3f {
|
|
||||||
float x, y, z;
|
|
||||||
} vec3f;
|
|
||||||
typedef struct vec4f {
|
|
||||||
float x, y, z, w;
|
|
||||||
} vec4f;
|
|
||||||
|
|
||||||
typedef struct vec2u {
|
|
||||||
uint32_t x, y;
|
|
||||||
} vec2u;
|
|
||||||
typedef struct vec3u {
|
|
||||||
uint32_t x, y, z;
|
|
||||||
} vec3u;
|
|
||||||
typedef struct vec4u {
|
|
||||||
uint32_t x, y, z, w;
|
|
||||||
} vec4u;
|
|
||||||
|
|
||||||
typedef struct vec2 {
|
|
||||||
int32_t x, y;
|
|
||||||
} vec2;
|
|
||||||
typedef struct vec3 {
|
|
||||||
int32_t x, y, z;
|
|
||||||
} vec3;
|
|
||||||
typedef struct vec4 {
|
|
||||||
int32_t x, y, z, w;
|
|
||||||
} vec4;
|
|
||||||
|
|
||||||
typedef struct mat4x4f {
|
|
||||||
float e[4][4];
|
|
||||||
} mat4x4f;
|
|
||||||
typedef struct mat4x4u {
|
|
||||||
uint32_t e[4][4];
|
|
||||||
} mat4x4u;
|
|
||||||
typedef union mat4x4 {
|
|
||||||
int32_t e[4][4];
|
|
||||||
} mat4x4;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
45
src/shaders/shape.wgsl
Normal file
45
src/shaders/shape.wgsl
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
struct VsUniform {
|
||||||
|
mvp: mat4x4f,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShapeUniform {
|
||||||
|
transform: mat4x4f,
|
||||||
|
base_color: vec4f,
|
||||||
|
state: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VsIn {
|
||||||
|
@location(0) position: vec2f,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vs2Fs {
|
||||||
|
@builtin(position) pos: vec4f,
|
||||||
|
@location(0) @interpolate(linear) color: vec4f,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FsOut {
|
||||||
|
@location(0) color: vec4f,
|
||||||
|
};
|
||||||
|
|
||||||
|
@binding(0) @group(0) var<uniform> vs_uniforms: VsUniform;
|
||||||
|
@binding(1) @group(0) var<uniform> shape_uniform: ShapeUniform;
|
||||||
|
|
||||||
|
@vertex fn vs_main(input: VsIn) -> Vs2Fs {
|
||||||
|
var output: Vs2Fs;
|
||||||
|
let world_pos = vec4f(input.position.x, input.position.y, 0.0, 1.0) * shape_uniform.transform;
|
||||||
|
output.pos = world_pos * vs_uniforms.mvp;
|
||||||
|
if (shape_uniform.state == 2u) {
|
||||||
|
output.color = vec4f(1.0, 0.84, 0.0, 1.0);
|
||||||
|
} else if (shape_uniform.state == 1u) {
|
||||||
|
output.color = clamp(shape_uniform.base_color * 1.5, vec4f(0.0), vec4f(1.0));
|
||||||
|
} else {
|
||||||
|
output.color = shape_uniform.base_color;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment fn fs_main(input: Vs2Fs) -> FsOut {
|
||||||
|
var output: FsOut;
|
||||||
|
output.color = input.color;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
315
src/shape.h
Normal file
315
src/shape.h
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
#ifndef SHAPE_H
|
||||||
|
#define SHAPE_H
|
||||||
|
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
|
typedef struct shape_vertex_t {
|
||||||
|
float x, y;
|
||||||
|
} shape_vertex_t;
|
||||||
|
|
||||||
|
typedef struct shape_uniform_t {
|
||||||
|
mat4 transform;
|
||||||
|
float base_color[4];
|
||||||
|
uint32_t state;
|
||||||
|
uint8_t _pad[12];
|
||||||
|
} shape_uniform_t;
|
||||||
|
|
||||||
|
typedef enum shape_kind_t {
|
||||||
|
SHAPE_CIRCLE,
|
||||||
|
SHAPE_STAR,
|
||||||
|
} shape_kind_t;
|
||||||
|
|
||||||
|
typedef struct shape_t {
|
||||||
|
shape_vertex_t *verts;
|
||||||
|
uint16_t *indices;
|
||||||
|
uint32_t num_indices;
|
||||||
|
uint32_t num_verts;
|
||||||
|
sg_buffer vbuf;
|
||||||
|
sg_buffer ibuf;
|
||||||
|
shape_uniform_t uniform;
|
||||||
|
bool hovered;
|
||||||
|
bool selected;
|
||||||
|
|
||||||
|
shape_kind_t kind;
|
||||||
|
float cx, cy;
|
||||||
|
float sx, sy;
|
||||||
|
float rotation;
|
||||||
|
int star_points;
|
||||||
|
float star_inner_ratio;
|
||||||
|
} shape_t;
|
||||||
|
|
||||||
|
#define SHAPE_HOVER_PX 6.0f
|
||||||
|
|
||||||
|
static sg_pipeline shape_pipeline;
|
||||||
|
static sg_pipeline overlay_pipeline;
|
||||||
|
static sg_shader shape_shader;
|
||||||
|
|
||||||
|
static void shape_init_pipeline(void)
|
||||||
|
{
|
||||||
|
shape_shader = sg_make_shader(&(sg_shader_desc) {
|
||||||
|
.vertex_func = {
|
||||||
|
.source = (const char*) src_shaders_shape_wgsl,
|
||||||
|
.entry = "vs_main",
|
||||||
|
},
|
||||||
|
.fragment_func = {
|
||||||
|
.source = (const char*) src_shaders_shape_wgsl,
|
||||||
|
.entry = "fs_main",
|
||||||
|
},
|
||||||
|
.uniform_blocks = {
|
||||||
|
[0] = {
|
||||||
|
.size = sizeof(mat4),
|
||||||
|
.stage = SG_SHADERSTAGE_VERTEX,
|
||||||
|
.wgsl_group0_binding_n = 0,
|
||||||
|
},
|
||||||
|
[1] = {
|
||||||
|
.size = sizeof(shape_uniform_t),
|
||||||
|
.stage = SG_SHADERSTAGE_VERTEX,
|
||||||
|
.wgsl_group0_binding_n = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
[0] = { .base_type = SG_SHADERATTRBASETYPE_FLOAT },
|
||||||
|
},
|
||||||
|
.label = "Shape shader",
|
||||||
|
});
|
||||||
|
|
||||||
|
shape_pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
|
||||||
|
.shader = shape_shader,
|
||||||
|
.index_type = SG_INDEXTYPE_UINT16,
|
||||||
|
.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP,
|
||||||
|
.layout.attrs = {
|
||||||
|
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||||
|
},
|
||||||
|
.label = "Shape pipeline",
|
||||||
|
});
|
||||||
|
|
||||||
|
overlay_pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
|
||||||
|
.shader = shape_shader,
|
||||||
|
.index_type = SG_INDEXTYPE_UINT16,
|
||||||
|
.primitive_type = SG_PRIMITIVETYPE_TRIANGLES,
|
||||||
|
.layout.attrs = {
|
||||||
|
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||||
|
},
|
||||||
|
.label = "Overlay pipeline",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_shutdown_pipeline(void)
|
||||||
|
{
|
||||||
|
sg_destroy_pipeline(shape_pipeline);
|
||||||
|
sg_destroy_pipeline(overlay_pipeline);
|
||||||
|
sg_destroy_shader(shape_shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shape_calc_segments(float r)
|
||||||
|
{
|
||||||
|
int n = (int)(fabsf(r) * 0.5f) + 16;
|
||||||
|
if (n < 8) n = 8;
|
||||||
|
if (n > 128) n = 128;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_init_common(shape_t *s, const float color[4])
|
||||||
|
{
|
||||||
|
s->hovered = false;
|
||||||
|
s->selected = false;
|
||||||
|
glm_mat4_identity(s->uniform.transform);
|
||||||
|
memcpy(s->uniform.base_color, color, sizeof(float[4]));
|
||||||
|
s->uniform.state = 0;
|
||||||
|
memset(s->uniform._pad, 0, sizeof(s->uniform._pad));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_make_buffers(shape_t *s)
|
||||||
|
{
|
||||||
|
s->vbuf = sg_make_buffer(&(sg_buffer_desc) {
|
||||||
|
.data = { s->verts, s->num_indices * sizeof(shape_vertex_t) },
|
||||||
|
.label = "Shape vertices",
|
||||||
|
});
|
||||||
|
s->ibuf = sg_make_buffer(&(sg_buffer_desc) {
|
||||||
|
.usage = { .index_buffer = true },
|
||||||
|
.data = { s->indices, s->num_indices * sizeof(uint16_t) },
|
||||||
|
.label = "Shape indices",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_shutdown(shape_t *s)
|
||||||
|
{
|
||||||
|
sg_destroy_buffer(s->vbuf);
|
||||||
|
sg_destroy_buffer(s->ibuf);
|
||||||
|
FREE(s->verts);
|
||||||
|
FREE(s->indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_regenerate(shape_t *s)
|
||||||
|
{
|
||||||
|
sg_destroy_buffer(s->vbuf);
|
||||||
|
sg_destroy_buffer(s->ibuf);
|
||||||
|
|
||||||
|
int n, count;
|
||||||
|
if (s->kind == SHAPE_CIRCLE) {
|
||||||
|
int segs = shape_calc_segments(s->sx);
|
||||||
|
n = segs;
|
||||||
|
count = segs + 1;
|
||||||
|
|
||||||
|
if (s->num_indices != (uint32_t)count) {
|
||||||
|
FREE(s->verts);
|
||||||
|
FREE(s->indices);
|
||||||
|
s->verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
|
||||||
|
s->indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < segs; i++) {
|
||||||
|
float a = (float)i / (float)segs * 2.0f * GLM_PIf - GLM_PI_2f + s->rotation;
|
||||||
|
s->verts[i] = (shape_vertex_t) {
|
||||||
|
s->cx + cosf(a) * s->sx,
|
||||||
|
s->cy + sinf(a) * s->sy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
s->verts[segs] = s->verts[0];
|
||||||
|
} else {
|
||||||
|
n = s->star_points * 2;
|
||||||
|
count = n + 1;
|
||||||
|
|
||||||
|
if (s->num_indices != (uint32_t)count) {
|
||||||
|
FREE(s->verts);
|
||||||
|
FREE(s->indices);
|
||||||
|
s->verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
|
||||||
|
s->indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
float a = (float)i / (float)n * 2.0f * GLM_PIf - GLM_PI_2f + s->rotation;
|
||||||
|
float r = (i & 1) ? s->star_inner_ratio * s->sx : s->sx;
|
||||||
|
s->verts[i] = (shape_vertex_t) {
|
||||||
|
s->cx + cosf(a) * r,
|
||||||
|
s->cy + sinf(a) * r,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
s->verts[n] = s->verts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
s->num_indices = (uint32_t)count;
|
||||||
|
s->num_verts = (uint32_t)n;
|
||||||
|
for (int i = 0; i <= n; i++) s->indices[i] = (uint16_t)i;
|
||||||
|
|
||||||
|
shape_make_buffers(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_set_state(shape_t *s, bool hovered, bool selected)
|
||||||
|
{
|
||||||
|
s->hovered = hovered;
|
||||||
|
s->selected = selected;
|
||||||
|
s->uniform.state = selected ? 2u : (hovered ? 1u : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool point_in_polygon(float px, float py, shape_vertex_t *verts, uint32_t n)
|
||||||
|
{
|
||||||
|
bool inside = false;
|
||||||
|
for (uint32_t i = 0, j = n - 1; i < n; j = i++) {
|
||||||
|
float xi = verts[i].x, yi = verts[i].y;
|
||||||
|
float xj = verts[j].x, yj = verts[j].y;
|
||||||
|
if ((yi > py) != (yj > py) && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shape_hit_test(shape_t *s, float wx, float wy, float world_tol)
|
||||||
|
{
|
||||||
|
float tol_sq = world_tol * world_tol;
|
||||||
|
|
||||||
|
if (point_in_polygon(wx, wy, s->verts, s->num_verts))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (uint32_t i = 0, j = s->num_verts - 1; i < s->num_verts; j = i++) {
|
||||||
|
float ax = s->verts[i].x, ay = s->verts[i].y;
|
||||||
|
float bx = s->verts[j].x, by = s->verts[j].y;
|
||||||
|
float abx = bx - ax, aby = by - ay;
|
||||||
|
float len_sq = abx * abx + aby * aby;
|
||||||
|
if (len_sq < 0.0001f) continue;
|
||||||
|
float t = ((wx - ax) * abx + (wy - ay) * aby) / len_sq;
|
||||||
|
t = fmaxf(0.0f, fminf(1.0f, t));
|
||||||
|
float cx = ax + t * abx, cy = ay + t * aby;
|
||||||
|
float dx = wx - cx, dy = wy - cy;
|
||||||
|
if (dx * dx + dy * dy <= tol_sq) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shape_draw(shape_t *s, const mat4 *mvp)
|
||||||
|
{
|
||||||
|
sg_apply_pipeline(shape_pipeline);
|
||||||
|
sg_apply_uniforms(0, &SG_RANGE(*mvp));
|
||||||
|
sg_apply_uniforms(1, &SG_RANGE(s->uniform));
|
||||||
|
sg_apply_bindings(&(sg_bindings) {
|
||||||
|
.vertex_buffers[0] = s->vbuf,
|
||||||
|
.index_buffer = s->ibuf,
|
||||||
|
});
|
||||||
|
sg_draw(0, s->num_indices, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static shape_t shape_circle(float x, float y, float r, const float color[4])
|
||||||
|
{
|
||||||
|
shape_t s;
|
||||||
|
s.kind = SHAPE_CIRCLE;
|
||||||
|
s.cx = x; s.cy = y;
|
||||||
|
s.sx = r; s.sy = r;
|
||||||
|
s.rotation = 0.0f;
|
||||||
|
|
||||||
|
int segs = shape_calc_segments(r);
|
||||||
|
int count = segs + 1;
|
||||||
|
s.verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
|
||||||
|
s.indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
|
||||||
|
|
||||||
|
for (int i = 0; i < segs; i++) {
|
||||||
|
float a = (float)i / (float)segs * 2.0f * GLM_PIf - GLM_PI_2f;
|
||||||
|
s.verts[i] = (shape_vertex_t) {
|
||||||
|
x + cosf(a) * r,
|
||||||
|
y + sinf(a) * r,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
s.verts[segs] = s.verts[0];
|
||||||
|
for (int i = 0; i <= segs; i++) s.indices[i] = (uint16_t)i;
|
||||||
|
s.num_indices = (uint32_t)count;
|
||||||
|
s.num_verts = (uint32_t)segs;
|
||||||
|
|
||||||
|
shape_init_common(&s, color);
|
||||||
|
shape_make_buffers(&s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static shape_t shape_star(float x, float y, float outer_r, float inner_r,
|
||||||
|
int points, const float color[4])
|
||||||
|
{
|
||||||
|
shape_t s;
|
||||||
|
s.kind = SHAPE_STAR;
|
||||||
|
s.cx = x; s.cy = y;
|
||||||
|
s.sx = outer_r; s.sy = outer_r;
|
||||||
|
s.rotation = 0.0f;
|
||||||
|
s.star_points = points;
|
||||||
|
s.star_inner_ratio = inner_r / outer_r;
|
||||||
|
|
||||||
|
int n = points * 2;
|
||||||
|
int count = n + 1;
|
||||||
|
s.verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
|
||||||
|
s.indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
float a = (float)i / (float)n * 2.0f * GLM_PIf - GLM_PI_2f;
|
||||||
|
float r = (i & 1) ? inner_r : outer_r;
|
||||||
|
s.verts[i] = (shape_vertex_t) {
|
||||||
|
x + cosf(a) * r,
|
||||||
|
y + sinf(a) * r,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
s.verts[n] = s.verts[0];
|
||||||
|
for (int i = 0; i <= n; i++) s.indices[i] = (uint16_t)i;
|
||||||
|
s.num_indices = (uint32_t)count;
|
||||||
|
s.num_verts = (uint32_t)n;
|
||||||
|
|
||||||
|
shape_init_common(&s, color);
|
||||||
|
shape_make_buffers(&s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
79
src/sprite.h
79
src/sprite.h
@@ -1,79 +0,0 @@
|
|||||||
#ifndef SPRITE_H
|
|
||||||
#define SPRITE_H
|
|
||||||
|
|
||||||
#include "api.h"
|
|
||||||
|
|
||||||
#define KB (1024u)
|
|
||||||
#define MB (1024u * KB)
|
|
||||||
#define GB (1024u * MB)
|
|
||||||
#define MAX_FILE_SIZE (10u * MB)
|
|
||||||
|
|
||||||
typedef struct texture_t {
|
|
||||||
uint32_t id; //Texture ID
|
|
||||||
sg_bindings binding; //Texture bindings (texture, sampler and buffer)
|
|
||||||
vector_t sprites;
|
|
||||||
bool dirty;
|
|
||||||
} texture_t;
|
|
||||||
|
|
||||||
typedef struct sprite_t {
|
|
||||||
mat4x4f transform;
|
|
||||||
} sprite_t;
|
|
||||||
|
|
||||||
typedef struct manager_t {
|
|
||||||
vector_t textures;
|
|
||||||
} manager_t;
|
|
||||||
|
|
||||||
typedef struct loader_t {
|
|
||||||
uint8_t buffer[MAX_FILE_SIZE];
|
|
||||||
} loader_t;
|
|
||||||
|
|
||||||
typedef void (*ForEachTexture)(texture_t *texture, void *userdata);
|
|
||||||
typedef void (*ForEachSprite)(sprite_t *sprite, void *userdata);
|
|
||||||
|
|
||||||
void gs_init(manager_t *manager);
|
|
||||||
void gs_shutdown(manager_t *manager);
|
|
||||||
void gs_import_file(const sapp_html5_fetch_response *response);
|
|
||||||
|
|
||||||
void gs_each_textures(manager_t *manager, ForEachTexture callback, void *userdata);
|
|
||||||
void gs_each_sprites(manager_t *manager, ForEachSprite callback, void *userdata);
|
|
||||||
|
|
||||||
void gs_init(manager_t *manager)
|
|
||||||
{
|
|
||||||
manager->textures = vector_create(sizeof(texture_t));
|
|
||||||
}
|
|
||||||
void gs_shutdown(manager_t *manager)
|
|
||||||
{
|
|
||||||
vector_free(&manager->textures);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gs_import_file(const sapp_html5_fetch_response *response)
|
|
||||||
{
|
|
||||||
const char* filename = sapp_get_dropped_file_path(response->file_index);
|
|
||||||
|
|
||||||
if(response->succeeded)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Toast error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void gs_each_textures(manager_t *manager, ForEachTexture callback, void *userdata)
|
|
||||||
{
|
|
||||||
for(uint32_t i = 0; i < manager->textures.size; i++)
|
|
||||||
callback((texture_t*) manager->textures.data[i], userdata);
|
|
||||||
}
|
|
||||||
void gs_each_sprites(manager_t *manager, ForEachSprite callback, void *userdata)
|
|
||||||
{
|
|
||||||
for(uint32_t i = 0; i < manager->textures.size; i++)
|
|
||||||
{
|
|
||||||
const texture_t* texture = (texture_t*) manager->textures.data[i];
|
|
||||||
for(uint32_t j = 0; j < texture->sprites.size; j++)
|
|
||||||
{
|
|
||||||
callback((sprite_t*) texture->sprites.data[j], userdata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
256
src/util.h
256
src/util.h
@@ -1,241 +1,61 @@
|
|||||||
#ifndef UTIL_H
|
#ifndef UTIL_H
|
||||||
#define UTIL_H
|
#define UTIL_H
|
||||||
|
|
||||||
#include "api.h"
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
/*typedef struct linked_list_t {
|
|
||||||
linked_item_t *first, *last;
|
|
||||||
uint16_t size;
|
|
||||||
} linked_list_t;
|
|
||||||
|
|
||||||
typedef struct linked_item_t {
|
|
||||||
linked_item_t *next, *prev;
|
|
||||||
void *data;
|
|
||||||
} linked_item_t;
|
|
||||||
|
|
||||||
typedef void (*linked_list_callback)(void *item);
|
|
||||||
|
|
||||||
//Currently, these are the only method required.
|
|
||||||
//Many more could be implemented but this is unnecessary.
|
|
||||||
static void l_list_push(linked_list_t *l_list, void *item);
|
|
||||||
static void l_list_append(linked_list_t *l_list, void *item);
|
|
||||||
static void* l_list_pop(linked_list_t *l_list);
|
|
||||||
static void* l_list_unppend(linked_list_t *l_list);
|
|
||||||
static void l_list_each(linked_list_t *l_list, linked_list_callback callback);
|
|
||||||
|
|
||||||
static inline void l_list_push(linked_list_t *l_list, void *item)
|
|
||||||
{
|
|
||||||
linked_item_t l_item = (linked_item_t) { .data = item, .prev = l_list->last, .next = nullptr };
|
|
||||||
|
|
||||||
if(l_list->first == nullptr)
|
|
||||||
l_list->first = &l_item;
|
|
||||||
|
|
||||||
l_list->last->next = &l_item;
|
|
||||||
l_list->last = &l_item;
|
|
||||||
l_list->size++;
|
|
||||||
}
|
|
||||||
static inline void l_list_append(linked_list_t *l_list, void *item)
|
|
||||||
{
|
|
||||||
linked_item_t l_item = (linked_item_t) { .data = item, .prev = nullptr, .next = l_list->last };
|
|
||||||
|
|
||||||
if(l_list->last == nullptr)
|
|
||||||
l_list->last = &l_item;
|
|
||||||
|
|
||||||
l_list->first->prev = &l_item;
|
|
||||||
l_list->first = &l_item;
|
|
||||||
l_list->size++;
|
|
||||||
}
|
|
||||||
static inline void* l_list_pop(linked_list_t *l_list)
|
|
||||||
{
|
|
||||||
if(l_list->last == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(l_list->first == l_list->last)
|
|
||||||
l_list->first = nullptr;
|
|
||||||
|
|
||||||
linked_item_t *item = l_list->last->prev;
|
|
||||||
l_list->last->prev = nullptr;
|
|
||||||
l_list->last = item;
|
|
||||||
l_list->size--;
|
|
||||||
}
|
|
||||||
static inline void* l_list_unppend(linked_list_t *l_list)
|
|
||||||
{
|
|
||||||
if(l_list->first == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(l_list->last == l_list->first)
|
|
||||||
l_list->last = nullptr;
|
|
||||||
|
|
||||||
linked_item_t *item = l_list->first->next;
|
|
||||||
l_list->first->next = nullptr;
|
|
||||||
l_list->first = item;
|
|
||||||
l_list->size--;
|
|
||||||
}
|
|
||||||
static inline void l_list_each(linked_list_t *l_list, linked_list_callback callback)
|
|
||||||
{
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
typedef struct vector_t {
|
typedef struct vector_t {
|
||||||
void **data; //Memory pool
|
uint8_t *data;
|
||||||
uint16_t stripe; //Bit per item
|
int count;
|
||||||
uint32_t size; //Current amount of items
|
int capacity;
|
||||||
uint32_t capacity; //Max capacity
|
int stride;
|
||||||
} vector_t;
|
} vector_t;
|
||||||
|
|
||||||
#define MAX_BUCKET_SIZE (0xffffffffu)
|
static void vec_init(vector_t *v, int stride) {
|
||||||
#define MAX_STRIPE_SIZE (0xffffffu)
|
memset(v, 0, sizeof(*v));
|
||||||
#define FIXED_START (0xfffu)
|
v->stride = stride;
|
||||||
|
|
||||||
static vector_t vector_create(uint32_t stripe); //Create a new vector with a default size
|
|
||||||
static void vector_clear(vector_t *vector);
|
|
||||||
static void vector_free(vector_t *vector);
|
|
||||||
|
|
||||||
static uint32_t vector_length(vector_t *vector);
|
|
||||||
static void* vector_get(vector_t *vector, uint32_t index);
|
|
||||||
static void vector_set(vector_t *vector, uint32_t index, void *data);
|
|
||||||
static uint32_t vector_push(vector_t *vector, void *data);
|
|
||||||
static sg_range vector_range(vector_t *vector);
|
|
||||||
|
|
||||||
static vector_t vector_create(uint32_t stripe)
|
|
||||||
{
|
|
||||||
assert(stripe >= sizeof(uint32_t));
|
|
||||||
assert(stripe <= MAX_STRIPE_SIZE);
|
|
||||||
assert(FIXED_START * stripe <= MAX_BUCKET_SIZE);
|
|
||||||
|
|
||||||
return (vector_t) {
|
|
||||||
.capacity = FIXED_START,
|
|
||||||
.size = 0,
|
|
||||||
.stripe = stripe,
|
|
||||||
.data = (void**) malloc(FIXED_START * stripe),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
static void vector_clear(vector_t *vector)
|
|
||||||
{
|
|
||||||
vector->size = 0;
|
|
||||||
}
|
|
||||||
static void vector_free(vector_t *vector)
|
|
||||||
{
|
|
||||||
free(vector->data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t vector_length(vector_t *vector)
|
static void vec_grow(vector_t *v, int min_capacity) {
|
||||||
{
|
int new_cap = v->capacity ? v->capacity * 2 : 8;
|
||||||
return vector->size;
|
if (new_cap < min_capacity) new_cap = min_capacity;
|
||||||
}
|
uint8_t *new_data = (uint8_t*) ALLOC(new_cap * v->stride);
|
||||||
static void* vector_get(vector_t *vector, uint32_t index)
|
if (v->data) {
|
||||||
{
|
memcpy(new_data, v->data, v->count * v->stride);
|
||||||
assert(index > 0);
|
FREE(v->data);
|
||||||
assert(index < vector->size);
|
|
||||||
|
|
||||||
return vector->data[index * vector->stripe];
|
|
||||||
}
|
|
||||||
static void vector_set(vector_t *vector, uint32_t index, void *data)
|
|
||||||
{
|
|
||||||
assert(index > 0);
|
|
||||||
assert(index < vector->size);
|
|
||||||
|
|
||||||
vector->data[index * vector->stripe] = data;
|
|
||||||
}
|
|
||||||
static uint32_t vector_push(vector_t *vector, void *data)
|
|
||||||
{
|
|
||||||
if(vector->size >= vector->capacity)
|
|
||||||
{
|
|
||||||
vector->capacity *= 2;
|
|
||||||
|
|
||||||
vector->data = (void**) realloc(vector->data, vector->capacity * vector->stripe);
|
|
||||||
}
|
}
|
||||||
|
v->data = new_data;
|
||||||
return vector->size++;
|
v->capacity = new_cap;
|
||||||
}
|
|
||||||
static sg_range vector_range(vector_t *vector)
|
|
||||||
{
|
|
||||||
return (sg_range) {
|
|
||||||
.ptr = vector->data,
|
|
||||||
.size = vector->size,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct mem_pool_t {
|
static void *vec_push(vector_t *v) {
|
||||||
void **data; //Memory pool
|
if (v->count >= v->capacity) vec_grow(v, v->count + 1);
|
||||||
uint32_t stripe; //Bit per item
|
return v->data + (v->count++) * v->stride;
|
||||||
uint32_t size; //Current amount of items
|
|
||||||
uint32_t capacity; //Max capacity
|
|
||||||
uint32_t free; //Linked list of available indices
|
|
||||||
} mem_pool_t;
|
|
||||||
|
|
||||||
static mem_pool_t pool_create_default(uint32_t stripe); //Create a new memory pool with a default size
|
|
||||||
static mem_pool_t pool_create(uint32_t stripe, uint32_t size); //Create a new memory pool with a default size
|
|
||||||
static void pool_clear(mem_pool_t *pool);
|
|
||||||
static void pool_free(mem_pool_t *pool);
|
|
||||||
|
|
||||||
static const uint32_t pool_add(mem_pool_t *pool); //Request a new free index
|
|
||||||
static const void* pool_get(mem_pool_t *pool, uint32_t index); //Get the pointer
|
|
||||||
static void pool_remove(mem_pool_t *pool, uint32_t index); //Flag the given index as free
|
|
||||||
|
|
||||||
static mem_pool_t pool_create_default(uint32_t stripe)
|
|
||||||
{
|
|
||||||
return pool_create(stripe, FIXED_START);
|
|
||||||
}
|
|
||||||
static mem_pool_t pool_create(uint32_t stripe, uint32_t size)
|
|
||||||
{
|
|
||||||
assert(stripe >= sizeof(uint32_t));
|
|
||||||
assert(stripe <= MAX_STRIPE_SIZE);
|
|
||||||
assert(size * stripe <= MAX_BUCKET_SIZE);
|
|
||||||
|
|
||||||
return (mem_pool_t) {
|
|
||||||
.capacity = size,
|
|
||||||
.size = 0,
|
|
||||||
.stripe = stripe,
|
|
||||||
.free = UINT32_MAX,
|
|
||||||
.data = (void**) malloc(size * stripe),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
static void pool_clear(mem_pool_t *pool)
|
|
||||||
{
|
|
||||||
pool->size = 0;
|
|
||||||
pool->free = UINT32_MAX;
|
|
||||||
}
|
|
||||||
static void pool_free(mem_pool_t *pool)
|
|
||||||
{
|
|
||||||
free(pool->data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint32_t pool_add(mem_pool_t *pool)
|
static void vec_pop(vector_t *v) {
|
||||||
{
|
if (v->count > 0) v->count--;
|
||||||
if(pool->free != UINT32_MAX)
|
}
|
||||||
{
|
|
||||||
const uint32_t index = pool->free;
|
|
||||||
|
|
||||||
pool->free = (uint32_t) pool->data[index * pool->stripe];
|
static void vec_remove(vector_t *v, int index) {
|
||||||
|
if (index < 0 || index >= v->count) return;
|
||||||
return index;
|
if (index < v->count - 1) {
|
||||||
}
|
memcpy(v->data + index * v->stride,
|
||||||
else
|
v->data + (v->count - 1) * v->stride,
|
||||||
{
|
v->stride);
|
||||||
if(pool->size >= pool->capacity)
|
|
||||||
{
|
|
||||||
pool->capacity *= 2;
|
|
||||||
|
|
||||||
pool->data = (void**) realloc(pool->data, pool->capacity * pool->stripe);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool->size++;
|
|
||||||
}
|
}
|
||||||
|
v->count--;
|
||||||
}
|
}
|
||||||
static const void* pool_get(mem_pool_t *pool, uint32_t index)
|
|
||||||
{
|
|
||||||
assert(index > 0);
|
|
||||||
assert(index < pool->capacity);
|
|
||||||
|
|
||||||
return pool->data[index * pool->stripe];
|
static void *vec_get(vector_t *v, int index) {
|
||||||
|
return v->data + index * v->stride;
|
||||||
}
|
}
|
||||||
static void pool_remove(mem_pool_t *pool, uint32_t index)
|
|
||||||
{
|
|
||||||
const uint32_t pos = index * pool->stripe;
|
|
||||||
|
|
||||||
pool->data[pos] = (void*) pool->free;
|
static void vec_free(vector_t *v) {
|
||||||
pool->free = index;
|
if (v->data) FREE(v->data);
|
||||||
|
v->data = NULL;
|
||||||
|
v->count = 0;
|
||||||
|
v->capacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user