Add basic loader for gltf

Added loading of avocado. Something is still wrong though,
becaus I cannot see the seed. But basic functionality is there.
This commit is contained in:
Piotr Krygier
2026-03-19 21:00:39 +01:00
parent 984a56a8e8
commit 99fb36d9fd
14 changed files with 186 additions and 32 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

+129
View File
@@ -10,13 +10,16 @@
*/ */
#include <SDL3/SDL_log.h> #include <SDL3/SDL_log.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include "src/graphics_context.h" #include "src/graphics_context.h"
#include "src/vulkan_commons.h" #include "src/vulkan_commons.h"
#include "utilities/cgltf.h"
#include "utilities/commons.h" #include "utilities/commons.h"
#include "utilities/entity.h" #include "utilities/entity.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "vulkan_buffers.h" #include "vulkan_buffers.h"
rse_err_t mesh_create(struct graphics_context_t* context, rse_err_t mesh_create(struct graphics_context_t* context,
@@ -46,6 +49,132 @@ rse_err_t mesh_create(struct graphics_context_t* context,
return status; 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));
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Attribute name: %s", data->meshes->primitives[0].attributes[3].name);
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Accessor: %ld", data->meshes->primitives[0].attributes[3].data->count);
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];
for (attribute_index = 0; attribute_index < primitive->attributes_count; ++attribute_index) {
attribute = &primitive->attributes[attribute_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));
}
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(vertices);
cgltf_free(data);
return status;
}
rse_err_t mesh_create_instance(struct graphics_context_t* context, rse_err_t mesh_create_instance(struct graphics_context_t* context,
entity_t entity, entity_t entity,
struct instance_data_t instance_data) struct instance_data_t instance_data)
+1
View File
@@ -41,6 +41,7 @@ rse_err_t mesh_create(struct graphics_context_t* context, struct vertex_t* verti
rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity, rse_err_t mesh_create_instance(struct graphics_context_t* context, entity_t entity,
struct instance_data_t instance_data); struct instance_data_t instance_data);
rse_err_t mesh_create_from_file(struct graphics_context_t* context, const char* file_name, rse_id_t* mesh_id);
/** /**
* @brief Get the vertex count for mesh with mesh_id ID * @brief Get the vertex count for mesh with mesh_id ID
* *
+1 -1
View File
@@ -124,7 +124,7 @@ static rse_err_t pipeline_add_rasterizer(struct pipeline_t* pipeline)
pipeline->rasterizer.rasterizerDiscardEnable = VK_FALSE; /* Disable/Enable output to framebuffer*/ pipeline->rasterizer.rasterizerDiscardEnable = VK_FALSE; /* Disable/Enable output to framebuffer*/
pipeline->rasterizer.polygonMode = VK_POLYGON_MODE_FILL; pipeline->rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
pipeline->rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; pipeline->rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
pipeline->rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; pipeline->rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
pipeline->rasterizer.depthBiasEnable = VK_FALSE; pipeline->rasterizer.depthBiasEnable = VK_FALSE;
pipeline->rasterizer.depthBiasConstantFactor = 0.0f; pipeline->rasterizer.depthBiasConstantFactor = 0.0f;
pipeline->rasterizer.depthBiasClamp = 0.0f; pipeline->rasterizer.depthBiasClamp = 0.0f;
+34 -19
View File
@@ -116,7 +116,7 @@ rse_err_t rse_graphics_init(struct graphics_context_t* context)
return status; return status;
} }
#define TEXTURES_COUNT (2U) #define TEXTURES_COUNT (3U)
rse_err_t rse_graphics_test_function(struct graphics_context_t* context) rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
@@ -125,28 +125,35 @@ rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
rse_id_t descriptor_set_handle = 0; rse_id_t descriptor_set_handle = 0;
struct descriptor_set_layout_bindings_t layout_bindings = {0}; struct descriptor_set_layout_bindings_t layout_bindings = {0};
struct pipeline_infos_t pipeline_infos = {0}; struct pipeline_infos_t pipeline_infos = {0};
rse_id_t mesh_id_1, mesh_id_2 = 0; rse_id_t mesh_id_1 = 0;
rse_id_t font_id = 0; rse_id_t mesh_id_2 = 0;
rse_id_t avocado_id = 0;
// rse_id_t font_id = 0;
rse_id_t static_mesh_renderer = 0U; rse_id_t static_mesh_renderer = 0U;
rse_id_t vertex_shader_id, fragment_shader_id; rse_id_t vertex_shader_id, fragment_shader_id;
STATUS_CHECK(texture_load_from_file(context, "../../test_image.jpg", &textures[0])); STATUS_CHECK(texture_load_from_file(context, "../../test_image.jpg", &textures[0]));
STATUS_CHECK(texture_load_from_file(context, "../../test_image_2.jpg", &textures[1])); STATUS_CHECK(texture_load_from_file(context, "../../test_image_2.jpg", &textures[1]));
STATUS_CHECK(texture_load_from_file(context, "../../Avocado_baseColor.png", &textures[2]));
STATUS_CHECK(fonts_init()); // STATUS_CHECK(fonts_init());
STATUS_CHECK(fonts_load_from_file(context, "../../NotoSansMono-Regular.ttf", &font_id)); // STATUS_CHECK(fonts_load_from_file(context, "../../NotoSansMono-Regular.ttf", &font_id));
STATUS_CHECK(fonts_set_font_size(context, font_id, 16)); // STATUS_CHECK(fonts_set_font_size(context, font_id, 16));
// FIXME: Temporary array of vertices, for testing purposes // FIXME: Temporary array of vertices, for testing purposes
struct vertex_t vertices[] = {{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, struct vertex_t vertices[] = {
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}}; {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
};
struct vertex_t vertices2[] = {{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, struct vertex_t vertices_2[] = {
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}}; {{0.2f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.2f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
};
// FIXME: Temporary array of vertices, for testing purposes // FIXME: Temporary array of vertices, for testing purposes
uint16_t indices[] = {0, 1, 2, 2, 3, 0}; uint16_t indices[] = {0, 1, 2, 2, 3, 0};
@@ -155,7 +162,7 @@ rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
if (RSE_ERROR_NO_ERROR != if (RSE_ERROR_NO_ERROR !=
mesh_create_instance(context, mesh_create_instance(context,
mesh_id_1, mesh_id_1,
(struct instance_data_t){{0.0f, 0.0f, 2.4f}, {0.0f, 0.0f, 0.0}, 1.0f, textures[1]})) { (struct instance_data_t){{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0}, 1.0f, textures[0]})) {
exit(1); exit(1);
} }
@@ -163,17 +170,25 @@ rse_err_t rse_graphics_test_function(struct graphics_context_t* context)
mesh_create_instance( mesh_create_instance(
context, context,
mesh_id_1, mesh_id_1,
(struct instance_data_t){{-0.5f, 0.0f, -1.0f}, {0.0f, 0.0f, glm_rad(10.0f)}, 1.0f, textures[0]})) { (struct instance_data_t){{-0.5f, 0.0f, -1.0f}, {0.0f, 0.0f, glm_rad(10.0f)}, 1.0f, textures[1]})) {
exit(1); exit(1);
} }
STATUS_CHECK(mesh_create(context, vertices2, indices, 4, 6, &mesh_id_2)); STATUS_CHECK(mesh_create(context, vertices_2, indices, 4, 6, &mesh_id_2));
if (RSE_ERROR_NO_ERROR != if (RSE_ERROR_NO_ERROR !=
mesh_create_instance( mesh_create_instance(
context, context,
mesh_id_2, mesh_id_2,
(struct instance_data_t){{1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, glm_rad(0.0f)}, 1.0f, textures[0]})) { (struct instance_data_t){{0.5f, 0.0f, -1.0f}, {0.0f, 0.0f, glm_rad(0.0f)}, 1.0f, textures[1]})) {
exit(1);
}
mesh_create_from_file(context, "../../Avocado.glb", &avocado_id);
if (RSE_ERROR_NO_ERROR !=
mesh_create_instance(
context,
avocado_id,
(struct instance_data_t){{0.0f, 0.0f, -4.0f}, {0.0f, glm_rad(130.0f), 0.0f}, 2.0f, textures[2]})) {
exit(1); exit(1);
} }
+9 -3
View File
@@ -210,8 +210,11 @@ rse_err_t add_vertices(struct graphics_context_t* context,
size_t vertices_count, size_t vertices_count,
const struct vertex_t* vertices, const struct vertex_t* vertices,
size_t indices_count, size_t indices_count,
const uint16_t* indices) uint16_t* indices)
{ {
// static uint16_t last_index = 0U;
// uint16_t max_index = 0U;
// size_t idx = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
struct vulkan_buffer_t staging_buffer; struct vulkan_buffer_t staging_buffer;
size_t vertices_size = sizeof(vertices[0]) * vertices_count; size_t vertices_size = sizeof(vertices[0]) * vertices_count;
@@ -235,6 +238,7 @@ rse_err_t add_vertices(struct graphics_context_t* context,
copy_buffer(context, staging_buffer.buffer, vertex_buffer->buffer, vertices_size, vertex_buffer->allocated_size); copy_buffer(context, staging_buffer.buffer, vertex_buffer->buffer, vertices_size, vertex_buffer->allocated_size);
vertex_buffer->allocated_size += vertices_size; vertex_buffer->allocated_size += vertices_size;
/* Fill staging buffer with index data */ /* Fill staging buffer with index data */
memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size); memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size);
copy_buffer(context, staging_buffer.buffer, index_buffer->buffer, indices_size, index_buffer->allocated_size); copy_buffer(context, staging_buffer.buffer, index_buffer->buffer, indices_size, index_buffer->allocated_size);
@@ -274,6 +278,7 @@ rse_err_t buffers_update(struct graphics_context_t* context)
sizeof(VkDrawIndexedIndirectCommand) * context->mesh_data.mesh_count; sizeof(VkDrawIndexedIndirectCommand) * context->mesh_data.mesh_count;
struct vulkan_buffer_t staging_buffer; struct vulkan_buffer_t staging_buffer;
struct mesh_t* mesh = NULL; struct mesh_t* mesh = NULL;
uint32_t indices_count = 0U;
rse_malloc(draw_indirect_commands_buffer, draw_indirect_commands_buffer_size); rse_malloc(draw_indirect_commands_buffer, draw_indirect_commands_buffer_size);
for (iter = 0U; iter < context->mesh_data.mesh_count; ++iter) { for (iter = 0U; iter < context->mesh_data.mesh_count; ++iter) {
@@ -295,13 +300,14 @@ rse_err_t buffers_update(struct graphics_context_t* context)
draw_indirect_command->firstInstance = 0; draw_indirect_command->firstInstance = 0;
draw_indirect_command->vertexOffset = 0; draw_indirect_command->vertexOffset = 0;
} else { } else {
draw_indirect_command->firstIndex = context->mesh_data.mesh_bucket[iter - 1].index_count; draw_indirect_command->firstIndex = indices_count;
draw_indirect_command->firstInstance = draw_indirect_commands_buffer[iter - 1].firstInstance + draw_indirect_command->firstInstance = draw_indirect_commands_buffer[iter - 1].firstInstance +
draw_indirect_commands_buffer[iter - 1].instanceCount; draw_indirect_commands_buffer[iter - 1].instanceCount;
draw_indirect_command->vertexOffset = (context->mesh_data.mesh_bucket[iter - 1].vertex_count); draw_indirect_command->vertexOffset = (context->mesh_data.mesh_bucket[iter - 1].vertex_count + draw_indirect_commands_buffer[iter-1].vertexOffset);
} }
draw_indirect_command->indexCount = mesh->index_count; draw_indirect_command->indexCount = mesh->index_count;
draw_indirect_command->instanceCount = mesh->instances_count; draw_indirect_command->instanceCount = mesh->instances_count;
indices_count += mesh->index_count;
} }
/* Copy Draw indirect commands into buffer */ /* Copy Draw indirect commands into buffer */
+1 -1
View File
@@ -36,7 +36,7 @@ rse_err_t buffer_create_uniform(struct graphics_context_t* context, struct vulka
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @return rse_err_t RSE_ERROR_NO_ERROR on success
*/ */
rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id, size_t vertices_count, rse_err_t add_vertices(struct graphics_context_t* context, uint16_t mesh_id, size_t vertices_count,
const struct vertex_t* vertices, size_t indices_count, const uint16_t* indices); const struct vertex_t* vertices, size_t indices_count, uint16_t* indices);
/** /**
* @brief Add instance data to vulkan buffers * @brief Add instance data to vulkan buffers
+1 -1
View File
@@ -30,7 +30,7 @@ rse_err_t window_init(SDL_Window** window_handle, bool* is_framebuffer_resized)
*window_handle = SDL_CreateWindow("RedScarfEngine", 1024, 768, SDL_WINDOW_VULKAN); *window_handle = SDL_CreateWindow("RedScarfEngine", 1024, 768, SDL_WINDOW_VULKAN);
if (NULL == *window_handle) { if (NULL == *window_handle) {
SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed"); SDL_LogCritical(SDL_LOG_CATEGORY_GPU, "Window context creation failed. Reason: %s", SDL_GetError());
return RSE_ERROR_INTERNAL_ERROR; return RSE_ERROR_INTERNAL_ERROR;
} }
+2
View File
@@ -6,6 +6,8 @@ project('RedScarfEngine',
'werror=true' 'werror=true'
]) ])
# add_project_arguments('-DWAYLAND=1', language : 'c')
subdir('utilities') subdir('utilities')
subdir('graphics') subdir('graphics')
subdir('events') subdir('events')
+2
View File
@@ -1,5 +1,7 @@
#include "red_scarf_engine.h" #include "red_scarf_engine.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h" #include "utilities/errors_common.h"
#include "graphics/src/mesh_controller.h"
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
+1 -1
View File
@@ -28,7 +28,7 @@
exit(1); \ exit(1); \
} }
#define rse_free(ptr) free(ptr); #define rse_free(ptr) if (ptr != NULL) free(ptr);
#define rse_memcpy(dst, src, size) memcpy(dst, src, size); #define rse_memcpy(dst, src, size) memcpy(dst, src, size);
#define rse_memset(ptr, value, size) memset(ptr, value, size); #define rse_memset(ptr, value, size) memset(ptr, value, size);
+2 -1
View File
@@ -3,6 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include "commons.h" #include "commons.h"
#include "cgltf.h"
#define MAX_PATH_LENGTH (4096U) #define MAX_PATH_LENGTH (4096U)
@@ -22,7 +23,7 @@ rse_err_t file_load_pixels(const char* file_name, unsigned char* buffer, size_t*
rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size); rse_err_t file_append_full_path(char* file_path, size_t max_buffer_size);
rse_err_t file_json_parser(const char* file_name); rse_err_t file_load_gltf(const char* file_name, cgltf_data** data);
#endif /* FILE_UTILS_H */ #endif /* FILE_UTILS_H */
+2 -4
View File
@@ -247,24 +247,22 @@ rse_err_t file_write(const char* file_name, size_t bytes_count, char* buffer)
return ret; return ret;
} }
rse_err_t file_load_gltf(const char* file_name) rse_err_t file_load_gltf(const char* file_name, cgltf_data** data)
{ {
char real_path[MAX_PATH_LENGTH] = {0}; char real_path[MAX_PATH_LENGTH] = {0};
rse_err_t ret = RSE_ERROR_NO_ERROR; rse_err_t ret = RSE_ERROR_NO_ERROR;
cgltf_options options = {0}; cgltf_options options = {0};
cgltf_data* data = NULL;
strncpy(real_path, file_name, MAX_PATH_LENGTH); strncpy(real_path, file_name, MAX_PATH_LENGTH);
if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) { if ((ret = file_append_full_path(real_path, MAX_PATH_LENGTH)) != RSE_ERROR_NO_ERROR) {
return ret; return ret;
} }
if (cgltf_parse_file(&options, real_path, &data) != cgltf_result_success) { if (cgltf_parse_file(&options, real_path, data) != cgltf_result_success) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse GLTF data"); SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse GLTF data");
return RSE_ERROR_INTERNAL_ERROR; return RSE_ERROR_INTERNAL_ERROR;
} }
cgltf_free(data);
return ret; return ret;
} }