wip sw renderer...bsp is borked, also lots of other things
This commit is contained in:
parent
415d424057
commit
c771fa665d
56 changed files with 8151 additions and 1261 deletions
331
src/procgen/pxl8_graph.c
Normal file
331
src/procgen/pxl8_graph.c
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
#include "pxl8_graph.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_math.h"
|
||||
|
||||
static inline u32 hash2d(i32 x, i32 y, u32 seed) {
|
||||
return pxl8_hash32((u32)x ^ ((u32)y * 2654435769u) ^ seed);
|
||||
}
|
||||
|
||||
static inline f32 hash2d_f(i32 x, i32 y, u32 seed) {
|
||||
return (f32)hash2d(x, y, seed) / (f32)0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static f32 gradient2d(i32 ix, i32 iy, f32 fx, f32 fy, u32 seed) {
|
||||
u32 h = hash2d(ix, iy, seed);
|
||||
f32 angle = (f32)h / (f32)0xFFFFFFFF * 6.28318530718f;
|
||||
f32 gx = cosf(angle);
|
||||
f32 gy = sinf(angle);
|
||||
return gx * fx + gy * fy;
|
||||
}
|
||||
|
||||
static inline f32 smoothstep(f32 t) {
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
static inline f32 lerp(f32 a, f32 b, f32 t) {
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
static f32 noise_value(f32 x, f32 y, f32 scale, u32 seed) {
|
||||
f32 sx = x * scale;
|
||||
f32 sy = y * scale;
|
||||
i32 ix = (i32)floorf(sx);
|
||||
i32 iy = (i32)floorf(sy);
|
||||
f32 fx = sx - (f32)ix;
|
||||
f32 fy = sy - (f32)iy;
|
||||
f32 u = smoothstep(fx);
|
||||
f32 v = smoothstep(fy);
|
||||
|
||||
f32 n00 = hash2d_f(ix, iy, seed);
|
||||
f32 n10 = hash2d_f(ix + 1, iy, seed);
|
||||
f32 n01 = hash2d_f(ix, iy + 1, seed);
|
||||
f32 n11 = hash2d_f(ix + 1, iy + 1, seed);
|
||||
|
||||
return lerp(lerp(n00, n10, u), lerp(n01, n11, u), v);
|
||||
}
|
||||
|
||||
static f32 noise_perlin(f32 x, f32 y, f32 scale, u32 seed) {
|
||||
f32 sx = x * scale;
|
||||
f32 sy = y * scale;
|
||||
i32 ix = (i32)floorf(sx);
|
||||
i32 iy = (i32)floorf(sy);
|
||||
f32 fx = sx - (f32)ix;
|
||||
f32 fy = sy - (f32)iy;
|
||||
f32 u = smoothstep(fx);
|
||||
f32 v = smoothstep(fy);
|
||||
|
||||
f32 n00 = gradient2d(ix, iy, fx, fy, seed);
|
||||
f32 n10 = gradient2d(ix + 1, iy, fx - 1.0f, fy, seed);
|
||||
f32 n01 = gradient2d(ix, iy + 1, fx, fy - 1.0f, seed);
|
||||
f32 n11 = gradient2d(ix + 1, iy + 1, fx - 1.0f, fy - 1.0f, seed);
|
||||
|
||||
f32 result = lerp(lerp(n00, n10, u), lerp(n01, n11, u), v);
|
||||
return result * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static f32 noise_fbm(f32 x, f32 y, i32 octaves, f32 scale, f32 persistence, u32 seed) {
|
||||
f32 value = 0.0f;
|
||||
f32 amplitude = 1.0f;
|
||||
f32 frequency = scale;
|
||||
f32 max_value = 0.0f;
|
||||
|
||||
for (i32 i = 0; i < octaves; i++) {
|
||||
value += amplitude * noise_perlin(x, y, frequency, seed + (u32)i * 1337);
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
return value / max_value;
|
||||
}
|
||||
|
||||
static f32 noise_ridged(f32 x, f32 y, i32 octaves, f32 scale, f32 persistence, u32 seed) {
|
||||
f32 value = 0.0f;
|
||||
f32 amplitude = 1.0f;
|
||||
f32 frequency = scale;
|
||||
f32 max_value = 0.0f;
|
||||
|
||||
for (i32 i = 0; i < octaves; i++) {
|
||||
f32 n = noise_perlin(x, y, frequency, seed + (u32)i * 1337);
|
||||
n = 1.0f - fabsf(n * 2.0f - 1.0f);
|
||||
value += amplitude * n;
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
return value / max_value;
|
||||
}
|
||||
|
||||
static f32 noise_turbulence(f32 x, f32 y, i32 octaves, f32 scale, f32 persistence, u32 seed) {
|
||||
f32 value = 0.0f;
|
||||
f32 amplitude = 1.0f;
|
||||
f32 frequency = scale;
|
||||
f32 max_value = 0.0f;
|
||||
|
||||
for (i32 i = 0; i < octaves; i++) {
|
||||
f32 n = noise_perlin(x, y, frequency, seed + (u32)i * 1337);
|
||||
value += amplitude * fabsf(n * 2.0f - 1.0f);
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
return value / max_value;
|
||||
}
|
||||
|
||||
static void voronoi(f32 x, f32 y, f32 scale, u32 seed, f32* cell_dist, f32* edge_dist, i32* cell_id) {
|
||||
f32 sx = x * scale;
|
||||
f32 sy = y * scale;
|
||||
i32 cx = (i32)floorf(sx);
|
||||
i32 cy = (i32)floorf(sy);
|
||||
f32 fx = sx - (f32)cx;
|
||||
f32 fy = sy - (f32)cy;
|
||||
|
||||
f32 min_dist = 1e30f;
|
||||
f32 second_dist = 1e30f;
|
||||
i32 closest_id = 0;
|
||||
|
||||
for (i32 dy = -1; dy <= 1; dy++) {
|
||||
for (i32 dx = -1; dx <= 1; dx++) {
|
||||
i32 nx = cx + dx;
|
||||
i32 ny = cy + dy;
|
||||
u32 h = hash2d(nx, ny, seed);
|
||||
f32 px = (f32)dx + (f32)(h & 0xFF) / 255.0f - 0.5f - fx;
|
||||
f32 py = (f32)dy + (f32)((h >> 8) & 0xFF) / 255.0f - 0.5f - fy;
|
||||
f32 dist = px * px + py * py;
|
||||
|
||||
if (dist < min_dist) {
|
||||
second_dist = min_dist;
|
||||
min_dist = dist;
|
||||
closest_id = (i32)h;
|
||||
} else if (dist < second_dist) {
|
||||
second_dist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*cell_dist = sqrtf(min_dist);
|
||||
*edge_dist = sqrtf(second_dist) - sqrtf(min_dist);
|
||||
*cell_id = closest_id;
|
||||
}
|
||||
|
||||
static f32 gradient_linear(f32 x, f32 y, f32 angle) {
|
||||
f32 dx = cosf(angle);
|
||||
f32 dy = sinf(angle);
|
||||
f32 result = x * dx + y * dy;
|
||||
return fmaxf(0.0f, fminf(1.0f, result));
|
||||
}
|
||||
|
||||
static f32 gradient_radial(f32 x, f32 y, f32 cx, f32 cy) {
|
||||
f32 dx = x - cx;
|
||||
f32 dy = y - cy;
|
||||
return fmaxf(0.0f, fminf(1.0f, sqrtf(dx * dx + dy * dy)));
|
||||
}
|
||||
|
||||
pxl8_graph* pxl8_graph_create(u32 capacity) {
|
||||
pxl8_graph* graph = calloc(1, sizeof(pxl8_graph));
|
||||
if (!graph) return NULL;
|
||||
|
||||
graph->nodes = calloc(capacity, sizeof(pxl8_node));
|
||||
if (!graph->nodes) {
|
||||
free(graph);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
graph->capacity = capacity;
|
||||
graph->count = 0;
|
||||
graph->seed = 0;
|
||||
graph->output_reg = 0;
|
||||
return graph;
|
||||
}
|
||||
|
||||
void pxl8_graph_destroy(pxl8_graph* graph) {
|
||||
if (!graph) return;
|
||||
free(graph->nodes);
|
||||
free(graph);
|
||||
}
|
||||
|
||||
void pxl8_graph_clear(pxl8_graph* graph) {
|
||||
if (!graph) return;
|
||||
graph->count = 0;
|
||||
graph->output_reg = 0;
|
||||
}
|
||||
|
||||
u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param) {
|
||||
if (!graph || graph->count >= graph->capacity) return 0;
|
||||
|
||||
u8 out = (u8)(graph->count + 4);
|
||||
pxl8_node* node = &graph->nodes[graph->count++];
|
||||
node->op = (u8)op;
|
||||
node->out = out;
|
||||
node->in[0] = in0;
|
||||
node->in[1] = in1;
|
||||
node->in[2] = in2;
|
||||
node->in[3] = in3;
|
||||
node->param = param;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void pxl8_graph_set_output(pxl8_graph* graph, u8 reg) {
|
||||
if (graph) graph->output_reg = reg;
|
||||
}
|
||||
|
||||
void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed) {
|
||||
if (graph) graph->seed = seed;
|
||||
}
|
||||
|
||||
f32 pxl8_graph_eval(const pxl8_graph* graph, pxl8_graph_context* ctx) {
|
||||
if (!graph || !ctx) return 0.0f;
|
||||
|
||||
for (u32 i = 0; i < graph->count; i++) {
|
||||
const pxl8_node* n = &graph->nodes[i];
|
||||
f32 a = ctx->regs[n->in[0]];
|
||||
f32 b = ctx->regs[n->in[1]];
|
||||
f32 c = ctx->regs[n->in[2]];
|
||||
f32 d = ctx->regs[n->in[3]];
|
||||
f32 result = 0.0f;
|
||||
|
||||
switch (n->op) {
|
||||
case PXL8_OP_CONST: result = n->param; break;
|
||||
case PXL8_OP_INPUT_AGE: result = ctx->regs[3]; break;
|
||||
case PXL8_OP_INPUT_SEED: result = (f32)ctx->seed; break;
|
||||
case PXL8_OP_INPUT_TIME: result = ctx->regs[2]; break;
|
||||
case PXL8_OP_INPUT_X: result = ctx->regs[0]; break;
|
||||
case PXL8_OP_INPUT_Y: result = ctx->regs[1]; break;
|
||||
|
||||
case PXL8_OP_ABS: result = fabsf(a); break;
|
||||
case PXL8_OP_CEIL: result = ceilf(a); break;
|
||||
case PXL8_OP_COS: result = cosf(a); break;
|
||||
case PXL8_OP_FLOOR: result = floorf(a); break;
|
||||
case PXL8_OP_FRACT: result = a - floorf(a); break;
|
||||
case PXL8_OP_NEGATE: result = -a; break;
|
||||
case PXL8_OP_SIN: result = sinf(a); break;
|
||||
case PXL8_OP_SQRT: result = sqrtf(a); break;
|
||||
|
||||
case PXL8_OP_ADD: result = a + b; break;
|
||||
case PXL8_OP_DIV: result = b != 0.0f ? a / b : 0.0f; break;
|
||||
case PXL8_OP_MAX: result = fmaxf(a, b); break;
|
||||
case PXL8_OP_MIN: result = fminf(a, b); break;
|
||||
case PXL8_OP_MOD: result = b != 0.0f ? fmodf(a, b) : 0.0f; break;
|
||||
case PXL8_OP_MUL: result = a * b; break;
|
||||
case PXL8_OP_POW: result = powf(a, b); break;
|
||||
case PXL8_OP_SUB: result = a - b; break;
|
||||
|
||||
case PXL8_OP_CLAMP: result = fmaxf(b, fminf(c, a)); break;
|
||||
case PXL8_OP_LERP: result = a + c * (b - a); break;
|
||||
case PXL8_OP_SELECT: result = c > 0.0f ? a : b; break;
|
||||
case PXL8_OP_SMOOTHSTEP: {
|
||||
f32 t = fmaxf(0.0f, fminf(1.0f, (c - a) / (b - a)));
|
||||
result = t * t * (3.0f - 2.0f * t);
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_OP_NOISE_FBM: result = noise_fbm(a, b, (i32)n->param, c, d, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_PERLIN: result = noise_perlin(a, b, c, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_RIDGED: result = noise_ridged(a, b, (i32)n->param, c, d, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_TURBULENCE:result = noise_turbulence(a, b, (i32)n->param, c, d, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_VALUE: result = noise_value(a, b, c, ctx->seed); break;
|
||||
|
||||
case PXL8_OP_VORONOI_CELL: {
|
||||
f32 cell, edge; i32 id;
|
||||
voronoi(a, b, c, ctx->seed, &cell, &edge, &id);
|
||||
result = cell;
|
||||
break;
|
||||
}
|
||||
case PXL8_OP_VORONOI_EDGE: {
|
||||
f32 cell, edge; i32 id;
|
||||
voronoi(a, b, c, ctx->seed, &cell, &edge, &id);
|
||||
result = edge;
|
||||
break;
|
||||
}
|
||||
case PXL8_OP_VORONOI_ID: {
|
||||
f32 cell, edge; i32 id;
|
||||
voronoi(a, b, c, ctx->seed, &cell, &edge, &id);
|
||||
result = (f32)(id & 0xFF) / 255.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_OP_GRADIENT_LINEAR: result = gradient_linear(a, b, c); break;
|
||||
case PXL8_OP_GRADIENT_RADIAL: result = gradient_radial(a, b, c, d); break;
|
||||
|
||||
case PXL8_OP_QUANTIZE: {
|
||||
u8 base = (u8)n->param;
|
||||
f32 range = b;
|
||||
f32 clamped = fmaxf(0.0f, fminf(1.0f, a));
|
||||
result = (f32)(base + (u8)fminf(clamped * range, range - 1.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
ctx->regs[n->out] = result;
|
||||
}
|
||||
|
||||
return ctx->regs[graph->output_reg];
|
||||
}
|
||||
|
||||
void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height) {
|
||||
if (!graph || !buffer) return;
|
||||
|
||||
pxl8_graph_context ctx = {0};
|
||||
ctx.seed = graph->seed;
|
||||
|
||||
for (i32 y = 0; y < height; y++) {
|
||||
for (i32 x = 0; x < width; x++) {
|
||||
ctx.regs[0] = (f32)x / (f32)width;
|
||||
ctx.regs[1] = (f32)y / (f32)height;
|
||||
ctx.regs[2] = 0.0f;
|
||||
ctx.regs[3] = 0.0f;
|
||||
|
||||
f32 result = pxl8_graph_eval(graph, &ctx);
|
||||
buffer[y * width + x] = (u8)fmaxf(0.0f, fminf(255.0f, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/procgen/pxl8_graph.h
Normal file
90
src/procgen/pxl8_graph.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef enum pxl8_graph_op {
|
||||
PXL8_OP_CONST, // param -> out
|
||||
PXL8_OP_INPUT_AGE, // particle normalized age -> out
|
||||
PXL8_OP_INPUT_SEED, // seed -> out
|
||||
PXL8_OP_INPUT_TIME, // time -> out
|
||||
PXL8_OP_INPUT_X, // x coord -> out
|
||||
PXL8_OP_INPUT_Y, // y coord -> out
|
||||
|
||||
PXL8_OP_ABS, // |a| -> out
|
||||
PXL8_OP_CEIL, // ceil(a) -> out
|
||||
PXL8_OP_COS, // cos(a) -> out
|
||||
PXL8_OP_FLOOR, // floor(a) -> out
|
||||
PXL8_OP_FRACT, // fract(a) -> out
|
||||
PXL8_OP_NEGATE, // -a -> out
|
||||
PXL8_OP_SIN, // sin(a) -> out
|
||||
PXL8_OP_SQRT, // sqrt(a) -> out
|
||||
|
||||
PXL8_OP_ADD, // a + b -> out
|
||||
PXL8_OP_DIV, // a / b -> out
|
||||
PXL8_OP_MAX, // max(a, b) -> out
|
||||
PXL8_OP_MIN, // min(a, b) -> out
|
||||
PXL8_OP_MOD, // fmod(a, b) -> out
|
||||
PXL8_OP_MUL, // a * b -> out
|
||||
PXL8_OP_POW, // pow(a, b) -> out
|
||||
PXL8_OP_SUB, // a - b -> out
|
||||
|
||||
PXL8_OP_CLAMP, // clamp(a, min, max) -> out
|
||||
PXL8_OP_LERP, // lerp(a, b, t) -> out
|
||||
PXL8_OP_SELECT, // t > 0 ? a : b -> out
|
||||
PXL8_OP_SMOOTHSTEP, // smoothstep(edge0, edge1, x) -> out
|
||||
|
||||
PXL8_OP_NOISE_FBM, // fbm(x, y, octaves, scale, persistence) -> out
|
||||
PXL8_OP_NOISE_PERLIN, // perlin noise(x, y, scale) -> out
|
||||
PXL8_OP_NOISE_RIDGED, // ridged(x, y, octaves, scale, persistence) -> out
|
||||
PXL8_OP_NOISE_TURBULENCE,// turbulence(x, y, octaves, scale, persistence) -> out
|
||||
PXL8_OP_NOISE_VALUE, // value noise(x, y, scale) -> out
|
||||
|
||||
PXL8_OP_VORONOI_CELL, // voronoi cell distance(x, y, scale) -> out
|
||||
PXL8_OP_VORONOI_EDGE, // voronoi edge distance(x, y, scale) -> out
|
||||
PXL8_OP_VORONOI_ID, // voronoi cell id(x, y, scale) -> out
|
||||
|
||||
PXL8_OP_GRADIENT_LINEAR, // linear gradient(x, y, angle) -> out
|
||||
PXL8_OP_GRADIENT_RADIAL, // radial gradient(x, y, cx, cy) -> out
|
||||
|
||||
PXL8_OP_QUANTIZE, // quantize to palette: base + floor(a * range) -> out
|
||||
|
||||
PXL8_OP_COUNT
|
||||
} pxl8_graph_op;
|
||||
|
||||
typedef struct pxl8_node {
|
||||
u8 in[4];
|
||||
u8 op;
|
||||
u8 out;
|
||||
f32 param;
|
||||
} pxl8_node;
|
||||
|
||||
typedef struct pxl8_graph {
|
||||
u32 capacity;
|
||||
u32 count;
|
||||
pxl8_node* nodes;
|
||||
u8 output_reg;
|
||||
u32 seed;
|
||||
} pxl8_graph;
|
||||
|
||||
typedef struct pxl8_graph_context {
|
||||
f32 regs[256];
|
||||
u32 seed;
|
||||
} pxl8_graph_context;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
f32 pxl8_graph_eval(const pxl8_graph* graph, pxl8_graph_context* ctx);
|
||||
void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height);
|
||||
|
||||
u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param);
|
||||
void pxl8_graph_clear(pxl8_graph* graph);
|
||||
pxl8_graph* pxl8_graph_create(u32 capacity);
|
||||
void pxl8_graph_destroy(pxl8_graph* graph);
|
||||
void pxl8_graph_set_output(pxl8_graph* graph, u8 reg);
|
||||
void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue