better lighting
This commit is contained in:
parent
805a2713a3
commit
6ed4e17065
75 changed files with 6417 additions and 3667 deletions
319
src/vxl/pxl8_vxl.c
Normal file
319
src/vxl/pxl8_vxl.c
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
#include "pxl8_vxl.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
typedef struct pxl8_vxl_block_def {
|
||||
char name[32];
|
||||
u8 texture_top;
|
||||
u8 texture_side;
|
||||
u8 texture_bottom;
|
||||
pxl8_vxl_geometry geometry;
|
||||
bool registered;
|
||||
} pxl8_vxl_block_def;
|
||||
|
||||
struct pxl8_vxl_block_registry {
|
||||
pxl8_vxl_block_def blocks[PXL8_VXL_BLOCK_COUNT];
|
||||
};
|
||||
|
||||
static inline bool vxl_in_bounds(i32 x, i32 y, i32 z) {
|
||||
return x >= 0 && x < PXL8_VXL_CHUNK_SIZE &&
|
||||
y >= 0 && y < PXL8_VXL_CHUNK_SIZE &&
|
||||
z >= 0 && z < PXL8_VXL_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
static inline u32 vxl_index(i32 x, i32 y, i32 z) {
|
||||
return (u32)(x + y * PXL8_VXL_CHUNK_SIZE + z * PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
static pxl8_vxl_octree_node* octree_alloc_children(void) {
|
||||
pxl8_vxl_octree_node* children = pxl8_calloc(8, sizeof(pxl8_vxl_octree_node));
|
||||
for (i32 i = 0; i < 8; i++) {
|
||||
children[i].type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
children[i].block = PXL8_VXL_BLOCK_AIR;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
static void octree_free_node(pxl8_vxl_octree_node* node) {
|
||||
if (node->type == PXL8_VXL_OCTREE_BRANCH && node->children) {
|
||||
for (i32 i = 0; i < 8; i++) {
|
||||
octree_free_node(&node->children[i]);
|
||||
}
|
||||
pxl8_free(node->children);
|
||||
node->children = NULL;
|
||||
}
|
||||
node->type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
node->block = PXL8_VXL_BLOCK_AIR;
|
||||
}
|
||||
|
||||
static u8 octree_get(const pxl8_vxl_octree_node* node, i32 depth, i32 x, i32 y, i32 z) {
|
||||
if (node->type == PXL8_VXL_OCTREE_UNIFORM) {
|
||||
return node->block;
|
||||
}
|
||||
|
||||
if (depth == 0 || !node->children) {
|
||||
return PXL8_VXL_BLOCK_AIR;
|
||||
}
|
||||
|
||||
i32 half = 1 << (depth - 1);
|
||||
i32 idx = ((x >= half) ? 1 : 0) |
|
||||
((y >= half) ? 2 : 0) |
|
||||
((z >= half) ? 4 : 0);
|
||||
|
||||
return octree_get(&node->children[idx], depth - 1,
|
||||
x & (half - 1), y & (half - 1), z & (half - 1));
|
||||
}
|
||||
|
||||
static void octree_subdivide(pxl8_vxl_octree_node* node) {
|
||||
if (node->type == PXL8_VXL_OCTREE_BRANCH) return;
|
||||
|
||||
u8 block = node->block;
|
||||
node->type = PXL8_VXL_OCTREE_BRANCH;
|
||||
node->children = octree_alloc_children();
|
||||
|
||||
for (i32 i = 0; i < 8; i++) {
|
||||
node->children[i].type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
node->children[i].block = block;
|
||||
}
|
||||
}
|
||||
|
||||
static bool octree_try_simplify(pxl8_vxl_octree_node* node) {
|
||||
if (node->type != PXL8_VXL_OCTREE_BRANCH || !node->children) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < 8; i++) {
|
||||
if (node->children[i].type != PXL8_VXL_OCTREE_UNIFORM) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
u8 first_block = node->children[0].block;
|
||||
for (i32 i = 1; i < 8; i++) {
|
||||
if (node->children[i].block != first_block) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_free(node->children);
|
||||
node->children = NULL;
|
||||
node->type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
node->block = first_block;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void octree_set(pxl8_vxl_octree_node* node, i32 depth, i32 x, i32 y, i32 z, u8 block) {
|
||||
if (depth == 0) {
|
||||
if (node->type == PXL8_VXL_OCTREE_BRANCH) {
|
||||
octree_free_node(node);
|
||||
}
|
||||
node->type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
node->block = block;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->type == PXL8_VXL_OCTREE_UNIFORM) {
|
||||
if (node->block == block) {
|
||||
return;
|
||||
}
|
||||
octree_subdivide(node);
|
||||
}
|
||||
|
||||
i32 half = 1 << (depth - 1);
|
||||
i32 idx = ((x >= half) ? 1 : 0) |
|
||||
((y >= half) ? 2 : 0) |
|
||||
((z >= half) ? 4 : 0);
|
||||
|
||||
octree_set(&node->children[idx], depth - 1,
|
||||
x & (half - 1), y & (half - 1), z & (half - 1), block);
|
||||
|
||||
octree_try_simplify(node);
|
||||
}
|
||||
|
||||
static void octree_linearize_recursive(const pxl8_vxl_octree_node* node, i32 depth,
|
||||
i32 ox, i32 oy, i32 oz, u8* out) {
|
||||
i32 size = 1 << depth;
|
||||
|
||||
if (node->type == PXL8_VXL_OCTREE_UNIFORM) {
|
||||
for (i32 z = 0; z < size; z++) {
|
||||
for (i32 y = 0; y < size; y++) {
|
||||
for (i32 x = 0; x < size; x++) {
|
||||
out[vxl_index(ox + x, oy + y, oz + z)] = node->block;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (depth == 0 || !node->children) return;
|
||||
|
||||
i32 half = size / 2;
|
||||
for (i32 i = 0; i < 8; i++) {
|
||||
i32 cx = ox + ((i & 1) ? half : 0);
|
||||
i32 cy = oy + ((i & 2) ? half : 0);
|
||||
i32 cz = oz + ((i & 4) ? half : 0);
|
||||
octree_linearize_recursive(&node->children[i], depth - 1, cx, cy, cz, out);
|
||||
}
|
||||
}
|
||||
|
||||
static void octree_build_recursive(pxl8_vxl_octree_node* node, i32 depth,
|
||||
i32 ox, i32 oy, i32 oz, const u8* data) {
|
||||
i32 size = 1 << depth;
|
||||
|
||||
if (depth == 0) {
|
||||
node->type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
node->block = data[vxl_index(ox, oy, oz)];
|
||||
return;
|
||||
}
|
||||
|
||||
u8 first_block = data[vxl_index(ox, oy, oz)];
|
||||
bool all_same = true;
|
||||
|
||||
for (i32 z = 0; z < size && all_same; z++) {
|
||||
for (i32 y = 0; y < size && all_same; y++) {
|
||||
for (i32 x = 0; x < size && all_same; x++) {
|
||||
if (data[vxl_index(ox + x, oy + y, oz + z)] != first_block) {
|
||||
all_same = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_same) {
|
||||
node->type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
node->block = first_block;
|
||||
return;
|
||||
}
|
||||
|
||||
node->type = PXL8_VXL_OCTREE_BRANCH;
|
||||
node->children = octree_alloc_children();
|
||||
|
||||
i32 half = size / 2;
|
||||
for (i32 i = 0; i < 8; i++) {
|
||||
i32 cx = ox + ((i & 1) ? half : 0);
|
||||
i32 cy = oy + ((i & 2) ? half : 0);
|
||||
i32 cz = oz + ((i & 4) ? half : 0);
|
||||
octree_build_recursive(&node->children[i], depth - 1, cx, cy, cz, data);
|
||||
}
|
||||
|
||||
octree_try_simplify(node);
|
||||
}
|
||||
|
||||
pxl8_vxl_chunk* pxl8_vxl_chunk_create(void) {
|
||||
pxl8_vxl_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_vxl_chunk));
|
||||
if (!chunk) return NULL;
|
||||
chunk->root.type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
chunk->root.block = PXL8_VXL_BLOCK_AIR;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void pxl8_vxl_chunk_destroy(pxl8_vxl_chunk* chunk) {
|
||||
if (!chunk) return;
|
||||
octree_free_node(&chunk->root);
|
||||
pxl8_free(chunk);
|
||||
}
|
||||
|
||||
u8 pxl8_vxl_block_get(const pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z) {
|
||||
if (!chunk || !vxl_in_bounds(x, y, z)) return PXL8_VXL_BLOCK_AIR;
|
||||
return octree_get(&chunk->root, PXL8_VXL_OCTREE_DEPTH, x, y, z);
|
||||
}
|
||||
|
||||
void pxl8_vxl_block_set(pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z, u8 block) {
|
||||
if (!chunk || !vxl_in_bounds(x, y, z)) return;
|
||||
octree_set(&chunk->root, PXL8_VXL_OCTREE_DEPTH, x, y, z, block);
|
||||
}
|
||||
|
||||
void pxl8_vxl_block_fill(pxl8_vxl_chunk* chunk, u8 block) {
|
||||
if (!chunk) return;
|
||||
octree_free_node(&chunk->root);
|
||||
chunk->root.type = PXL8_VXL_OCTREE_UNIFORM;
|
||||
chunk->root.block = block;
|
||||
}
|
||||
|
||||
void pxl8_vxl_block_clear(pxl8_vxl_chunk* chunk) {
|
||||
pxl8_vxl_block_fill(chunk, PXL8_VXL_BLOCK_AIR);
|
||||
}
|
||||
|
||||
void pxl8_vxl_chunk_linearize(const pxl8_vxl_chunk* chunk, u8* out) {
|
||||
if (!chunk || !out) return;
|
||||
octree_linearize_recursive(&chunk->root, PXL8_VXL_OCTREE_DEPTH, 0, 0, 0, out);
|
||||
}
|
||||
|
||||
void pxl8_vxl_chunk_from_linear(pxl8_vxl_chunk* chunk, const u8* data) {
|
||||
if (!chunk || !data) return;
|
||||
octree_free_node(&chunk->root);
|
||||
octree_build_recursive(&chunk->root, PXL8_VXL_OCTREE_DEPTH, 0, 0, 0, data);
|
||||
}
|
||||
|
||||
bool pxl8_vxl_chunk_is_uniform(const pxl8_vxl_chunk* chunk) {
|
||||
if (!chunk) return true;
|
||||
return chunk->root.type == PXL8_VXL_OCTREE_UNIFORM;
|
||||
}
|
||||
|
||||
u8 pxl8_vxl_chunk_uniform_block(const pxl8_vxl_chunk* chunk) {
|
||||
if (!chunk) return PXL8_VXL_BLOCK_AIR;
|
||||
if (chunk->root.type == PXL8_VXL_OCTREE_UNIFORM) {
|
||||
return chunk->root.block;
|
||||
}
|
||||
return PXL8_VXL_BLOCK_AIR;
|
||||
}
|
||||
|
||||
pxl8_vxl_block_registry* pxl8_vxl_block_registry_create(void) {
|
||||
pxl8_vxl_block_registry* registry = pxl8_calloc(1, sizeof(pxl8_vxl_block_registry));
|
||||
if (!registry) return NULL;
|
||||
|
||||
pxl8_vxl_block_registry_register(registry, 1, "stone", 8, PXL8_VXL_GEOMETRY_CUBE);
|
||||
pxl8_vxl_block_registry_register(registry, 2, "dirt", 52, PXL8_VXL_GEOMETRY_CUBE);
|
||||
pxl8_vxl_block_registry_register_ex(registry, 3, "grass", 200, 52, 52, PXL8_VXL_GEOMETRY_CUBE);
|
||||
pxl8_vxl_block_registry_register_ex(registry, 4, "grass_slope_n", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_NORTH);
|
||||
pxl8_vxl_block_registry_register_ex(registry, 5, "grass_slope_s", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_SOUTH);
|
||||
pxl8_vxl_block_registry_register_ex(registry, 6, "grass_slope_e", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_EAST);
|
||||
pxl8_vxl_block_registry_register_ex(registry, 7, "grass_slope_w", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_WEST);
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
void pxl8_vxl_block_registry_destroy(pxl8_vxl_block_registry* registry) {
|
||||
pxl8_free(registry);
|
||||
}
|
||||
|
||||
void pxl8_vxl_block_registry_register(pxl8_vxl_block_registry* registry, u8 id, const char* name, u8 texture_id, pxl8_vxl_geometry geo) {
|
||||
pxl8_vxl_block_registry_register_ex(registry, id, name, texture_id, texture_id, texture_id, geo);
|
||||
}
|
||||
|
||||
void pxl8_vxl_block_registry_register_ex(pxl8_vxl_block_registry* registry, u8 id, const char* name,
|
||||
u8 texture_top, u8 texture_side, u8 texture_bottom, pxl8_vxl_geometry geo) {
|
||||
if (!registry || id == PXL8_VXL_BLOCK_AIR) return;
|
||||
|
||||
pxl8_vxl_block_def* def = ®istry->blocks[id];
|
||||
strncpy(def->name, name ? name : "", sizeof(def->name) - 1);
|
||||
def->texture_top = texture_top;
|
||||
def->texture_side = texture_side;
|
||||
def->texture_bottom = texture_bottom;
|
||||
def->geometry = geo;
|
||||
def->registered = true;
|
||||
}
|
||||
|
||||
const char* pxl8_vxl_block_registry_name(const pxl8_vxl_block_registry* registry, u8 id) {
|
||||
if (!registry || !registry->blocks[id].registered) return NULL;
|
||||
return registry->blocks[id].name;
|
||||
}
|
||||
|
||||
pxl8_vxl_geometry pxl8_vxl_block_registry_geometry(const pxl8_vxl_block_registry* registry, u8 id) {
|
||||
if (!registry || !registry->blocks[id].registered) return PXL8_VXL_GEOMETRY_CUBE;
|
||||
return registry->blocks[id].geometry;
|
||||
}
|
||||
|
||||
u8 pxl8_vxl_block_registry_texture(const pxl8_vxl_block_registry* registry, u8 id) {
|
||||
if (!registry || !registry->blocks[id].registered) return 0;
|
||||
return registry->blocks[id].texture_top;
|
||||
}
|
||||
|
||||
u8 pxl8_vxl_block_registry_texture_for_face(const pxl8_vxl_block_registry* registry, u8 id, i32 face) {
|
||||
if (!registry || !registry->blocks[id].registered) return 0;
|
||||
if (face == 3) return registry->blocks[id].texture_top;
|
||||
if (face == 2) return registry->blocks[id].texture_bottom;
|
||||
return registry->blocks[id].texture_side;
|
||||
}
|
||||
78
src/vxl/pxl8_vxl.h
Normal file
78
src/vxl/pxl8_vxl.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PXL8_VXL_BLOCK_COUNT 256
|
||||
#define PXL8_VXL_CHUNK_SIZE 32
|
||||
#define PXL8_VXL_CHUNK_VOLUME (PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE)
|
||||
#define PXL8_VXL_SCALE 16.0f
|
||||
#define PXL8_VXL_WORLD_CHUNK_SIZE (PXL8_VXL_CHUNK_SIZE * PXL8_VXL_SCALE)
|
||||
#define PXL8_VXL_OCTREE_DEPTH 5
|
||||
|
||||
#define PXL8_VXL_BLOCK_AIR 0
|
||||
#define PXL8_VXL_BLOCK_UNKNOWN 255
|
||||
|
||||
typedef struct pxl8_vxl_block_registry pxl8_vxl_block_registry;
|
||||
|
||||
typedef enum pxl8_vxl_octree_type {
|
||||
PXL8_VXL_OCTREE_UNIFORM = 0,
|
||||
PXL8_VXL_OCTREE_BRANCH = 1
|
||||
} pxl8_vxl_octree_type;
|
||||
|
||||
typedef struct pxl8_vxl_octree_node {
|
||||
u8 type;
|
||||
union {
|
||||
u8 block;
|
||||
struct pxl8_vxl_octree_node* children;
|
||||
};
|
||||
} pxl8_vxl_octree_node;
|
||||
|
||||
typedef struct pxl8_vxl_chunk {
|
||||
pxl8_vxl_octree_node root;
|
||||
} pxl8_vxl_chunk;
|
||||
|
||||
typedef enum pxl8_vxl_geometry {
|
||||
PXL8_VXL_GEOMETRY_CUBE,
|
||||
PXL8_VXL_GEOMETRY_SLAB_BOTTOM,
|
||||
PXL8_VXL_GEOMETRY_SLAB_TOP,
|
||||
PXL8_VXL_GEOMETRY_SLOPE_NORTH,
|
||||
PXL8_VXL_GEOMETRY_SLOPE_SOUTH,
|
||||
PXL8_VXL_GEOMETRY_SLOPE_EAST,
|
||||
PXL8_VXL_GEOMETRY_SLOPE_WEST,
|
||||
PXL8_VXL_GEOMETRY_STAIRS_NORTH,
|
||||
PXL8_VXL_GEOMETRY_STAIRS_SOUTH,
|
||||
PXL8_VXL_GEOMETRY_STAIRS_EAST,
|
||||
PXL8_VXL_GEOMETRY_STAIRS_WEST
|
||||
} pxl8_vxl_geometry;
|
||||
|
||||
pxl8_vxl_chunk* pxl8_vxl_chunk_create(void);
|
||||
void pxl8_vxl_chunk_destroy(pxl8_vxl_chunk* chunk);
|
||||
|
||||
u8 pxl8_vxl_block_get(const pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z);
|
||||
void pxl8_vxl_block_set(pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z, u8 block);
|
||||
void pxl8_vxl_block_fill(pxl8_vxl_chunk* chunk, u8 block);
|
||||
void pxl8_vxl_block_clear(pxl8_vxl_chunk* chunk);
|
||||
|
||||
void pxl8_vxl_chunk_linearize(const pxl8_vxl_chunk* chunk, u8* out);
|
||||
void pxl8_vxl_chunk_from_linear(pxl8_vxl_chunk* chunk, const u8* data);
|
||||
|
||||
bool pxl8_vxl_chunk_is_uniform(const pxl8_vxl_chunk* chunk);
|
||||
u8 pxl8_vxl_chunk_uniform_block(const pxl8_vxl_chunk* chunk);
|
||||
|
||||
pxl8_vxl_block_registry* pxl8_vxl_block_registry_create(void);
|
||||
void pxl8_vxl_block_registry_destroy(pxl8_vxl_block_registry* registry);
|
||||
void pxl8_vxl_block_registry_register(pxl8_vxl_block_registry* registry, u8 id, const char* name, u8 texture_id, pxl8_vxl_geometry geo);
|
||||
void pxl8_vxl_block_registry_register_ex(pxl8_vxl_block_registry* registry, u8 id, const char* name,
|
||||
u8 texture_top, u8 texture_side, u8 texture_bottom, pxl8_vxl_geometry geo);
|
||||
const char* pxl8_vxl_block_registry_name(const pxl8_vxl_block_registry* registry, u8 id);
|
||||
pxl8_vxl_geometry pxl8_vxl_block_registry_geometry(const pxl8_vxl_block_registry* registry, u8 id);
|
||||
u8 pxl8_vxl_block_registry_texture(const pxl8_vxl_block_registry* registry, u8 id);
|
||||
u8 pxl8_vxl_block_registry_texture_for_face(const pxl8_vxl_block_registry* registry, u8 id, i32 face);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
576
src/vxl/pxl8_vxl_render.c
Normal file
576
src/vxl/pxl8_vxl_render.c
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
#include "pxl8_vxl_render.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_noise.h"
|
||||
#include "pxl8_vxl.h"
|
||||
|
||||
typedef struct pxl8_vxl_greedy_mask {
|
||||
u8 block[PXL8_VXL_CHUNK_SIZE][PXL8_VXL_CHUNK_SIZE];
|
||||
bool done[PXL8_VXL_CHUNK_SIZE][PXL8_VXL_CHUNK_SIZE];
|
||||
} pxl8_vxl_greedy_mask;
|
||||
|
||||
static const pxl8_vec3 face_normals[6] = {
|
||||
{-1, 0, 0}, { 1, 0, 0},
|
||||
{ 0, -1, 0}, { 0, 1, 0},
|
||||
{ 0, 0, -1}, { 0, 0, 1}
|
||||
};
|
||||
|
||||
static const i32 face_dirs[6][3] = {
|
||||
{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}
|
||||
};
|
||||
|
||||
static inline bool vxl_in_bounds(i32 x, i32 y, i32 z) {
|
||||
return x >= 0 && x < PXL8_VXL_CHUNK_SIZE &&
|
||||
y >= 0 && y < PXL8_VXL_CHUNK_SIZE &&
|
||||
z >= 0 && z < PXL8_VXL_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
static bool block_is_opaque(u8 block) {
|
||||
return block != PXL8_VXL_BLOCK_AIR && block != PXL8_VXL_BLOCK_UNKNOWN;
|
||||
}
|
||||
|
||||
static bool block_is_full_cube(u8 block, const pxl8_vxl_block_registry* registry) {
|
||||
if (block == PXL8_VXL_BLOCK_AIR) return false;
|
||||
pxl8_vxl_geometry geo = pxl8_vxl_block_registry_geometry(registry, block);
|
||||
return geo == PXL8_VXL_GEOMETRY_CUBE;
|
||||
}
|
||||
|
||||
static u8 get_block_or_neighbor(const pxl8_vxl_chunk* chunk, const pxl8_vxl_chunk** neighbors, i32 x, i32 y, i32 z) {
|
||||
if (vxl_in_bounds(x, y, z)) {
|
||||
return pxl8_vxl_block_get(chunk, x, y, z);
|
||||
}
|
||||
|
||||
i32 nx = x, ny = y, nz = z;
|
||||
i32 neighbor_idx = -1;
|
||||
|
||||
if (x < 0) { neighbor_idx = 0; nx = x + PXL8_VXL_CHUNK_SIZE; }
|
||||
else if (x >= PXL8_VXL_CHUNK_SIZE) { neighbor_idx = 1; nx = x - PXL8_VXL_CHUNK_SIZE; }
|
||||
else if (y < 0) { neighbor_idx = 2; ny = y + PXL8_VXL_CHUNK_SIZE; }
|
||||
else if (y >= PXL8_VXL_CHUNK_SIZE) { neighbor_idx = 3; ny = y - PXL8_VXL_CHUNK_SIZE; }
|
||||
else if (z < 0) { neighbor_idx = 4; nz = z + PXL8_VXL_CHUNK_SIZE; }
|
||||
else if (z >= PXL8_VXL_CHUNK_SIZE) { neighbor_idx = 5; nz = z - PXL8_VXL_CHUNK_SIZE; }
|
||||
|
||||
if (neighbor_idx >= 0 && neighbors && neighbors[neighbor_idx]) {
|
||||
return pxl8_vxl_block_get(neighbors[neighbor_idx], nx, ny, nz);
|
||||
}
|
||||
|
||||
return PXL8_VXL_BLOCK_UNKNOWN;
|
||||
}
|
||||
|
||||
static f32 compute_ao(const pxl8_vxl_chunk* chunk, const pxl8_vxl_chunk** neighbors,
|
||||
i32 x, i32 y, i32 z, i32 dx1, i32 dy1, i32 dz1, i32 dx2, i32 dy2, i32 dz2) {
|
||||
u8 s1 = get_block_or_neighbor(chunk, neighbors, x + dx1, y + dy1, z + dz1);
|
||||
u8 s2 = get_block_or_neighbor(chunk, neighbors, x + dx2, y + dy2, z + dz2);
|
||||
u8 corner = get_block_or_neighbor(chunk, neighbors, x + dx1 + dx2, y + dy1 + dy2, z + dz1 + dz2);
|
||||
|
||||
bool side1 = block_is_opaque(s1);
|
||||
bool side2 = block_is_opaque(s2);
|
||||
bool has_corner = block_is_opaque(corner);
|
||||
|
||||
if (side1 && side2) return 0.0f;
|
||||
return (3.0f - (f32)side1 - (f32)side2 - (f32)has_corner) / 3.0f;
|
||||
}
|
||||
|
||||
static f32 sample_corner_displacement(const pxl8_vxl_chunk* chunk, const pxl8_vxl_chunk** neighbors,
|
||||
i32 cx, i32 cz, i32 base_y) {
|
||||
f32 sum = 0.0f;
|
||||
i32 count = 0;
|
||||
|
||||
for (i32 dz = -1; dz <= 0; dz++) {
|
||||
for (i32 dx = -1; dx <= 0; dx++) {
|
||||
i32 x = cx + dx;
|
||||
i32 z = cz + dz;
|
||||
|
||||
u8 block = get_block_or_neighbor(chunk, neighbors, x, base_y, z);
|
||||
u8 above = get_block_or_neighbor(chunk, neighbors, x, base_y + 1, z);
|
||||
|
||||
if (block_is_opaque(block) && !block_is_opaque(above)) {
|
||||
sum += 1.0f;
|
||||
} else if (!block_is_opaque(block)) {
|
||||
sum -= 1.0f;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count > 0 ? (sum / (f32)count) * 0.15f : 0.0f;
|
||||
}
|
||||
|
||||
static void slice_to_world(i32 face, i32 slice, i32 u, i32 v, i32* x, i32* y, i32* z) {
|
||||
switch (face) {
|
||||
case 0: *x = slice; *y = v; *z = u; break;
|
||||
case 1: *x = slice; *y = v; *z = u; break;
|
||||
case 2: *x = u; *y = slice; *z = v; break;
|
||||
case 3: *x = u; *y = slice; *z = v; break;
|
||||
case 4: *x = u; *y = v; *z = slice; break;
|
||||
case 5: *x = u; *y = v; *z = slice; break;
|
||||
}
|
||||
}
|
||||
|
||||
static f32 terrain_noise(f32 wx, f32 wz, u64 seed) {
|
||||
f32 noise = pxl8_fbm(wx * 0.08f, wz * 0.08f, seed, 3);
|
||||
return (noise - 0.5f) * 0.4f;
|
||||
}
|
||||
|
||||
static void emit_greedy_quad(pxl8_mesh* mesh, const pxl8_vxl_chunk* chunk,
|
||||
const pxl8_vxl_chunk** neighbors,
|
||||
const pxl8_vxl_mesh_config* config,
|
||||
i32 face, i32 slice,
|
||||
i32 u, i32 v, i32 width, i32 height,
|
||||
u8 texture_id, f32 ao_avg, f32 ao_strength) {
|
||||
pxl8_vec3 normal = face_normals[face];
|
||||
u8 light = (u8)(255.0f * (1.0f - ao_strength * (1.0f - ao_avg)));
|
||||
|
||||
f32 p[4][3];
|
||||
|
||||
switch (face) {
|
||||
case 0:
|
||||
p[0][0] = (f32)slice; p[0][1] = (f32)v; p[0][2] = (f32)(u + width);
|
||||
p[1][0] = (f32)slice; p[1][1] = (f32)v; p[1][2] = (f32)u;
|
||||
p[2][0] = (f32)slice; p[2][1] = (f32)(v + height); p[2][2] = (f32)u;
|
||||
p[3][0] = (f32)slice; p[3][1] = (f32)(v + height); p[3][2] = (f32)(u + width);
|
||||
break;
|
||||
case 1:
|
||||
p[0][0] = (f32)(slice + 1); p[0][1] = (f32)v; p[0][2] = (f32)u;
|
||||
p[1][0] = (f32)(slice + 1); p[1][1] = (f32)v; p[1][2] = (f32)(u + width);
|
||||
p[2][0] = (f32)(slice + 1); p[2][1] = (f32)(v + height); p[2][2] = (f32)(u + width);
|
||||
p[3][0] = (f32)(slice + 1); p[3][1] = (f32)(v + height); p[3][2] = (f32)u;
|
||||
break;
|
||||
case 2:
|
||||
p[0][0] = (f32)u; p[0][1] = (f32)slice; p[0][2] = (f32)v;
|
||||
p[1][0] = (f32)(u + width); p[1][1] = (f32)slice; p[1][2] = (f32)v;
|
||||
p[2][0] = (f32)(u + width); p[2][1] = (f32)slice; p[2][2] = (f32)(v + height);
|
||||
p[3][0] = (f32)u; p[3][1] = (f32)slice; p[3][2] = (f32)(v + height);
|
||||
break;
|
||||
case 3: {
|
||||
f32 base_y = (f32)(slice + 1);
|
||||
f32 wx0 = (f32)(config->chunk_x * PXL8_VXL_CHUNK_SIZE + u);
|
||||
f32 wz0 = (f32)(config->chunk_z * PXL8_VXL_CHUNK_SIZE + v);
|
||||
|
||||
f32 d0 = sample_corner_displacement(chunk, neighbors, u, v + height, slice);
|
||||
f32 d1 = sample_corner_displacement(chunk, neighbors, u + width, v + height, slice);
|
||||
f32 d2 = sample_corner_displacement(chunk, neighbors, u + width, v, slice);
|
||||
f32 d3 = sample_corner_displacement(chunk, neighbors, u, v, slice);
|
||||
|
||||
f32 n0 = terrain_noise(wx0, wz0 + (f32)height, config->seed);
|
||||
f32 n1 = terrain_noise(wx0 + (f32)width, wz0 + (f32)height, config->seed);
|
||||
f32 n2 = terrain_noise(wx0 + (f32)width, wz0, config->seed);
|
||||
f32 n3 = terrain_noise(wx0, wz0, config->seed);
|
||||
|
||||
p[0][0] = (f32)u; p[0][1] = base_y + d0 + n0; p[0][2] = (f32)(v + height);
|
||||
p[1][0] = (f32)(u + width); p[1][1] = base_y + d1 + n1; p[1][2] = (f32)(v + height);
|
||||
p[2][0] = (f32)(u + width); p[2][1] = base_y + d2 + n2; p[2][2] = (f32)v;
|
||||
p[3][0] = (f32)u; p[3][1] = base_y + d3 + n3; p[3][2] = (f32)v;
|
||||
|
||||
pxl8_vec3 e1 = {p[1][0] - p[0][0], p[1][1] - p[0][1], p[1][2] - p[0][2]};
|
||||
pxl8_vec3 e2 = {p[3][0] - p[0][0], p[3][1] - p[0][1], p[3][2] - p[0][2]};
|
||||
normal = (pxl8_vec3){
|
||||
e1.y * e2.z - e1.z * e2.y,
|
||||
e1.z * e2.x - e1.x * e2.z,
|
||||
e1.x * e2.y - e1.y * e2.x
|
||||
};
|
||||
f32 len_sq = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z;
|
||||
if (len_sq > 0.0001f) {
|
||||
f32 inv_len = pxl8_fast_inv_sqrt(len_sq);
|
||||
normal.x *= inv_len; normal.y *= inv_len; normal.z *= inv_len;
|
||||
} else {
|
||||
normal = (pxl8_vec3){0, 1, 0};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
p[0][0] = (f32)u; p[0][1] = (f32)v; p[0][2] = (f32)slice;
|
||||
p[1][0] = (f32)u; p[1][1] = (f32)(v + height); p[1][2] = (f32)slice;
|
||||
p[2][0] = (f32)(u + width); p[2][1] = (f32)(v + height); p[2][2] = (f32)slice;
|
||||
p[3][0] = (f32)(u + width); p[3][1] = (f32)v; p[3][2] = (f32)slice;
|
||||
break;
|
||||
case 5:
|
||||
p[0][0] = (f32)(u + width); p[0][1] = (f32)v; p[0][2] = (f32)(slice + 1);
|
||||
p[1][0] = (f32)(u + width); p[1][1] = (f32)(v + height); p[1][2] = (f32)(slice + 1);
|
||||
p[2][0] = (f32)u; p[2][1] = (f32)(v + height); p[2][2] = (f32)(slice + 1);
|
||||
p[3][0] = (f32)u; p[3][1] = (f32)v; p[3][2] = (f32)(slice + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
f32 tex_u = (f32)width;
|
||||
f32 tex_v = (f32)height;
|
||||
|
||||
pxl8_vertex verts[4];
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
verts[i].position.x = p[i][0];
|
||||
verts[i].position.y = p[i][1];
|
||||
verts[i].position.z = p[i][2];
|
||||
verts[i].normal = normal;
|
||||
verts[i].color = texture_id;
|
||||
verts[i].light = light;
|
||||
}
|
||||
|
||||
verts[0].u = 0; verts[0].v = tex_v;
|
||||
verts[1].u = tex_u; verts[1].v = tex_v;
|
||||
verts[2].u = tex_u; verts[2].v = 0;
|
||||
verts[3].u = 0; verts[3].v = 0;
|
||||
|
||||
u16 i0 = pxl8_mesh_push_vertex(mesh, verts[0]);
|
||||
u16 i1 = pxl8_mesh_push_vertex(mesh, verts[1]);
|
||||
u16 i2 = pxl8_mesh_push_vertex(mesh, verts[2]);
|
||||
u16 i3 = pxl8_mesh_push_vertex(mesh, verts[3]);
|
||||
|
||||
pxl8_mesh_push_quad(mesh, i0, i1, i2, i3);
|
||||
}
|
||||
|
||||
static void build_greedy_slice(const pxl8_vxl_chunk* chunk,
|
||||
const pxl8_vxl_chunk** neighbors,
|
||||
const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config,
|
||||
i32 face, i32 slice,
|
||||
pxl8_mesh* mesh) {
|
||||
pxl8_vxl_greedy_mask mask;
|
||||
memset(&mask, 0, sizeof(mask));
|
||||
|
||||
i32 dx = face_dirs[face][0];
|
||||
i32 dy = face_dirs[face][1];
|
||||
i32 dz = face_dirs[face][2];
|
||||
|
||||
for (i32 v = 0; v < PXL8_VXL_CHUNK_SIZE; v++) {
|
||||
for (i32 u = 0; u < PXL8_VXL_CHUNK_SIZE; u++) {
|
||||
i32 x, y, z;
|
||||
slice_to_world(face, slice, u, v, &x, &y, &z);
|
||||
|
||||
u8 block = pxl8_vxl_block_get(chunk, x, y, z);
|
||||
if (block == PXL8_VXL_BLOCK_AIR) continue;
|
||||
|
||||
pxl8_vxl_geometry geo = pxl8_vxl_block_registry_geometry(registry, block);
|
||||
if (geo != PXL8_VXL_GEOMETRY_CUBE) continue;
|
||||
|
||||
i32 nx = x + dx;
|
||||
i32 ny = y + dy;
|
||||
i32 nz = z + dz;
|
||||
|
||||
u8 neighbor = get_block_or_neighbor(chunk, neighbors, nx, ny, nz);
|
||||
if (neighbor == PXL8_VXL_BLOCK_UNKNOWN) continue;
|
||||
if (block_is_full_cube(neighbor, registry)) continue;
|
||||
|
||||
mask.block[u][v] = block;
|
||||
}
|
||||
}
|
||||
|
||||
f32 ao_strength = config->ambient_occlusion ? config->ao_strength : 0.0f;
|
||||
|
||||
for (i32 v = 0; v < PXL8_VXL_CHUNK_SIZE; v++) {
|
||||
for (i32 u = 0; u < PXL8_VXL_CHUNK_SIZE; u++) {
|
||||
if (mask.done[u][v] || mask.block[u][v] == 0) continue;
|
||||
|
||||
u8 block = mask.block[u][v];
|
||||
u8 texture_id = pxl8_vxl_block_registry_texture_for_face(registry, block, face);
|
||||
|
||||
i32 width = 1;
|
||||
while (u + width < PXL8_VXL_CHUNK_SIZE &&
|
||||
!mask.done[u + width][v] &&
|
||||
mask.block[u + width][v] == block) {
|
||||
width++;
|
||||
}
|
||||
|
||||
i32 height = 1;
|
||||
bool can_expand = true;
|
||||
while (can_expand && v + height < PXL8_VXL_CHUNK_SIZE) {
|
||||
for (i32 wu = 0; wu < width; wu++) {
|
||||
if (mask.done[u + wu][v + height] ||
|
||||
mask.block[u + wu][v + height] != block) {
|
||||
can_expand = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (can_expand) height++;
|
||||
}
|
||||
|
||||
for (i32 dv = 0; dv < height; dv++) {
|
||||
for (i32 du = 0; du < width; du++) {
|
||||
mask.done[u + du][v + dv] = true;
|
||||
}
|
||||
}
|
||||
|
||||
f32 ao_sum = 0.0f;
|
||||
i32 ao_count = 0;
|
||||
if (config->ambient_occlusion) {
|
||||
i32 cx, cy, cz;
|
||||
slice_to_world(face, slice, u, v, &cx, &cy, &cz);
|
||||
i32 fx = cx + dx;
|
||||
i32 fy = cy + dy;
|
||||
i32 fz = cz + dz;
|
||||
|
||||
static const i32 ao_offsets[6][4][2][3] = {
|
||||
[0] = {{{0,-1,0}, {0,0,1}}, {{0,-1,0}, {0,0,-1}}, {{0,1,0}, {0,0,-1}}, {{0,1,0}, {0,0,1}}},
|
||||
[1] = {{{0,-1,0}, {0,0,-1}}, {{0,-1,0}, {0,0,1}}, {{0,1,0}, {0,0,1}}, {{0,1,0}, {0,0,-1}}},
|
||||
[2] = {{{-1,0,0}, {0,0,-1}}, {{1,0,0}, {0,0,-1}}, {{1,0,0}, {0,0,1}}, {{-1,0,0}, {0,0,1}}},
|
||||
[3] = {{{-1,0,0}, {0,0,1}}, {{1,0,0}, {0,0,1}}, {{1,0,0}, {0,0,-1}}, {{-1,0,0}, {0,0,-1}}},
|
||||
[4] = {{{-1,0,0}, {0,1,0}}, {{-1,0,0}, {0,-1,0}}, {{1,0,0}, {0,-1,0}}, {{1,0,0}, {0,1,0}}},
|
||||
[5] = {{{1,0,0}, {0,-1,0}}, {{1,0,0}, {0,1,0}}, {{-1,0,0}, {0,1,0}}, {{-1,0,0}, {0,-1,0}}}
|
||||
};
|
||||
|
||||
for (i32 corner = 0; corner < 4; corner++) {
|
||||
f32 ao = compute_ao(chunk, neighbors, fx, fy, fz,
|
||||
ao_offsets[face][corner][0][0],
|
||||
ao_offsets[face][corner][0][1],
|
||||
ao_offsets[face][corner][0][2],
|
||||
ao_offsets[face][corner][1][0],
|
||||
ao_offsets[face][corner][1][1],
|
||||
ao_offsets[face][corner][1][2]);
|
||||
ao_sum += ao;
|
||||
ao_count++;
|
||||
}
|
||||
}
|
||||
|
||||
f32 ao_avg = ao_count > 0 ? ao_sum / (f32)ao_count : 1.0f;
|
||||
|
||||
emit_greedy_quad(mesh, chunk, neighbors, config, face, slice, u, v, width, height,
|
||||
texture_id, ao_avg, ao_strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_face_vertices(pxl8_mesh* mesh, const pxl8_vxl_mesh_config* config,
|
||||
pxl8_vec3 pos, i32 face, u8 texture_id,
|
||||
f32 ao0, f32 ao1, f32 ao2, f32 ao3) {
|
||||
static const i32 face_vertices[6][4][3] = {
|
||||
{{0,0,1}, {0,0,0}, {0,1,0}, {0,1,1}},
|
||||
{{1,0,0}, {1,0,1}, {1,1,1}, {1,1,0}},
|
||||
{{0,0,0}, {1,0,0}, {1,0,1}, {0,0,1}},
|
||||
{{0,1,1}, {1,1,1}, {1,1,0}, {0,1,0}},
|
||||
{{0,0,0}, {0,1,0}, {1,1,0}, {1,0,0}},
|
||||
{{1,0,1}, {1,1,1}, {0,1,1}, {0,0,1}}
|
||||
};
|
||||
|
||||
static const f32 face_uvs[4][2] = {
|
||||
{0, 1}, {1, 1}, {1, 0}, {0, 0}
|
||||
};
|
||||
|
||||
f32 ao_values[4] = {ao0, ao1, ao2, ao3};
|
||||
f32 ao_strength = config->ambient_occlusion ? config->ao_strength : 0.0f;
|
||||
f32 tex_scale = config->texture_scale;
|
||||
|
||||
pxl8_vec3 normal = face_normals[face];
|
||||
u16 indices[4];
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
pxl8_vertex v = {0};
|
||||
v.position.x = pos.x + (f32)face_vertices[face][i][0];
|
||||
v.position.y = pos.y + (f32)face_vertices[face][i][1];
|
||||
v.position.z = pos.z + (f32)face_vertices[face][i][2];
|
||||
v.normal = normal;
|
||||
v.u = face_uvs[i][0] * tex_scale;
|
||||
v.v = face_uvs[i][1] * tex_scale;
|
||||
v.color = texture_id;
|
||||
v.light = (u8)(255.0f * (1.0f - ao_strength * (1.0f - ao_values[i])));
|
||||
|
||||
indices[i] = pxl8_mesh_push_vertex(mesh, v);
|
||||
}
|
||||
|
||||
if (ao0 + ao2 > ao1 + ao3) {
|
||||
pxl8_mesh_push_quad(mesh, indices[0], indices[1], indices[2], indices[3]);
|
||||
} else {
|
||||
pxl8_mesh_push_triangle(mesh, indices[0], indices[1], indices[2]);
|
||||
pxl8_mesh_push_triangle(mesh, indices[0], indices[2], indices[3]);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_slab_faces(pxl8_mesh* mesh, const pxl8_vxl_chunk* chunk,
|
||||
const pxl8_vxl_chunk** neighbors, const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config,
|
||||
i32 x, i32 y, i32 z, u8 block, bool top) {
|
||||
u8 texture_id = pxl8_vxl_block_registry_texture(registry, block);
|
||||
f32 y_offset = top ? 0.5f : 0.0f;
|
||||
pxl8_vec3 pos = {(f32)x, (f32)y + y_offset, (f32)z};
|
||||
|
||||
static const i32 horiz_faces[4] = {0, 1, 4, 5};
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
i32 face = horiz_faces[i];
|
||||
i32 nx = x + face_dirs[face][0];
|
||||
i32 nz = z + face_dirs[face][2];
|
||||
|
||||
u8 neighbor = get_block_or_neighbor(chunk, neighbors, nx, y, nz);
|
||||
if (neighbor == PXL8_VXL_BLOCK_UNKNOWN) continue;
|
||||
if (block_is_full_cube(neighbor, registry)) continue;
|
||||
|
||||
add_face_vertices(mesh, config, pos, face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
i32 top_face = 3;
|
||||
i32 bot_face = 2;
|
||||
|
||||
if (top) {
|
||||
u8 above = get_block_or_neighbor(chunk, neighbors, x, y + 1, z);
|
||||
if (above != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(above, registry)) {
|
||||
add_face_vertices(mesh, config, pos, top_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
add_face_vertices(mesh, config, (pxl8_vec3){(f32)x, (f32)y + 0.5f, (f32)z}, bot_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
} else {
|
||||
add_face_vertices(mesh, config, pos, top_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
u8 below = get_block_or_neighbor(chunk, neighbors, x, y - 1, z);
|
||||
if (below != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(below, registry)) {
|
||||
add_face_vertices(mesh, config, pos, bot_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_slope_faces(pxl8_mesh* mesh, const pxl8_vxl_chunk* chunk,
|
||||
const pxl8_vxl_chunk** neighbors, const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config,
|
||||
i32 x, i32 y, i32 z, u8 block, i32 direction) {
|
||||
u8 texture_top = pxl8_vxl_block_registry_texture_for_face(registry, block, 3);
|
||||
u8 texture_side = pxl8_vxl_block_registry_texture_for_face(registry, block, 0);
|
||||
pxl8_vec3 pos = {(f32)x, (f32)y, (f32)z};
|
||||
|
||||
u8 below = get_block_or_neighbor(chunk, neighbors, x, y - 1, z);
|
||||
if (below != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(below, registry)) {
|
||||
add_face_vertices(mesh, config, pos, 2, texture_side, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static const i32 dir_to_back_face[4] = {5, 4, 1, 0};
|
||||
i32 back_face = dir_to_back_face[direction];
|
||||
|
||||
i32 bx = x + face_dirs[back_face][0];
|
||||
i32 bz = z + face_dirs[back_face][2];
|
||||
u8 back_neighbor = get_block_or_neighbor(chunk, neighbors, bx, y, bz);
|
||||
if (back_neighbor != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(back_neighbor, registry)) {
|
||||
add_face_vertices(mesh, config, pos, back_face, texture_side, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static const f32 slope_verts[4][4][3] = {
|
||||
{{0,0,0}, {1,0,0}, {1,1,1}, {0,1,1}},
|
||||
{{0,0,1}, {1,0,1}, {1,1,0}, {0,1,0}},
|
||||
{{1,0,0}, {1,0,1}, {0,1,1}, {0,1,0}},
|
||||
{{0,0,0}, {0,0,1}, {1,1,1}, {1,1,0}}
|
||||
};
|
||||
|
||||
static const pxl8_vec3 slope_normals[4] = {
|
||||
{0, 0.707f, -0.707f}, {0, 0.707f, 0.707f},
|
||||
{-0.707f, 0.707f, 0}, {0.707f, 0.707f, 0}
|
||||
};
|
||||
|
||||
pxl8_vertex verts[4];
|
||||
memset(verts, 0, sizeof(verts));
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
verts[i].position.x = pos.x + slope_verts[direction][i][0];
|
||||
verts[i].position.y = pos.y + slope_verts[direction][i][1];
|
||||
verts[i].position.z = pos.z + slope_verts[direction][i][2];
|
||||
verts[i].normal = slope_normals[direction];
|
||||
verts[i].color = texture_top;
|
||||
verts[i].light = 255;
|
||||
}
|
||||
|
||||
u16 i0 = pxl8_mesh_push_vertex(mesh, verts[0]);
|
||||
u16 i1 = pxl8_mesh_push_vertex(mesh, verts[1]);
|
||||
u16 i2 = pxl8_mesh_push_vertex(mesh, verts[2]);
|
||||
u16 i3 = pxl8_mesh_push_vertex(mesh, verts[3]);
|
||||
pxl8_mesh_push_quad(mesh, i0, i1, i2, i3);
|
||||
|
||||
static const i32 side_faces[4][2] = {{0, 1}, {0, 1}, {4, 5}, {4, 5}};
|
||||
static const f32 side_tris[4][2][3][3] = {
|
||||
{{{0,0,0}, {0,1,1}, {0,0,1}}, {{1,0,0}, {1,0,1}, {1,1,1}}},
|
||||
{{{0,0,0}, {0,0,1}, {0,1,0}}, {{1,0,0}, {1,1,0}, {1,0,1}}},
|
||||
{{{0,0,0}, {0,0,1}, {0,1,1}}, {{1,0,0}, {1,1,0}, {1,0,1}}},
|
||||
{{{0,0,0}, {0,1,0}, {0,0,1}}, {{1,0,0}, {1,0,1}, {1,1,1}}}
|
||||
};
|
||||
static const pxl8_vec3 side_normals[6] = {
|
||||
{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}
|
||||
};
|
||||
|
||||
for (i32 s = 0; s < 2; s++) {
|
||||
i32 side_face = side_faces[direction][s];
|
||||
i32 snx = x + face_dirs[side_face][0];
|
||||
i32 snz = z + face_dirs[side_face][2];
|
||||
u8 side_neighbor = get_block_or_neighbor(chunk, neighbors, snx, y, snz);
|
||||
if (side_neighbor != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(side_neighbor, registry)) {
|
||||
pxl8_vertex tv[3];
|
||||
memset(tv, 0, sizeof(tv));
|
||||
for (i32 vi = 0; vi < 3; vi++) {
|
||||
tv[vi].position.x = pos.x + side_tris[direction][s][vi][0];
|
||||
tv[vi].position.y = pos.y + side_tris[direction][s][vi][1];
|
||||
tv[vi].position.z = pos.z + side_tris[direction][s][vi][2];
|
||||
tv[vi].normal = side_normals[side_face];
|
||||
tv[vi].color = texture_side;
|
||||
tv[vi].light = 255;
|
||||
}
|
||||
u16 ti0 = pxl8_mesh_push_vertex(mesh, tv[0]);
|
||||
u16 ti1 = pxl8_mesh_push_vertex(mesh, tv[1]);
|
||||
u16 ti2 = pxl8_mesh_push_vertex(mesh, tv[2]);
|
||||
pxl8_mesh_push_triangle(mesh, ti0, ti1, ti2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_mesh* pxl8_vxl_build_mesh(const pxl8_vxl_chunk* chunk,
|
||||
const pxl8_vxl_chunk** neighbors,
|
||||
const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config) {
|
||||
if (!chunk || !registry) return NULL;
|
||||
|
||||
pxl8_vxl_mesh_config cfg = config ? *config : PXL8_VXL_MESH_CONFIG_DEFAULT;
|
||||
|
||||
u32 max_faces = PXL8_VXL_CHUNK_VOLUME * 6;
|
||||
pxl8_mesh* mesh = pxl8_mesh_create(max_faces * 4, max_faces * 6);
|
||||
if (!mesh) return NULL;
|
||||
|
||||
for (i32 face = 0; face < 6; face++) {
|
||||
for (i32 slice = 0; slice < PXL8_VXL_CHUNK_SIZE; slice++) {
|
||||
build_greedy_slice(chunk, neighbors, registry, &cfg, face, slice, mesh);
|
||||
}
|
||||
}
|
||||
|
||||
for (i32 z = 0; z < PXL8_VXL_CHUNK_SIZE; z++) {
|
||||
for (i32 y = 0; y < PXL8_VXL_CHUNK_SIZE; y++) {
|
||||
for (i32 x = 0; x < PXL8_VXL_CHUNK_SIZE; x++) {
|
||||
u8 block = pxl8_vxl_block_get(chunk, x, y, z);
|
||||
if (block == PXL8_VXL_BLOCK_AIR) continue;
|
||||
|
||||
pxl8_vxl_geometry geo = pxl8_vxl_block_registry_geometry(registry, block);
|
||||
|
||||
switch (geo) {
|
||||
case PXL8_VXL_GEOMETRY_CUBE:
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_SLAB_BOTTOM:
|
||||
add_slab_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, false);
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_SLAB_TOP:
|
||||
add_slab_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, true);
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_SLOPE_NORTH:
|
||||
add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 0);
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_SLOPE_SOUTH:
|
||||
add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 1);
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_SLOPE_EAST:
|
||||
add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 2);
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_SLOPE_WEST:
|
||||
add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 3);
|
||||
break;
|
||||
case PXL8_VXL_GEOMETRY_STAIRS_NORTH:
|
||||
case PXL8_VXL_GEOMETRY_STAIRS_SOUTH:
|
||||
case PXL8_VXL_GEOMETRY_STAIRS_EAST:
|
||||
case PXL8_VXL_GEOMETRY_STAIRS_WEST:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
pxl8_vxl_render_state* pxl8_vxl_render_state_create(void) {
|
||||
return pxl8_calloc(1, sizeof(pxl8_vxl_render_state));
|
||||
}
|
||||
|
||||
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state) {
|
||||
pxl8_free(state);
|
||||
}
|
||||
|
||||
void pxl8_vxl_set_wireframe(pxl8_vxl_render_state* state, bool wireframe) {
|
||||
if (!state) return;
|
||||
state->wireframe = wireframe;
|
||||
}
|
||||
48
src/vxl/pxl8_vxl_render.h
Normal file
48
src/vxl/pxl8_vxl_render.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_vxl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pxl8_vxl_render_state {
|
||||
bool wireframe;
|
||||
} pxl8_vxl_render_state;
|
||||
|
||||
pxl8_vxl_render_state* pxl8_vxl_render_state_create(void);
|
||||
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state);
|
||||
void pxl8_vxl_set_wireframe(pxl8_vxl_render_state* state, bool wireframe);
|
||||
|
||||
typedef struct pxl8_vxl_mesh_config {
|
||||
bool ambient_occlusion;
|
||||
bool vertex_heights;
|
||||
f32 ao_strength;
|
||||
f32 texture_scale;
|
||||
i32 chunk_x;
|
||||
i32 chunk_y;
|
||||
i32 chunk_z;
|
||||
u64 seed;
|
||||
} pxl8_vxl_mesh_config;
|
||||
|
||||
#define PXL8_VXL_MESH_CONFIG_DEFAULT ((pxl8_vxl_mesh_config){ \
|
||||
.ambient_occlusion = true, \
|
||||
.vertex_heights = true, \
|
||||
.ao_strength = 0.2f, \
|
||||
.texture_scale = 1.0f, \
|
||||
.chunk_x = 0, \
|
||||
.chunk_y = 0, \
|
||||
.chunk_z = 0, \
|
||||
.seed = 12345 \
|
||||
})
|
||||
|
||||
pxl8_mesh* pxl8_vxl_build_mesh(const pxl8_vxl_chunk* chunk,
|
||||
const pxl8_vxl_chunk** neighbors,
|
||||
const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue