transfered from codeberg

This commit is contained in:
2026-03-26 14:46:39 -05:00
parent 630f28bb7e
commit 5ed2173793
136 changed files with 14932 additions and 0 deletions

View File

@@ -0,0 +1,359 @@
#include "private_world.h"
#include "player.h"
#include "chunk.h"
#include "chunk_scheduler.h"
#include "chunk_mesh_builder.h"
#include "hashmap.h"
#include "world.h"
#include "raylib.h"
#include <dynamic_list.h>
#include <pthread.h>
#include <raymath.h>
#include <string.h>
#include <stdio.h>
#include <stdatomic.h>
static void _static_find_new_chunks_to_load(struct world_struct* world,
struct player_struct* player);
static void _static_send_chunks_to_renderer(struct world_struct* world);
static void _static_schedule_chunks(struct world_struct* world);
static void _static_update_ready_to_load_list(struct world_struct* world);
static void _static_clean_hashmap_outside_render_distance(struct world_struct* world,
struct player_struct* player);
static void _static_find_new_chunks_to_load(struct world_struct* world,
struct player_struct* player)
{
for (int32_t x = 0; x < render_distance; x++)
{
for (int32_t y = 0; y < vertical_render_distance; y++)
{
for (int32_t z = 0; z < render_distance; z++)
{
chunk_position_struct new_chunk_key =
{
player->occupied_chunks_position.x +
(x * chunk_size),
player->occupied_chunks_position.y +
(y * chunk_size),
player->occupied_chunks_position.z +
(z * chunk_size),
};
struct worlds_chunk_hashmap_entry new_chunk_entry;
new_chunk_entry.chunk_position = &new_chunk_key;
pthread_mutex_lock(&world->mutex);
if (hashmap_get(world->active_chunks,
&new_chunk_entry) == NULL)
{
new_chunk_entry.chunk_pointer =
initialize_chunk(new_chunk_key);
if (new_chunk_entry.chunk_pointer != NULL)
{
hashmap_set(world->active_chunks,
&new_chunk_entry);
}
}
pthread_mutex_unlock(&world->mutex);
}
}
}
}
static void _static_update_ready_to_load_list(struct world_struct* world)
{
pthread_mutex_lock(&world->mutex);
size_t iterator = 0;
void *item;
while (hashmap_iter(world->active_chunks, &iterator, &item))
{
struct worlds_chunk_hashmap_entry *entry = item;
chunk_state_enum chunk_build_state =
(chunk_state_enum)
atomic_load_explicit(
&entry->chunk_pointer->build_state,
memory_order_acquire);
if (chunk_build_state > state_new)
{
continue;
}
if (world->count_chunks_awaiting_delete < maximum_possible_chunks)
{
world->chunks_awaiting_load
[world->count_chunks_awaiting_load++] =
entry->chunk_pointer;
}
else
{
TraceLog(LOG_ERROR, "chunks_awaiting_delete overflow");
}
}
pthread_mutex_unlock(&world->mutex);
}
static void _static_schedule_chunks(struct world_struct* world)
{
while (world->count_chunks_awaiting_load > 0)
{
chunk_struct* chunk = world->chunks_awaiting_load
[--world->count_chunks_awaiting_load];
atomic_fetch_add_explicit(&chunk->reference_counter,
1,
memory_order_acquire);
schedule_chunk_job(chunk);
}
}
static void _static_send_chunks_to_renderer(struct world_struct* world)
{
uint32_t awaiting_draw_index = atomic_load(&world->count_chunks_awaiting_draw);
while (awaiting_draw_index > 0)
{
upload_chunk_mesh_to_gpu(world->chunks_awaiting_load[awaiting_draw_index],
world->fog_shader,
world->texture_atlas);
awaiting_draw_index =
atomic_fetch_sub_explicit(&world->count_chunks_awaiting_draw,
1,
memory_order_acq_rel);
sem_post(&world->semaphore_free_slots_in_awaiting_draw);
}
}
static void _static_clean_hashmap_outside_render_distance(struct world_struct* world,
struct player_struct* player)
{
pthread_mutex_lock(&world->mutex);
size_t iterator = 0;
void* item;
while (hashmap_iter(world->active_chunks, &iterator, &item))
{
struct worlds_chunk_hashmap_entry* entry = item;
struct chunk_position_struct *position = entry->chunk_position;
if (position->x < player->negative_borders.x
|| position->x > player->positive_borders.x
|| position->y < player->negative_borders.y
|| position->y > player->positive_borders.y
|| position->z < player->negative_borders.z
|| position->z > player->positive_borders.z)
{
atomic_store_explicit(&entry->chunk_pointer->marked_for_unload,
true,
memory_order_release);
hashmap_delete(world->active_chunks, &entry->chunk_position);
world->chunks_awaiting_delete
[world->count_chunks_awaiting_delete++] =
entry->chunk_pointer;
}
}
pthread_mutex_unlock(&world->mutex);
}
world_struct* _initialize_world(struct world_struct* world,
world_struct_error_enum* error_code)
{
if (pthread_mutex_init(&world->mutex, NULL) != 0)
{
*error_code = mutex_failed_to_initialize;
}
world->texture_atlas = LoadTexture("resources/atlas.png");
world->active_chunks = hashmap_new(
sizeof(worlds_chunk_hashmap_entry),
1024,
0, 0,
_private_active_chunk_hashmap_key_hasher,
_private_chunk_discriminator,
NULL,
NULL
);
if (world->active_chunks == NULL)
{
pthread_mutex_destroy(&world->mutex);
*error_code = hashmap_failed_to_initialize;
}
if ( sem_init(&world->semaphore_free_slots_in_awaiting_draw,
0,
maximum_possible_chunks) == -1)
{
pthread_mutex_destroy(&world->mutex);
hashmap_free(world->active_chunks);
*error_code = semaphore_failed_to_initialize;
}
world->count_chunks_awaiting_delete = 0;
world->count_chunks_awaiting_draw = 0;
world->count_chunks_awaiting_load = 0;
memset(world->chunks_awaiting_delete,
0,
sizeof(world->chunks_awaiting_delete));
memset(world->chunks_awaiting_draw,
0,
sizeof(world->chunks_awaiting_draw));
memset(world->chunks_awaiting_load,
0,
sizeof(world->chunks_awaiting_load));
world->fog_density = 0.019f;
world->fog_start = 1.0f;
world->fog_shader = LoadShader("resources/shaders/fog.vs",
"resources/shaders/fog.fs");
world->fog_density_location = GetShaderLocation(world->fog_shader,
"fogDensity");
world->fog_start_location = GetShaderLocation(world->fog_shader,
"fogStart");
Matrix identity = MatrixIdentity();
SetShaderValueMatrix(world->fog_shader,
GetShaderLocation(world->fog_shader,
"matModel"),
identity);
world->fog_shader.locs[SHADER_LOC_VECTOR_VIEW] =
GetShaderLocation(world->fog_shader,
"viewPos");
return world;
}
void _free_world(struct world_struct* world)
{
UnloadShader(world->fog_shader);
stop_chunk_scheduler();
if (world->active_chunks != NULL)
{
hashmap_free(world->active_chunks);
}
sem_destroy(&world->semaphore_free_slots_in_awaiting_draw);
UnloadTexture(world->texture_atlas);
pthread_mutex_destroy(&world->mutex);
}
uint64_t _private_active_chunk_hashmap_key_hasher(const void *item,
uint64_t seed_zero,
uint64_t seed_one)
{
const worlds_chunk_hashmap_entry *entry = item;
return hashmap_sip(&entry->chunk_position,
sizeof(chunk_position_struct),
seed_zero, seed_one);
}
void _private_update_shader_values(struct world_struct* world,
struct player_struct* player)
{
const float fog_limit = chunk_size * render_distance;
float player_y = player->camera.position.y;
float altitude_factor = player_y / 200.0f;
if (altitude_factor < 0.0f)
{
altitude_factor = 0.0f;
}
if (altitude_factor > 1.0f)
{
altitude_factor = 1.0f;
}
const float maximum_fog_thickness = fog_limit;
const float minimum_fog_thickness = 48.0f;
float fogThickness = maximum_fog_thickness +
(minimum_fog_thickness - maximum_fog_thickness) *
altitude_factor;
world->fog_start = fog_limit - fogThickness;
if (world->fog_start < 0.0f)
{
world->fog_start = 0.0f;
}
const float total_fog_mass = 2.146f;
world->fog_density = total_fog_mass / fogThickness;
SetShaderValue(world->fog_shader,
world->fog_density_location,
&world->fog_density,
SHADER_UNIFORM_FLOAT);
SetShaderValue(world->fog_shader,
world->fog_start_location,
&world->fog_start,
SHADER_UNIFORM_FLOAT);
SetShaderValue(world->fog_shader,
world->fog_shader.locs[SHADER_LOC_VECTOR_VIEW],
&player->camera.position,
SHADER_UNIFORM_VEC3);
}
int _private_chunk_discriminator(const void *element_a,
const void *element_b,
void *user_data)
{
(void)user_data;
const worlds_chunk_hashmap_entry *entry_a = element_a;
const worlds_chunk_hashmap_entry *entry_b = element_b;
if (entry_a->chunk_position->x == entry_b->chunk_position->x
&& entry_a->chunk_position->y == entry_b->chunk_position->y
&& entry_a->chunk_position->z == entry_b->chunk_position->z)
{
return 0;
}
return 1;
}
void _private_update_which_chunks_are_active(struct world_struct* world,
struct player_struct* player)
{
if (!player->has_entered_new_chunk)
{
return;
}
else
{
player->has_entered_new_chunk = false;
}
_static_find_new_chunks_to_load(world, player);
_static_update_ready_to_load_list(world);
_static_schedule_chunks(world);
_static_send_chunks_to_renderer(world);
_static_clean_hashmap_outside_render_distance(world, player);
}

View File

@@ -0,0 +1,39 @@
#ifndef private_world_h
#define private_world_h
#include <stdint.h>
struct chunk_position_struct;
struct player_struct;
struct world_struct;
typedef enum world_struct_error_enum
{
okay = 0,
mutex_failed_to_initialize,
hashmap_failed_to_initialize,
semaphore_failed_to_initialize
} world_struct_error_enum;
struct world_struct* _initialize_world(struct world_struct* world,
world_struct_error_enum* error_code);
void _free_world(struct world_struct* world);
uint64_t _private_active_chunk_hashmap_key_hasher(const void *item,
uint64_t seed_zero,
uint64_t seed_one);
void _private_update_shader_values(struct world_struct* world,
struct player_struct* player);
int _private_chunk_discriminator(const void *element_a,
const void *element_b,
void *user_data);
void _private_update_which_chunks_are_active(struct world_struct* world,
struct player_struct* player);
#endif

View File

@@ -0,0 +1,84 @@
#include "world.h"
#include "_internal/private_world.h"
#include "chunk.h"
#include "chunk_mesh_builder.h"
#include "chunk_scheduler.h"
#include "player.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
uint8_t create_world(struct world_struct** world)
{
*world = (world_struct *) calloc(1, sizeof(world_struct));
if(*world == NULL)
{
return EXIT_FAILURE;
}
world_struct_error_enum error_code = okay;
_initialize_world(*world, &error_code);
switch (error_code)
{
case mutex_failed_to_initialize:
fprintf(stderr, "Error: Failed to initialize world->mutex\n");
assert(false);
break;
case hashmap_failed_to_initialize:
fprintf(stderr, "Error: Failed to initialize world->hashmap\n");
assert(false);
break;
case semaphore_failed_to_initialize:
fprintf(stderr, "Error: Failed to initialize world->semaphore\n");
assert(false);
break;
default:
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
void destroy_world(struct world_struct **world)
{
if (world == NULL)
{
return;
}
_free_world(*world);
}
void update_world(struct world_struct* world,
struct player_struct* player)
{
_private_update_which_chunks_are_active(world, player);
}
void destroy_inactive_chunks(struct world_struct* world)
{
if (world == NULL)
{
return;
}
for (uint32_t i = 0; i < world->count_chunks_awaiting_delete; i++)
{
if (world->chunks_awaiting_delete[i] != NULL)
{
if(world->chunks_awaiting_delete[i]->reference_counter != 0)
{
continue;
}
destroy_chunk(world->chunks_awaiting_delete[i]);
world->chunks_awaiting_delete[i] = NULL;
}
}
world->count_chunks_awaiting_delete = 0;
}

View File

@@ -0,0 +1,108 @@
#ifndef world_h
#define world_h
#include "config.h"
#include "raylib.h"
#include <stdint.h>
#include <semaphore.h>
#include <pthread.h>
struct chunk_position_struct;
struct chunk;
struct player_struct;
struct chunk_struct;
struct dynamic_list_struct;
struct hashmap;
#define maximum_possible_chunks render_distance*vertical_render_distance*render_distance
/**
* @brief Hashmap entry structure for world chunks
*
* Associates a chunk position with its corresponding chunk data.
* Used as the key-value pair in the active chunks hashmap.
*/
typedef struct worlds_chunk_hashmap_entry
{
struct chunk_position_struct* chunk_position;
struct chunk_struct* chunk_pointer;
} worlds_chunk_hashmap_entry;
/**
* @brief World structure
*
* Main world state management structure.
* Contains all data related to world generation, rendering, and chunk management.
*/
typedef struct world_struct
{
float fog_density;
Shader fog_shader;
int fog_density_location;
int fog_start_location;
float fog_start;
struct hashmap* active_chunks;
pthread_mutex_t mutex;
_Atomic uint32_t count_chunks_awaiting_draw;
uint32_t count_chunks_awaiting_load;
uint32_t count_chunks_awaiting_delete;
sem_t semaphore_free_slots_in_awaiting_draw;
struct chunk_struct* chunks_awaiting_draw[maximum_possible_chunks];
struct chunk_struct* chunks_awaiting_load[maximum_possible_chunks];
struct chunk_struct* chunks_awaiting_delete[maximum_possible_chunks];
Texture2D texture_atlas;
} world_struct;
/**
* @brief Creates a new world instance
*
* Allocates memory for a new world structure and initializes all subsystems
* including mutexes, hashmaps, and semaphores.
*
* @param world Double pointer to world structure to be created
* @return 0 on success, non-zero on failure
* @retval 0 Successfully created world
* @retval non-zero Failed to create world (memory allocation or initialization error)
*/
uint8_t create_world(struct world_struct** world);
/**
* @brief Destroys a world instance
*
* Frees all memory associated with the world and cleans up all subsystems.
*
* @param world Double pointer to world structure to destroy
* @pre world must not be NULL
* @post world is set to NULL
*/
void destroy_world(struct world_struct** world);
/**
* @brief Updates world state
*
* Performs all world updates including chunk management, rendering preparation,
* and chunk scheduling for generation.
*
* @param world Pointer to world structure
* @param player Pointer to player structure
* @pre world must not be NULL
* @pre player must not be NULL
*/
void update_world(struct world_struct* world,
struct player_struct* player);
/**
* @brief Destroys inactive chunks
*
* Cleans up chunks that are no longer needed and have been marked for deletion.
* Checks reference counters to ensure safe destruction.
*
* @param world Pointer to world structure
* @pre world must not be NULL
*/
void destroy_inactive_chunks(struct world_struct* world);
#endif