Added handle system and improve resource management.
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
#include "Rendering/Debug.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
|
||||
static void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t node_index, uint32_t* count_out)
|
||||
static void ray_intersect_tlas_count(ray_t ray, const tlas_tree_t* tlas_tree, uint32_t node_index, uint32_t* count_out)
|
||||
{
|
||||
const float _MAX_DIST = 1e6f;
|
||||
if (bvh_tree.nodes == NULL || bvh_tree.primitive_indices == NULL || count_out == NULL)
|
||||
if (tlas_tree->nodes == NULL || tlas_tree->primitive_indices == NULL || count_out == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bvh_node_t* node = &bvh_tree.nodes[node_index];
|
||||
const bvh_node_t* node = &tlas_tree->nodes[node_index];
|
||||
|
||||
float enter, exit;
|
||||
if (!ray_intersect_aabb(&ray, node->bounds, &enter, &exit))
|
||||
@@ -26,8 +26,8 @@ static void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t nod
|
||||
if (node->primitive_count == 0)
|
||||
{
|
||||
// Internal node
|
||||
ray_intersect_bvh_count(ray, bvh_tree, node->left_child_offset, count_out);
|
||||
ray_intersect_bvh_count(ray, bvh_tree, node->right_child_offset, count_out);
|
||||
ray_intersect_tlas_count(ray, tlas_tree, node->left_child_offset, count_out);
|
||||
ray_intersect_tlas_count(ray, tlas_tree, node->right_child_offset, count_out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ vec4s render_debug(scene_t* scene, ray_t ray, uint16_t sample_index, int flag)
|
||||
{
|
||||
case DEBUG_BVH:
|
||||
uint32_t count = 0;
|
||||
ray_intersect_bvh_count(ray, scene->bvh_tree, 0, &count);
|
||||
ray_intersect_tlas_count(ray, &scene->tlas, 0, &count);
|
||||
|
||||
vec4s result = glms_vec4_zero();
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
|
||||
@@ -20,7 +20,7 @@ static inline void create_target_if_required(aov_flags_t aov_flags, aov_flags_t
|
||||
*render_target = temp;
|
||||
}
|
||||
|
||||
bool renderer_aov_target_init(render_job_t* job, aov_flags_t aov_flags)
|
||||
bool renderer_aov_target_init(render_job_t* job)
|
||||
{
|
||||
job->aov_target = (render_target_t**)malloc(sizeof(render_target_t*) * MAX_AOV_TARGET);
|
||||
if (job->aov_target == NULL)
|
||||
@@ -28,14 +28,14 @@ bool renderer_aov_target_init(render_job_t* job, aov_flags_t aov_flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
create_target_if_required(aov_flags, AOV_BEAUTY, &job->aov_target[AOV_BEAUTY_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(aov_flags, AOV_AlBEDO, &job->aov_target[AOV_AlBEDO_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(aov_flags, AOV_NORMAL, &job->aov_target[AOV_NORMAL_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(aov_flags, AOV_DEPTH, &job->aov_target[AOV_DEPTH_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(aov_flags, AOV_POSITION, &job->aov_target[AOV_POSITION_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_BEAUTY, &job->aov_target[AOV_BEAUTY_INDEX], job->config.width, job->config.height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_AlBEDO, &job->aov_target[AOV_AlBEDO_INDEX], job->config.width, job->config.height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_NORMAL, &job->aov_target[AOV_NORMAL_INDEX], job->config.width, job->config.height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_DEPTH, &job->aov_target[AOV_DEPTH_INDEX], job->config.width, job->config.height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_POSITION, &job->aov_target[AOV_POSITION_INDEX], job->config.width, job->config.height);
|
||||
|
||||
create_target_if_required(aov_flags, AOV_DIRECT, &job->aov_target[AOV_DIRECT_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(aov_flags, AOV_INDIRECT, &job->aov_target[AOV_INDIRECT_INDEX], job->config->width, job->config->height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_DIRECT, &job->aov_target[AOV_DIRECT_INDEX], job->config.width, job->config.height);
|
||||
create_target_if_required(job->config.aov_flags, AOV_INDIRECT, &job->aov_target[AOV_INDIRECT_INDEX], job->config.width, job->config.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -67,17 +67,34 @@ static inline bool aov_needs_lighting_samples(aov_flags_t flags)
|
||||
return has_flag(flags, AOV_BEAUTY) || has_flag(flags, AOV_DIRECT) || has_flag(flags, AOV_INDIRECT);
|
||||
}
|
||||
|
||||
static inline uint32_t get_minimal_sample_count(render_job_t* job)
|
||||
{
|
||||
if (aov_needs_lighting_samples(job->config.aov_flags))
|
||||
{
|
||||
if (job->config.aov_flags == AOV_DIRECT)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return job->config.sample_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline vec4s running_average_vec4(vec4s prev_avg, vec4s sample, uint32_t prev_count)
|
||||
{
|
||||
float n = (float)prev_count;
|
||||
float inv = 1.0f / (n + 1.0f);
|
||||
|
||||
vec4s out;
|
||||
out.x = (prev_avg.x * n + sample.x) * inv;
|
||||
out.y = (prev_avg.y * n + sample.y) * inv;
|
||||
out.z = (prev_avg.z * n + sample.z) * inv;
|
||||
out.w = 1.0f;
|
||||
return out;
|
||||
return (vec4s){
|
||||
.x = (prev_avg.x * n + sample.x) * inv,
|
||||
.y = (prev_avg.y * n + sample.y) * inv,
|
||||
.z = (prev_avg.z * n + sample.z) * inv,
|
||||
.w = 1.0f
|
||||
};
|
||||
}
|
||||
|
||||
static inline void clear_render_target(render_target_t* target)
|
||||
@@ -108,7 +125,13 @@ static inline void clear_aov_targets(render_job_t* job)
|
||||
}
|
||||
}
|
||||
|
||||
static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s coord, uint32_t x, uint32_t y, aov_flags_t aov_flags, aov_output_t* pixel_output)
|
||||
static void render_pixel(const rendering_config_t* config,
|
||||
const scene_t* scene,
|
||||
vec3s coord,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
aov_flags_t aov_flags,
|
||||
aov_output_t* pixel_output)
|
||||
{
|
||||
aov_output_t accumulated_color = {0};
|
||||
|
||||
@@ -155,12 +178,11 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
|
||||
}
|
||||
|
||||
static void render_pixel_one_sample(const rendering_config_t* config,
|
||||
scene_t* scene,
|
||||
const scene_t* scene,
|
||||
vec3s coord,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
uint32_t sample_index,
|
||||
aov_flags_t aov_flags,
|
||||
aov_output_t* pixel_output)
|
||||
{
|
||||
uint32_t pixel_id = y * config->width + x;
|
||||
@@ -192,7 +214,7 @@ static void render_pixel_one_sample(const rendering_config_t* config,
|
||||
spread_angle);
|
||||
|
||||
aov_output_t out = {0};
|
||||
path_trace_aov(scene, ray, sobol_idx, config->max_depth, aov_flags, &out);
|
||||
path_trace_aov(scene, ray, sobol_idx, config->max_depth, config->aov_flags, &out);
|
||||
*pixel_output = out;
|
||||
}
|
||||
|
||||
@@ -220,23 +242,24 @@ static inline void update_aov(render_target_t** target, const aov_output_t* aov,
|
||||
|
||||
void renderer_start(render_job_t* job)
|
||||
{
|
||||
ensure_camera_aspect_ratio(&job->scene->camera, job->config);
|
||||
ensure_camera_aspect_ratio(&job->scene->camera, &job->config);
|
||||
job->config.sample_count = get_minimal_sample_count(job);
|
||||
|
||||
// Reset progressive state whenever we (re)start.
|
||||
job->progressive_sample_index = 0;
|
||||
|
||||
vec3s coord = glms_vec3_add(job->scene->camera.position, glms_vec3_scale(quat_get_forward(job->scene->camera.rotation), job->scene->camera.focal_length));
|
||||
|
||||
if (job->rendering_mode == RENDER_PROGRESSIVE)
|
||||
if (job->config.rendering_mode == RENDER_PROGRESSIVE)
|
||||
{
|
||||
// Progressive mode: accumulate 1 spp per pass until sample_count or stop requested.
|
||||
clear_aov_targets(job);
|
||||
job->is_done = false;
|
||||
|
||||
uint32_t width = job->config->width;
|
||||
uint32_t height = job->config->height;
|
||||
uint32_t width = job->config.width;
|
||||
uint32_t height = job->config.height;
|
||||
|
||||
for (uint32_t s = 0; s < job->config->sample_count; ++s)
|
||||
for (uint32_t s = 0; s < job->config.sample_count; ++s)
|
||||
{
|
||||
if (job->is_done)
|
||||
{
|
||||
@@ -257,10 +280,10 @@ void renderer_start(render_job_t* job)
|
||||
}
|
||||
|
||||
aov_output_t pixel = {0};
|
||||
render_pixel_one_sample(job->config, job->scene, coord, (uint32_t)x, (uint32_t)y, s, job->aov_flags, &pixel);
|
||||
render_pixel_one_sample(&job->config, job->scene, coord, (uint32_t)x, (uint32_t)y, s, &pixel);
|
||||
|
||||
// Accumulate lighting AOVs; write non-stochastic AOVs once.
|
||||
if (has_flag(job->aov_flags, AOV_BEAUTY))
|
||||
if (has_flag(job->config.aov_flags, AOV_BEAUTY))
|
||||
{
|
||||
vec4s prev = render_target_get_pixel(job->aov_target[AOV_BEAUTY_INDEX], (uint32_t)x, (uint32_t)y);
|
||||
vec4s avg = running_average_vec4(prev, pixel.beauty, s);
|
||||
@@ -275,14 +298,14 @@ void renderer_start(render_job_t* job)
|
||||
update_aov_pixel_if_exist(&job->aov_target[AOV_POSITION_INDEX], pixel.position, (uint32_t)x, (uint32_t)y);
|
||||
}
|
||||
|
||||
if (has_flag(job->aov_flags, AOV_DIRECT))
|
||||
if (has_flag(job->config.aov_flags, AOV_DIRECT))
|
||||
{
|
||||
vec4s prev = render_target_get_pixel(job->aov_target[AOV_DIRECT_INDEX], (uint32_t)x, (uint32_t)y);
|
||||
vec4s avg = running_average_vec4(prev, pixel.direct, s);
|
||||
render_target_set_pixel(job->aov_target[AOV_DIRECT_INDEX], (uint32_t)x, (uint32_t)y, avg);
|
||||
}
|
||||
|
||||
if (has_flag(job->aov_flags, AOV_INDIRECT))
|
||||
if (has_flag(job->config.aov_flags, AOV_INDIRECT))
|
||||
{
|
||||
vec4s prev = render_target_get_pixel(job->aov_target[AOV_INDIRECT_INDEX], (uint32_t)x, (uint32_t)y);
|
||||
vec4s avg = running_average_vec4(prev, pixel.indirect, s);
|
||||
@@ -299,20 +322,20 @@ void renderer_start(render_job_t* job)
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t tile_count_x = (job->config->width + job->config->bucket_size - 1) / job->config->bucket_size;
|
||||
uint32_t tile_count_y = (job->config->height + job->config->bucket_size - 1) / job->config->bucket_size;
|
||||
uint32_t tile_count_x = (job->config.width + job->config.bucket_size - 1) / job->config.bucket_size;
|
||||
uint32_t tile_count_y = (job->config.height + job->config.bucket_size - 1) / job->config.bucket_size;
|
||||
uint32_t tile_count = tile_count_x * tile_count_y;
|
||||
|
||||
int64_t x, y, tile_index; // OpenMP requires these to be declared outside the parallel region.
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_count_x, tile_count_y, tile_count, coord, job) \
|
||||
private(x, y, tile_index)
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_count_x, tile_count_y, tile_count, coord, job) \
|
||||
private(x, y, tile_index)
|
||||
for (tile_index = 0; tile_index < tile_count; tile_index++)
|
||||
{
|
||||
uint32_t tile_x_0 = (uint32_t)tile_index % tile_count_x * job->config->bucket_size;
|
||||
uint32_t tile_y_0 = (uint32_t)tile_index / tile_count_x * job->config->bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + job->config->bucket_size, job->config->width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + job->config->bucket_size, job->config->height);
|
||||
uint32_t tile_x_0 = (uint32_t)tile_index % tile_count_x * job->config.bucket_size;
|
||||
uint32_t tile_y_0 = (uint32_t)tile_index / tile_count_x * job->config.bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + job->config.bucket_size, job->config.width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + job->config.bucket_size, job->config.height);
|
||||
|
||||
for (y = tile_y_0; y < tile_y_1; y++)
|
||||
{
|
||||
@@ -324,7 +347,7 @@ void renderer_start(render_job_t* job)
|
||||
}
|
||||
|
||||
aov_output_t pixel_output = {0};
|
||||
render_pixel(job->config, job->scene, coord, (uint32_t)x, (uint32_t)y, job->aov_flags, &pixel_output);
|
||||
render_pixel(&job->config, job->scene, coord, (uint32_t)x, (uint32_t)y, job->config.aov_flags, &pixel_output);
|
||||
update_aov(job->aov_target, &pixel_output, (uint32_t)x, (uint32_t)y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,56 +3,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static inline void mesh_model_collection_init(mesh_model_collection_t* models, uint32_t capacity)
|
||||
{
|
||||
models->count = capacity;
|
||||
models->capacity = capacity;
|
||||
models->buffer = (mesh_model_t*)calloc(capacity, sizeof(mesh_model_t));
|
||||
}
|
||||
|
||||
static inline void mesh_instance_collection_init(mesh_instance_collection_t* instances, uint32_t capacity)
|
||||
{
|
||||
instances->count = capacity;
|
||||
instances->capacity = capacity;
|
||||
instances->buffer = (mesh_instance_t*)calloc(capacity, sizeof(mesh_instance_t));
|
||||
}
|
||||
|
||||
static inline void mesh_model_collection_free(mesh_model_collection_t* models)
|
||||
{
|
||||
if (models == NULL || models->buffer == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < models->capacity; ++i)
|
||||
{
|
||||
mesh_model_t* m = &models->buffer[i];
|
||||
if (m->active)
|
||||
{
|
||||
bvh_tree_free(&m->blas);
|
||||
triangle_collection_free(&m->triangles);
|
||||
}
|
||||
}
|
||||
|
||||
free(models->buffer);
|
||||
models->buffer = NULL;
|
||||
models->count = 0;
|
||||
models->capacity = 0;
|
||||
}
|
||||
|
||||
static inline void mesh_instance_collection_free(mesh_instance_collection_t* instances)
|
||||
{
|
||||
if (instances == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
free(instances->buffer);
|
||||
instances->buffer = NULL;
|
||||
instances->count = 0;
|
||||
instances->capacity = 0;
|
||||
}
|
||||
|
||||
static inline vec3s mat4_mul_point(mat4s m, vec3s p)
|
||||
{
|
||||
return glms_mat4_mulv3(m, p, 1.0f);
|
||||
@@ -101,7 +51,7 @@ static bool scene_rebuild_tlas(scene_t* scene)
|
||||
uint64_t active_count = 0;
|
||||
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
||||
{
|
||||
if (scene->mesh_instances.buffer[i].active)
|
||||
if (scene->mesh_instances.occupied[i])
|
||||
{
|
||||
active_count++;
|
||||
}
|
||||
@@ -123,7 +73,7 @@ static bool scene_rebuild_tlas(scene_t* scene)
|
||||
uint64_t cursor = 0;
|
||||
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
||||
{
|
||||
if (scene->mesh_instances.buffer[i].active)
|
||||
if (scene->mesh_instances.occupied[i])
|
||||
{
|
||||
indices[cursor++] = i;
|
||||
}
|
||||
@@ -165,26 +115,21 @@ static bool scene_rebuild_tlas(scene_t* scene)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool scene_init(scene_t* scene, uint64_t triangle_count, uint16_t texture_count, uint8_t material_count, uint32_t punctual_light_count)
|
||||
bool scene_init(scene_t* scene, uint32_t triangle_count, uint32_t texture_count, uint32_t material_count, uint32_t punctual_light_count)
|
||||
{
|
||||
scene_t temp = {0};
|
||||
|
||||
if (!triangle_collection_init(triangle_count, &temp.triangles))
|
||||
{
|
||||
goto triangle_failed;
|
||||
}
|
||||
|
||||
if (!texture_collection_init(texture_count, &temp.textures))
|
||||
if (texture_slot_map_init(&temp.textures, texture_count) != RESULT_SUCCESS)
|
||||
{
|
||||
goto texture_failed;
|
||||
}
|
||||
|
||||
if (!material_collection_init(material_count, &temp.materials))
|
||||
if (material_slot_map_init(&temp.materials, material_count) != RESULT_SUCCESS)
|
||||
{
|
||||
goto material_failed;
|
||||
}
|
||||
|
||||
if (!light_collection_create(punctual_light_count, 16, &temp.lights)) // NOTE: We just fixed the max directional light count to 16.
|
||||
if (light_collection_create( &temp.lights, punctual_light_count, 16) != RESULT_SUCCESS) // NOTE: We just fixed the max directional light count to 16.
|
||||
{
|
||||
goto light_failed;
|
||||
}
|
||||
@@ -199,20 +144,20 @@ bool scene_init(scene_t* scene, uint64_t triangle_count, uint16_t texture_count,
|
||||
|
||||
// New mesh system: start with small default capacities (simple first).
|
||||
(void)triangle_count;
|
||||
mesh_model_collection_init(&temp.mesh_models, 64);
|
||||
mesh_instance_collection_init(&temp.mesh_instances, 128);
|
||||
mesh_model_slot_map_init(&temp.mesh_models, 64);
|
||||
memset(temp.mesh_models.buffer, 0, sizeof(mesh_model_t) * temp.mesh_models.capacity);
|
||||
|
||||
mesh_instance_slot_map_init(&temp.mesh_instances, 128);
|
||||
temp.tlas_dirty = true;
|
||||
|
||||
*scene = temp;
|
||||
return true;
|
||||
|
||||
light_failed:
|
||||
material_collection_free(&temp.materials);
|
||||
material_slot_map_free(&temp.materials);
|
||||
material_failed:
|
||||
texture_collection_free(&temp.textures);
|
||||
texture_slot_map_free(&temp.textures);
|
||||
texture_failed:
|
||||
triangle_collection_free(&temp.triangles);
|
||||
triangle_failed:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -223,7 +168,6 @@ bool scene_build_bvh(scene_t* scene)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer TLAS if any mesh instances exist.
|
||||
if (scene->tlas_dirty)
|
||||
{
|
||||
if (!scene_rebuild_tlas(scene))
|
||||
@@ -232,21 +176,6 @@ bool scene_build_bvh(scene_t* scene)
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy BVH build if triangles are present.
|
||||
if (scene->triangles.count > 0)
|
||||
{
|
||||
bvh_tree_free(&scene->bvh_tree);
|
||||
|
||||
bvh_tree_t bvh_tree = {0};
|
||||
if (!bvh_tree_init(&bvh_tree, &scene->triangles))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bvh_tree_build(&bvh_tree);
|
||||
scene->bvh_tree = bvh_tree;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -257,148 +186,138 @@ void scene_free(scene_t* scene)
|
||||
return;
|
||||
}
|
||||
|
||||
bvh_tree_free(&scene->bvh_tree);
|
||||
triangle_collection_free(&scene->triangles);
|
||||
|
||||
// Mesh system
|
||||
tlas_tree_free(&scene->tlas);
|
||||
free((void*)scene->tlas.instance_bounds);
|
||||
scene->tlas.instance_bounds = NULL;
|
||||
mesh_instance_collection_free(&scene->mesh_instances);
|
||||
mesh_model_collection_free(&scene->mesh_models);
|
||||
texture_collection_free(&scene->textures);
|
||||
material_collection_free(&scene->materials);
|
||||
|
||||
for (size_t i = 0; i < scene->mesh_models.capacity; ++i)
|
||||
{
|
||||
if (scene->mesh_instances.occupied[i])
|
||||
{
|
||||
mesh_model_t* m = &scene->mesh_models.buffer[i];
|
||||
bvh_tree_free(&m->blas);
|
||||
triangle_list_free(&m->triangles);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < scene->textures.capacity; i++)
|
||||
{
|
||||
if (scene->textures.occupied[i] != 0)
|
||||
{
|
||||
texture_asset_t* asset = &scene->textures.buffer[i];
|
||||
texture_free(&asset->texture);
|
||||
|
||||
if (asset->full_name != NULL)
|
||||
{
|
||||
free(asset->full_name);
|
||||
asset->full_name = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < scene->materials.capacity; i++)
|
||||
{
|
||||
if (scene->materials.occupied[i] != 0)
|
||||
{
|
||||
material_t* material = &scene->materials.buffer[i];
|
||||
material_free(material);
|
||||
}
|
||||
}
|
||||
|
||||
mesh_instance_slot_map_free(&scene->mesh_instances);
|
||||
mesh_model_slot_map_free(&scene->mesh_models);
|
||||
texture_slot_map_free(&scene->textures);
|
||||
material_slot_map_free(&scene->materials);
|
||||
light_collection_free(&scene->lights);
|
||||
}
|
||||
|
||||
static uint32_t find_free_mesh_model_slot(scene_t* scene)
|
||||
{
|
||||
for (uint32_t i = 0; i < scene->mesh_models.capacity; ++i)
|
||||
{
|
||||
if (!scene->mesh_models.buffer[i].active)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t old_capacity = scene->mesh_models.capacity;
|
||||
uint32_t new_capacity = old_capacity == 0 ? 64 : old_capacity * 2;
|
||||
mesh_model_t* resized = (mesh_model_t*)realloc(scene->mesh_models.buffer, sizeof(mesh_model_t) * new_capacity);
|
||||
if (resized == NULL)
|
||||
{
|
||||
return UINT32_MAX;
|
||||
}
|
||||
memset(resized + old_capacity, 0, sizeof(mesh_model_t) * (new_capacity - old_capacity));
|
||||
scene->mesh_models.buffer = resized;
|
||||
scene->mesh_models.capacity = new_capacity;
|
||||
scene->mesh_models.count = new_capacity;
|
||||
|
||||
return old_capacity;
|
||||
}
|
||||
|
||||
static uint32_t find_free_mesh_instance_slot(scene_t* scene)
|
||||
{
|
||||
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
||||
{
|
||||
if (!scene->mesh_instances.buffer[i].active)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t old_capacity = scene->mesh_instances.capacity;
|
||||
uint32_t new_capacity = old_capacity == 0 ? 128 : old_capacity * 2;
|
||||
mesh_instance_t* resized = (mesh_instance_t*)realloc(scene->mesh_instances.buffer, sizeof(mesh_instance_t) * new_capacity);
|
||||
if (resized == NULL)
|
||||
{
|
||||
return UINT32_MAX;
|
||||
}
|
||||
memset(resized + old_capacity, 0, sizeof(mesh_instance_t) * (new_capacity - old_capacity));
|
||||
scene->mesh_instances.buffer = resized;
|
||||
scene->mesh_instances.capacity = new_capacity;
|
||||
scene->mesh_instances.count = new_capacity;
|
||||
|
||||
return old_capacity;
|
||||
}
|
||||
|
||||
uint32_t scene_add_mesh_model(scene_t* scene, uint64_t triangle_reserve)
|
||||
mesh_model_handle_t scene_add_mesh_model(scene_t* scene, uint64_t triangle_reserve)
|
||||
{
|
||||
if (scene == NULL)
|
||||
{
|
||||
return UINT32_MAX;
|
||||
return INVALID_HANDLE(mesh_model_handle_t);
|
||||
}
|
||||
|
||||
uint32_t slot = find_free_mesh_model_slot(scene);
|
||||
if (slot == UINT32_MAX)
|
||||
mesh_model_t model =
|
||||
{
|
||||
return UINT32_MAX;
|
||||
}
|
||||
.local_bounds = invalid_aabb()
|
||||
};
|
||||
|
||||
mesh_model_t* model = &scene->mesh_models.buffer[slot];
|
||||
*model = (mesh_model_t){0};
|
||||
model->active = true;
|
||||
model->local_bounds = invalid_aabb();
|
||||
|
||||
if (!triangle_collection_init((size_t)(triangle_reserve > 0 ? triangle_reserve : 1), &model->triangles))
|
||||
if (triangle_list_init(&model.triangles, triangle_reserve) != RESULT_SUCCESS)
|
||||
{
|
||||
model->active = false;
|
||||
return UINT32_MAX;
|
||||
return INVALID_HANDLE(mesh_model_handle_t);
|
||||
}
|
||||
|
||||
return slot;
|
||||
mesh_model_handle_t handle;
|
||||
if (mesh_model_slot_map_add(&scene->mesh_models, model, &handle.id, &handle.generation) != RESULT_SUCCESS)
|
||||
{
|
||||
return INVALID_HANDLE(mesh_model_handle_t);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
uint32_t scene_add_mesh_instance(scene_t* scene, uint32_t model_id, mat4s local_to_world)
|
||||
mesh_instance_handle_t scene_add_mesh_instance(scene_t* scene, mesh_model_handle_t model, mat4s local_to_world)
|
||||
{
|
||||
if (scene == NULL || model_id >= scene->mesh_models.capacity || !scene->mesh_models.buffer[model_id].active)
|
||||
if (scene == NULL)
|
||||
{
|
||||
return UINT32_MAX;
|
||||
return INVALID_HANDLE(mesh_instance_handle_t);
|
||||
}
|
||||
|
||||
uint32_t slot = find_free_mesh_instance_slot(scene);
|
||||
if (slot == UINT32_MAX)
|
||||
mesh_model_t* mesh_model = mesh_model_slot_map_get(&scene->mesh_models, model.id, model.generation);
|
||||
if (mesh_model == NULL)
|
||||
{
|
||||
return UINT32_MAX;
|
||||
return INVALID_HANDLE(mesh_instance_handle_t);
|
||||
}
|
||||
|
||||
mesh_instance_t* inst = &scene->mesh_instances.buffer[slot];
|
||||
*inst = (mesh_instance_t){0};
|
||||
inst->active = true;
|
||||
inst->model_id = model_id;
|
||||
inst->local_to_world = local_to_world;
|
||||
inst->world_to_local = glms_mat4_inv(local_to_world);
|
||||
inst->normal_matrix = compute_normal_matrix(local_to_world);
|
||||
inst->world_bounds = aabb_transform(local_to_world, scene->mesh_models.buffer[model_id].local_bounds);
|
||||
mesh_instance_t instance =
|
||||
{
|
||||
.model = model,
|
||||
.local_to_world = local_to_world,
|
||||
.world_to_local = glms_mat4_inv(local_to_world),
|
||||
.normal_matrix = compute_normal_matrix(local_to_world),
|
||||
.world_bounds = aabb_transform(local_to_world, mesh_model->local_bounds)
|
||||
};
|
||||
|
||||
mesh_instance_handle_t handle;
|
||||
if (mesh_instance_slot_map_add(&scene->mesh_instances, instance, &handle.id, &handle.generation) != RESULT_SUCCESS)
|
||||
{
|
||||
return INVALID_HANDLE(mesh_instance_handle_t);
|
||||
}
|
||||
|
||||
scene->tlas_dirty = true;
|
||||
return slot;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void scene_remove_mesh_instance(scene_t* scene, uint32_t instance_id)
|
||||
void scene_remove_mesh_instance(scene_t* scene, mesh_instance_handle_t instance)
|
||||
{
|
||||
if (scene == NULL || instance_id >= scene->mesh_instances.capacity)
|
||||
if (scene == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scene->mesh_instances.buffer[instance_id].active)
|
||||
if (mesh_instance_slot_map_remove(&scene->mesh_instances, instance.id, instance.generation) == RESULT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
scene->tlas_dirty = true;
|
||||
}
|
||||
|
||||
scene->mesh_instances.buffer[instance_id].active = false;
|
||||
scene->tlas_dirty = true;
|
||||
}
|
||||
|
||||
void scene_set_mesh_instance_transform(scene_t* scene, uint32_t instance_id, mat4s local_to_world)
|
||||
void scene_set_mesh_instance_transform(scene_t* scene, mesh_instance_handle_t instance, mat4s local_to_world)
|
||||
{
|
||||
if (scene == NULL || instance_id >= scene->mesh_instances.capacity)
|
||||
if (scene == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mesh_instance_t* inst = &scene->mesh_instances.buffer[instance_id];
|
||||
if (!inst->active)
|
||||
mesh_instance_t* inst = mesh_instance_slot_map_get(&scene->mesh_instances, instance.id, instance.generation);
|
||||
if (inst == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mesh_model_t* mesh_model = mesh_model_slot_map_get(&scene->mesh_models, inst->model.id, inst->model.generation);
|
||||
if (mesh_model == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -406,10 +325,7 @@ void scene_set_mesh_instance_transform(scene_t* scene, uint32_t instance_id, mat
|
||||
inst->local_to_world = local_to_world;
|
||||
inst->world_to_local = glms_mat4_inv(local_to_world);
|
||||
inst->normal_matrix = compute_normal_matrix(local_to_world);
|
||||
if (inst->model_id < scene->mesh_models.capacity && scene->mesh_models.buffer[inst->model_id].active)
|
||||
{
|
||||
inst->world_bounds = aabb_transform(local_to_world, scene->mesh_models.buffer[inst->model_id].local_bounds);
|
||||
}
|
||||
inst->world_bounds = aabb_transform(local_to_world, mesh_model->local_bounds);
|
||||
|
||||
scene->tlas_dirty = true;
|
||||
}
|
||||
|
||||
@@ -6,65 +6,6 @@
|
||||
|
||||
#define GET_CHANNEL_DATA(pixel, channel, channel_count, default, max) (channel < channel_count ? pixel[channel] : default) / max
|
||||
|
||||
bool texture_collection_init(uint16_t size, texture_collection_t* textures)
|
||||
{
|
||||
texture_collection_t temp = {0};
|
||||
temp.buffer = (texture_asset_t*)malloc(size * sizeof(texture_asset_t));
|
||||
if (temp.buffer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
temp.size = size;
|
||||
temp.count = 0;
|
||||
*textures = temp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void texture_collection_resize(texture_collection_t* textures, uint16_t size)
|
||||
{
|
||||
if (size == INVALID_TEXTURE_ID)
|
||||
{
|
||||
size = INVALID_TEXTURE_ID - 1;
|
||||
}
|
||||
|
||||
if (size == textures->size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
texture_asset_t* temp = (texture_asset_t*)realloc(textures->buffer, size * sizeof(texture_asset_t));
|
||||
if (temp != NULL)
|
||||
{
|
||||
textures->buffer = temp;
|
||||
textures->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
void texture_collection_free(texture_collection_t* textures)
|
||||
{
|
||||
if (textures == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < textures->count; i++)
|
||||
{
|
||||
texture_free(&textures->buffer[i].texture);
|
||||
|
||||
char* full_name = textures->buffer[i].full_name;
|
||||
if (full_name != NULL)
|
||||
{
|
||||
free(full_name);
|
||||
}
|
||||
}
|
||||
|
||||
free(textures->buffer);
|
||||
textures->buffer = NULL;
|
||||
}
|
||||
|
||||
|
||||
static inline void read_pixel_raw(const char* data, uint32_t x, uint32_t y, uint32_t width, uint8_t channel_count, stride_t stride, char* out_pixel_data)
|
||||
{
|
||||
size_t pixel_offset = (size_t)(y * width + x) * channel_count * stride;
|
||||
@@ -253,7 +194,7 @@ static void generate_mipmap(char* raw_data, mipmap_t* texture_data, uint32_t wid
|
||||
#endif
|
||||
}
|
||||
|
||||
texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stride_t stride, texture_collection_t* textures)
|
||||
texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stride_t stride, texture_slot_map_t* textures)
|
||||
{
|
||||
// TODO: This hurts performance, consider using a hash map or similar structure for faster lookups
|
||||
|
||||
@@ -284,7 +225,7 @@ texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stri
|
||||
|
||||
if (raw_data == NULL)
|
||||
{
|
||||
return invalid_texture_handle();
|
||||
return INVALID_HANDLE(texture_handle_t);
|
||||
}
|
||||
|
||||
uint8_t max_mip_level = mipmap ? (uint8_t)log2f(fmaxf((float)width, (float)height)) : 0;
|
||||
@@ -292,7 +233,7 @@ texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stri
|
||||
if (temp_texture_data == NULL)
|
||||
{
|
||||
stbi_image_free(raw_data);
|
||||
return invalid_texture_handle();
|
||||
return INVALID_HANDLE(texture_handle_t);
|
||||
}
|
||||
|
||||
generate_mipmap(raw_data, temp_texture_data, (uint32_t)width, (uint32_t)height, (uint8_t)channels, max_mip_level, stride);
|
||||
@@ -311,17 +252,19 @@ texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stri
|
||||
texture.wrap_mode = WM_REPEAT;
|
||||
texture.filter_mode = FM_LINEAR;
|
||||
|
||||
if (textures->count >= textures->size)
|
||||
texture_handle_t handle;
|
||||
texture_asset_t asset = {.full_name = string_copy(filename), .texture = texture};
|
||||
if (texture_slot_map_add(textures, asset, &handle.id, &handle.generation) != RESULT_SUCCESS)
|
||||
{
|
||||
texture_collection_resize(textures, textures->size * 2);
|
||||
texture_free(&texture);
|
||||
if (asset.full_name != NULL)
|
||||
{
|
||||
free(asset.full_name);
|
||||
}
|
||||
return INVALID_HANDLE(texture_handle_t);
|
||||
}
|
||||
|
||||
texture_handle_t entity = {.id = textures->count};
|
||||
|
||||
textures->buffer[textures->count] = (texture_asset_t){.full_name = string_copy(filename), .texture = texture};
|
||||
textures->count++;
|
||||
|
||||
return entity;
|
||||
return handle;
|
||||
}
|
||||
|
||||
static inline void warp_uv(wrap_mode_t mode, vec2s* uv)
|
||||
|
||||
Reference in New Issue
Block a user