/** * @file mesh_controller.h * @author Piotr Krygier (piotrkrygier@everyonencancode.xyz) * @brief Graphics context for Red Scarf Engine * @version 0.1 * @date 2025-09-23 * * @copyright Copyright (c) 2025 * */ #include #include #include #include #include "src/graphics_context.h" #include "src/vulkan_commons.h" #include "utilities/cgltf.h" #include "utilities/commons.h" #include "utilities/entity.h" #include "utilities/errors_common.h" #include "utilities/file_utils.h" #include "vulkan_buffers.h" rse_err_t mesh_create(struct graphics_context_t* context, struct vertex_t* vertices, uint16_t* indices, size_t vertices_count, size_t indices_count, rse_id_t* mesh_id) { rse_err_t status = RSE_ERROR_NO_ERROR; if (mesh_id == NULL) { SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Provided mesh id handle is NULL"); return RSE_ERROR_NULL_POINTER; } *mesh_id = context->mesh_data.mesh_count; if (context->mesh_data.mesh_count > MAX_MESH_NUMBER) { SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Could not allocate new mesh, reached maximum."); return RSE_ERROR_INTERNAL_ERROR; } STATUS_CHECK(add_vertices(context, *mesh_id, vertices_count, vertices, indices_count, indices)); context->mesh_data.mesh_count++; return status; } rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id) { struct vertex_t* vertices = NULL; uint16_t* indices = NULL; size_t indices_count = 0U; size_t mesh_index = 0U; size_t primitive_index = 0U; size_t attribute_index = 0U; cgltf_mesh* mesh = NULL; cgltf_primitive* primitive = NULL; cgltf_attribute* attribute = NULL; size_t* position_data_offset = NULL; size_t* color_data_offset = NULL; size_t* texture_coord_data_offset = NULL; size_t* indices_data_offset = 0U; size_t data_count = 0U; size_t data_index = 0U; size_t tmp_offset = 0U; uint8_t* bin = NULL; (void)context; (void)mesh_id; cgltf_data* data = NULL; rse_err_t status = RSE_ERROR_NO_ERROR; STATUS_CHECK(file_load_gltf(file_name, &data)); for (mesh_index = 0U; mesh_index < data->meshes_count; ++mesh_index) { mesh = &data->meshes[mesh_index]; for (primitive_index = 0U; primitive_index < mesh->primitives_count; ++primitive_index) { primitive = &mesh->primitives[primitive_index]; if (primitive->indices == NULL) { SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "No indices for mesh found. Currently only indexed drawing is supported"); return RSE_ERROR_INVALID_PARAM; } else { rse_malloc(indices_data_offset, sizeof(size_t)); *indices_data_offset = primitive->indices->buffer_view->offset; indices_count = primitive->indices->count; rse_malloc(indices, (sizeof(uint16_t) * indices_count)); } for (attribute_index = 0; attribute_index < primitive->attributes_count; ++attribute_index) { attribute = &primitive->attributes[attribute_index]; tmp_offset = attribute->data->buffer_view->offset; switch (attribute->type) { case cgltf_attribute_type_position: rse_malloc(position_data_offset, sizeof(size_t)); *position_data_offset = tmp_offset; data_count = attribute->data->count; break; case cgltf_attribute_type_color: rse_malloc(color_data_offset, sizeof(size_t)); *color_data_offset = tmp_offset; break; case cgltf_attribute_type_texcoord: rse_malloc(texture_coord_data_offset, sizeof(size_t)); *texture_coord_data_offset = tmp_offset; break; default: break; } } } if (position_data_offset == NULL) { SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failed to retrieve position data for mesh %s", file_name); status = RSE_ERROR_INVALID_PARAM; goto exit; } if (data_count == 0) { SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Failure: data count for mesh is 0"); status = RSE_ERROR_INVALID_PARAM; goto exit; } rse_malloc(vertices, sizeof(struct vertex_t) * data_count); bin = (uint8_t*)data->bin; for (data_index = 0U; data_index < data_count; ++data_index) { rse_memcpy(vertices[data_index].pos, (bin + *position_data_offset + (sizeof(vec3) * data_index)), sizeof(vec3)); if (color_data_offset != NULL) { rse_memcpy(vertices[data_index].color, (bin + *color_data_offset + (sizeof(vec3) * data_index)), sizeof(vec3)); } else { vertices[data_index].color[0] = 1.0; vertices[data_index].color[1] = 1.0; vertices[data_index].color[2] = 1.0; } if (texture_coord_data_offset != NULL) { rse_memcpy(vertices[data_index].tex_coords, (bin + *texture_coord_data_offset + (sizeof(vec2) * data_index)), sizeof(vec2)); } } rse_memcpy(indices, (bin + *indices_data_offset), (sizeof(uint16_t) * indices_count)); STATUS_CHECK(mesh_create(context, vertices, indices, data_count, indices_count, mesh_id)); } exit: rse_free(position_data_offset); rse_free(color_data_offset); rse_free(texture_coord_data_offset); rse_free(indices_data_offset); rse_free(vertices); rse_free(indices); cgltf_free(data); return status; } rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity, struct instance_data_t instance_data) { if (context->mesh_data.mesh_bucket[entity].instances_count >= MAX_INSTANCE_NUMBER) { SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Could not allocate new mesh, reached maximum."); return RSE_ERROR_INTERNAL_ERROR; } return mesh_add_instance(context, entity, &instance_data); }