2025-11-09 06:30:17 -06:00
|
|
|
#include "pxl8_procgen.h"
|
|
|
|
|
|
|
|
|
|
#include "pxl8_macros.h"
|
|
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
typedef struct cave_grid {
|
|
|
|
|
u8* cells;
|
|
|
|
|
i32 width;
|
|
|
|
|
i32 height;
|
|
|
|
|
} cave_grid;
|
|
|
|
|
|
|
|
|
|
static u32 prng_state = 0;
|
|
|
|
|
|
|
|
|
|
static void prng_seed(u32 seed) {
|
|
|
|
|
prng_state = seed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static u32 prng_next(void) {
|
|
|
|
|
prng_state ^= prng_state << 13;
|
|
|
|
|
prng_state ^= prng_state >> 17;
|
|
|
|
|
prng_state ^= prng_state << 5;
|
|
|
|
|
return prng_state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static f32 prng_float(void) {
|
|
|
|
|
return (f32)prng_next() / (f32)0xFFFFFFFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cave_grid* cave_grid_create(i32 width, i32 height) {
|
|
|
|
|
cave_grid* grid = malloc(sizeof(cave_grid));
|
|
|
|
|
if (!grid) return NULL;
|
|
|
|
|
|
|
|
|
|
grid->width = width;
|
|
|
|
|
grid->height = height;
|
|
|
|
|
grid->cells = calloc(width * height, sizeof(u8));
|
|
|
|
|
|
|
|
|
|
if (!grid->cells) {
|
|
|
|
|
free(grid);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return grid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cave_grid_destroy(cave_grid* grid) {
|
|
|
|
|
if (!grid) return;
|
|
|
|
|
free(grid->cells);
|
|
|
|
|
free(grid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static u8 cave_grid_get(const cave_grid* grid, i32 x, i32 y) {
|
|
|
|
|
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return grid->cells[y * grid->width + x];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cave_grid_set(cave_grid* grid, i32 x, i32 y, u8 value) {
|
|
|
|
|
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
grid->cells[y * grid->width + x] = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static i32 cave_grid_count_neighbors(const cave_grid* grid, i32 x, i32 y) {
|
|
|
|
|
i32 count = 0;
|
|
|
|
|
for (i32 dy = -1; dy <= 1; dy++) {
|
|
|
|
|
for (i32 dx = -1; dx <= 1; dx++) {
|
|
|
|
|
if (dx == 0 && dy == 0) continue;
|
|
|
|
|
if (cave_grid_get(grid, x + dx, y + dy)) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
static inline void compute_face_aabb(pxl8_bsp_face* face, const pxl8_bsp_vertex* verts, u32 vert_idx) {
|
|
|
|
|
face->aabb_min = (pxl8_vec3){1e30f, 1e30f, 1e30f};
|
|
|
|
|
face->aabb_max = (pxl8_vec3){-1e30f, -1e30f, -1e30f};
|
|
|
|
|
|
|
|
|
|
for (u32 i = 0; i < 4; i++) {
|
|
|
|
|
pxl8_vec3 v = verts[vert_idx + i].position;
|
|
|
|
|
if (v.x < face->aabb_min.x) face->aabb_min.x = v.x;
|
|
|
|
|
if (v.x > face->aabb_max.x) face->aabb_max.x = v.x;
|
|
|
|
|
if (v.y < face->aabb_min.y) face->aabb_min.y = v.y;
|
|
|
|
|
if (v.y > face->aabb_max.y) face->aabb_max.y = v.y;
|
|
|
|
|
if (v.z < face->aabb_min.z) face->aabb_min.z = v.z;
|
|
|
|
|
if (v.z > face->aabb_max.z) face->aabb_max.z = v.z;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
static void cave_grid_initialize(cave_grid* grid, f32 density) {
|
|
|
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
|
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
|
|
|
u8 value = (prng_float() < density) ? 1 : 0;
|
|
|
|
|
cave_grid_set(grid, x, y, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cave_grid_smooth(cave_grid* grid) {
|
|
|
|
|
cave_grid* temp = cave_grid_create(grid->width, grid->height);
|
|
|
|
|
if (!temp) return;
|
|
|
|
|
|
|
|
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
|
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
|
|
|
i32 neighbors = cave_grid_count_neighbors(grid, x, y);
|
|
|
|
|
u8 value = (neighbors > 4) ? 1 : 0;
|
|
|
|
|
cave_grid_set(temp, x, y, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(grid->cells, temp->cells, grid->width * grid->height);
|
|
|
|
|
cave_grid_destroy(temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
|
|
|
|
|
i32 vertex_count = 0;
|
|
|
|
|
i32 face_count = 0;
|
|
|
|
|
i32 floor_ceiling_count = 0;
|
|
|
|
|
|
|
|
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
|
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
|
|
|
if (cave_grid_get(grid, x, y) == 0) {
|
|
|
|
|
if (cave_grid_get(grid, x - 1, y) == 1) face_count++;
|
|
|
|
|
if (cave_grid_get(grid, x + 1, y) == 1) face_count++;
|
|
|
|
|
if (cave_grid_get(grid, x, y - 1) == 1) face_count++;
|
|
|
|
|
if (cave_grid_get(grid, x, y + 1) == 1) face_count++;
|
|
|
|
|
floor_ceiling_count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
face_count += floor_ceiling_count * 2;
|
|
|
|
|
vertex_count = face_count * 4;
|
|
|
|
|
|
|
|
|
|
pxl8_debug("Cave generation: %dx%d grid -> %d faces, %d vertices",
|
|
|
|
|
grid->width, grid->height, face_count, vertex_count);
|
|
|
|
|
|
|
|
|
|
bsp->vertices = calloc(vertex_count, sizeof(pxl8_bsp_vertex));
|
|
|
|
|
bsp->faces = calloc(face_count, sizeof(pxl8_bsp_face));
|
|
|
|
|
bsp->planes = calloc(face_count, sizeof(pxl8_bsp_plane));
|
|
|
|
|
bsp->edges = calloc(vertex_count, sizeof(pxl8_bsp_edge));
|
|
|
|
|
bsp->surfedges = calloc(vertex_count, sizeof(i32));
|
|
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
if (!bsp->vertices || !bsp->faces || !bsp->planes || !bsp->edges || !bsp->surfedges) {
|
2025-11-09 06:30:17 -06:00
|
|
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->texinfo = NULL;
|
|
|
|
|
bsp->num_texinfo = 0;
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
i32 vert_idx = 0;
|
|
|
|
|
i32 face_idx = 0;
|
|
|
|
|
i32 edge_idx = 0;
|
|
|
|
|
|
|
|
|
|
const f32 cell_size = 64.0f;
|
|
|
|
|
const f32 wall_height = 128.0f;
|
|
|
|
|
|
|
|
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
|
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
|
|
|
if (cave_grid_get(grid, x, y) == 0) {
|
|
|
|
|
f32 fx = (f32)x * cell_size;
|
|
|
|
|
f32 fy = (f32)y * cell_size;
|
|
|
|
|
|
|
|
|
|
if (cave_grid_get(grid, x - 1, y) == 1) {
|
|
|
|
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
|
|
|
|
|
|
|
|
|
bsp->planes[face_idx].normal = (pxl8_vec3){-1, 0, 0};
|
|
|
|
|
bsp->planes[face_idx].dist = -fx;
|
|
|
|
|
|
|
|
|
|
bsp->faces[face_idx].plane_id = face_idx;
|
|
|
|
|
bsp->faces[face_idx].num_edges = 4;
|
|
|
|
|
bsp->faces[face_idx].first_edge = edge_idx;
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->faces[face_idx].texinfo_id = 0;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
|
|
|
|
for (i32 i = 0; i < 4; i++) {
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
|
|
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
vert_idx += 4;
|
|
|
|
|
edge_idx += 4;
|
|
|
|
|
face_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cave_grid_get(grid, x + 1, y) == 1) {
|
|
|
|
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
|
|
|
|
|
|
|
|
|
bsp->planes[face_idx].normal = (pxl8_vec3){1, 0, 0};
|
|
|
|
|
bsp->planes[face_idx].dist = fx + cell_size;
|
|
|
|
|
|
|
|
|
|
bsp->faces[face_idx].plane_id = face_idx;
|
|
|
|
|
bsp->faces[face_idx].num_edges = 4;
|
|
|
|
|
bsp->faces[face_idx].first_edge = edge_idx;
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->faces[face_idx].texinfo_id = 0;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
|
|
|
|
for (i32 i = 0; i < 4; i++) {
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
|
|
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
vert_idx += 4;
|
|
|
|
|
edge_idx += 4;
|
|
|
|
|
face_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cave_grid_get(grid, x, y - 1) == 1) {
|
|
|
|
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy};
|
|
|
|
|
|
|
|
|
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, -1};
|
|
|
|
|
bsp->planes[face_idx].dist = -fy;
|
|
|
|
|
|
|
|
|
|
bsp->faces[face_idx].plane_id = face_idx;
|
|
|
|
|
bsp->faces[face_idx].num_edges = 4;
|
|
|
|
|
bsp->faces[face_idx].first_edge = edge_idx;
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->faces[face_idx].texinfo_id = 0;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
|
|
|
|
for (i32 i = 0; i < 4; i++) {
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
|
|
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
vert_idx += 4;
|
|
|
|
|
edge_idx += 4;
|
|
|
|
|
face_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cave_grid_get(grid, x, y + 1) == 1) {
|
|
|
|
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
|
|
|
|
|
|
|
|
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, 1};
|
|
|
|
|
bsp->planes[face_idx].dist = fy + cell_size;
|
|
|
|
|
|
|
|
|
|
bsp->faces[face_idx].plane_id = face_idx;
|
|
|
|
|
bsp->faces[face_idx].num_edges = 4;
|
|
|
|
|
bsp->faces[face_idx].first_edge = edge_idx;
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->faces[face_idx].texinfo_id = 0;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
|
|
|
|
for (i32 i = 0; i < 4; i++) {
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
|
|
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
vert_idx += 4;
|
|
|
|
|
edge_idx += 4;
|
|
|
|
|
face_idx++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
|
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
|
|
|
if (cave_grid_get(grid, x, y) == 0) {
|
|
|
|
|
f32 fx = (f32)x * cell_size;
|
|
|
|
|
f32 fy = (f32)y * cell_size;
|
|
|
|
|
|
|
|
|
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
|
|
|
|
|
|
|
|
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, 1, 0};
|
|
|
|
|
bsp->planes[face_idx].dist = 0;
|
|
|
|
|
|
|
|
|
|
bsp->faces[face_idx].plane_id = face_idx;
|
|
|
|
|
bsp->faces[face_idx].num_edges = 4;
|
|
|
|
|
bsp->faces[face_idx].first_edge = edge_idx;
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->faces[face_idx].texinfo_id = 0;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
|
|
|
|
for (i32 i = 0; i < 4; i++) {
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
|
|
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
vert_idx += 4;
|
|
|
|
|
edge_idx += 4;
|
|
|
|
|
face_idx++;
|
|
|
|
|
|
|
|
|
|
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, wall_height, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
|
|
|
|
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
|
|
|
|
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
|
|
|
|
|
|
|
|
|
bsp->planes[face_idx].normal = (pxl8_vec3){0, -1, 0};
|
|
|
|
|
bsp->planes[face_idx].dist = -wall_height;
|
|
|
|
|
|
|
|
|
|
bsp->faces[face_idx].plane_id = face_idx;
|
|
|
|
|
bsp->faces[face_idx].num_edges = 4;
|
|
|
|
|
bsp->faces[face_idx].first_edge = edge_idx;
|
2025-11-11 21:24:53 -06:00
|
|
|
bsp->faces[face_idx].texinfo_id = 0;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
|
|
|
|
for (i32 i = 0; i < 4; i++) {
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
|
|
|
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
|
|
|
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 09:39:33 -06:00
|
|
|
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
vert_idx += 4;
|
|
|
|
|
edge_idx += 4;
|
|
|
|
|
face_idx++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bsp->num_vertices = vertex_count;
|
|
|
|
|
bsp->num_faces = face_count;
|
|
|
|
|
bsp->num_planes = face_count;
|
|
|
|
|
bsp->num_edges = vertex_count;
|
|
|
|
|
bsp->num_surfedges = vertex_count;
|
|
|
|
|
|
|
|
|
|
bsp->leafs = calloc(1, sizeof(pxl8_bsp_leaf));
|
|
|
|
|
bsp->marksurfaces = calloc(face_count, sizeof(u16));
|
|
|
|
|
|
|
|
|
|
if (!bsp->leafs || !bsp->marksurfaces) {
|
|
|
|
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bsp->num_leafs = 1;
|
|
|
|
|
bsp->num_marksurfaces = face_count;
|
|
|
|
|
|
|
|
|
|
bsp->leafs[0].first_marksurface = 0;
|
|
|
|
|
bsp->leafs[0].num_marksurfaces = face_count;
|
|
|
|
|
bsp->leafs[0].contents = -2;
|
|
|
|
|
|
|
|
|
|
for (i32 i = 0; i < face_count; i++) {
|
|
|
|
|
bsp->marksurfaces[i] = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PXL8_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static pxl8_result procgen_cave(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
|
|
|
|
prng_seed(params->seed);
|
|
|
|
|
|
|
|
|
|
cave_grid* grid = cave_grid_create(params->width, params->height);
|
|
|
|
|
if (!grid) {
|
|
|
|
|
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cave_grid_initialize(grid, params->density);
|
|
|
|
|
|
|
|
|
|
for (i32 i = 0; i < params->iterations; i++) {
|
|
|
|
|
cave_grid_smooth(grid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pxl8_result result = cave_to_bsp(bsp, grid);
|
|
|
|
|
cave_grid_destroy(grid);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
|
|
|
|
if (!bsp || !params) {
|
|
|
|
|
return PXL8_ERROR_NULL_POINTER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (params->type) {
|
|
|
|
|
case PXL8_PROCGEN_CAVE:
|
|
|
|
|
return procgen_cave(bsp, params);
|
|
|
|
|
|
|
|
|
|
case PXL8_PROCGEN_DUNGEON:
|
|
|
|
|
pxl8_error("Dungeon generation not yet implemented");
|
|
|
|
|
return PXL8_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
|
|
case PXL8_PROCGEN_TERRAIN:
|
|
|
|
|
pxl8_error("Terrain generation not yet implemented");
|
|
|
|
|
return PXL8_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
pxl8_error("Unknown procgen type: %d", params->type);
|
|
|
|
|
return PXL8_ERROR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
static u32 hash2d(i32 x, i32 y) {
|
|
|
|
|
u32 h = ((u32)x * 374761393u) + ((u32)y * 668265263u);
|
|
|
|
|
h ^= h >> 13;
|
|
|
|
|
h ^= h << 17;
|
|
|
|
|
h ^= h >> 5;
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-09 06:30:17 -06:00
|
|
|
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) {
|
|
|
|
|
if (!buffer || !params) return;
|
|
|
|
|
|
|
|
|
|
prng_seed(params->seed);
|
|
|
|
|
|
|
|
|
|
for (i32 y = 0; y < params->height; y++) {
|
|
|
|
|
for (i32 x = 0; x < params->width; x++) {
|
2025-11-11 21:24:53 -06:00
|
|
|
f32 u = (f32)x / (f32)params->width;
|
|
|
|
|
f32 v = (f32)y / (f32)params->height;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
u8 color = params->base_color;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
// Tile-based pattern (floor style)
|
|
|
|
|
if (params->seed == 11111) {
|
|
|
|
|
i32 tile_x = (i32)floorf(u * 8.0f);
|
|
|
|
|
i32 tile_y = (i32)floorf(v * 8.0f);
|
|
|
|
|
u32 h = hash2d(tile_x, tile_y);
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
f32 pattern = (f32)(h & 0xFF) / 255.0f;
|
|
|
|
|
i32 quantized = (pattern < 0.3f) ? 0 : (pattern < 0.7f) ? 1 : 2;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
color = params->base_color + quantized;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
// Checkerboard dither
|
|
|
|
|
if (((tile_x + tile_y) & 1) == 0 && (h & 0x100)) {
|
2025-11-11 22:42:10 -06:00
|
|
|
color = (color < 255) ? color + 1 : color;
|
2025-11-11 21:24:53 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Large tile pattern (ceiling style)
|
|
|
|
|
else if (params->seed == 22222) {
|
|
|
|
|
i32 coarse_x = (i32)floorf(u * 2.0f);
|
|
|
|
|
i32 coarse_y = (i32)floorf(v * 2.0f);
|
|
|
|
|
u32 coarse_h = hash2d(coarse_x, coarse_y);
|
|
|
|
|
|
|
|
|
|
i32 subdivision = (coarse_h >> 8) & 0x3;
|
|
|
|
|
i32 tile_x, tile_y;
|
|
|
|
|
|
|
|
|
|
switch (subdivision) {
|
|
|
|
|
case 0: tile_x = (i32)floorf(u * 3.0f); tile_y = (i32)floorf(v * 3.0f); break;
|
|
|
|
|
case 1: tile_x = (i32)floorf(u * 5.0f); tile_y = (i32)floorf(v * 5.0f); break;
|
|
|
|
|
case 2: tile_x = (i32)floorf(u * 2.0f); tile_y = (i32)floorf(v * 4.0f); break;
|
|
|
|
|
default: tile_x = (i32)floorf(u * 4.0f); tile_y = (i32)floorf(v * 2.0f); break;
|
|
|
|
|
}
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
u32 h = hash2d(tile_x, tile_y);
|
|
|
|
|
f32 pattern = (f32)(h & 0xFF) / 255.0f;
|
2025-11-09 06:30:17 -06:00
|
|
|
|
2025-11-11 21:24:53 -06:00
|
|
|
if (pattern < 0.25f) color = params->base_color;
|
|
|
|
|
else if (pattern < 0.50f) color = params->base_color + 1;
|
|
|
|
|
else if (pattern < 0.75f) color = params->base_color + 2;
|
|
|
|
|
else color = params->base_color + 1;
|
|
|
|
|
}
|
|
|
|
|
// Brick pattern (wall style)
|
|
|
|
|
else {
|
|
|
|
|
f32 brick_y = floorf(v * 4.0f);
|
|
|
|
|
f32 offset = ((i32)brick_y & 1) ? 0.5f : 0.0f;
|
|
|
|
|
i32 brick_x = (i32)floorf(u * 4.0f + offset);
|
|
|
|
|
brick_y = (i32)brick_y;
|
|
|
|
|
|
|
|
|
|
f32 brick_u = fabsf((u * 4.0f + offset) - floorf(u * 4.0f + offset) - 0.5f);
|
|
|
|
|
f32 brick_v = fabsf((v * 4.0f) - floorf(v * 4.0f) - 0.5f);
|
|
|
|
|
|
|
|
|
|
u32 h = hash2d(brick_x, (i32)brick_y);
|
|
|
|
|
f32 noise = (f32)(h & 0xFF) / 255.0f;
|
|
|
|
|
|
|
|
|
|
// Mortar lines
|
|
|
|
|
if (brick_u > 0.47f || brick_v > 0.47f) {
|
|
|
|
|
color = params->base_color - 2;
|
|
|
|
|
} else {
|
|
|
|
|
i32 shade = (i32)(noise * 3.0f);
|
|
|
|
|
color = params->base_color + shade;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer[y * params->width + x] = color;
|
2025-11-09 06:30:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|