transfered from codeberg
This commit is contained in:
359
source_code/world_module/world/_internal/private_world.c
Normal file
359
source_code/world_module/world/_internal/private_world.c
Normal 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);
|
||||
}
|
||||
39
source_code/world_module/world/_internal/private_world.h
Normal file
39
source_code/world_module/world/_internal/private_world.h
Normal 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
|
||||
84
source_code/world_module/world/world.c
Normal file
84
source_code/world_module/world/world.c
Normal 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;
|
||||
}
|
||||
108
source_code/world_module/world/world.h
Normal file
108
source_code/world_module/world/world.h
Normal 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
|
||||
Reference in New Issue
Block a user