diff options
author | Omni <omniscient.oce@gmail.com> | 2024-08-17 17:17:11 +1000 |
---|---|---|
committer | Omni <omniscient.oce@gmail.com> | 2024-08-17 17:17:11 +1000 |
commit | eadfaaa86c29c36dd5ef5d0b6b6fa27af0cdb8b3 (patch) | |
tree | af18d90ba02d5e26dd6ca746154ce9df6d3b0327 | |
parent | cfd7266c21a43cbd37fa712725cea85cdd1f7aab (diff) |
improving the way I load animation data and store it. Load -> Tick(Armature*) -> RenderEnt.Armature -> matrix shenanigans
-rw-r--r-- | deps/cgltf/cgltf.h | 413 | ||||
-rw-r--r-- | examples/game_demo/game_demo.c | 20 | ||||
-rw-r--r-- | examples/skinned_animation/ex_skinned_animation.c | 24 | ||||
-rw-r--r-- | src/animation.c | 21 | ||||
-rw-r--r-- | src/animation.h | 46 | ||||
-rw-r--r-- | src/maths/primitives.c | 11 | ||||
-rw-r--r-- | src/render/pbr.c | 22 | ||||
-rw-r--r-- | src/render/render_types.h | 5 | ||||
-rw-r--r-- | src/resources/gltf.c | 93 |
9 files changed, 392 insertions, 263 deletions
diff --git a/deps/cgltf/cgltf.h b/deps/cgltf/cgltf.h index af24c65..36fd644 100644 --- a/deps/cgltf/cgltf.h +++ b/deps/cgltf/cgltf.h @@ -1,7 +1,7 @@ /** * cgltf - a single-file glTF 2.0 parser written in C99. * - * Version: 1.13 + * Version: 1.14 * * Website: https://github.com/jkuhlmann/cgltf * @@ -63,9 +63,14 @@ * By passing null for the output pointer, users can find out how many floats are required in the * output buffer. * + * `cgltf_accessor_unpack_indices` reads in the index data from an accessor. Assumes that + * `cgltf_load_buffers` has already been called. By passing null for the output pointer, users can + * find out how many indices are required in the output buffer. Returns 0 if the accessor is + * sparse or if the output component size is less than the accessor's component size. + * * `cgltf_num_components` is a tiny utility that tells you the dimensionality of * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate - * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for + * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for * similar purposes. * * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to @@ -75,7 +80,7 @@ * * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading * vector types and does not support matrix types. The passed-in element size is the number of uints - * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in + * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in * element_size is too small, or if the accessor is sparse. * * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t @@ -197,6 +202,7 @@ typedef enum cgltf_type typedef enum cgltf_primitive_type { + cgltf_primitive_type_invalid, cgltf_primitive_type_points, cgltf_primitive_type_lines, cgltf_primitive_type_line_loop, @@ -328,15 +334,6 @@ typedef struct cgltf_accessor_sparse cgltf_component_type indices_component_type; cgltf_buffer_view* values_buffer_view; cgltf_size values_byte_offset; - cgltf_extras extras; - cgltf_extras indices_extras; - cgltf_extras values_extras; - cgltf_size extensions_count; - cgltf_extension* extensions; - cgltf_size indices_extensions_count; - cgltf_extension* indices_extensions; - cgltf_size values_extensions_count; - cgltf_extension* values_extensions; } cgltf_accessor_sparse; typedef struct cgltf_accessor @@ -398,6 +395,8 @@ typedef struct cgltf_texture cgltf_sampler* sampler; cgltf_bool has_basisu; cgltf_image* basisu_image; + cgltf_bool has_webp; + cgltf_image* webp_image; cgltf_extras extras; cgltf_size extensions_count; cgltf_extension* extensions; @@ -419,9 +418,6 @@ typedef struct cgltf_texture_view cgltf_float scale; /* equivalent to strength for occlusion_texture */ cgltf_bool has_transform; cgltf_texture_transform transform; - cgltf_extras extras; - cgltf_size extensions_count; - cgltf_extension* extensions; } cgltf_texture_view; typedef struct cgltf_pbr_metallic_roughness @@ -511,6 +507,11 @@ typedef struct cgltf_anisotropy cgltf_texture_view anisotropy_texture; } cgltf_anisotropy; +typedef struct cgltf_dispersion +{ + cgltf_float dispersion; +} cgltf_dispersion; + typedef struct cgltf_material { char* name; @@ -525,6 +526,7 @@ typedef struct cgltf_material cgltf_bool has_emissive_strength; cgltf_bool has_iridescence; cgltf_bool has_anisotropy; + cgltf_bool has_dispersion; cgltf_pbr_metallic_roughness pbr_metallic_roughness; cgltf_pbr_specular_glossiness pbr_specular_glossiness; cgltf_clearcoat clearcoat; @@ -536,6 +538,7 @@ typedef struct cgltf_material cgltf_emissive_strength emissive_strength; cgltf_iridescence iridescence; cgltf_anisotropy anisotropy; + cgltf_dispersion dispersion; cgltf_texture_view normal_texture; cgltf_texture_view occlusion_texture; cgltf_texture_view emissive_texture; @@ -850,7 +853,7 @@ cgltf_size cgltf_component_size(cgltf_component_type component_type); cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); -cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_uint* out, cgltf_size index_count); +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count); /* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); @@ -950,8 +953,8 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t #ifndef CGLTF_CONSTS -static const cgltf_size GlbHeaderSize = 12; -static const cgltf_size GlbChunkHeaderSize = 8; +#define GlbHeaderSize 12 +#define GlbChunkHeaderSize 8 static const uint32_t GlbVersion = 2; static const uint32_t GlbMagic = 0x46546C67; static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; @@ -1045,7 +1048,7 @@ static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* m fclose(file); return cgltf_result_out_of_memory; } - + cgltf_size read_size = fread(file_data, 1, file_size, file); fclose(file); @@ -1153,7 +1156,7 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s // JSON chunk: length uint32_t json_length; memcpy(&json_length, json_chunk, 4); - if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size) + if (json_length > size - GlbHeaderSize - GlbChunkHeaderSize) { return cgltf_result_data_too_short; } @@ -1167,10 +1170,10 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s json_chunk += GlbChunkHeaderSize; - const void* bin = 0; + const void* bin = NULL; cgltf_size bin_size = 0; - if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size) + if (GlbChunkHeaderSize <= size - GlbHeaderSize - GlbChunkHeaderSize - json_length) { // We can read another chunk const uint8_t* bin_chunk = json_chunk + json_length; @@ -1178,7 +1181,7 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s // Bin chunk: length uint32_t bin_length; memcpy(&bin_length, bin_chunk, 4); - if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size) + if (bin_length > size - GlbHeaderSize - GlbChunkHeaderSize - json_length - GlbChunkHeaderSize) { return cgltf_result_data_too_short; } @@ -1564,6 +1567,9 @@ cgltf_result cgltf_validate(cgltf_data* data) { cgltf_accessor* accessor = &data->accessors[i]; + CGLTF_ASSERT_IF(data->accessors[i].component_type == cgltf_component_type_invalid, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->accessors[i].type == cgltf_type_invalid, cgltf_result_invalid_gltf); + cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); if (accessor->buffer_view) @@ -1577,7 +1583,7 @@ cgltf_result cgltf_validate(cgltf_data* data) { cgltf_accessor_sparse* sparse = &accessor->sparse; - cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type); + cgltf_size indices_component_size = cgltf_component_size(sparse->indices_component_type); cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; @@ -1643,43 +1649,48 @@ cgltf_result cgltf_validate(cgltf_data* data) for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].type == cgltf_primitive_type_invalid, cgltf_result_invalid_gltf); CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); - if (data->meshes[i].primitives[j].attributes_count) + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes_count == 0, cgltf_result_invalid_gltf); + + cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; + + CGLTF_ASSERT_IF(first->count == 0, cgltf_result_invalid_gltf); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) { - cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); } + } - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) - { - for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); - } - } + cgltf_accessor* indices = data->meshes[i].primitives[j].indices; - cgltf_accessor* indices = data->meshes[i].primitives[j].indices; + CGLTF_ASSERT_IF(indices && + indices->component_type != cgltf_component_type_r_8u && + indices->component_type != cgltf_component_type_r_16u && + indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); - CGLTF_ASSERT_IF(indices && - indices->component_type != cgltf_component_type_r_8u && - indices->component_type != cgltf_component_type_r_16u && - indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(indices && indices->type != cgltf_type_scalar, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(indices && indices->stride != cgltf_component_size(indices->component_type), cgltf_result_invalid_gltf); - if (indices && indices->buffer_view && indices->buffer_view->buffer->data) - { - cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); + if (indices && indices->buffer_view && indices->buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); - CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); - } + CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); + } - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) - { - CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); - } + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); } } } @@ -1688,7 +1699,20 @@ cgltf_result cgltf_validate(cgltf_data* data) { if (data->nodes[i].weights && data->nodes[i].mesh) { - CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->nodes[i].has_mesh_gpu_instancing) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh == NULL, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes_count == 0, cgltf_result_invalid_gltf); + + cgltf_accessor* first = data->nodes[i].mesh_gpu_instancing.attributes[0].data; + + for (cgltf_size k = 0; k < data->nodes[i].mesh_gpu_instancing.attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } } } @@ -1736,10 +1760,15 @@ cgltf_result cgltf_validate(cgltf_data* data) cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; - CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_data_too_short); + CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_invalid_gltf); } } + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + CGLTF_ASSERT_IF(!data->variants[i].name, cgltf_result_invalid_gltf); + } + return cgltf_result_success; } @@ -1786,12 +1815,6 @@ static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, data->memory.free_func(data->memory.user_data, extensions); } -static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view) -{ - cgltf_free_extensions(data, view->extensions, view->extensions_count); - cgltf_free_extras(data, &view->extras); -} - void cgltf_free(cgltf_data* data) { if (!data) @@ -1813,15 +1836,6 @@ void cgltf_free(cgltf_data* data) { data->memory.free_func(data->memory.user_data, data->accessors[i].name); - if(data->accessors[i].is_sparse) - { - cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count); - cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count); - cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count); - cgltf_free_extras(data, &data->accessors[i].sparse.extras); - cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras); - cgltf_free_extras(data, &data->accessors[i].sparse.values_extras); - } cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); cgltf_free_extras(data, &data->accessors[i].extras); } @@ -1923,61 +1937,13 @@ void cgltf_free(cgltf_data* data) { data->memory.free_func(data->memory.user_data, data->materials[i].name); - if(data->materials[i].has_pbr_metallic_roughness) - { - cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture); - cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture); - } - if(data->materials[i].has_pbr_specular_glossiness) - { - cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture); - cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture); - } - if(data->materials[i].has_clearcoat) - { - cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture); - cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture); - cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture); - } - if(data->materials[i].has_specular) - { - cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture); - cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture); - } - if(data->materials[i].has_transmission) - { - cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture); - } - if (data->materials[i].has_volume) - { - cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture); - } - if(data->materials[i].has_sheen) - { - cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture); - cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture); - } - if(data->materials[i].has_iridescence) - { - cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture); - cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture); - } - if (data->materials[i].has_anisotropy) - { - cgltf_free_texture_view(data, &data->materials[i].anisotropy.anisotropy_texture); - } - - cgltf_free_texture_view(data, &data->materials[i].normal_texture); - cgltf_free_texture_view(data, &data->materials[i].occlusion_texture); - cgltf_free_texture_view(data, &data->materials[i].emissive_texture); - cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); cgltf_free_extras(data, &data->materials[i].extras); } data->memory.free_func(data->memory.user_data, data->materials); - for (cgltf_size i = 0; i < data->images_count; ++i) + for (cgltf_size i = 0; i < data->images_count; ++i) { data->memory.free_func(data->memory.user_data, data->images[i].name); data->memory.free_func(data->memory.user_data, data->images[i].uri); @@ -2225,8 +2191,6 @@ static cgltf_ssize cgltf_component_read_integer(const void* in, cgltf_component_ return *((const uint16_t*) in); case cgltf_component_type_r_32u: return *((const uint32_t*) in); - case cgltf_component_type_r_32f: - return (cgltf_ssize)*((const float*) in); case cgltf_component_type_r_8: return *((const int8_t*) in); case cgltf_component_type_r_8u: @@ -2244,8 +2208,6 @@ static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_typ return *((const uint16_t*) in); case cgltf_component_type_r_32u: return *((const uint32_t*) in); - case cgltf_component_type_r_32f: - return (cgltf_size)((cgltf_ssize)*((const float*) in)); case cgltf_component_type_r_8u: return *((const uint8_t*) in); default: @@ -2629,7 +2591,7 @@ cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const return (cgltf_size)(object - animation->channels); } -cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_uint* out, cgltf_size index_count) +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count) { if (out == NULL) { @@ -2637,6 +2599,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u } index_count = accessor->count < index_count ? accessor->count : index_count; + cgltf_size index_component_size = cgltf_component_size(accessor->component_type); if (accessor->is_sparse) { @@ -2646,6 +2609,10 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u { return 0; } + if (index_component_size > out_component_size) + { + return 0; + } const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); if (element == NULL) { @@ -2653,18 +2620,29 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u } element += accessor->offset; - if (accessor->component_type == cgltf_component_type_r_32u && accessor->stride == sizeof(cgltf_uint)) + if (index_component_size == out_component_size && accessor->stride == out_component_size) { - memcpy(out, element, index_count * sizeof(cgltf_uint)); + memcpy(out, element, index_count * index_component_size); + return index_count; } - else - { - cgltf_uint* dest = out; - for (cgltf_size index = 0; index < index_count; index++, dest++, element += accessor->stride) + // The component size of the output array is larger than the component size of the index data, so index data will be padded. + switch (out_component_size) + { + case 2: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) + { + ((uint16_t*)out)[index] = (uint16_t)cgltf_component_read_index(element, accessor->component_type); + } + break; + case 4: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) { - *dest = (cgltf_uint)cgltf_component_read_index(element, accessor->component_type); + ((uint32_t*)out)[index] = (uint32_t)cgltf_component_read_index(element, accessor->component_type); } + break; + default: + break; } return index_count; @@ -2675,7 +2653,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u #define CGLTF_ERROR_LEGACY -3 #define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } -#define CGLTF_CHECK_TOKTYPE_RETTYPE(tok_, type_, ret_) if ((tok_).type != (type_)) { return (ret_)CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_TOKTYPE_RET(tok_, type_, ret_) if ((tok_).type != (type_)) { return ret_; } #define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ #define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) @@ -2702,12 +2680,13 @@ static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) { - CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size); + CGLTF_CHECK_TOKTYPE_RET(*tok, JSMN_PRIMITIVE, 0); char tmp[128]; int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); strncpy(tmp, (const char*)json_chunk + tok->start, size); tmp[size] = 0; - return (cgltf_size)CGLTF_ATOLL(tmp); + long long res = CGLTF_ATOLL(tmp); + return res < 0 ? 0 : (cgltf_size)res; } static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) @@ -2889,6 +2868,11 @@ static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* o if (us && *out_type != cgltf_attribute_type_invalid) { *out_index = CGLTF_ATOI(us + 1); + if (*out_index < 0) + { + *out_type = cgltf_attribute_type_invalid; + *out_index = 0; + } } } @@ -3221,6 +3205,31 @@ static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t return i; } +static cgltf_primitive_type cgltf_json_to_primitive_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 0: + return cgltf_primitive_type_points; + case 1: + return cgltf_primitive_type_lines; + case 2: + return cgltf_primitive_type_line_loop; + case 3: + return cgltf_primitive_type_line_strip; + case 4: + return cgltf_primitive_type_triangles; + case 5: + return cgltf_primitive_type_triangle_strip; + case 6: + return cgltf_primitive_type_triangle_fan; + default: + return cgltf_primitive_type_invalid; + } +} + static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -3237,9 +3246,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) { ++i; - out_prim->type - = (cgltf_primitive_type) - cgltf_json_to_int(tokens+i, json_chunk); + out_prim->type = cgltf_json_to_primitive_type(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) @@ -3475,7 +3482,7 @@ static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, c } } -static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) +static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -3489,7 +3496,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) { ++i; - out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk); + out_sparse->count = cgltf_json_to_size(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) @@ -3522,14 +3529,6 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); ++i; } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->indices_extensions_count, &out_sparse->indices_extensions); - } else { i = cgltf_skip_json(tokens, i+1); @@ -3565,14 +3564,6 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); ++i; } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->values_extensions_count, &out_sparse->values_extensions); - } else { i = cgltf_skip_json(tokens, i+1); @@ -3584,14 +3575,6 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } } } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras); - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) - { - i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->extensions_count, &out_sparse->extensions); - } else { i = cgltf_skip_json(tokens, i+1); @@ -3649,8 +3632,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) { ++i; - out_accessor->count = - cgltf_json_to_int(tokens+i, json_chunk); + out_accessor->count = cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) @@ -3705,7 +3687,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) { out_accessor->is_sparse = 1; - i = cgltf_parse_json_accessor_sparse(options, tokens, i + 1, json_chunk, &out_accessor->sparse); + i = cgltf_parse_json_accessor_sparse(tokens, i + 1, json_chunk, &out_accessor->sparse); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { @@ -3777,6 +3759,8 @@ static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, co static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view) { + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); out_texture_view->scale = 1.0f; @@ -3801,7 +3785,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); ++i; } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) { ++i; out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); @@ -3813,28 +3797,12 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); ++i; } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras); - } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { ++i; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - if(out_texture_view->extensions) - { - return CGLTF_ERROR_JSON; - } - int extensions_size = tokens[i].size; - out_texture_view->extensions_count = 0; - out_texture_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); - - if (!out_texture_view->extensions) - { - return CGLTF_ERROR_NOMEM; - } ++i; @@ -3849,7 +3817,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const } else { - i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture_view->extensions[out_texture_view->extensions_count++])); + i = cgltf_skip_json(tokens, i + 1); } if (i < 0) @@ -3886,11 +3854,11 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) { ++i; - out_pbr->metallic_factor = + out_pbr->metallic_factor = cgltf_json_to_float(tokens + i, json_chunk); ++i; } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) { ++i; out_pbr->roughness_factor = @@ -3903,13 +3871,11 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt } else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_pbr->base_color_texture); + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->base_color_texture); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) { - i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, - &out_pbr->metallic_roughness_texture); + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); } else { @@ -4353,6 +4319,37 @@ static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* return i; } +static int cgltf_parse_json_dispersion(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_dispersion* out_dispersion) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "dispersion") == 0) + { + ++i; + out_dispersion->dispersion = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -4360,11 +4357,11 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token int size = tokens[i].size; ++i; - for (int j = 0; j < size; ++j) + for (int j = 0; j < size; ++j) { CGLTF_CHECK_KEY(tokens[i]); - if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) { i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); } @@ -4444,7 +4441,7 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok = cgltf_json_to_int(tokens + i, json_chunk); ++i; } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) { ++i; out_sampler->wrap_t @@ -4494,7 +4491,7 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); ++i; } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) { ++i; out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); @@ -4556,6 +4553,34 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0) + { + out_texture->has_webp = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + if (i < 0) + { + return i; + } + } + } else { i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); @@ -4746,6 +4771,11 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to out_material->has_anisotropy = 1; i = cgltf_parse_json_anisotropy(options, tokens, i + 1, json_chunk, &out_material->anisotropy); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_dispersion") == 0) + { + out_material->has_dispersion = 1; + i = cgltf_parse_json_dispersion(tokens, i + 1, json_chunk, &out_material->dispersion); + } else { i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); @@ -4905,7 +4935,7 @@ static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_ else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) { ++i; - out_meshopt_compression->count = cgltf_json_to_int(tokens+i, json_chunk); + out_meshopt_compression->count = cgltf_json_to_size(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) @@ -6561,6 +6591,7 @@ static int cgltf_fixup_pointers(cgltf_data* data) { CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count); CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); } diff --git a/examples/game_demo/game_demo.c b/examples/game_demo/game_demo.c index 88de681..69023d7 100644 --- a/examples/game_demo/game_demo.c +++ b/examples/game_demo/game_demo.c @@ -117,20 +117,24 @@ int main() { // Y Axis (blue) Immdraw_Cone(transform_create(vec3(0.0, 2.0, 0.0), quat_ident(), vec3(0.3, 1.0, 0.3)), vec4(0.0, 0.0, 1.0, 1.0), false); - Immdraw_Cylinder(transform_create(VEC3_ZERO, quat_ident(), vec3(thickness, 1.1, thickness)), vec4(0.0, 0.0, 1.0, 1.0), - false); + Immdraw_Cylinder(transform_create(VEC3_ZERO, quat_ident(), vec3(thickness, 1.1, thickness)), + vec4(0.0, 0.0, 1.0, 1.0), false); // X Axis (green) - Immdraw_Cone(transform_create(vec3(2.0, 0.0, 0.0), quat_from_axis_angle(VEC3_Z, HALF_PI, true), vec3(0.3, 1.0, 0.3)), + Immdraw_Cone(transform_create(vec3(2.0, 0.0, 0.0), quat_from_axis_angle(VEC3_Z, HALF_PI, true), + vec3(0.3, 1.0, 0.3)), vec4(0.0, 1.0, 0.0, 1.0), false); - Immdraw_Cylinder(transform_create(VEC3_ZERO, quat_from_axis_angle(VEC3_Z, HALF_PI, true), vec3(thickness, 1.1, thickness)), vec4(0.0, 1.0, 0.0, 1.0), - false); + Immdraw_Cylinder(transform_create(VEC3_ZERO, quat_from_axis_angle(VEC3_Z, HALF_PI, true), + vec3(thickness, 1.1, thickness)), + vec4(0.0, 1.0, 0.0, 1.0), false); // Z Axis (red) - Immdraw_Cone(transform_create(vec3(0.0, 0.0, 2.0), quat_from_axis_angle(VEC3_X, -HALF_PI, true), vec3(0.3, 1.0, 0.3)), + Immdraw_Cone(transform_create(vec3(0.0, 0.0, 2.0), quat_from_axis_angle(VEC3_X, -HALF_PI, true), + vec3(0.3, 1.0, 0.3)), vec4(1.0, 0.0, 0.0, 1.0), false); - Immdraw_Cylinder(transform_create(VEC3_ZERO, quat_from_axis_angle(VEC3_X, -HALF_PI, true), vec3(thickness, 1.1, thickness)), vec4(1.0, 0.0, 0.0, 1.0), - false); + Immdraw_Cylinder(transform_create(VEC3_ZERO, quat_from_axis_angle(VEC3_X, -HALF_PI, true), + vec3(thickness, 1.1, thickness)), + vec4(1.0, 0.0, 0.0, 1.0), false); if (draw_debug) { // draw the player model with shadows diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c index e1ebd64..81642ae 100644 --- a/examples/skinned_animation/ex_skinned_animation.c +++ b/examples/skinned_animation/ex_skinned_animation.c @@ -15,7 +15,7 @@ int main() { double currentFrame = glfwGetTime(); double lastFrame = currentFrame; - double deltaTime; + double delta_time; Core_Bringup("Skinned Animation", NULL); @@ -40,7 +40,7 @@ int main() { // scene our_scene = make_default_scene(); - Vec3 cam_pos = vec3_create(0, 1.2, 4); + Vec3 cam_pos = vec3_create(1.5, 2.2, 8); Camera cam = Camera_Create(cam_pos, VEC3_NEG_Z, VEC3_Y, deg_to_rad(45.0)); SetCamera(cam); @@ -50,18 +50,20 @@ int main() { // animation // animation_clip track = simple_skin->animations->data[0]; - // f64 total_time = 0.0; + CASSERT(AnimationClip_darray_len(simple_skin->animations) > 0); + AnimationClip track = simple_skin->animations->data[0]; + f64 total_time = 0.0; while (!ShouldExit()) { Frame_Begin(); currentFrame = glfwGetTime(); - deltaTime = currentFrame - lastFrame; + delta_time = currentFrame - lastFrame; lastFrame = currentFrame; - // total_time += deltaTime; + total_time += delta_time; // printf("delta time %f\n", deltaTime); - // f64 t = fmod(total_time, track.rotation->max); - // INFO("Total time: %f", t); + f64 t = fmod(total_time, track.channels->data[0].max); + INFO("Delta time %f Animation time: %f", delta_time, t); // bone rotation // Quat rot = animation_sample(track.rotation, t).rotation; @@ -75,17 +77,17 @@ int main() { Mesh* m = Mesh_Get(simple_skin->meshes[0]); RenderEnt render_ents[1] = { (RenderEnt){ .mesh = simple_skin->meshes[0], .material = m->material, - .animation_clip = simple_skin->animations->data[0], - .is_playing = true, - .t = 0.0, + .armature = &m->armature, .affine = mat4_ident(), .flags = 0 } }; // ModelExtractRenderEnts(rend_ents, handle, mat4_translation(vec3(0, 0, 0)), 0); // draw_skinned_model(&core->renderer, &game.camera, simple_skin, tf, &our_scene); + Animation_Tick(&track, &m->armature, t); + Render_RenderEntities(render_ents, 1); - // Animation_VisualiseJoints(&m->armature); + Animation_VisualiseJoints(&m->armature); RenderEnt_darray_clear(rend_ents); diff --git a/src/animation.c b/src/animation.c index 48a5ff1..274602c 100644 --- a/src/animation.c +++ b/src/animation.c @@ -4,7 +4,6 @@ #include "maths.h" #include "maths_types.h" #include "ral_types.h" -#include "transform_hierarchy.h" Keyframe Animation_Sample(AnimationSampler* sampler, f32 t) { size_t previous_index = 0; @@ -47,6 +46,26 @@ Keyframe Animation_Sample(AnimationSampler* sampler, f32 t) { } } +void Animation_Tick(AnimationClip* clip, Armature* armature, f32 time) { + TRACE("Ticking animation %s", clip->clip_name); + + for (u32 c_i = 0; c_i < clip->channels->len; c_i++) { + AnimationSampler* sampler = clip->channels->data; + + // Interpolated keyframe based on time + Keyframe k = Animation_Sample(sampler, time); + + // Get the joint in the armature + Joint* joint = &armature->joints->data[sampler->target_joint_idx]; + if (sampler->animation.values.kind == KEYFRAME_ROTATION) { + // Update the joints rotation + joint->transform_components.rotation = k.rotation; + } else { + WARN("not yet implemented animation kind"); + } + } +} + void Animation_VisualiseJoints(Armature* armature) { for (int j = 0; j < armature->joints->len; j++) { Joint joint = armature->joints->data[j]; diff --git a/src/animation.h b/src/animation.h index e0ad543..2a489b8 100644 --- a/src/animation.h +++ b/src/animation.h @@ -1,5 +1,6 @@ #pragma once +#include "cgltf.h" #include "darray.h" #include "defines.h" #include "maths_types.h" @@ -36,9 +37,16 @@ typedef struct Keyframes { } Keyframes; typedef struct Joint { + // used instead of pointers later to update correct joints + size_t node_idx; + ssize_t parent; // parent bone. -1 means its the root + size_t children[8]; // children bones, upto 8 + u8 children_count; char* debug_label; // optional Mat4 inverse_bind_matrix; Mat4 local_transform; + /** @brief holds position, rotation, and scale that will be written to by animation + samplers every tick of the animation system. */ Transform transform_components; } Joint; #ifndef TYPED_JOINT_ARRAY @@ -50,7 +58,6 @@ typedef u32 JointIdx; typedef struct Armature { char* label; - arena arena; Joint_darray* joints; } Armature; @@ -65,41 +72,52 @@ typedef struct AnimationSpline { Interpolation interpolation; } AnimationSpline; +// combines a sampler and a channel in gltf typedef struct AnimationSampler { int current_index; f32 min; f32 max; AnimationSpline animation; + u32 target_joint_idx; // index into the array of joints in an armature } AnimationSampler; +#ifndef TYPED_ANIM_SAMPLER_ARRAY +KITC_DECL_TYPED_ARRAY(AnimationSampler); +#define TYPED_ANIM_SAMPLER_ARRAY +#endif /** @brief Sample an animation at a given time `t` returning an interpolated keyframe */ PUB Keyframe Animation_Sample(AnimationSampler* sampler, f32 t); +/** @brief A clip contains one or more animation curves. */ typedef struct AnimationClip { - // A clip contains one or more animation curves. - // For now I think we can just enumerate all of the properties (assuming *only* one per type is in - // a clip) and NULL = the property is not animated in this clip - // NOTE: when I call 'property' is typically referred to as a 'channel' by GLTF - AnimationSampler* rotation; - AnimationSampler* translation; - AnimationSampler* scale; - AnimationSampler* weights; + const char* clip_name; + bool is_playing; + f32 time; + AnimationSampler_darray* channels; } AnimationClip; #ifndef TYPED_ANIM_CLIP_ARRAY KITC_DECL_TYPED_ARRAY(AnimationClip); #define TYPED_ANIM_CLIP_ARRAY #endif -typedef struct SkinnedAnimation { - Mat4* joint_matrices; - size_t n_joints; -} SkinnedAnimation; +// typedef struct SkinnedAnimation { +// Mat4* joint_matrices; +// size_t n_joints; +// } SkinnedAnimation; PUB void Animation_Play(AnimationClip* clip); +void Animation_Tick(AnimationClip* clip, Armature* armature, f32 delta_time); + void Animation_VisualiseJoints(Armature* armature); +#define MAX_BONES 100 + typedef struct AnimDataUniform { - Mat4 bone_matrices[4]; + Mat4 bone_matrices[MAX_BONES]; } AnimDataUniform; ShaderDataLayout AnimData_GetLayout(void* data); + +// Animation Targets: +// - Mesh +// - Joint diff --git a/src/maths/primitives.c b/src/maths/primitives.c index 551a5df..c24d1e2 100644 --- a/src/maths/primitives.c +++ b/src/maths/primitives.c @@ -296,7 +296,8 @@ Geometry Geo_CreateCylinder(f32 radius, f32 height, u32 resolution) { // bot cap VERT_3D(vertices, VEC3_ZERO, VEC3_NEG_Y, vec2(0, 0)); for (u32 i = 0; i < resolution; i++) { - VERT_3D(vertices, vec3(cos(step * i) * radius, 0.0, sin(step * i) * radius), VEC3_NEG_Y, vec2(0, 0)); + VERT_3D(vertices, vec3(cos(step * i) * radius, 0.0, sin(step * i) * radius), VEC3_NEG_Y, + vec2(0, 0)); } for (u32 i = 1; i < resolution; i++) { push_triangle(indices, 0, i, i + 1); @@ -325,10 +326,10 @@ Geometry Geo_CreateCylinder(f32 radius, f32 height, u32 resolution) { VERT_3D(vertices, vec3(x, 0.0, z), vec3_normalise(vec3(x, 0.0, z)), vec2(0, 0)); } for (u32 i = 0; i < resolution; i++) { - u32 current = sides_start + i * 2; - u32 next = sides_start + ((i + 1) % resolution) * 2; - push_triangle(indices, current, next, current + 1); - push_triangle(indices, current + 1, next, next + 1); + u32 current = sides_start + i * 2; + u32 next = sides_start + ((i + 1) % resolution) * 2; + push_triangle(indices, current, next, current + 1); + push_triangle(indices, current + 1, next, next + 1); } Geometry geo = { diff --git a/src/render/pbr.c b/src/render/pbr.c index 044e6eb..6ea5746 100644 --- a/src/render/pbr.c +++ b/src/render/pbr.c @@ -13,6 +13,7 @@ #include "render_types.h" #include "shader_layouts.h" + void PBR_Init(PBR_Storage* storage) { INFO("PBR shaders init"); storage->pbr_pass = PBR_RPassCreate(); @@ -144,12 +145,27 @@ void PBR_Execute(PBR_Storage* storage, Camera camera, TextureHandle shadowmap_te Binding_Model model_data = { .model = renderable.affine }; GPU_EncodeBindShaderData(enc, 1, Binding_Model_GetLayout(&model_data)); + // Skinning matrices + + // 1. calculate matrices AnimDataUniform anim_data = { 0 }; - for (int i = 0; i < 4; i++) { - anim_data.bone_matrices[i] = mat4_ident(); + CASSERT(renderable.armature); + Armature* skeleton = renderable.armature; + // Skip the first one as we assume its root for this test + for (int j_i = 1; j_i < skeleton->joints->len; j_i++) { + Joint* j = &skeleton->joints->data[j_i]; + j->local_transform = transform_to_mat(&j->transform_components); + Mat4 m = mat4_mult(j->local_transform, j->inverse_bind_matrix); + Joint* p = &skeleton->joints->data[j->parent]; + j->local_transform = mat4_mult(j->local_transform, p->local_transform); + printf("Quat %f \n", j->transform_components.rotation.z); + } + + // 2. bind and upload + for (int j_i = 1; j_i < skeleton->joints->len; j_i++) { + anim_data.bone_matrices[j_i] = skeleton->joints->data[j_i].local_transform; } GPU_EncodeBindShaderData(enc, 3, AnimData_GetLayout(&anim_data)); - // Calculate matrices here // set buffers GPU_EncodeSetVertexBuffer(enc, mesh->vertex_buffer); diff --git a/src/render/render_types.h b/src/render/render_types.h index dd09896..16dee1d 100644 --- a/src/render/render_types.h +++ b/src/render/render_types.h @@ -135,9 +135,8 @@ typedef u32 RenderEntityFlags; typedef struct RenderEnt { MeshHandle mesh; MaterialHandle material; - AnimationClip animation_clip; - bool is_playing; - f32 t; + /** If NULL, no armature and the mesh is static geometry, else it is to be skinned */ + Armature* armature; Mat4 affine; // In the future this should be updated by the transform graph Bbox_3D bounding_box; RenderEntityFlags flags; diff --git a/src/resources/gltf.c b/src/resources/gltf.c index 76ba0a5..8d9217e 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -37,6 +37,7 @@ KITC_DECL_TYPED_ARRAY(Vec4u) KITC_DECL_TYPED_ARRAY(Vec4i) KITC_DECL_TYPED_ARRAY(Vec4) KITC_DECL_TYPED_ARRAY(face) +KITC_DECL_TYPED_ARRAY(i32) size_t GLTF_LoadMaterials(cgltf_data* data, Str8 relative_path, Material_darray* out_materials); @@ -149,7 +150,7 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel Vec4_darray* tmp_weights = Vec4_darray_new(1000); Material_darray* tmp_materials = Material_darray_new(1); Mesh_darray* tmp_meshes = Mesh_darray_new(1); - u32_darray* tmp_material_indexes = u32_darray_new(1); + i32_darray* tmp_material_indexes = i32_darray_new(1); Joint_darray* joints = Joint_darray_new(256); @@ -183,24 +184,35 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel DEBUG("# Joints %d", num_joints); // Create our data that will be placed onto the model - Armature armature = { .arena = arena_create(malloc(MB(1)), MB(1)) }; + Armature armature = { + .label = "test_skin"}; printf("Skin %s\n", gltf_skin->name); - armature.label = "test_skin"; // armature.label = Clone_cstr(&armature.arena, gltf_skin->name); armature.joints = joints; // ! Make sure not to free this cgltf_accessor* gltf_inverse_bind_matrices = gltf_skin->inverse_bind_matrices; + // --- Joints // for each one we'll spit out a joint for (size_t i = 0; i < num_joints; i++) { - TRACE("Joint %d", i); + // Get the joint and assign its node index for later referencing cgltf_node* joint_node = gltf_skin->joints[i]; - Joint joint_i = { .debug_label = "test_joint" }; + TRACE("Joint %d (node index %d)", i, cgltf_node_index(data, joint_node)); + Joint joint_i = { + .debug_label = "test_joint", + .node_idx = cgltf_node_index(data, joint_node), + .inverse_bind_matrix = mat4_ident() + }; if (joint_node->children_count > 0 && !joint_node->has_translation && !joint_node->has_rotation) { - WARN("joint Node with index %d is the root node", i); + WARN("Joint node with index %d is the root node", i); joint_i.transform_components = TRANSFORM_DEFAULT; + joint_i.parent = -1; + for (u32 c_i = 0; c_i < joint_node->children_count; c_i++) { + joint_i.children[c_i] = cgltf_node_index(data, joint_node->children[c_i]); + joint_i.children_count++; + } } else { TRACE("Storing joint transform"); joint_i.transform_components = TRANSFORM_DEFAULT; @@ -213,8 +225,11 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel if (joint_node->has_scale) { memcpy(&joint_i.transform_components.scale, &joint_node->scale, 3 * sizeof(f32)); } + joint_i.parent = cgltf_node_index(data, joint_node->parent); } + // Calculate and store the starting transform of the joint joint_i.local_transform = transform_to_mat(&joint_i.transform_components); + // Read in the inverse bind matrix cgltf_accessor_read_float(gltf_inverse_bind_matrices, i, &joint_i.inverse_bind_matrix.data[0], 16); Joint_darray_push(armature.joints, joint_i); @@ -280,10 +295,12 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel if (strcmp(primitive.material->name, tmp_materials->data[i].name) == 0) { INFO("Found material"); mat_idx = i; - u32_darray_push(tmp_material_indexes, mat_idx); + i32_darray_push(tmp_material_indexes, mat_idx); break; } } + } else { + i32_darray_push(tmp_material_indexes, -1); } TRACE("Vertex data has been loaded"); @@ -361,34 +378,32 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel out_model->anim_arena = arena_create(malloc(MB(1)), MB(1)); arena* arena = &out_model->anim_arena; + // Iterate over each animation in the GLTF for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) { cgltf_animation animation = data->animations[anim_idx]; AnimationClip clip = { 0 }; + clip.clip_name = "test anim clip"; + clip.channels = AnimationSampler_darray_new(1); // for each animation, loop through all the channels for (size_t c = 0; c < animation.channels_count; c++) { cgltf_animation_channel channel = animation.channels[c]; - AnimationSampler* sampler = arena_alloc(arena, sizeof(AnimationSampler)); + AnimationSampler sampler = {0}; - AnimationSampler** target_property; KeyframeKind data_type; switch (channel.target_path) { case cgltf_animation_path_type_rotation: - target_property = &clip.rotation; data_type = KEYFRAME_ROTATION; break; case cgltf_animation_path_type_translation: - target_property = &clip.translation; data_type = KEYFRAME_TRANSLATION; break; case cgltf_animation_path_type_scale: - target_property = &clip.scale; data_type = KEYFRAME_SCALE; break; case cgltf_animation_path_type_weights: - target_property = &clip.weights; data_type = KEYFRAME_WEIGHTS; WARN("Morph target weights arent supported yet"); return false; @@ -397,16 +412,16 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel return false; } - sampler->current_index = 0; - sampler->animation.interpolation = INTERPOLATION_LINEAR; // NOTE: hardcoded for now + sampler.current_index = 0; + sampler.animation.interpolation = INTERPOLATION_LINEAR; // NOTE: hardcoded for now // Keyframe times size_t n_frames = channel.sampler->input->count; CASSERT_MSG(channel.sampler->input->component_type == cgltf_component_type_r_32f, "Expected animation sampler input component to be type f32"); f32* times = arena_alloc(arena, n_frames * sizeof(f32)); - sampler->animation.n_timestamps = n_frames; - sampler->animation.timestamps = times; + sampler.animation.n_timestamps = n_frames; + sampler.animation.timestamps = times; cgltf_accessor_unpack_floats(channel.sampler->input, times, n_frames); // Keyframe values @@ -422,7 +437,7 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel case KEYFRAME_ROTATION: { Quat rot; cgltf_accessor_read_float(channel.sampler->output, v, &rot.x, 4); - printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w); + // printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w); keyframes.values[v].rotation = rot; break; } @@ -444,18 +459,35 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel } } } - sampler->animation.values = keyframes; - sampler->min = channel.sampler->input->min[0]; - sampler->max = channel.sampler->input->max[0]; - - *target_property = sampler; - printf("%d timestamps between %f and %f\n", sampler->animation.n_timestamps, sampler->min, - sampler->max); + sampler.animation.values = keyframes; + sampler.min = channel.sampler->input->min[0]; + sampler.max = channel.sampler->input->max[0]; + + // *target_property = sampler; + printf("%d timestamps between %f and %f\n", sampler.animation.n_timestamps, sampler.min, + sampler.max); + + // TODO: get target + size_t target_index = cgltf_node_index(data, channel.target_node); + size_t joint_index = 0; + bool found = false; + for (u32 ji = 0; ji < main_skeleton.joints->len; ji++) { + if (main_skeleton.joints->data[ji].node_idx == target_index) { + joint_index = ji; + found = true; + break; + } + } + if (!found) { WARN("Coulndnt find joint index");} + sampler.target_joint_idx = joint_index; // NOTE: this assuming the target is a joint at the moment + AnimationSampler_darray_push(clip.channels, sampler); } AnimationClip_darray_push(out_model->animations, clip); } } + + // exit(0); } num_meshes = tmp_meshes->len; @@ -474,8 +506,15 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel memcpy(out_model->materials, mat_handles, num_materials * sizeof(MaterialHandle)); for (u32 mesh_i = 0; mesh_i < num_meshes; mesh_i++) { - u32 mat_idx = tmp_material_indexes->data[mesh_i]; - tmp_meshes->data[mesh_i].material = mat_handles[mat_idx]; + i32 mat_idx = tmp_material_indexes->data[mesh_i]; + if (mat_idx > 0) { + tmp_meshes->data[mesh_i].material = mat_handles[mat_idx]; + + } else { + Material default_mat = PBRMaterialDefault(); + tmp_meshes->data[mesh_i].material = + Material_pool_insert(Render_GetMaterialPool(), &default_mat); + } MeshHandle mesh = Mesh_pool_insert(Render_GetMeshPool(), &tmp_meshes->data[mesh_i]); out_model->meshes[mesh_i] = mesh; } |