#include "engine.h" #include #include i32 _object_uid_counter = 0; i32 sys_w(void); i32 sys_h(void); i32 sys_x(void); i32 sys_y(void); f32 sys_time(void); gpu_pipeline_t *_mesh_object_last_pipeline = NULL; vec4_t _camera_object_sphere_center = {0}; gpu_buffer_t *gpu_create_vertex_buffer(i32 count, gpu_vertex_structure_t *structure); gpu_buffer_t *gpu_create_index_buffer(i32 count); void gpu_delete_buffer(gpu_buffer_t *buffer); f32 math_floor(f32 x); gpu_shader_t *gpu_create_shader(buffer_t *data, i32 shader_type); gpu_shader_t *gpu_create_shader_from_source(char *source, int source_size, gpu_shader_type_t shader_type); void gpu_delete_shader(gpu_shader_t *shader); gpu_pipeline_t *gpu_create_pipeline(); typedef struct { char *source; int source_size; gpu_shader_type_t shader_type; gpu_shader_t *result; } shader_compile_job_t; static void shader_compile_worker(void *param) { shader_compile_job_t *job = (shader_compile_job_t *)param; job->result = gpu_create_shader_from_source(job->source, job->source_size, job->shader_type); } void gpu_delete_pipeline(gpu_pipeline_t *pipeline); #ifdef arm_embed gpu_shader_t *sys_get_shader(char *name); #endif // ██████╗ ██████╗ ██╗███████╗ ██████╗████████╗ // ██╔═══██╗██╔══██╗ ██║██╔════╝██╔════╝╚══██╔══╝ // ██║ ██║██████╔╝ ██║█████╗ ██║ ██║ // ██║ ██║██╔══██╗██ ██║██╔══╝ ██║ ██║ // ╚██████╔╝██████╔╝╚█████╔╝███████╗╚██████╗ ██║ // ╚═════╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝ ╚═╝ object_t *object_create(bool is_empty) { object_t *raw = calloc(1, sizeof(object_t)); raw->name = ""; raw->children = any_array_create(0); raw->visible = true; raw->culled = false; raw->uid = _object_uid_counter++; raw->transform = transform_create(raw); raw->is_empty = is_empty; raw->_ = calloc(1, sizeof(object_runtime_t)); if (raw->is_empty) { any_array_push(scene_empties, raw); } return raw; } void object_set_parent(object_t *raw, object_t *parent_object) { if (parent_object == raw || parent_object == raw->parent) { return; } if (raw->parent != NULL) { array_remove(raw->parent->children, raw); raw->parent = NULL; // Rebuild matrix without a parent transform_build_matrix(raw->transform); } if (parent_object == NULL) { parent_object = _scene_root; } raw->parent = parent_object; any_array_push(raw->parent->children, raw); } void object_remove_super(object_t *raw) { if (raw->is_empty) { array_remove(scene_empties, raw); } #ifdef WITH_PHYSICS if (raw->_ != NULL && raw->_->body != NULL) { physics_body_remove(raw->_->body); } #endif while (raw->children->length > 0) { object_remove((object_t *)raw->children->buffer[0]); } if (raw->parent != NULL) { array_remove(raw->parent->children, raw); raw->parent = NULL; } } void object_free(object_t *raw) { if (raw->owns_raw && raw->raw != NULL) { array_delete(raw->raw->dimensions); free(raw->raw); } free(raw->transform); array_delete(raw->children); free(raw->_); free(raw); } void object_remove(object_t *raw) { if (string_equals(raw->ext_type, "mesh_object_t")) { mesh_object_remove(raw->ext); } else if (string_equals(raw->ext_type, "camera_object_t")) { camera_object_remove(raw->ext); } else { object_remove_super(raw); object_free(raw); } } object_t *object_get_child(object_t *raw, char *name) { if (string_equals(raw->name, name)) { return raw; } for (i32 i = 0; i < raw->children->length; ++i) { object_t *c = (object_t *)raw->children->buffer[i]; object_t *r = object_get_child(c, name); if (r != NULL) { return r; } } return NULL; } // ████████╗██████╗ █████╗ ███╗ ██╗███████╗███████╗ ██████╗ ██████╗ ███╗ ███╗ // ╚══██╔══╝██╔══██╗██╔══██╗████╗ ██║██╔════╝██╔════╝██╔═══██╗██╔══██╗████╗ ████║ // ██║ ██████╔╝███████║██╔██╗ ██║███████╗█████╗ ██║ ██║██████╔╝██╔████╔██║ // ██║ ██╔══██╗██╔══██║██║╚██╗██║╚════██║██╔══╝ ██║ ██║██╔══██╗██║╚██╔╝██║ // ██║ ██║ ██║██║ ██║██║ ╚████║███████║██║ ╚██████╔╝██║ ██║██║ ╚═╝ ██║ // ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ transform_t *transform_create(object_t *object) { transform_t *raw = calloc(1, sizeof(transform_t)); raw->scale_world = 1.0; raw->object = object; transform_reset(raw); return raw; } void transform_reset(transform_t *raw) { raw->world = mat4_identity(); raw->world_unpack = mat4_identity(); raw->local = mat4_identity(); raw->loc = (vec4_t){0.0, 0.0, 0.0, 0.0}; raw->rot = (quat_t){0.0, 0.0, 0.0, 1.0}; raw->scale = (vec4_t){1.0, 1.0, 1.0, 0.0}; raw->dim = (vec4_t){2.0, 2.0, 2.0, 0.0}; raw->radius = 1.0; raw->dirty = true; } void transform_update(transform_t *raw) { if (raw->dirty) { transform_build_matrix(raw); } } void transform_build_matrix(transform_t *raw) { raw->local = mat4_compose(raw->loc, raw->rot, raw->scale); if (raw->object->parent != NULL) { raw->world = mat4_mult_mat3x4(raw->local, raw->object->parent->transform->world); } else { raw->world = raw->local; } raw->world_unpack = raw->world; if (raw->scale_world != 1.0) { raw->world_unpack.m00 *= raw->scale_world; raw->world_unpack.m01 *= raw->scale_world; raw->world_unpack.m02 *= raw->scale_world; raw->world_unpack.m03 *= raw->scale_world; raw->world_unpack.m10 *= raw->scale_world; raw->world_unpack.m11 *= raw->scale_world; raw->world_unpack.m12 *= raw->scale_world; raw->world_unpack.m13 *= raw->scale_world; raw->world_unpack.m20 *= raw->scale_world; raw->world_unpack.m21 *= raw->scale_world; raw->world_unpack.m22 *= raw->scale_world; raw->world_unpack.m23 *= raw->scale_world; } transform_compute_dim(raw); // Update children for (i32 i = 0; i < raw->object->children->length; ++i) { object_t *n = (object_t *)raw->object->children->buffer[i]; transform_build_matrix(n->transform); } raw->dirty = false; } void transform_set_matrix(transform_t *raw, mat4_t mat) { raw->local = mat; transform_decompose(raw); transform_build_matrix(raw); } void transform_decompose(transform_t *raw) { mat4_decomposed_t *dec = mat4_decompose(raw->local); raw->loc = dec->loc; raw->rot = dec->rot; raw->scale = dec->scl; } void transform_rotate(transform_t *raw, vec4_t axis, float f) { quat_t q = quat_from_axis_angle(axis, f); raw->rot = quat_mult(q, raw->rot); transform_build_matrix(raw); } void transform_move(transform_t *raw, vec4_t axis, float f) { raw->loc = vec4_fadd(raw->loc, axis.x * f, axis.y * f, axis.z * f, 0.0); transform_build_matrix(raw); } void transform_compute_radius(transform_t *raw) { raw->radius = sqrtf(raw->dim.x * raw->dim.x + raw->dim.y * raw->dim.y + raw->dim.z * raw->dim.z); } void transform_compute_dim(transform_t *raw) { if (raw->object->raw == NULL && string_equals(raw->object->ext_type, "mesh_object_t")) { mesh_object_t *mo = (mesh_object_t *)raw->object->ext; vec4_t aabb = mesh_data_calculate_aabb(mo->data); obj_t *o_raw = calloc(1, sizeof(obj_t)); o_raw->dimensions = f32_array_create_xyz(aabb.x, aabb.y, aabb.z); raw->object->raw = o_raw; raw->object->owns_raw = true; } if (raw->object->raw == NULL || raw->object->raw->dimensions == NULL) { raw->dim = (vec4_t){2.0 * raw->scale.x, 2.0 * raw->scale.y, 2.0 * raw->scale.z, 0.0}; } else { f32_array_t *d = raw->object->raw->dimensions; raw->dim = (vec4_t){d->buffer[0] * raw->scale.x, d->buffer[1] * raw->scale.y, d->buffer[2] * raw->scale.z, 0.0}; } transform_compute_radius(raw); } vec4_t transform_look(transform_t *raw) { return mat4_look(raw->world); } vec4_t transform_right(transform_t *raw) { return mat4_right(raw->world); } vec4_t transform_up(transform_t *raw) { return mat4_up(raw->world); } float transform_world_x(transform_t *raw) { return raw->world.m30; } float transform_world_y(transform_t *raw) { return raw->world.m31; } float transform_world_z(transform_t *raw) { return raw->world.m32; } // ██████╗ █████╗ ███╗ ███╗███████╗██████╗ █████╗ ██████╗ █████╗ ████████╗ █████╗ // ██╔════╝██╔══██╗████╗ ████║██╔════╝██╔══██╗██╔══██╗ ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ // ██║ ███████║██╔████╔██║█████╗ ██████╔╝███████║ ██║ ██║███████║ ██║ ███████║ // ██║ ██╔══██║██║╚██╔╝██║██╔══╝ ██╔══██╗██╔══██║ ██║ ██║██╔══██║ ██║ ██╔══██║ // ╚██████╗██║ ██║██║ ╚═╝ ██║███████╗██║ ██║██║ ██║ ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ camera_data_t *camera_data_parse(char *name, char *id) { scene_t *format = data_get_scene_raw(name); camera_data_t *raw = camera_data_get_raw_by_name(format->camera_datas, id); if (raw == NULL) { iron_log("Camera data '%s' not found!", id); } return raw; } camera_data_t *camera_data_get_raw_by_name(any_array_t *datas, char *name) { if (strcmp(name, "") == 0) { return (camera_data_t *)datas->buffer[0]; } for (i32 i = 0; i < datas->length; ++i) { camera_data_t *d = (camera_data_t *)datas->buffer[i]; if (strcmp(d->name, name) == 0) { return d; } } return NULL; } // ██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ ████████╗ █████╗ // ██║ ██║██╔═══██╗██╔══██╗██║ ██╔══██╗ ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ // ██║ █╗ ██║██║ ██║██████╔╝██║ ██║ ██║ ██║ ██║███████║ ██║ ███████║ // ██║███╗██║██║ ██║██╔══██╗██║ ██║ ██║ ██║ ██║██╔══██║ ██║ ██╔══██║ // ╚███╔███╔╝╚██████╔╝██║ ██║███████╗ ██████╔╝ ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ f32_array_t *_world_data_empty_irr = NULL; world_data_t *world_data_parse(char *name, char *id) { scene_t *format = data_get_scene_raw(name); world_data_t *raw = world_data_get_raw_by_name(format->world_datas, id); if (raw == NULL) { iron_log("World data '%s' not found!", id); return NULL; } raw->_ = calloc(1, sizeof(world_data_runtime_t)); raw->_->radiance_mipmaps = any_array_create(0); raw->_->irradiance = world_data_set_irradiance(raw); if (raw->radiance != NULL) { raw->_->radiance = data_get_texture(raw->radiance); while (raw->_->radiance_mipmaps->length < raw->radiance_mipmaps) { any_array_push(raw->_->radiance_mipmaps, NULL); } i32 dot = string_last_index_of(raw->radiance, "."); char *ext = substring(raw->radiance, dot, string_length(raw->radiance)); char *base = substring(raw->radiance, 0, dot); for (i32 i = 0; i < raw->radiance_mipmaps; ++i) { char *mip_name = string("%s_%s%s", base, i32_to_string(i), ext); gpu_texture_t *mipimg = data_get_texture(mip_name); raw->_->radiance_mipmaps->buffer[i] = mipimg; } } return raw; } world_data_t *world_data_get_raw_by_name(any_array_t *datas, char *name) { if (strcmp(name, "") == 0) { return (world_data_t *)datas->buffer[0]; } for (i32 i = 0; i < datas->length; ++i) { world_data_t *d = (world_data_t *)datas->buffer[i]; if (strcmp(d->name, name) == 0) { return d; } } return NULL; } f32_array_t *world_data_get_empty_irradiance() { if (_world_data_empty_irr == NULL) { _world_data_empty_irr = f32_array_create(28); for (i32 i = 0; i < _world_data_empty_irr->length; ++i) { _world_data_empty_irr->buffer[i] = 0.0; } } return _world_data_empty_irr; } f32_array_t *world_data_set_irradiance(world_data_t *raw) { if (raw->irradiance == NULL) { return world_data_get_empty_irradiance(); } buffer_t *b = data_get_blob(string("%s.arm", raw->irradiance)); irradiance_t *irradiance_parsed = (irradiance_t *)armpack_decode(b); f32_array_t *irr = f32_array_create(28); // Align to mult of 4 - 27->28 for (i32 i = 0; i < 27; ++i) { irr->buffer[i] = irradiance_parsed->irradiance->buffer[i]; } return irr; } void world_data_load_envmap(world_data_t *raw) { if (raw->envmap != NULL) { raw->_->envmap = data_get_texture(raw->envmap); } } // ███╗ ███╗ █████╗ ████████╗███████╗██████╗ ██╗ █████╗ ██╗ ██████╗ █████╗ ████████╗ █████╗ // ████╗ ████║██╔══██╗╚══██╔══╝██╔════╝██╔══██╗██║██╔══██╗██║ ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ // ██╔████╔██║███████║ ██║ █████╗ ██████╔╝██║███████║██║ ██║ ██║███████║ ██║ ███████║ // ██║╚██╔╝██║██╔══██║ ██║ ██╔══╝ ██╔══██╗██║██╔══██║██║ ██║ ██║██╔══██║ ██║ ██╔══██║ // ██║ ╚═╝ ██║██║ ██║ ██║ ███████╗██║ ██║██║██║ ██║███████╗ ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ i32 _material_data_uid_counter = 0; material_data_t *material_data_create(material_data_t *raw, char *file) { raw->_ = calloc(1, sizeof(material_data_runtime_t)); raw->_->uid = (float)(++_material_data_uid_counter); // Start from 1 any_array_t *ref = string_split(raw->shader, "/"); char *object_file = ""; char *data_ref = ""; if (ref->length == 2) { // File reference object_file = (char *)ref->buffer[0]; data_ref = (char *)ref->buffer[1]; } else { // Local data object_file = file; data_ref = raw->shader; } raw->_->shader = data_get_shader(object_file, data_ref); for (i32 i = 0; i < (i32)raw->contexts->length; ++i) { material_context_t *c = (material_context_t *)raw->contexts->buffer[i]; material_context_load(c); } return raw; } material_data_t *material_data_parse(char *file, char *name) { scene_t *format = data_get_scene_raw(file); material_data_t *raw = material_data_get_raw_by_name(format->material_datas, name); if (raw == NULL) { iron_log("Material data '%s' not found!", name); return NULL; } return material_data_create(raw, file); } material_data_t *material_data_get_raw_by_name(any_array_t *datas, char *name) { if (strcmp(name, "") == 0) { return (material_data_t *)datas->buffer[0]; } for (i32 i = 0; i < (i32)datas->length; ++i) { material_data_t *d = (material_data_t *)datas->buffer[i]; if (strcmp(d->name, name) == 0) { return d; } } return NULL; } material_context_t *material_data_get_context(material_data_t *raw, char *name) { for (i32 i = 0; i < (i32)raw->contexts->length; ++i) { material_context_t *c = (material_context_t *)raw->contexts->buffer[i]; if (strcmp(c->name, name) == 0) { return c; } } return NULL; } void material_context_load(material_context_t *raw) { raw->_ = calloc(1, sizeof(material_context_runtime_t)); if (raw->bind_textures != NULL && raw->bind_textures->length > 0) { raw->_->textures = any_array_create(0); for (i32 i = 0; i < (i32)raw->bind_textures->length; ++i) { bind_tex_t *tex = (bind_tex_t *)raw->bind_textures->buffer[i]; if (strcmp(tex->file, "") == 0) { // Empty texture continue; } gpu_texture_t *image = data_get_texture(tex->file); any_array_push(raw->_->textures, image); } } } // ███████╗██╗ ██╗ █████╗ ██████╗ ███████╗██████╗ ██████╗ █████╗ ████████╗ █████╗ // ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔════╝██╔══██╗ ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ // ███████╗███████║███████║██║ ██║█████╗ ██████╔╝ ██║ ██║███████║ ██║ ███████║ // ╚════██║██╔══██║██╔══██║██║ ██║██╔══╝ ██╔══██╗ ██║ ██║██╔══██║ ██║ ██╔══██║ // ███████║██║ ██║██║ ██║██████╔╝███████╗██║ ██║ ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ shader_data_t *shader_data_create(shader_data_t *raw) { for (i32 i = 0; i < raw->contexts->length; ++i) { shader_context_t *c = (shader_context_t *)raw->contexts->buffer[i]; shader_context_load(c); } return raw; } char *shader_data_ext() { #ifdef IRON_VULKAN return ".spirv"; #elif defined(IRON_METAL) return ".metal"; #elif defined(IRON_WASM) return ".wgsl"; #else return ".d3d11"; #endif } shader_data_t *shader_data_parse(char *file, char *name) { scene_t *format = data_get_scene_raw(file); shader_data_t *raw = shader_data_get_raw_by_name(format->shader_datas, name); if (raw == NULL) { iron_log("Shader data '%s' not found!", name); return NULL; } return shader_data_create(raw); } shader_data_t *shader_data_get_raw_by_name(shader_data_t_array_t *datas, char *name) { if (strcmp(name, "") == 0) { return (shader_data_t *)datas->buffer[0]; } for (i32 i = 0; i < datas->length; ++i) { shader_data_t *d = (shader_data_t *)datas->buffer[i]; if (strcmp(d->name, name) == 0) { return d; } } return NULL; } void shader_data_delete(shader_data_t *raw) { for (i32 i = 0; i < raw->contexts->length; ++i) { shader_context_t *c = (shader_context_t *)raw->contexts->buffer[i]; shader_context_delete(c); } } shader_context_t *shader_data_get_context(shader_data_t *raw, char *name) { for (i32 i = 0; i < raw->contexts->length; ++i) { shader_context_t *c = (shader_context_t *)raw->contexts->buffer[i]; if (strcmp(c->name, name) == 0) { return c; } } return NULL; } void shader_context_load(shader_context_t *raw) { if (raw->_ == NULL) { raw->_ = calloc(1, sizeof(shader_context_runtime_t)); } shader_context_parse_vertex_struct(raw); shader_context_compile(raw); } static void shader_context_delete_shaders(shader_context_t *raw) { #ifdef arm_embed if (!raw->shader_from_source) { return; // Owned by sys_get_shader } #endif if (raw->_->pipe->fragment_shader != NULL) { gpu_delete_shader(raw->_->pipe->fragment_shader); raw->_->pipe->fragment_shader = NULL; } if (raw->_->pipe->vertex_shader != NULL) { gpu_delete_shader(raw->_->pipe->vertex_shader); raw->_->pipe->vertex_shader = NULL; } } void shader_context_compile(shader_context_t *raw) { raw->_->pipe = gpu_create_pipeline(); raw->_->constants = i32_array_create(0); raw->_->tex_units = i32_array_create(0); raw->_->pipe->input_layout = &raw->_->structure; raw->_->pipe->depth_write = raw->depth_write; raw->_->pipe->depth_mode = shader_context_get_compare_mode(raw->compare_mode); raw->_->pipe->cull_mode = shader_context_get_cull_mode(raw->cull_mode); if (raw->blend_source != NULL) { raw->_->pipe->blend_source = shader_context_get_blend_fac(raw->blend_source); } if (raw->blend_destination != NULL) { raw->_->pipe->blend_destination = shader_context_get_blend_fac(raw->blend_destination); } if (raw->alpha_blend_source != NULL) { raw->_->pipe->alpha_blend_source = shader_context_get_blend_fac(raw->alpha_blend_source); } if (raw->alpha_blend_destination != NULL) { raw->_->pipe->alpha_blend_destination = shader_context_get_blend_fac(raw->alpha_blend_destination); } if (raw->color_writes_red != NULL) { for (i32 i = 0; i < raw->color_writes_red->length; ++i) { raw->_->pipe->color_write_mask_red[i] = (bool)(intptr_t)raw->color_writes_red->buffer[i]; } } if (raw->color_writes_green != NULL) { for (i32 i = 0; i < raw->color_writes_green->length; ++i) { raw->_->pipe->color_write_mask_green[i] = (bool)(intptr_t)raw->color_writes_green->buffer[i]; } } if (raw->color_writes_blue != NULL) { for (i32 i = 0; i < raw->color_writes_blue->length; ++i) { raw->_->pipe->color_write_mask_blue[i] = (bool)(intptr_t)raw->color_writes_blue->buffer[i]; } } if (raw->color_writes_alpha != NULL) { for (i32 i = 0; i < raw->color_writes_alpha->length; ++i) { raw->_->pipe->color_write_mask_alpha[i] = (bool)(intptr_t)raw->color_writes_alpha->buffer[i]; } } if (raw->color_attachments != NULL) { raw->_->pipe->color_attachment_count = raw->color_attachments->length; for (i32 i = 0; i < raw->color_attachments->length; ++i) { raw->_->pipe->color_attachment[i] = shader_context_get_tex_format((char *)raw->color_attachments->buffer[i]); } } if (raw->depth_attachment != NULL) { raw->_->pipe->depth_attachment_bits = strcmp(raw->depth_attachment, "NONE") == 0 ? 0 : 32; } if (raw->shader_from_source) { #ifdef IRON_WASM raw->_->pipe->vertex_shader = gpu_create_shader_from_source(raw->vertex_shader, raw->_->vertex_shader_size, GPU_SHADER_TYPE_VERTEX); raw->_->pipe->fragment_shader = gpu_create_shader_from_source(raw->fragment_shader, raw->_->fragment_shader_size, GPU_SHADER_TYPE_FRAGMENT); #else shader_compile_job_t vs_job = {raw->vertex_shader, raw->_->vertex_shader_size, GPU_SHADER_TYPE_VERTEX, NULL}; shader_compile_job_t fs_job = {raw->fragment_shader, raw->_->fragment_shader_size, GPU_SHADER_TYPE_FRAGMENT, NULL}; iron_thread_t vs_thread; iron_thread_init(&vs_thread, shader_compile_worker, &vs_job); shader_compile_worker(&fs_job); iron_thread_wait_and_destroy(&vs_thread); raw->_->pipe->vertex_shader = vs_job.result; raw->_->pipe->fragment_shader = fs_job.result; #endif #ifdef IRON_WASM free(raw->vertex_shader); raw->vertex_shader = NULL; free(raw->fragment_shader); raw->fragment_shader = NULL; #endif if (raw->_->pipe->vertex_shader == NULL || raw->_->pipe->fragment_shader == NULL) { return; } } else { #ifdef arm_embed raw->_->pipe->fragment_shader = sys_get_shader(raw->fragment_shader); raw->_->pipe->vertex_shader = sys_get_shader(raw->vertex_shader); #else buffer_t *vs_buffer = data_get_blob(string_tmp("%s%s", raw->vertex_shader, shader_data_ext())); raw->_->pipe->vertex_shader = gpu_create_shader(vs_buffer, GPU_SHADER_TYPE_VERTEX); buffer_t *fs_buffer = data_get_blob(string_tmp("%s%s", raw->fragment_shader, shader_data_ext())); raw->_->pipe->fragment_shader = gpu_create_shader(fs_buffer, GPU_SHADER_TYPE_FRAGMENT); #endif } shader_context_finish_compile(raw); } i32 shader_context_type_size(char *t) { if (strcmp(t, "int") == 0) return 4; if (strcmp(t, "float") == 0) return 4; if (strcmp(t, "float2") == 0) return 8; if (strcmp(t, "float4") == 0) return 16; if (strcmp(t, "float4x4") == 0) return 64; #ifdef IRON_DIRECT3D12 if (strcmp(t, "float3") == 0) return 12; if (strcmp(t, "float3x3") == 0) return 44; // 16 + 16 + 12 #else if (strcmp(t, "float3") == 0) return 16; if (strcmp(t, "float3x3") == 0) return 48; // std140 array elements are padded to 16 #endif return 0; } i32 shader_context_type_pad(i32 offset, i32 size) { #ifdef IRON_DIRECT3D12 i32 r = offset % 16; if (r == 0) { return 0; } if (size >= 16 || r + size > 16) { return 16 - r; } return 0; #else if (size > 16) { size = 16; } return (size - (offset % size)) % size; #endif } void shader_context_finish_compile(shader_context_t *raw) { gpu_pipeline_compile(raw->_->pipe); if (raw->constants != NULL) { i32 offset = 0; for (i32 i = 0; i < raw->constants->length; ++i) { shader_const_t *c = (shader_const_t *)raw->constants->buffer[i]; i32 size = shader_context_type_size(c->type); offset += shader_context_type_pad(offset, size); shader_context_add_const(raw, offset); offset += size; } } if (raw->texture_units != NULL) { for (i32 i = 0; i < raw->texture_units->length; ++i) { shader_context_add_tex(raw, i); } } } gpu_vertex_data_t shader_context_parse_data(char *data) { if (strcmp(data, "float1") == 0) return GPU_VERTEX_DATA_F32_1X; if (strcmp(data, "float2") == 0) return GPU_VERTEX_DATA_F32_2X; if (strcmp(data, "float3") == 0) return GPU_VERTEX_DATA_F32_3X; if (strcmp(data, "float4") == 0) return GPU_VERTEX_DATA_F32_4X; if (strcmp(data, "short2norm") == 0) return GPU_VERTEX_DATA_I16_2X_NORM; if (strcmp(data, "short4norm") == 0) return GPU_VERTEX_DATA_I16_4X_NORM; return GPU_VERTEX_DATA_F32_1X; } void shader_context_parse_vertex_struct(shader_context_t *raw) { raw->_->structure = (gpu_vertex_structure_t){0}; for (i32 i = 0; i < raw->vertex_elements->length; ++i) { vertex_element_t *elem = (vertex_element_t *)raw->vertex_elements->buffer[i]; gpu_vertex_structure_add(&raw->_->structure, elem->name, shader_context_parse_data(elem->data)); } } void shader_context_delete(shader_context_t *raw) { shader_context_delete_shaders(raw); gpu_delete_pipeline(raw->_->pipe); } gpu_compare_mode_t shader_context_get_compare_mode(char *s) { if (strcmp(s, "always") == 0) return GPU_COMPARE_MODE_ALWAYS; if (strcmp(s, "never") == 0) return GPU_COMPARE_MODE_NEVER; if (strcmp(s, "equal") == 0) return GPU_COMPARE_MODE_EQUAL; return GPU_COMPARE_MODE_LESS; } gpu_cull_mode_t shader_context_get_cull_mode(char *s) { if (strcmp(s, "none") == 0) return GPU_CULL_MODE_NONE; if (strcmp(s, "clockwise") == 0) return GPU_CULL_MODE_CLOCKWISE; return GPU_CULL_MODE_COUNTER_CLOCKWISE; } gpu_blend_t shader_context_get_blend_fac(char *s) { if (strcmp(s, "blend_one") == 0) return GPU_BLEND_ONE; if (strcmp(s, "blend_zero") == 0) return GPU_BLEND_ZERO; if (strcmp(s, "source_alpha") == 0) return GPU_BLEND_SOURCE_ALPHA; if (strcmp(s, "destination_alpha") == 0) return GPU_BLEND_DEST_ALPHA; if (strcmp(s, "inverse_source_alpha") == 0) return GPU_BLEND_INV_SOURCE_ALPHA; if (strcmp(s, "inverse_destination_alpha") == 0) return GPU_BLEND_INV_DEST_ALPHA; return GPU_BLEND_ONE; } gpu_texture_format_t shader_context_get_tex_format(char *s) { if (strcmp(s, "RGBA32") == 0) return GPU_TEXTURE_FORMAT_RGBA32; if (strcmp(s, "RGBA64") == 0) return GPU_TEXTURE_FORMAT_RGBA64; if (strcmp(s, "RGBA128") == 0) return GPU_TEXTURE_FORMAT_RGBA128; if (strcmp(s, "R32") == 0) return GPU_TEXTURE_FORMAT_R32; if (strcmp(s, "R16") == 0) return GPU_TEXTURE_FORMAT_R16; if (strcmp(s, "R8") == 0) return GPU_TEXTURE_FORMAT_R8; return GPU_TEXTURE_FORMAT_RGBA32; } void shader_context_add_const(shader_context_t *raw, i32 offset) { i32_array_push(raw->_->constants, offset); } void shader_context_add_tex(shader_context_t *raw, i32 i) { i32_array_push(raw->_->tex_units, i); } // ███╗ ███╗███████╗███████╗██╗ ██╗ ██████╗ █████╗ ████████╗ █████╗ // ████╗ ████║██╔════╝██╔════╝██║ ██║ ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ // ██╔████╔██║█████╗ ███████╗███████║ ██║ ██║███████║ ██║ ███████║ // ██║╚██╔╝██║██╔══╝ ╚════██║██╔══██║ ██║ ██║██╔══██║ ██║ ██╔══██║ // ██║ ╚═╝ ██║███████╗███████║██║ ██║ ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ mesh_data_t *mesh_data_parse(char *name, char *id) { scene_t *format = data_get_scene_raw(name); mesh_data_t *raw = mesh_data_get_raw_by_name(format->mesh_datas, id); if (raw == NULL) { iron_log("Mesh data '%s' not found!", id); return NULL; } return mesh_data_create(raw); } mesh_data_t *mesh_data_get_raw_by_name(any_array_t *datas, char *name) { if (string_equals(name, "")) { return (mesh_data_t *)datas->buffer[0]; } for (i32 i = 0; i < datas->length; ++i) { mesh_data_t *d = (mesh_data_t *)datas->buffer[i]; if (string_equals(d->name, name)) { return d; } } return NULL; } mesh_data_t *mesh_data_create(mesh_data_t *raw) { raw->_ = calloc(1, sizeof(mesh_data_runtime_t)); if (raw->scale_pos == 0.0) { raw->scale_pos = 1.0; } if (raw->scale_tex == 0.0) { raw->scale_tex = 1.0; } raw->_->structure = mesh_data_get_vertex_struct(raw->vertex_arrays); mesh_data_build(raw); return raw; } gpu_vertex_structure_t mesh_data_get_vertex_struct(any_array_t *vertex_arrays) { gpu_vertex_structure_t structure = {0}; for (i32 i = 0; i < vertex_arrays->length; ++i) { vertex_array_t *va = (vertex_array_t *)vertex_arrays->buffer[i]; gpu_vertex_structure_add(&structure, va->attrib, mesh_data_get_vertex_data(va->data)); } return structure; } gpu_vertex_data_t mesh_data_get_vertex_data(char *data) { if (string_equals(data, "short4norm")) { return GPU_VERTEX_DATA_I16_4X_NORM; } return GPU_VERTEX_DATA_I16_2X_NORM; // short2norm } i32 mesh_data_get_vertex_size(char *vertex_data) { if (string_equals(vertex_data, "short4norm")) { return 4; } return 2; // short2norm } void mesh_data_build_vertices(gpu_buffer_t *vertex_buffer, any_array_t *vertex_arrays) { vertex_array_t *va0 = (vertex_array_t *)vertex_arrays->buffer[0]; int16_t *vertices = gpu_vertex_buffer_lock(vertex_buffer); i32 size = mesh_data_get_vertex_size(va0->data); i32 num_verts = va0->values->length / size; i32 di = -1; for (i32 i = 0; i < num_verts; ++i) { for (i32 va = 0; va < vertex_arrays->length; ++va) { vertex_array_t *v = (vertex_array_t *)vertex_arrays->buffer[va]; i32 l = mesh_data_get_vertex_size(v->data); for (i32 o = 0; o < l; ++o) { vertices[++di] = v->values->buffer[i * l + o]; } } } gpu_vertex_buffer_unlock(vertex_buffer); } void mesh_data_build_indices(gpu_buffer_t *index_buffer, u32_array_t *index_array) { uint32_t *ia = gpu_index_buffer_lock(index_buffer); memcpy(ia, index_array->buffer, index_buffer->count * 4); gpu_index_buffer_unlock(index_buffer); } vertex_array_t *mesh_data_get_vertex_array(mesh_data_t *raw, char *name) { for (i32 i = 0; i < raw->vertex_arrays->length; ++i) { vertex_array_t *va = (vertex_array_t *)raw->vertex_arrays->buffer[i]; if (string_equals(va->attrib, name)) { return va; } } return NULL; } void mesh_data_build(mesh_data_t *raw) { vertex_array_t *positions = mesh_data_get_vertex_array(raw, "pos"); i32 size = mesh_data_get_vertex_size(positions->data); raw->_->vertex_buffer = gpu_create_vertex_buffer(positions->values->length / size, &raw->_->structure); mesh_data_build_vertices(raw->_->vertex_buffer, raw->vertex_arrays); raw->_->index_buffer = gpu_create_index_buffer(raw->index_array->length); mesh_data_build_indices(raw->_->index_buffer, raw->index_array); } void mesh_data_calculate_aabb_min_max(mesh_data_t *raw, vec4_t *out_min, vec4_t *out_max) { // Bounds relative to origin vec4_t aabb_min = (vec4_t){-0.01, -0.01, -0.01, 0.0}; vec4_t aabb_max = (vec4_t){0.01, 0.01, 0.01, 0.0}; i32 i = 0; vertex_array_t *positions = mesh_data_get_vertex_array(raw, "pos"); while (i < positions->values->length) { if (positions->values->buffer[i] > aabb_max.x) { aabb_max.x = positions->values->buffer[i]; } if (positions->values->buffer[i + 1] > aabb_max.y) { aabb_max.y = positions->values->buffer[i + 1]; } if (positions->values->buffer[i + 2] > aabb_max.z) { aabb_max.z = positions->values->buffer[i + 2]; } if (positions->values->buffer[i] < aabb_min.x) { aabb_min.x = positions->values->buffer[i]; } if (positions->values->buffer[i + 1] < aabb_min.y) { aabb_min.y = positions->values->buffer[i + 1]; } if (positions->values->buffer[i + 2] < aabb_min.z) { aabb_min.z = positions->values->buffer[i + 2]; } i += 4; } f32 f = raw->scale_pos / 32767.0f; *out_min = (vec4_t){aabb_min.x * f, aabb_min.y * f, aabb_min.z * f, 0.0}; *out_max = (vec4_t){aabb_max.x * f, aabb_max.y * f, aabb_max.z * f, 0.0}; } vec4_t mesh_data_calculate_aabb(mesh_data_t *raw) { vec4_t aabb_min; vec4_t aabb_max; mesh_data_calculate_aabb_min_max(raw, &aabb_min, &aabb_max); return (vec4_t){aabb_max.x - aabb_min.x, aabb_max.y - aabb_min.y, aabb_max.z - aabb_min.z, 0.0}; } void mesh_data_delete(mesh_data_t *raw) { gpu_delete_buffer(raw->_->vertex_buffer); gpu_delete_buffer(raw->_->index_buffer); if (raw->_->owns_arrays) { // Skip armpack meshes pointing to a decoded blob for (i32 i = 0; i < raw->vertex_arrays->length; ++i) { vertex_array_t *va = raw->vertex_arrays->buffer[i]; if (va->values != NULL) { array_free(va->values); free(va->values); } free(va); } array_free(raw->vertex_arrays); free(raw->vertex_arrays); if (raw->index_array != NULL) { array_free(raw->index_array); free(raw->index_array); } free(raw->_); free(raw); } } // ███╗ ███╗███████╗███████╗██╗ ██╗ ██████╗ ██████╗ ██╗███████╗ ██████╗████████╗ // ████╗ ████║██╔════╝██╔════╝██║ ██║ ██╔═══██╗██╔══██╗ ██║██╔════╝██╔════╝╚══██╔══╝ // ██╔████╔██║█████╗ ███████╗███████║ ██║ ██║██████╔╝ ██║█████╗ ██║ ██║ // ██║╚██╔╝██║██╔══╝ ╚════██║██╔══██║ ██║ ██║██╔══██╗██ ██║██╔══╝ ██║ ██║ // ██║ ╚═╝ ██║███████╗███████║██║ ██║ ╚██████╔╝██████╔╝╚█████╔╝███████╗╚██████╗ ██║ // ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝ ╚═╝ mesh_object_t *mesh_object_create(mesh_data_t *data, material_data_t *material) { mesh_object_t *raw = calloc(1, sizeof(mesh_object_t)); raw->frustum_culling = true; raw->base = object_create(false); raw->base->ext = raw; raw->base->ext_type = "mesh_object_t"; raw->material = material; mesh_object_set_data(raw, data); any_array_push(scene_meshes, raw); return raw; } void mesh_object_set_data(mesh_object_t *raw, mesh_data_t *data) { raw->data = data; // Scale-up packed (-1,1) mesh coords raw->base->transform->scale_world = data->scale_pos; } void mesh_object_remove(mesh_object_t *raw) { array_remove(scene_meshes, raw); object_remove_super(raw->base); object_free(raw->base); free(raw); } bool mesh_object_cull_material(mesh_object_t *raw, char *context) { // Skip render if material does not contain current context if (!mesh_object_valid_context(raw, raw->material, context)) { raw->base->culled = true; return true; } if (raw->skip_context != NULL && string_equals(raw->skip_context, context)) { raw->base->culled = true; return true; } if (raw->force_context != NULL && !string_equals(raw->force_context, context)) { raw->base->culled = true; return true; } raw->base->culled = false; return false; } bool mesh_object_cull_mesh(mesh_object_t *raw, char *context, camera_object_t *camera) { if (camera->data->frustum_culling && raw->frustum_culling) { f32 radius_scale = 1.0; if (!camera_object_sphere_in_frustum(&camera->frustum_planes, raw->base->transform, radius_scale, 0.0, 0.0, 0.0)) { raw->base->culled = true; return true; } } raw->base->culled = false; return false; } void mesh_object_render(mesh_object_t *raw, char *context, string_array_t *bind_params) { if (!raw->base->visible) { return; // Skip render if object is hidden } if (mesh_object_cull_mesh(raw, context, scene_camera)) { return; } if (mesh_object_cull_material(raw, context)) { return; } uniforms_pos_unpack = raw->data->scale_pos; uniforms_tex_unpack = raw->data->scale_tex; transform_update(raw->base->transform); shader_context_t *scontext = NULL; material_context_t *mcontext = NULL; material_data_t *mat = raw->material; for (i32 j = 0; j < (i32)mat->contexts->length; ++j) { material_context_t *c = (material_context_t *)mat->contexts->buffer[j]; if (string_equals(c->name, context)) { scontext = shader_data_get_context(mat->_->shader, context); mcontext = c; break; } } if (scontext->_->pipe != _mesh_object_last_pipeline) { gpu_set_pipeline(scontext->_->pipe); _mesh_object_last_pipeline = scontext->_->pipe; } uniforms_set_context_consts(scontext, bind_params); uniforms_set_obj_consts(scontext, raw->base); uniforms_set_material_consts(scontext, mcontext); gpu_set_vertex_buffer(raw->data->_->vertex_buffer); gpu_set_index_buffer(raw->data->_->index_buffer); gpu_draw(); } bool mesh_object_valid_context(mesh_object_t *raw, material_data_t *mat, char *context) { return material_data_get_context(mat, context) != NULL; } // ██████╗ █████╗ ███╗ ███╗███████╗██████╗ █████╗ ██████╗ ██████╗ ██╗███████╗ ██████╗████████╗ // ██╔════╝██╔══██╗████╗ ████║██╔════╝██╔══██╗██╔══██╗ ██╔═══██╗██╔══██╗ ██║██╔════╝██╔════╝╚══██╔══╝ // ██║ ███████║██╔████╔██║█████╗ ██████╔╝███████║ ██║ ██║██████╔╝ ██║█████╗ ██║ ██║ // ██║ ██╔══██║██║╚██╔╝██║██╔══╝ ██╔══██╗██╔══██║ ██║ ██║██╔══██╗██ ██║██╔══╝ ██║ ██║ // ╚██████╗██║ ██║██║ ╚═╝ ██║███████╗██║ ██║██║ ██║ ╚██████╔╝██████╔╝╚█████╔╝███████╗╚██████╗ ██║ // ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝ ╚═╝ camera_object_t *camera_object_create(camera_data_t *data) { camera_object_t *raw = calloc(1, sizeof(camera_object_t)); raw->no_jitter_p = mat4_identity(); raw->frame = 0; raw->base = object_create(false); raw->base->ext = raw; raw->base->ext_type = "camera_object_t"; raw->data = data; camera_object_build_proj(raw, -1.0); raw->v = mat4_identity(); raw->vp = mat4_identity(); if (data->frustum_culling) { raw->frustum_planes = any_array_create(0); for (i32 i = 0; i < 6; ++i) { any_array_push(raw->frustum_planes, frustum_plane_create()); } } any_array_push(scene_cameras, raw); return raw; } void camera_object_build_proj(camera_object_t *raw, f32 screen_aspect) { if (raw->data->ortho != NULL) { raw->p = mat4_ortho(raw->data->ortho->buffer[0], raw->data->ortho->buffer[1], raw->data->ortho->buffer[2], raw->data->ortho->buffer[3], -raw->data->far_plane, raw->data->far_plane); } else { if (screen_aspect < 0) { screen_aspect = (f32)sys_w() / (f32)sys_h(); } f32 aspect = raw->data->aspect != 0.0 ? raw->data->aspect : screen_aspect; raw->p = mat4_persp(raw->data->fov, aspect, raw->data->near_plane, raw->data->far_plane); } raw->no_jitter_p = raw->p; } void camera_object_remove(camera_object_t *raw) { array_remove(scene_cameras, raw); object_remove_super(raw->base); object_free(raw->base); if (raw->frustum_planes != NULL) { for (i32 i = 0; i < raw->frustum_planes->length; ++i) { free(raw->frustum_planes->buffer[i]); } array_delete(raw->frustum_planes); } free(raw); } void camera_object_proj_jitter(camera_object_t *raw) { i32 w = render_path_current_w; i32 h = render_path_current_h; raw->p = raw->no_jitter_p; i32 i = raw->frame % 2; f32 x = i == 0 ? -0.5 : 0.5; f32 y = i == 0 ? 0.5 : -0.5; raw->p.m20 += x / w; raw->p.m21 += y / h; } void camera_object_build_mat(camera_object_t *raw) { transform_build_matrix(raw->base->transform); raw->v = mat4_inv(raw->base->transform->world); raw->vp = mat4_mult_mat(raw->v, raw->p); if (raw->data->frustum_culling) { camera_object_build_view_frustum(raw->vp, raw->frustum_planes); } } vec4_t camera_object_right(camera_object_t *raw) { return vec4_norm((vec4_t){raw->base->transform->local.m00, raw->base->transform->local.m01, raw->base->transform->local.m02, 0.0}); } vec4_t camera_object_up(camera_object_t *raw) { return vec4_norm((vec4_t){raw->base->transform->local.m10, raw->base->transform->local.m11, raw->base->transform->local.m12, 0.0}); } vec4_t camera_object_look(camera_object_t *raw) { return vec4_norm((vec4_t){-raw->base->transform->local.m20, -raw->base->transform->local.m21, -raw->base->transform->local.m22, 0.0}); } vec4_t camera_object_right_world(camera_object_t *raw) { return vec4_norm((vec4_t){raw->base->transform->world.m00, raw->base->transform->world.m01, raw->base->transform->world.m02, 0.0}); } vec4_t camera_object_up_world(camera_object_t *raw) { return vec4_norm((vec4_t){raw->base->transform->world.m10, raw->base->transform->world.m11, raw->base->transform->world.m12, 0.0}); } vec4_t camera_object_look_world(camera_object_t *raw) { return vec4_norm((vec4_t){-raw->base->transform->world.m20, -raw->base->transform->world.m21, -raw->base->transform->world.m22, 0.0}); } void camera_object_build_view_frustum(mat4_t vp, frustum_plane_array_t *frustum_planes) { // Left plane frustum_plane_set_components(frustum_planes->buffer[0], vp.m03 + vp.m00, vp.m13 + vp.m10, vp.m23 + vp.m20, vp.m33 + vp.m30); // Right plane frustum_plane_set_components(frustum_planes->buffer[1], vp.m03 - vp.m00, vp.m13 - vp.m10, vp.m23 - vp.m20, vp.m33 - vp.m30); // Top plane frustum_plane_set_components(frustum_planes->buffer[2], vp.m03 - vp.m01, vp.m13 - vp.m11, vp.m23 - vp.m21, vp.m33 - vp.m31); // Bottom plane frustum_plane_set_components(frustum_planes->buffer[3], vp.m03 + vp.m01, vp.m13 + vp.m11, vp.m23 + vp.m21, vp.m33 + vp.m31); // Near plane frustum_plane_set_components(frustum_planes->buffer[4], vp.m02, vp.m12, vp.m22, vp.m32); // Far plane frustum_plane_set_components(frustum_planes->buffer[5], vp.m03 - vp.m02, vp.m13 - vp.m12, vp.m23 - vp.m22, vp.m33 - vp.m32); // Normalize planes for (i32 i = 0; i < frustum_planes->length; ++i) { frustum_plane_normalize(frustum_planes->buffer[i]); } } bool camera_object_sphere_in_frustum(frustum_plane_array_t *frustum_planes, transform_t *t, f32 radius_scale, f32 offset_x, f32 offset_y, f32 offset_z) { // Use scale when radius is changing f32 radius = t->radius * radius_scale; for (i32 i = 0; i < frustum_planes->length; ++i) { frustum_plane_t *plane = frustum_planes->buffer[i]; _camera_object_sphere_center = (vec4_t){transform_world_x(t) + offset_x, transform_world_y(t) + offset_y, transform_world_z(t) + offset_z, 0.0}; // Outside the frustum if (frustum_plane_dist_to_sphere(plane, _camera_object_sphere_center, radius) + radius * 2 < 0) { return false; } } return true; } // ███████╗██████╗ ██╗ ██╗███████╗████████╗██╗ ██╗███╗ ███╗ ██████╗ ██╗ █████╗ ███╗ ██╗███████╗ // ██╔════╝██╔══██╗██║ ██║██╔════╝╚══██╔══╝██║ ██║████╗ ████║ ██╔══██╗██║ ██╔══██╗████╗ ██║██╔════╝ // █████╗ ██████╔╝██║ ██║███████╗ ██║ ██║ ██║██╔████╔██║ ██████╔╝██║ ███████║██╔██╗ ██║█████╗ // ██╔══╝ ██╔══██╗██║ ██║╚════██║ ██║ ██║ ██║██║╚██╔╝██║ ██╔═══╝ ██║ ██╔══██║██║╚██╗██║██╔══╝ // ██║ ██║ ██║╚██████╔╝███████║ ██║ ╚██████╔╝██║ ╚═╝ ██║ ██║ ███████╗██║ ██║██║ ╚████║███████╗ // ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝ frustum_plane_t *frustum_plane_create() { frustum_plane_t *raw = calloc(1, sizeof(frustum_plane_t)); raw->normal = (vec4_t){1.0, 0.0, 0.0, 0.0}; raw->constant = 0.0; return raw; } void frustum_plane_normalize(frustum_plane_t *raw) { f32 inv_normal_length = 1.0 / vec4_len(raw->normal); raw->normal = vec4_mult(raw->normal, inv_normal_length); raw->constant *= inv_normal_length; } f32 frustum_plane_dist_to_sphere(frustum_plane_t *raw, vec4_t sphere_center, f32 sphere_radius) { return (vec4_dot(raw->normal, sphere_center) + raw->constant) - sphere_radius; } void frustum_plane_set_components(frustum_plane_t *raw, f32 x, f32 y, f32 z, f32 w) { raw->normal = (vec4_t){x, y, z, 0.0}; raw->constant = w; } // ██╗ ██╗███╗ ██╗██╗███████╗ ██████╗ ██████╗ ███╗ ███╗███████╗ // ██║ ██║████╗ ██║██║██╔════╝██╔═══██╗██╔══██╗████╗ ████║██╔════╝ // ██║ ██║██╔██╗ ██║██║█████╗ ██║ ██║██████╔╝██╔████╔██║███████╗ // ██║ ██║██║╚██╗██║██║██╔══╝ ██║ ██║██╔══██╗██║╚██╔╝██║╚════██║ // ╚██████╔╝██║ ╚████║██║██║ ╚██████╔╝██║ ██║██║ ╚═╝ ██║███████║ // ╚═════╝ ╚═╝ ╚═══╝╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ f32 uniforms_pos_unpack = 1.0; f32 uniforms_tex_unpack = 1.0; gpu_texture_t *(*uniforms_tex_links)(object_t *o, material_data_t *md, char *s) = NULL; mat4_t (*uniforms_mat4_links)(object_t *o, material_data_t *md, char *s) = NULL; vec4_t (*uniforms_vec4_links)(object_t *o, material_data_t *md, char *s) = NULL; vec4_t (*uniforms_vec3_links)(object_t *o, material_data_t *md, char *s) = NULL; vec2_t (*uniforms_vec2_links)(object_t *o, material_data_t *md, char *s) = NULL; f32 (*uniforms_f32_links)(object_t *o, material_data_t *md, char *s) = NULL; f32_array_t *(*uniforms_f32_array_links)(object_t *o, material_data_t *md, char *s) = NULL; i32 (*uniforms_i32_links)(object_t *o, material_data_t *md, char *s) = NULL; void uniforms_set_context_consts(shader_context_t *context, string_array_t *bind_params) { // On shader compile error, _.constants.length will be 0 if (context->constants != NULL && context->constants->length == context->_->constants->length) { for (i32 i = 0; i < context->constants->length; ++i) { shader_const_t *c = context->constants->buffer[i]; uniforms_set_context_const(context->_->constants->buffer[i], c); } } // Texture context constants if (bind_params != NULL) { // Bind targets for (i32 i = 0; i < (i32)math_floor(bind_params->length / 2); ++i) { i32 pos = i * 2; // bind params = [texture, sampler_id] char *rt_id = bind_params->buffer[pos]; char *sampler_id = bind_params->buffer[pos + 1]; render_target_t *rt = any_map_get(render_path_render_targets, rt_id); uniforms_bind_render_target(rt, context, sampler_id); } } // Texture links if (context->texture_units != NULL && context->texture_units->length == context->_->tex_units->length) { for (i32 j = 0; j < context->texture_units->length; ++j) { char *tulink = context->texture_units->buffer[j]->link; if (tulink == NULL) { continue; } if (tulink[0] == '$') { // Link to embedded data gpu_set_texture(context->_->tex_units->buffer[j], any_map_get(scene_embedded, substring(tulink, 1, string_length(tulink)))); } else if (string_equals(tulink, "_envmap_radiance")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->radiance); } } else if (string_equals(tulink, "_envmap_radiance0")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->radiance_mipmaps->buffer[0]); } } else if (string_equals(tulink, "_envmap_radiance1")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->radiance_mipmaps->buffer[1]); } } else if (string_equals(tulink, "_envmap_radiance2")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->radiance_mipmaps->buffer[2]); } } else if (string_equals(tulink, "_envmap_radiance3")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->radiance_mipmaps->buffer[3]); } } else if (string_equals(tulink, "_envmap_radiance4")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->radiance_mipmaps->buffer[4]); } } else if (string_equals(tulink, "_envmap")) { world_data_t *w = scene_world; if (w != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], w->_->envmap); } } } } } void uniforms_set_obj_consts(shader_context_t *context, object_t *object) { if (context->constants != NULL && context->constants->length == context->_->constants->length) { for (i32 i = 0; i < context->constants->length; ++i) { shader_const_t *c = context->constants->buffer[i]; uniforms_set_obj_const(object, context->_->constants->buffer[i], c); } } // Texture object constants // External if (uniforms_tex_links != NULL && context->texture_units != NULL && context->texture_units->length == context->_->tex_units->length) { for (i32 j = 0; j < context->texture_units->length; ++j) { tex_unit_t *tu = context->texture_units->buffer[j]; if (tu->link == NULL) { continue; } gpu_texture_t *image = uniforms_tex_links(object, current_material(object), tu->link); if (image != NULL) { gpu_set_texture(context->_->tex_units->buffer[j], image); } } } } void uniforms_bind_render_target(render_target_t *rt, shader_context_t *context, char *sampler_id) { if (rt == NULL) { return; } if (context->texture_units != NULL && context->texture_units->length == context->_->tex_units->length) { for (i32 j = 0; j < context->texture_units->length; ++j) { // Set texture if (string_equals(sampler_id, context->texture_units->buffer[j]->name)) { gpu_set_texture(context->_->tex_units->buffer[j], rt->_image); } } } } bool uniforms_set_context_const(i32 location, shader_const_t *c) { if (c->link == NULL) { return true; } camera_object_t *camera = scene_camera; if (string_equals(c->type, "float4x4")) { mat4_t m = mat4_nan(); if (string_equals(c->link, "_view_matrix")) { m = camera->v; } else if (string_equals(c->link, "_proj_matrix")) { m = camera->p; } else if (string_equals(c->link, "_inv_proj_matrix")) { m = mat4_inv(camera->p); } else if (string_equals(c->link, "_view_proj_matrix")) { m = camera->vp; } else if (string_equals(c->link, "_inv_view_proj_matrix")) { m = mat4_mult_mat(camera->v, camera->p); m = mat4_inv(m); } else if (string_equals(c->link, "_skydome_matrix")) { transform_t *tr = camera->base->transform; vec4_t v = (vec4_t){transform_world_x(tr), transform_world_y(tr), transform_world_z(tr), 0.0}; f32 bounds = camera->data->far_plane * 0.9f; vec4_t v2 = (vec4_t){bounds, bounds, bounds, 0.0}; m = mat4_compose(v, (quat_t){0.0, 0.0, 0.0, 1.0}, v2); m = mat4_mult_mat(m, camera->v); m = mat4_mult_mat(m, camera->p); } else { // Unknown uniform return false; } gpu_set_mat4(location, m); return true; } else if (string_equals(c->type, "float4")) { vec4_t v = vec4_nan(); if (string_equals(c->link, "_envmap_irradiance0")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[0]; v.y = fa->buffer[1]; v.z = fa->buffer[2]; v.w = fa->buffer[3]; } else if (string_equals(c->link, "_envmap_irradiance1")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[4]; v.y = fa->buffer[5]; v.z = fa->buffer[6]; v.w = fa->buffer[7]; } else if (string_equals(c->link, "_envmap_irradiance2")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[8]; v.y = fa->buffer[9]; v.z = fa->buffer[10]; v.w = fa->buffer[11]; } else if (string_equals(c->link, "_envmap_irradiance3")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[12]; v.y = fa->buffer[13]; v.z = fa->buffer[14]; v.w = fa->buffer[15]; } else if (string_equals(c->link, "_envmap_irradiance4")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[16]; v.y = fa->buffer[17]; v.z = fa->buffer[18]; v.w = fa->buffer[19]; } else if (string_equals(c->link, "_envmap_irradiance5")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[20]; v.y = fa->buffer[21]; v.z = fa->buffer[22]; v.w = fa->buffer[23]; } else if (string_equals(c->link, "_envmap_irradiance6")) { f32_array_t *fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; v.x = fa->buffer[24]; v.y = fa->buffer[25]; v.z = fa->buffer[26]; v.w = fa->buffer[27]; } else { return false; } if (!vec4_isnan(v)) { gpu_set_float4(location, v.x, v.y, v.z, v.w); } else { gpu_set_float4(location, 0, 0, 0, 0); } return true; } else if (string_equals(c->type, "float3")) { vec4_t v = vec4_nan(); if (string_equals(c->link, "_camera_pos")) { v = (vec4_t){transform_world_x(camera->base->transform), transform_world_y(camera->base->transform), transform_world_z(camera->base->transform), 0.0}; } else if (string_equals(c->link, "_camera_look")) { v = vec4_norm(camera_object_look_world(camera)); } else { return false; } if (!vec4_isnan(v)) { gpu_set_float3(location, v.x, v.y, v.z); } else { gpu_set_float3(location, 0.0, 0.0, 0.0); } return true; } else if (string_equals(c->type, "float2")) { vec4_t v = vec4_nan(); if (string_equals(c->link, "_vec2x")) { v.x = 1.0; v.y = 0.0; } else if (string_equals(c->link, "_vec2x_inv")) { v.x = 1.0f / render_path_current_w; v.y = 0.0; } else if (string_equals(c->link, "_vec2x2")) { v.x = 2.0; v.y = 0.0; } else if (string_equals(c->link, "_vec2x2_inv")) { v.x = 2.0f / render_path_current_w; v.y = 0.0; } else if (string_equals(c->link, "_vec2y")) { v.x = 0.0; v.y = 1.0; } else if (string_equals(c->link, "_vec2y_inv")) { v.x = 0.0; v.y = 1.0f / render_path_current_h; } else if (string_equals(c->link, "_vec2y2")) { v.x = 0.0; v.y = 2.0; } else if (string_equals(c->link, "_vec2y2_inv")) { v.x = 0.0; v.y = 2.0f / render_path_current_h; } else if (string_equals(c->link, "_vec2y3")) { v.x = 0.0; v.y = 3.0; } else if (string_equals(c->link, "_vec2y3_inv")) { v.x = 0.0; v.y = 3.0f / render_path_current_h; } else if (string_equals(c->link, "_screen_size")) { v.x = (f32)render_path_current_w; v.y = (f32)render_path_current_h; } else if (string_equals(c->link, "_screen_size_inv")) { v.x = 1.0f / render_path_current_w; v.y = 1.0f / render_path_current_h; } else if (string_equals(c->link, "_camera_plane_proj")) { f32 znear = camera->data->near_plane; f32 zfar = camera->data->far_plane; v.x = zfar / (zfar - znear); v.y = (-zfar * znear) / (zfar - znear); } else if (starts_with(c->link, "_size(")) { char *tex = string_tmp("%.*s", string_length(c->link) - 7, c->link + 6); gpu_texture_t *image = uniforms_tex_links != NULL ? uniforms_tex_links(NULL, NULL, tex) : NULL; if (image != NULL) { v.x = (f32)image->width; v.y = (f32)image->height; } else if (_render_path_bind_params != NULL) { for (i32 i = 0; i < (i32)math_floor(_render_path_bind_params->length / 2); ++i) { i32 pos = i * 2; // bind params = [texture, sampler_id] char *sampler_id = _render_path_bind_params->buffer[pos + 1]; if (string_equals(sampler_id, tex)) { char *rt_id = _render_path_bind_params->buffer[pos]; render_target_t *rt = any_map_get(render_path_render_targets, rt_id); v.x = (f32)rt->width; v.y = (f32)rt->height; } } } } else { return false; } if (!vec4_isnan(v)) { gpu_set_float2(location, v.x, v.y); } else { gpu_set_float2(location, 0.0, 0.0); } return true; } else if (string_equals(c->type, "float")) { f32 f = 0.0; if (string_equals(c->link, "_time")) { f = sys_time(); } else if (string_equals(c->link, "_aspect_ratio_window")) { f = (f32)sys_w() / (f32)sys_h(); } else { return false; } gpu_set_float(location, f); return true; } else if (string_equals(c->type, "floats")) { f32_array_t *fa = NULL; if (string_equals(c->link, "_envmap_irradiance")) { fa = scene_world == NULL ? world_data_get_empty_irradiance() : scene_world->_->irradiance; } if (fa != NULL) { gpu_set_floats(location, fa); return true; } } else if (string_equals(c->type, "int")) { i32 i = 0; if (string_equals(c->link, "_envmap_num_mipmaps")) { world_data_t *w = scene_world; i = w != NULL ? w->radiance_mipmaps + 1 - 2 : 1; // Include basecolor and exclude 2 scaled mips } else { return false; } gpu_set_int(location, i); return true; } return false; } vec4_t uniforms_vec4_zero_to_one(vec4_t v) { v.x = v.x == 0.0 ? 1.0 : v.x; v.y = v.y == 0.0 ? 1.0 : v.y; v.z = v.z == 0.0 ? 1.0 : v.z; return v; } void uniforms_set_obj_const(object_t *obj, i32 loc, shader_const_t *c) { if (c->link == NULL) { return; } camera_object_t *camera = scene_camera; if (string_equals(c->type, "float4x4")) { mat4_t m = mat4_nan(); if (string_equals(c->link, "_world_matrix")) { m = obj->transform->world_unpack; } else if (string_equals(c->link, "_inv_world_matrix")) { m = mat4_inv(obj->transform->world_unpack); } else if (string_equals(c->link, "_world_view_proj_matrix")) { m = mat4_mult_mat(obj->transform->world_unpack, camera->v); m = mat4_mult_mat(m, camera->p); } else if (string_equals(c->link, "_world_wiew_matrix")) { m = mat4_mult_mat(obj->transform->world_unpack, camera->v); } else if (uniforms_mat4_links != NULL) { m = uniforms_mat4_links(obj, current_material(obj), c->link); } if (mat4_isnan(m)) { return; } gpu_set_mat4(loc, m); } else if (string_equals(c->type, "float3x3")) { mat3_t m = mat3_nan(); if (string_equals(c->link, "_normal_matrix")) { mat4_t m4 = mat4_inv(obj->transform->world); m4 = mat4_transpose3(m4); m = mat3_set_from4(m4); } else if (string_equals(c->link, "_view_matrix3")) { m = mat3_set_from4(camera->v); } if (mat3_isnan(m)) { return; } gpu_set_mat3(loc, m); } else if (string_equals(c->type, "float4")) { vec4_t v = vec4_nan(); if (uniforms_vec4_links != NULL) { v = uniforms_vec4_links(obj, current_material(obj), c->link); } if (vec4_isnan(v)) { return; } gpu_set_float4(loc, v.x, v.y, v.z, v.w); } else if (string_equals(c->type, "float3")) { vec4_t v = vec4_nan(); if (string_equals(c->link, "_dim")) { // Model space vec4_t d = obj->transform->dim; vec4_t s = obj->transform->scale; d = uniforms_vec4_zero_to_one(d); s = uniforms_vec4_zero_to_one(s); v = (vec4_t){(d.x / s.x), (d.y / s.y), (d.z / s.z), 0.0}; } else if (string_equals(c->link, "_half_dim")) { // Model space vec4_t d = obj->transform->dim; vec4_t s = obj->transform->scale; d = uniforms_vec4_zero_to_one(d); s = uniforms_vec4_zero_to_one(s); v = (vec4_t){(d.x / s.x) / 2, (d.y / s.y) / 2, (d.z / s.z) / 2, 0.0}; } else if (uniforms_vec3_links != NULL) { v = uniforms_vec3_links(obj, current_material(obj), c->link); } if (vec4_isnan(v)) { return; } gpu_set_float3(loc, v.x, v.y, v.z); } else if (string_equals(c->type, "float2")) { vec2_t v = vec2_nan(); if (uniforms_vec2_links != NULL) { v = uniforms_vec2_links(obj, current_material(obj), c->link); } if (vec2_isnan(v)) { return; } gpu_set_float2(loc, v.x, v.y); } else if (string_equals(c->type, "float")) { f32 f = NAN; if (string_equals(c->link, "_object_info_index")) { f = (f32)obj->uid; } else if (string_equals(c->link, "_object_info_material_index")) { f = current_material(obj)->_->uid; } else if (string_equals(c->link, "_object_info_random")) { f = obj->urandom; } else if (string_equals(c->link, "_pos_unpack")) { f = uniforms_pos_unpack; } else if (string_equals(c->link, "_tex_unpack")) { f = uniforms_tex_unpack; } else if (uniforms_f32_links != NULL) { f = uniforms_f32_links(obj, current_material(obj), c->link); } if (isnan(f)) { return; } gpu_set_float(loc, f); } else if (string_equals(c->type, "floats")) { f32_array_t *fa = NULL; if (uniforms_f32_array_links != NULL) { fa = uniforms_f32_array_links(obj, current_material(obj), c->link); } if (fa == NULL) { return; } gpu_set_floats(loc, fa); } else if (string_equals(c->type, "int")) { i32 i = INT_MAX; if (string_equals(c->link, "_uid")) { i = obj->uid; } else if (uniforms_i32_links != NULL) { i = uniforms_i32_links(obj, current_material(obj), c->link); } if (i == INT_MAX) { return; } gpu_set_int(loc, i); } } void uniforms_set_material_consts(shader_context_t *context, material_context_t *material_context) { if (material_context->bind_constants != NULL) { for (i32 i = 0; i < material_context->bind_constants->length; ++i) { bind_const_t *matc = material_context->bind_constants->buffer[i]; i32 pos = -1; for (i32 j = 0; j < context->constants->length; ++j) { char *name = context->constants->buffer[j]->name; if (string_equals(name, matc->name)) { pos = j; break; } } if (pos == -1) { continue; } shader_const_t *c = context->constants->buffer[pos]; uniforms_set_material_const(context->_->constants->buffer[pos], c, matc); } } if (material_context->_->textures != NULL) { for (i32 i = 0; i < material_context->_->textures->length; ++i) { char *mname = material_context->bind_textures->buffer[i]->name; for (i32 j = 0; j < context->_->tex_units->length; ++j) { char *sname = context->texture_units->buffer[j]->name; if (string_equals(mname, sname)) { gpu_set_texture(context->_->tex_units->buffer[j], material_context->_->textures->buffer[i]); break; } } } } } material_data_t *current_material(object_t *object) { if (object != NULL && object->ext != NULL) { mesh_object_t *mo = object->ext; return mo->material; } return NULL; } void uniforms_set_material_const(i32 location, shader_const_t *shader_const, bind_const_t *material_const) { if (string_equals(shader_const->type, "float4")) { gpu_set_float4(location, material_const->vec->buffer[0], material_const->vec->buffer[1], material_const->vec->buffer[2], material_const->vec->buffer[3]); } else if (string_equals(shader_const->type, "float3")) { gpu_set_float3(location, material_const->vec->buffer[0], material_const->vec->buffer[1], material_const->vec->buffer[2]); } else if (string_equals(shader_const->type, "float2")) { gpu_set_float2(location, material_const->vec->buffer[0], material_const->vec->buffer[1]); } else if (string_equals(shader_const->type, "float")) { gpu_set_float(location, material_const->vec->buffer[0]); } else if (string_equals(shader_const->type, "bool")) { gpu_set_bool(location, material_const->vec->buffer[0] > 0.0f); } else if (string_equals(shader_const->type, "int")) { gpu_set_int(location, (i32)math_floor(material_const->vec->buffer[0])); } } // ██████╗ █████╗ ████████╗ █████╗ // ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ // ██║ ██║███████║ ██║ ███████║ // ██║ ██║██╔══██║ ██║ ██╔══██║ // ██████╔╝██║ ██║ ██║ ██║ ██║ // ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ any_map_t *data_cached_scene_raws = NULL; any_map_t *data_cached_meshes = NULL; any_map_t *data_cached_cameras = NULL; any_map_t *data_cached_materials = NULL; any_map_t *data_cached_worlds = NULL; any_map_t *data_cached_shaders = NULL; any_map_t *data_cached_blobs = NULL; any_map_t *data_cached_textures = NULL; any_map_t *data_cached_videos = NULL; any_map_t *data_cached_fonts = NULL; any_map_t *data_cached_sounds = NULL; i32 data_assets_loaded = 0; buffer_t *iron_load_blob(char *file); gpu_texture_t *iron_load_texture(char *file); void gpu_delete_texture(gpu_texture_t *texture); void *iron_load_sound(char *file); #ifdef WITH_EMBED bool embed_owns(uint8_t *buffer); #endif char *data_path(void) { #ifdef IRON_ANDROID return "data" PATH_SEP; #else return "." PATH_SEP "data" PATH_SEP; #endif } bool data_is_abs(char *file) { return file[0] == '/' || (file[0] != '\0' && file[1] == ':') || (file[0] == '\\' && file[1] == '\\'); } bool data_is_up(char *file) { return file[0] == '.' && file[1] == '.'; } char *data_resolve_path(char *file) { if (data_is_abs(file) || data_is_up(file)) { return file; } #ifdef IRON_WASM if (starts_with(file, "./")) { return file; } #endif return string("%s%s", data_path(), file); } mesh_data_t *data_get_mesh(char *file, char *name) { if (data_cached_meshes == NULL) { data_cached_meshes = any_map_create(); } char *handle = string("%s%s", file, name); mesh_data_t *cached = (mesh_data_t *)any_map_get(data_cached_meshes, handle); if (cached != NULL) { return cached; } mesh_data_t *b = mesh_data_parse(file, name); any_map_set(data_cached_meshes, string_copy(handle), b); b->_->handle = handle; return b; } camera_data_t *data_get_camera(char *file, char *name) { if (data_cached_cameras == NULL) { data_cached_cameras = any_map_create(); } char *handle = string("%s%s", file, name); camera_data_t *cached = (camera_data_t *)any_map_get(data_cached_cameras, handle); if (cached != NULL) { return cached; } camera_data_t *b = camera_data_parse(file, name); any_map_set(data_cached_cameras, string_copy(handle), b); return b; } material_data_t *data_get_material(char *file, char *name) { if (data_cached_materials == NULL) { data_cached_materials = any_map_create(); } char *handle = string("%s%s", file, name); material_data_t *cached = (material_data_t *)any_map_get(data_cached_materials, handle); if (cached != NULL) { return cached; } material_data_t *b = material_data_parse(file, name); any_map_set(data_cached_materials, string_copy(handle), b); return b; } world_data_t *data_get_world(char *file, char *name) { if (data_cached_worlds == NULL) { data_cached_worlds = any_map_create(); } char *handle = string("%s%s", file, name); world_data_t *cached = (world_data_t *)any_map_get(data_cached_worlds, handle); if (cached != NULL) { return cached; } world_data_t *b = world_data_parse(file, name); any_map_set(data_cached_worlds, string_copy(handle), b); return b; } shader_data_t *data_get_shader(char *file, char *name) { if (data_cached_shaders == NULL) { data_cached_shaders = any_map_create(); } // Only one context override per shader data for now char *handle = name; // Shader must have unique name shader_data_t *cached = (shader_data_t *)any_map_get(data_cached_shaders, handle); if (cached != NULL) { return cached; } shader_data_t *b = shader_data_parse(file, name); any_map_set(data_cached_shaders, string_copy(handle), b); return b; } scene_t *data_get_scene_raw(char *file) { if (data_cached_scene_raws == NULL) { data_cached_scene_raws = any_map_create(); } scene_t *cached = (scene_t *)any_map_get(data_cached_scene_raws, file); if (cached != NULL) { return cached; } // If no extension specified, set to .arm char *ext = ends_with(file, ".arm") ? "" : ".arm"; buffer_t *b = data_get_blob(string("%s%s", file, ext)); scene_t *parsed = (scene_t *)armpack_decode(b); any_map_set(data_cached_scene_raws, string_copy(file), parsed); return parsed; } buffer_t *data_get_blob(char *file) { if (data_cached_blobs == NULL) { data_cached_blobs = any_map_create(); } buffer_t *cached = (buffer_t *)any_map_get(data_cached_blobs, file); if (cached != NULL) { return cached; } buffer_t *b = iron_load_blob(data_resolve_path(file)); any_map_set(data_cached_blobs, string_copy(file), b); data_assets_loaded++; return b; } gpu_texture_t *data_get_texture(char *file) { if (data_cached_textures == NULL) { data_cached_textures = any_map_create(); } gpu_texture_t *cached = (gpu_texture_t *)any_map_get(data_cached_textures, file); if (cached != NULL) { return cached; } gpu_texture_t *b = iron_load_texture(data_resolve_path(file)); if (b == NULL) { return NULL; } any_map_set(data_cached_textures, string_copy(file), b); data_assets_loaded++; return b; } video_t *data_get_video(char *file) { if (data_cached_videos == NULL) { data_cached_videos = any_map_create(); } // Strip extension and use .webm char *base = substring(file, 0, string_length(file) - 4); char *webm = string("%s.webm", base); video_t *cached = (video_t *)any_map_get(data_cached_videos, webm); if (cached != NULL) { return cached; } // let b: video_t = iron_load_video(data_resolve_path(file)); // map_set(data_cached_videos, file, b); // data_assets_loaded++; // return b; return NULL; } draw_font_t *data_get_font(char *file) { if (data_cached_fonts == NULL) { data_cached_fonts = any_map_create(); } draw_font_t *cached = (draw_font_t *)any_map_get(data_cached_fonts, file); if (cached != NULL) { return cached; } buffer_t *blob = iron_load_blob(data_resolve_path(file)); draw_font_t *b = calloc(1, sizeof(draw_font_t)); b->buf = blob; b->index = 0; any_map_set(data_cached_fonts, string_copy(file), b); data_assets_loaded++; return b; } sound_t *data_get_sound(char *file) { if (data_cached_sounds == NULL) { data_cached_sounds = any_map_create(); } sound_t *cached = (sound_t *)any_map_get(data_cached_sounds, file); if (cached != NULL) { return cached; } sound_t *b = iron_load_sound(data_resolve_path(file)); any_map_set(data_cached_sounds, string_copy(file), b); data_assets_loaded++; return b; } void data_delete_sound(char *handle) { if (data_cached_sounds == NULL) { return; } sound_t *sound = (sound_t *)any_map_get(data_cached_sounds, handle); if (sound == NULL) { return; } #ifdef IRON_AUDIO iron_a1_sound_destroy(sound); #endif map_delete(data_cached_sounds, handle); } void data_delete_mesh(char *handle) { if (data_cached_meshes == NULL) { return; } mesh_data_t *mesh = (mesh_data_t *)any_map_get(data_cached_meshes, handle); if (mesh == NULL) { return; } mesh_data_delete(mesh); map_delete(data_cached_meshes, handle); } void data_delete_blob(char *handle) { if (data_cached_blobs == NULL) { return; } buffer_t *blob = (buffer_t *)any_map_get(data_cached_blobs, handle); if (blob == NULL) { return; } map_delete(data_cached_blobs, handle); #ifdef WITH_EMBED if (embed_owns(blob->buffer)) { free(blob); return; } #endif free(blob->buffer); free(blob); } void data_delete_texture(char *handle) { if (data_cached_textures == NULL) { return; } gpu_texture_t *image = (gpu_texture_t *)any_map_get(data_cached_textures, handle); if (image == NULL) { return; } gpu_delete_texture(image); map_delete(data_cached_textures, handle); } void data_delete_video(char *handle) { if (data_cached_videos == NULL) { return; } video_t *video = (video_t *)any_map_get(data_cached_videos, handle); if (video == NULL) { return; } video_unload(video); map_delete(data_cached_videos, handle); } void data_delete_font(char *handle) { if (data_cached_fonts == NULL) { return; } draw_font_t *font = (draw_font_t *)any_map_get(data_cached_fonts, handle); if (font == NULL) { return; } draw_font_destroy(font); map_delete(data_cached_fonts, handle); } // ███████╗ ██████╗███████╗███╗ ██╗███████╗ // ██╔════╝██╔════╝██╔════╝████╗ ██║██╔════╝ // ███████╗██║ █████╗ ██╔██╗ ██║█████╗ // ╚════██║██║ ██╔══╝ ██║╚██╗██║██╔══╝ // ███████║╚██████╗███████╗██║ ╚████║███████╗ // ╚══════╝ ╚═════╝╚══════╝╚═╝ ╚═══╝╚══════╝ camera_object_t *scene_camera = NULL; world_data_t *scene_world = NULL; any_array_t *scene_meshes = NULL; any_array_t *scene_cameras = NULL; any_array_t *scene_empties = NULL; any_map_t *scene_embedded = NULL; i32 _scene_uid_counter = 0; i32 _scene_uid = 0; scene_t *_scene_raw = NULL; object_t *_scene_root = NULL; i32 _scene_objects_traversed = 0; i32 _scene_objects_count = 0; object_t *scene_create(scene_t *format) { _scene_uid = _scene_uid_counter++; scene_meshes = any_array_create(0); scene_cameras = any_array_create(0); scene_empties = any_array_create(0); scene_embedded = any_map_create(); _scene_root = object_create(true); _scene_root->name = format->name; _scene_raw = format; scene_world = data_get_world(format->name, format->world_ref); // Startup scene scene_add_scene(format->name); scene_camera = (camera_object_t *)scene_cameras->buffer[0]; // format->camera_ref return _scene_root; } void scene_remove(void) { while (scene_meshes->length > 0) { mesh_object_remove((mesh_object_t *)scene_meshes->buffer[0]); } while (scene_cameras->length > 0) { camera_object_remove((camera_object_t *)scene_cameras->buffer[0]); } while (scene_empties->length > 0) { object_remove((object_t *)scene_empties->buffer[0]); } _scene_root = NULL; scene_camera = NULL; } object_t *scene_set_active(char *scene_name) { if (_scene_root != NULL) { scene_remove(); } scene_t *format = data_get_scene_raw(scene_name); object_t *o = scene_create(format); return o; } void scene_render_frame(void) { if (render_path_commands == NULL) { return; } for (i32 i = 0; i < scene_empties->length; ++i) { object_t *e = (object_t *)scene_empties->buffer[i]; if (e != NULL && e->parent != NULL) { transform_update(e->transform); } } camera_object_proj_jitter(scene_camera); camera_object_build_mat(scene_camera); render_path_render_frame(); scene_camera->frame++; } object_t *scene_add_object(object_t *parent) { object_t *object = object_create(true); if (parent != NULL) { object_set_parent(object, parent); } else { object_set_parent(object, _scene_root); } return object; } object_t *scene_get_child(char *name) { return object_get_child(_scene_root, name); } mesh_object_t *scene_add_mesh_object(mesh_data_t *data, material_data_t *material, object_t *parent) { mesh_object_t *object = mesh_object_create(data, material); if (parent != NULL) { object_set_parent(object->base, parent); } else { object_set_parent(object->base, _scene_root); } return object; } camera_object_t *scene_add_camera_object(camera_data_t *data, object_t *parent) { camera_object_t *object = camera_object_create(data); if (parent != NULL) { object_set_parent(object->base, parent); } else { object_set_parent(object->base, _scene_root); } return object; } void scene_traverse_objects(scene_t *format, object_t *parent, any_array_t *objects) { if (objects == NULL) { return; } for (i32 i = 0; i < objects->length; ++i) { obj_t *o = (obj_t *)objects->buffer[i]; if (!o->spawn) { continue; // Do not auto-create Scene object } object_t *object = scene_create_object(o, format, parent); scene_traverse_objects(format, object, (any_array_t *)(void *)o->children); } } void scene_add_scene(char *scene_name) { scene_t *format = data_get_scene_raw(scene_name); scene_load_embedded_data(format->embedded_datas); // Additional scene assets _scene_objects_traversed = 0; _scene_objects_count = scene_get_objects_count(format->objects); if (format->objects != NULL && format->objects->length > 0) { scene_traverse_objects(format, _scene_root, format->objects); // Scene objects } } i32 scene_get_objects_count(any_array_t *objects) { if (objects == NULL) { return 0; } i32 result = objects->length; for (i32 i = 0; i < objects->length; ++i) { obj_t *o = (obj_t *)objects->buffer[i]; if (!o->spawn) { continue; // Do not count children of non-spawned objects } if (o->children != NULL) { result += scene_get_objects_count((any_array_t *)(void *)o->children); } } return result; } object_t *_scene_spawn_object_tree(obj_t *obj, object_t *parent, bool spawn_children) { object_t *object = scene_create_object(obj, _scene_raw, parent); obj_t_array_t *obj_children = obj->children; if (spawn_children && obj_children != NULL) { for (i32 i = 0; i < obj_children->length; ++i) { obj_t *child = (obj_t *)obj_children->buffer[i]; _scene_spawn_object_tree(child, object, spawn_children); } } return object; } object_t *scene_spawn_object(char *name, object_t *parent, bool spawn_children) { obj_t *obj = scene_get_raw_object_by_name(_scene_raw, name); return _scene_spawn_object_tree(obj, parent, spawn_children); } obj_t *scene_get_raw_object_by_name(scene_t *format, char *name) { return scene_traverse_objs(format->objects, name); } obj_t *scene_traverse_objs(any_array_t *children, char *name) { for (i32 i = 0; i < children->length; ++i) { obj_t *o = (obj_t *)children->buffer[i]; if (string_equals(o->name, name)) { return o; } if (o->children != NULL) { obj_t *res = scene_traverse_objs((any_array_t *)(void *)o->children, name); if (res != NULL) { return res; } } } return NULL; } object_t *scene_create_object(obj_t *o, scene_t *format, object_t *parent) { char *scene_name = format->name; if (string_equals(o->type, "camera_object")) { camera_data_t *b = data_get_camera(scene_name, o->data_ref); camera_object_t *object = scene_add_camera_object(b, parent); return scene_return_object(object->base, o); } else if (string_equals(o->type, "mesh_object")) { if (o->material_ref == NULL) { return scene_create_mesh_object(o, format, parent, NULL); } else { char *ref = o->material_ref; material_data_t *mat = data_get_material(scene_name, ref); return scene_create_mesh_object(o, format, parent, mat); } } else if (string_equals(o->type, "object")) { object_t *object = scene_add_object(parent); return scene_return_object(object, o); } return NULL; } object_t *scene_create_mesh_object(obj_t *o, scene_t *format, object_t *parent, material_data_t *material) { // Mesh reference any_array_t *ref = string_split(o->data_ref, "/"); char *object_file = ""; char *data_ref = ""; char *scene_name = format->name; if (ref->length == 2) { // File reference object_file = (char *)ref->buffer[0]; data_ref = (char *)ref->buffer[1]; } else { // Local mesh data object_file = scene_name; data_ref = o->data_ref; } return scene_return_mesh_object(object_file, data_ref, material, parent, o); } object_t *scene_return_mesh_object(char *object_file, char *data_ref, material_data_t *material, object_t *parent, obj_t *o) { mesh_data_t *mesh = data_get_mesh(object_file, data_ref); mesh_object_t *object = scene_add_mesh_object(mesh, material, parent); return scene_return_object(object->base, o); } object_t *scene_return_object(object_t *object, obj_t *o) { if (object != NULL) { if (object->owns_raw && object->raw != NULL) { array_delete(object->raw->dimensions); free(object->raw); object->owns_raw = false; } object->raw = o; object->name = o->name; object->visible = o->visible; scene_gen_transform(o, object->transform); } return object; } void scene_gen_transform(obj_t *object, transform_t *transform) { transform->world = object->transform != NULL ? mat4_from_f32_array(object->transform, 0) : mat4_identity(); mat4_decomposed_t *dec = mat4_decompose(transform->world); transform->loc = dec->loc; transform->rot = dec->rot; transform->scale = dec->scl; if (transform->object->parent != NULL) { transform_update(transform); } } void scene_load_embedded_data(string_array_t *datas) { if (datas == NULL) { return; } for (i32 i = 0; i < datas->length; ++i) { char *file = datas->buffer[i]; scene_embed_data(file); } } void scene_embed_data(char *file) { gpu_texture_t *image = data_get_texture(file); any_map_set(scene_embedded, file, image); } // ██████╗ ███████╗███╗ ██╗██████╗ ███████╗██████╗ ██████╗ █████╗ ████████╗██╗ ██╗ // ██╔══██╗██╔════╝████╗ ██║██╔══██╗██╔════╝██╔══██╗ ██╔══██╗██╔══██╗╚══██╔══╝██║ ██║ // ██████╔╝█████╗ ██╔██╗ ██║██║ ██║█████╗ ██████╔╝ ██████╔╝███████║ ██║ ███████║ // ██╔══██╗██╔══╝ ██║╚██╗██║██║ ██║██╔══╝ ██╔══██╗ ██╔═══╝ ██╔══██║ ██║ ██╔══██║ // ██║ ██║███████╗██║ ╚████║██████╔╝███████╗██║ ██║ ██║ ██║ ██║ ██║ ██║ ██║ // ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ #include "const_data.h" void _gpu_begin(gpu_texture_t *render_target, any_array_t *additional, gpu_texture_t *depth_buffer, gpu_clear_t flags, unsigned color, float depth); gpu_texture_t *gpu_create_render_target(i32 width, i32 height, i32 format); int iron_window_width(void); int iron_window_height(void); void (*render_path_commands)(void) = NULL; any_map_t *render_path_render_targets = NULL; i32 render_path_current_w = 0; i32 render_path_current_h = 0; f32 _render_path_frame_time = 0.0; i32 _render_path_frame = 0; render_target_t *_render_path_current_target = NULL; gpu_texture_t *_render_path_current_image = NULL; bool _render_path_paused = false; i32 _render_path_last_w = 0; i32 _render_path_last_h = 0; string_array_t *_render_path_bind_params = NULL; f32 _render_path_last_frame_time = 0.0; i32 _render_path_loading = 0; any_map_t *_render_path_cached_shader_contexts = NULL; bool render_path_ready(void) { return _render_path_loading == 0; } void render_path_render_frame(void) { if (!render_path_ready() || _render_path_paused) { return; } if (_render_path_last_w > 0 && (_render_path_last_w != sys_w() || _render_path_last_h != sys_h())) { render_path_resize(); } _render_path_last_w = sys_w(); _render_path_last_h = sys_h(); _render_path_frame_time = sys_time() - _render_path_last_frame_time; _render_path_last_frame_time = sys_time(); render_path_current_w = sys_w(); render_path_current_h = sys_h(); render_path_commands(); _render_path_frame++; } void render_path_set_target(char *target, string_array_t *additional, char *depth_buffer, gpu_clear_t flags, i32 color, f32 depth) { if (_render_path_current_image != NULL) { render_path_end(); } _mesh_object_last_pipeline = NULL; if (string_equals(target, "")) { // Framebuffer _render_path_current_target = NULL; render_path_current_w = sys_w(); render_path_current_h = sys_h(); _gpu_begin(NULL, NULL, NULL, flags, color, depth); gpu_viewport(sys_x(), render_path_current_h - (sys_h() - sys_y()), sys_w(), sys_h()); } else { // Render target render_target_t *rt = (render_target_t *)any_map_get(render_path_render_targets, target); _render_path_current_target = rt; static any_array_t additional_images_scratch = {0}; any_array_t *additional_images = NULL; if (additional != NULL) { additional_images_scratch.length = 0; for (i32 i = 0; i < additional->length; ++i) { char *s = (char *)additional->buffer[i]; render_target_t *t = (render_target_t *)any_map_get(render_path_render_targets, s); any_array_push(&additional_images_scratch, t->_image); } additional_images = &additional_images_scratch; } render_path_current_w = rt->_image->width; render_path_current_h = rt->_image->height; render_target_t *db = depth_buffer != NULL ? (render_target_t *)any_map_get(render_path_render_targets, depth_buffer) : NULL; _render_path_current_image = rt->_image; _gpu_begin(rt->_image, additional_images, db != NULL ? db->_image : NULL, flags, color, depth); } _render_path_bind_params = NULL; } void render_path_end(void) { gpu_end(); _render_path_current_image = NULL; _render_path_bind_params = NULL; } void render_path_draw_meshes(char *context) { _mesh_object_last_pipeline = NULL; any_array_t *meshes = scene_meshes; for (i32 i = 0; i < meshes->length; ++i) { mesh_object_t *mesh = (mesh_object_t *)meshes->buffer[i]; mesh_object_render(mesh, context, _render_path_bind_params); } render_path_end(); } void render_path_draw_skydome(char *handle) { if (const_data_skydome_vb == NULL) { const_data_create_skydome_data(); } cached_shader_context_t *cc = (cached_shader_context_t *)any_map_get(_render_path_cached_shader_contexts, handle); gpu_set_pipeline(cc->context->_->pipe); uniforms_set_context_consts(cc->context, _render_path_bind_params); uniforms_set_obj_consts(cc->context, NULL); gpu_set_vertex_buffer(const_data_skydome_vb); gpu_set_index_buffer(const_data_skydome_ib); gpu_draw(); render_path_end(); } void render_path_bind_target(char *target, char *uniform) { static string_array_t bind_params = {0}; if (_render_path_bind_params == NULL) { bind_params.length = 0; _render_path_bind_params = &bind_params; } string_array_push(_render_path_bind_params, target); string_array_push(_render_path_bind_params, uniform); } void render_path_draw_shader(char *handle) { // Full-screen triangle // file/data_name/context cached_shader_context_t *cc = (cached_shader_context_t *)any_map_get(_render_path_cached_shader_contexts, handle); if (const_data_screen_aligned_vb == NULL) { const_data_create_screen_aligned_data(); } gpu_set_pipeline(cc->context->_->pipe); uniforms_set_context_consts(cc->context, _render_path_bind_params); uniforms_set_obj_consts(cc->context, NULL); gpu_set_vertex_buffer(const_data_screen_aligned_vb); gpu_set_index_buffer(const_data_screen_aligned_ib); gpu_draw(); render_path_end(); } void render_path_load_shader(char *handle) { _render_path_loading++; if (_render_path_cached_shader_contexts == NULL) { _render_path_cached_shader_contexts = any_map_create(); } cached_shader_context_t *cc = (cached_shader_context_t *)any_map_get(_render_path_cached_shader_contexts, handle); if (cc != NULL) { _render_path_loading--; return; } cc = calloc(1, sizeof(cached_shader_context_t)); any_map_set(_render_path_cached_shader_contexts, string_copy(handle), cc); // Callers may pass frame-temporary handles // file/data_name/context any_array_t *shader_path = string_split(handle, "/"); shader_data_t *res = data_get_shader((char *)shader_path->buffer[0], (char *)shader_path->buffer[1]); cc->context = shader_data_get_context(res, (char *)shader_path->buffer[2]); string_split_free(shader_path); _render_path_loading--; } void render_path_resize(void) { if (iron_window_width() == 0 || iron_window_height() == 0) { return; } any_array_t *render_targets_keys = map_keys(render_path_render_targets); for (i32 i = 0; i < render_targets_keys->length; ++i) { char *key = (char *)render_targets_keys->buffer[i]; render_target_t *rt = (render_target_t *)any_map_get(render_path_render_targets, key); if (rt != NULL && rt->width == 0) { gpu_delete_texture(rt->_image); rt->_image = render_path_create_image(rt); } } array_free(render_targets_keys); free(render_targets_keys); } render_target_t *render_path_create_render_target(render_target_t *t) { if (render_path_render_targets == NULL) { render_path_render_targets = any_map_create(); } t->_image = render_path_create_image(t); any_map_set(render_path_render_targets, t->name, t); return t; } gpu_texture_t *render_path_create_image(render_target_t *t) { i32 width = t->width == 0 ? sys_w() : t->width; i32 height = t->height == 0 ? sys_h() : t->height; width = (i32)math_floor(width * t->scale); height = (i32)math_floor(height * t->scale); if (width < 1) { width = 1; } if (height < 1) { height = 1; } return gpu_create_render_target(width, height, t->format != NULL ? render_path_get_tex_format(t->format) : GPU_TEXTURE_FORMAT_RGBA32); } gpu_texture_format_t render_path_get_tex_format(char *s) { if (string_equals(s, "RGBA32")) { return GPU_TEXTURE_FORMAT_RGBA32; } if (string_equals(s, "RGBA64")) { return GPU_TEXTURE_FORMAT_RGBA64; } if (string_equals(s, "RGBA128")) { return GPU_TEXTURE_FORMAT_RGBA128; } if (string_equals(s, "R32")) { return GPU_TEXTURE_FORMAT_R32; } if (string_equals(s, "R16")) { return GPU_TEXTURE_FORMAT_R16; } if (string_equals(s, "R8")) { return GPU_TEXTURE_FORMAT_R8; } if (string_equals(s, "D32")) { return GPU_TEXTURE_FORMAT_D32; } return GPU_TEXTURE_FORMAT_RGBA32; } render_target_t *render_target_create(void) { render_target_t *raw = calloc(1, sizeof(render_target_t)); raw->scale = 1.0; return raw; }