diff --git a/data/materials/island.peony_materials b/data/materials/island.peony_materials index 9593cd4..7579cfc 100644 --- a/data/materials/island.peony_materials +++ b/data/materials/island.peony_materials @@ -2,9 +2,9 @@ shader_asset.vert_path = standard.vert shader_asset.frag_path = standard.frag ; textures.albedo_texture = [albedo, island-texture.png] -; albedo_static = vec4(0.25, 0.2, 0.2, 1.0) +albedo_static = vec4(0.25, 0.2, 0.2, 1.0) ; albedo_static = vec4(0.32, 0.3, 0.3, 1.0) -textures.albedo_texture = [albedo, concrete.png] +; textures.albedo_texture = [albedo, concrete.png] metallic_static = 0.0 roughness_static = 1.0 ao_static = 1.0 diff --git a/data/materials/platform.peony_materials b/data/materials/platform.peony_materials new file mode 100644 index 0000000..30f60a7 --- /dev/null +++ b/data/materials/platform.peony_materials @@ -0,0 +1,7 @@ +> platform +shader_asset.vert_path = standard.vert +shader_asset.frag_path = standard.frag +textures.albedo_texture = [albedo, concrete.png] +metallic_static = 0.0 +roughness_static = 1.0 +ao_static = 1.0 diff --git a/data/scenes/demo.peony_scene b/data/scenes/demo.peony_scene index 269705c..b4b4043 100644 --- a/data/scenes/demo.peony_scene +++ b/data/scenes/demo.peony_scene @@ -79,7 +79,7 @@ spatial_component.scale = vec3(1.0, 1.0, 1.0) > platform model_path = platform/platform.obj -materials = [island] +materials = [platform] render_passes = [deferred, shadowcaster] spatial_component.position = vec3(-10.0, 0.0, 5.0) spatial_component.rotation = vec4(0.0, 0.0, 1.0, 0.0) diff --git a/src/array.hpp b/src/array.hpp index 9ca79f4..16b09e3 100644 --- a/src/array.hpp +++ b/src/array.hpp @@ -4,12 +4,25 @@ template class Array { public: + MemoryPool *memory_pool = nullptr; + const char *debug_name = nullptr; uint32 size = 0; uint32 max_size = 0; bool32 is_sparse = false; T *items = nullptr; + void alloc() { + this->items = (T*)Memory::push( + this->memory_pool, + sizeof(T) * this->max_size, + this->debug_name + ); + } + T* push() { + if (!this->items) { + alloc(); + } assert(this->size < this->max_size); uint32 new_idx = this->size; T* new_slot = this->items + new_idx; @@ -18,6 +31,9 @@ public: } T push(T new_item) { + if (!this->items) { + alloc(); + } T* new_slot = push(); *new_slot = new_item; return new_item; @@ -67,11 +83,10 @@ public: uint32 max_size, const char *debug_name ) : + memory_pool(memory_pool), + debug_name(debug_name), max_size(max_size) { - this->items = (T*)Memory::push( - memory_pool, sizeof(T) * this->max_size, debug_name - ); } Array( @@ -80,12 +95,12 @@ public: const char *debug_name, bool32 is_sparse ) : + memory_pool(memory_pool), + debug_name(debug_name), max_size(max_size), is_sparse(is_sparse) { - this->items = (T*)Memory::push( - memory_pool, sizeof(T) * this->max_size, debug_name - ); + alloc(); } }; diff --git a/src/constants.hpp b/src/constants.hpp index 2958b8f..2946be7 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -18,6 +18,7 @@ constexpr real64 PI = 3.14159265358979323846; constexpr uint32 MAX_N_ENTITIES = 256; constexpr uint32 MAX_N_MODELS = 128; +constexpr uint32 MAX_N_ANIMATED_MODELS = 16; constexpr uint32 MAX_DEBUG_NAME_LENGTH = 256; constexpr uint32 MAX_GENEROUS_STRING_LENGTH = 512; constexpr uint32 MAX_N_MESHES = 128; diff --git a/src/entities.hpp b/src/entities.hpp index fb9edb0..9c00a8f 100644 --- a/src/entities.hpp +++ b/src/entities.hpp @@ -26,43 +26,16 @@ struct BehaviorComponent { struct Bone { char name[MAX_NODE_NAME_LENGTH]; uint32 idx_parent; + uint32 n_anim_keys; + uint32 last_anim_key; glm::mat4 offset; }; -struct PositionKey { - glm::vec3 position; - real64 time; -}; - -struct RotationKey { - glm::quat rotation; - real64 time; -}; - -struct ScalingKey { - glm::vec3 scale; - real64 time; -}; - -struct AnimChannel { - uint32 n_position_keys; - uint32 n_rotation_keys; - uint32 n_scaling_keys; - uint32 last_position_key; - uint32 last_rotation_key; - uint32 last_scaling_key; - PositionKey position_keys[MAX_N_ANIM_KEYS]; - RotationKey rotation_keys[MAX_N_ANIM_KEYS]; - ScalingKey scaling_keys[MAX_N_ANIM_KEYS]; -}; - struct Animation { char name[MAX_NODE_NAME_LENGTH]; real64 duration; - real64 ticks_per_second; - // TODO: Make this a pointer to something that lives elsewhere. - AnimChannel anim_channels[MAX_N_BONES]; - uint32 n_anim_channels; + // NOTE: Index into the BoneMatrixPool + uint32 idx_bone_matrix_set; }; struct AnimationComponent { @@ -72,8 +45,6 @@ struct AnimationComponent { uint32 n_bones; Animation animations[MAX_N_ANIMATIONS]; uint32 n_animations; - glm::mat4 scene_root_transform; - glm::mat4 inverse_scene_root_transform; }; enum class LightType {none, point, directional}; diff --git a/src/entity_sets.cpp b/src/entity_sets.cpp index 38f6819..562fa49 100644 --- a/src/entity_sets.cpp +++ b/src/entity_sets.cpp @@ -1,3 +1,36 @@ +uint32 EntitySets::push_to_bone_matrix_pool(BoneMatrixPool *pool) { + return pool->n_bone_matrix_sets++; +} + + +glm::mat4* EntitySets::get_bone_matrix( + BoneMatrixPool *pool, + uint32 idx, + uint32 idx_bone, + uint32 idx_anim_key +) { + return pool->bone_matrices.get( + idx * MAX_N_ANIM_KEYS * MAX_N_BONES + + idx_anim_key * MAX_N_BONES + + idx_bone + ); +} + + +real64* EntitySets::get_bone_matrix_time( + BoneMatrixPool *pool, + uint32 idx, + uint32 idx_bone, + uint32 idx_anim_key +) { + return pool->times.get( + idx * MAX_N_ANIM_KEYS * MAX_N_BONES + + idx_anim_key * MAX_N_BONES + + idx_bone + ); +} + + EntityHandle EntitySets::make_handle( EntitySet *entity_set ) { @@ -59,21 +92,70 @@ glm::mat4 EntitySets::make_model_matrix( void EntitySets::update_animation_components( AnimationComponentSet *animation_component_set, SpatialComponentSet *spatial_component_set, - real64 t + real64 t, + BoneMatrixPool *bone_matrix_pool ) { - for ( - uint32 idx = 1; - idx < animation_component_set->components.size; - idx++ - ) { + for_range_named (handle, 1, animation_component_set->components.size) { AnimationComponent *animation_component = - animation_component_set->components.get(idx); + animation_component_set->components.get(handle); if (!Entities::is_animation_component_valid(animation_component)) { continue; } - make_bone_matrices(animation_component, t); + Animation *animation = &animation_component->animations[0]; + + for_range_named (idx_bone, 0, animation_component->n_bones) { + Bone *bone = &animation_component->bones[idx_bone]; + + // If we have no anim keys, just return the identity matrix. + if (bone->n_anim_keys == 0) { + animation_component->bone_matrices[idx_bone] = glm::mat4(1.0f); + + // If we only have one anim key, just return that. + } else if (bone->n_anim_keys == 1) { + animation_component->bone_matrices[idx_bone] = *EntitySets::get_bone_matrix( + bone_matrix_pool, + animation->idx_bone_matrix_set, + idx_bone, + 0 + ); + + // If we have multiple anim keys, find the right ones and interpolate. + } else { + real64 animation_timepoint = fmod(t, animation->duration); + + uint32 idx_anim_key = get_bone_matrix_anim_key_for_timepoint( + bone_matrix_pool, + animation_component, + animation_timepoint, + animation->idx_bone_matrix_set, + idx_bone + ); + + real64 t0 = *get_bone_matrix_time( + bone_matrix_pool, animation->idx_bone_matrix_set, idx_bone, idx_anim_key + ); + glm::mat4 transform_t0 = *get_bone_matrix( + bone_matrix_pool, animation->idx_bone_matrix_set, idx_bone, idx_anim_key + ); + + real64 t1 = *get_bone_matrix_time( + bone_matrix_pool, animation->idx_bone_matrix_set, idx_bone, idx_anim_key + 1 + ); + glm::mat4 transform_t1 = *get_bone_matrix( + bone_matrix_pool, animation->idx_bone_matrix_set, idx_bone, idx_anim_key + 1 + ); + + real32 lerp_factor = (real32)((animation_timepoint - t0) / (t1 - t0)); + + // NOTE: This is probably bad if we have scaling in our transform? + glm::mat4 interpolated_matrix = + (transform_t0 * (1.0f - lerp_factor)) + (transform_t1 * lerp_factor); + + animation_component->bone_matrices[idx_bone] = interpolated_matrix; + } + } } } @@ -150,190 +232,102 @@ void EntitySets::update_light_components( } -uint32 EntitySets::get_anim_channel_position_index_for_animation_timepoint( - AnimChannel *anim_channel, real64 animation_timepoint +uint32 EntitySets::get_bone_matrix_anim_key_for_timepoint( + BoneMatrixPool *bone_matrix_pool, + AnimationComponent *animation_component, + real64 animation_timepoint, + uint32 idx_bone_matrix_set, + uint32 idx_bone ) { - uint32 idx = anim_channel->last_position_key; + Bone *bone = &animation_component->bones[idx_bone]; + assert(bone->n_anim_keys > 1); + uint32 idx_anim_key = bone->last_anim_key; do { - if ( - animation_timepoint > anim_channel->position_keys[idx].time && - animation_timepoint < anim_channel->position_keys[idx + 1].time - ) { - anim_channel->last_position_key = idx; - return idx; + real64 t0 = *get_bone_matrix_time( + bone_matrix_pool, idx_bone_matrix_set, idx_bone, idx_anim_key + ); + real64 t1 = *get_bone_matrix_time( + bone_matrix_pool, idx_bone_matrix_set, idx_bone, idx_anim_key + 1 + ); + if (animation_timepoint > t0 && animation_timepoint < t1) { + bone->last_anim_key = idx_anim_key; + return idx_anim_key; } - idx++; - if (idx == anim_channel->n_position_keys - 1) { - idx = 0; + idx_anim_key++; + assert(idx_anim_key < bone->n_anim_keys); + if (idx_anim_key == bone->n_anim_keys - 1) { + idx_anim_key = 0; } - } while (idx != anim_channel->last_position_key); + } while (idx_anim_key != bone->last_anim_key); + log_fatal("Could not find anim key."); return 0; } -uint32 EntitySets::get_anim_channel_rotation_index_for_animation_timepoint( - AnimChannel *anim_channel, real64 animation_timepoint +void EntitySets::make_bone_matrices_for_animation_bone( + AnimationComponent *animation_component, + aiNodeAnim *ai_channel, + uint32 idx_animation, + uint32 idx_bone, + BoneMatrixPool *bone_matrix_pool ) { - uint32 idx = anim_channel->last_rotation_key; - do { - if ( - animation_timepoint > anim_channel->rotation_keys[idx].time && - animation_timepoint < anim_channel->rotation_keys[idx + 1].time - ) { - anim_channel->last_rotation_key = idx; - return idx; - } - idx++; - if (idx == anim_channel->n_rotation_keys - 1) { - idx = 0; - } - } while (idx != anim_channel->last_rotation_key); - return 0; -} + assert(ai_channel->mNumPositionKeys == ai_channel->mNumRotationKeys); + assert(ai_channel->mNumPositionKeys == ai_channel->mNumScalingKeys); + Bone *bone = &animation_component->bones[idx_bone]; + bone->n_anim_keys = ai_channel->mNumPositionKeys; -uint32 EntitySets::get_anim_channel_scaling_index_for_animation_timepoint( - AnimChannel *anim_channel, real64 animation_timepoint -) { - uint32 idx = anim_channel->last_scaling_key; - do { - if ( - animation_timepoint > anim_channel->scaling_keys[idx].time && - animation_timepoint < anim_channel->scaling_keys[idx + 1].time - ) { - anim_channel->last_scaling_key = idx; - return idx; - } - idx++; - if (idx == anim_channel->n_scaling_keys - 1) { - idx = 0; - } - } while (idx != anim_channel->last_scaling_key); - return 0; -} - - -void EntitySets::make_bone_translation( - AnimChannel *anim_channel, real64 animation_timepoint, glm::mat4 *translation -) { - if (anim_channel->n_position_keys == 1) { - *translation = glm::translate( - glm::mat4(1.0f), anim_channel->position_keys[0].position + for_range_named (idx_anim_key, 0, bone->n_anim_keys) { + assert( + ai_channel->mPositionKeys[idx_anim_key].mTime == + ai_channel->mRotationKeys[idx_anim_key].mTime ); - return; - } - - uint32 idx_position = get_anim_channel_position_index_for_animation_timepoint( - anim_channel, animation_timepoint - ); - real64 prev_keyframe = anim_channel->position_keys[idx_position].time; - real64 next_keyframe = anim_channel->position_keys[idx_position + 1].time; - - *translation = glm::translate( - glm::mat4(1.0f), - glm::mix( - anim_channel->position_keys[idx_position].position, - anim_channel->position_keys[idx_position + 1].position, - (animation_timepoint - prev_keyframe) / (next_keyframe - prev_keyframe) - ) - ); -} - - -void EntitySets::make_bone_rotation( - AnimChannel *anim_channel, real64 animation_timepoint, glm::mat4 *rotation -) { - if (anim_channel->n_rotation_keys == 1) { - *rotation = glm::toMat4(glm::normalize(anim_channel->rotation_keys[0].rotation)); - return; - } - - uint32 idx_rotation = get_anim_channel_rotation_index_for_animation_timepoint( - anim_channel, animation_timepoint - ); - real64 prev_keyframe = anim_channel->rotation_keys[idx_rotation].time; - real64 next_keyframe = anim_channel->rotation_keys[idx_rotation + 1].time; - - *rotation = glm::toMat4(glm::normalize(glm::slerp( - anim_channel->rotation_keys[idx_rotation].rotation, - anim_channel->rotation_keys[idx_rotation + 1].rotation, - (real32)((animation_timepoint - prev_keyframe) / (next_keyframe - prev_keyframe)) - ))); -} - - -void EntitySets::make_bone_scaling( - AnimChannel *anim_channel, real64 animation_timepoint, glm::mat4 *scaling -) { - if (anim_channel->n_scaling_keys == 1) { - *scaling = glm::scale( - glm::mat4(1.0f), anim_channel->scaling_keys[0].scale + assert( + ai_channel->mPositionKeys[idx_anim_key].mTime == + ai_channel->mScalingKeys[idx_anim_key].mTime ); - return; - } - - uint32 idx_scaling = get_anim_channel_scaling_index_for_animation_timepoint( - anim_channel, animation_timepoint - ); - real64 prev_keyframe = anim_channel->scaling_keys[idx_scaling].time; - real64 next_keyframe = anim_channel->scaling_keys[idx_scaling + 1].time; - - *scaling = glm::scale( - glm::mat4(1.0f), - glm::mix( - anim_channel->scaling_keys[idx_scaling].scale, - anim_channel->scaling_keys[idx_scaling + 1].scale, - (animation_timepoint - prev_keyframe) / (next_keyframe - prev_keyframe) - ) - ); -} + real64 anim_key_time = ai_channel->mPositionKeys[idx_anim_key].mTime; + glm::mat4 *bone_matrix = get_bone_matrix( + bone_matrix_pool, + animation_component->animations[idx_animation].idx_bone_matrix_set, + idx_bone, + idx_anim_key + ); -void EntitySets::make_bone_matrices( - AnimationComponent *animation_component, - real64 t -) { - glm::mat4 local_bone_matrices[MAX_N_BONES]; - - Animation *animation = &animation_component->animations[0]; - real64 duration_in_sec = animation->ticks_per_second * animation->duration; - real64 animation_timepoint = fmod(t, duration_in_sec); + real64 *time = get_bone_matrix_time( + bone_matrix_pool, + animation_component->animations[idx_animation].idx_bone_matrix_set, + idx_bone, + idx_anim_key + ); - for_range (0, animation_component->n_bones) { - // NOTE: Because bones are sorted such that parents always come first, - // it is guaranteed that each node's parent's matrix will always be set - // when we get to computing the child. - Bone *bone = &animation_component->bones[idx]; glm::mat4 parent_transform = glm::mat4(1.0f); - glm::mat4 translation = glm::mat4(1.0f); - glm::mat4 rotation = glm::mat4(1.0f); - glm::mat4 scaling = glm::mat4(1.0f); - - if (idx > 0) { - parent_transform = local_bone_matrices[bone->idx_parent]; - } - - AnimChannel *anim_channel = &animation->anim_channels[idx]; - - if (anim_channel->n_position_keys > 0) { - make_bone_translation(anim_channel, animation_timepoint, &translation); - } - if (anim_channel->n_rotation_keys > 0) { - make_bone_rotation(anim_channel, animation_timepoint, &rotation); + if (idx_bone > 0) { + parent_transform = *get_bone_matrix( + bone_matrix_pool, + animation_component->animations[idx_animation].idx_bone_matrix_set, + bone->idx_parent, + idx_anim_key + ); } - if (anim_channel->n_scaling_keys > 0) { - make_bone_scaling(anim_channel, animation_timepoint, &scaling); - } + glm::mat4 translation = glm::translate( + glm::mat4(1.0f), + Util::aiVector3D_to_glm(&ai_channel->mPositionKeys[idx_anim_key].mValue) + ); + glm::mat4 rotation = glm::toMat4(glm::normalize( + Util::aiQuaternion_to_glm(&ai_channel->mRotationKeys[idx_anim_key].mValue) + )); + glm::mat4 scaling = glm::scale( + glm::mat4(1.0f), + Util::aiVector3D_to_glm(&ai_channel->mScalingKeys[idx_anim_key].mValue) + ); glm::mat4 anim_transform = translation * rotation * scaling; - local_bone_matrices[idx] = parent_transform * anim_transform; - animation_component->bone_matrices[idx] = - animation_component->scene_root_transform * - local_bone_matrices[idx] * - bone->offset * - animation_component->inverse_scene_root_transform; + *bone_matrix = parent_transform * anim_transform; + *time = anim_key_time; } } diff --git a/src/entity_sets.hpp b/src/entity_sets.hpp index ee59c34..6299f6d 100644 --- a/src/entity_sets.hpp +++ b/src/entity_sets.hpp @@ -48,6 +48,19 @@ struct AnimationComponentSet { }; namespace EntitySets { + uint32 push_to_bone_matrix_pool(BoneMatrixPool *pool); + glm::mat4* get_bone_matrix( + BoneMatrixPool *pool, + uint32 idx, + uint32 idx_bone, + uint32 idx_anim_key + ); + real64* get_bone_matrix_time( + BoneMatrixPool *pool, + uint32 idx, + uint32 idx_bone, + uint32 idx_anim_key + ); EntityHandle make_handle(EntitySet *entity_set); Entity* add_entity_to_set( EntitySet *entity_set, @@ -60,7 +73,8 @@ namespace EntitySets { void update_animation_components( AnimationComponentSet *animation_component_set, SpatialComponentSet *spatial_component_set, - real64 t + real64 t, + BoneMatrixPool *bone_matrix_pool ); void update_behavior_components( BehaviorComponentSet *behavior_component_set, @@ -73,27 +87,19 @@ namespace EntitySets { real64 t, glm::vec3 camera_position ); - uint32 get_anim_channel_position_index_for_animation_timepoint( - AnimChannel *anim_channel, real64 t - ); - uint32 get_anim_channel_rotation_index_for_animation_timepoint( - AnimChannel *anim_channel, real64 t - ); - uint32 get_anim_channel_scaling_index_for_animation_timepoint( - AnimChannel *anim_channel, real64 t - ); - void make_bone_translation( - AnimChannel *anim_channel, real64 t, glm::mat4 *translation - ); - void make_bone_rotation( - AnimChannel *anim_channel, real64 animation_timepoint, glm::mat4 *translation - ); - void make_bone_scaling( - AnimChannel *anim_channel, real64 animation_timepoint, glm::mat4 *scaling + uint32 get_bone_matrix_anim_key_for_timepoint( + BoneMatrixPool *bone_matrix_pool, + AnimationComponent *animation_component, + real64 animation_timepoint, + uint32 idx_bone_matrix_set, + uint32 idx_bone ); - void make_bone_matrices( + void make_bone_matrices_for_animation_bone( AnimationComponent *animation_component, - real64 t + aiNodeAnim *ai_channel, + uint32 idx_animation, + uint32 idx_bone, + BoneMatrixPool *bone_matrix_pool ); AnimationComponent* find_animation_component( SpatialComponent *spatial, diff --git a/src/models.cpp b/src/models.cpp index f9d914a..cd95c3f 100644 --- a/src/models.cpp +++ b/src/models.cpp @@ -140,90 +140,76 @@ void Models::load_bones( void Models::load_animations( AnimationComponent *animation_component, - const aiScene *scene + const aiScene *scene, + BoneMatrixPool *bone_matrix_pool ) { - animation_component->scene_root_transform = + glm::mat4 scene_root_transform = Util::aimatrix4x4_to_glm(&scene->mRootNode->mTransformation); - animation_component->inverse_scene_root_transform = - glm::inverse(animation_component->scene_root_transform); + glm::mat4 inverse_scene_root_transform = glm::inverse(scene_root_transform); animation_component->n_animations = scene->mNumAnimations; for_range_named (idx_animation, 0, scene->mNumAnimations) { - Animation *animation = - &animation_component->animations[idx_animation]; + Animation *animation = &animation_component->animations[idx_animation]; aiAnimation *ai_animation = scene->mAnimations[idx_animation]; - animation->duration = ai_animation->mDuration; - animation->ticks_per_second = ai_animation->mTicksPerSecond; + *animation = { + .duration = ai_animation->mDuration * ai_animation->mTicksPerSecond, + .idx_bone_matrix_set = EntitySets::push_to_bone_matrix_pool(bone_matrix_pool), + }; strcpy(animation->name, ai_animation->mName.C_Str()); -#if USE_ANIMATION_DEBUG - log_info( - "animation %d (dur %f)", - idx_animation, - animation->duration - ); -#endif + // Calculate bone matrices. + // NOTE: We do not finalise the bone matrices at this stage! + // The matrices in local form are still needed for the children. + for_range_named(idx_bone, 0, animation_component->n_bones) { + Bone *bone = &animation_component->bones[idx_bone]; - animation->n_anim_channels = ai_animation->mNumChannels; - for_range_named (idx_channel, 0, ai_animation->mNumChannels) { - aiNodeAnim *ai_channel = ai_animation->mChannels[idx_channel]; + uint32 found_channel_idx = 0; + bool32 did_find_channel = false; - uint32 found_bone_idx = 0; - bool32 did_find_bone = false; - - for_range_named(idx_bone, 0, animation_component->n_bones) { - if (Str::eq( - ai_channel->mNodeName.C_Str(), animation_component->bones[idx_bone].name - )) { - found_bone_idx = idx_bone; - did_find_bone = true; + for_range_named (idx_channel, 0, ai_animation->mNumChannels) { + aiNodeAnim *ai_channel = ai_animation->mChannels[idx_channel]; + if (Str::eq(ai_channel->mNodeName.C_Str(), bone->name)) { + found_channel_idx = idx_channel; + did_find_channel = true; + break; } } - assert(did_find_bone); + if (!did_find_channel) { + // No channel for this bone. Maybe it's just not animated. Skip it. + continue; + } - AnimChannel *channel = &animation->anim_channels[found_bone_idx]; - *channel = { - .n_position_keys = ai_channel->mNumPositionKeys, - .n_rotation_keys = ai_channel->mNumRotationKeys, - .n_scaling_keys = ai_channel->mNumScalingKeys, - }; -#if USE_ANIMATION_DEBUG - log_info( - "channel %d: %d position, %d rotation, %d scaling", - idx_channel, - channel->n_position_keys, - channel->n_rotation_keys, - channel->n_scaling_keys + EntitySets::make_bone_matrices_for_animation_bone( + animation_component, + ai_animation->mChannels[found_channel_idx], + idx_animation, + idx_bone, + bone_matrix_pool ); -#endif - - for_range_named (idx_key, 0, ai_channel->mNumPositionKeys) { - channel->position_keys[idx_key] = { - .position = Util::aiVector3D_to_glm( - &ai_channel->mPositionKeys[idx_key].mValue - ), - .time = ai_channel->mPositionKeys[idx_key].mTime, - }; - } + } - for_range_named (idx_key, 0, ai_channel->mNumRotationKeys) { - channel->rotation_keys[idx_key] = { - .rotation = Util::aiQuaternion_to_glm( - &ai_channel->mRotationKeys[idx_key].mValue - ), - .time = ai_channel->mRotationKeys[idx_key].mTime, - }; - } + // Finalise bone matrices. + // NOTE: Now that we've calculated all the bone matrices for this + // animation, we can finalise them. + for_range_named(idx_bone, 0, animation_component->n_bones) { + Bone *bone = &animation_component->bones[idx_bone]; + + for_range_named (idx_anim_key, 0, bone->n_anim_keys) { + // #slow: We could avoid this multiplication here. + glm::mat4 *bone_matrix = EntitySets::get_bone_matrix( + bone_matrix_pool, + animation->idx_bone_matrix_set, + idx_bone, + idx_anim_key + ); - for_range_named (idx_key, 0, ai_channel->mNumScalingKeys) { - channel->scaling_keys[idx_key] = { - .scale = Util::aiVector3D_to_glm( - &ai_channel->mScalingKeys[idx_key].mValue - ), - .time = ai_channel->mScalingKeys[idx_key].mTime, - }; + *bone_matrix = + scene_root_transform * + *bone_matrix * + bone->offset * + inverse_scene_root_transform; } } } @@ -399,7 +385,10 @@ void Models::load_node( } -void Models::load_model(EntityLoader *entity_loader) { +void Models::load_model( + EntityLoader *entity_loader, + BoneMatrixPool *bone_matrix_pool +) { // If we're not loading from a file, all the data has already // been loaded previously, so we just need to handle the // shaders and textures and so on, so skip this. @@ -435,14 +424,14 @@ void Models::load_model(EntityLoader *entity_loader) { animation_component, scene ); - load_animations( - animation_component, scene - ); - load_node( entity_loader, scene->mRootNode, scene, glm::mat4(1.0f), 0ULL ); + load_animations( + animation_component, scene, bone_matrix_pool + ); + aiReleaseImport(scene); } @@ -556,7 +545,8 @@ bool32 Models::prepare_model_and_check_if_done( SpatialComponentSet *spatial_component_set, LightComponentSet *light_component_set, BehaviorComponentSet *behavior_component_set, - AnimationComponentSet *animation_component_set + AnimationComponentSet *animation_component_set, + BoneMatrixPool *bone_matrix_pool ) { if (entity_loader->state == EntityLoaderState::initialized) { task_queue->push({ @@ -565,6 +555,7 @@ bool32 Models::prepare_model_and_check_if_done( .entity_loader = entity_loader, }, .persistent_pbo = nullptr, + .bone_matrix_pool = bone_matrix_pool, }); entity_loader->state = EntityLoaderState::mesh_data_being_loaded; } diff --git a/src/models.hpp b/src/models.hpp index 231a5a4..4925028 100644 --- a/src/models.hpp +++ b/src/models.hpp @@ -87,7 +87,8 @@ namespace Models { ); void load_animations( AnimationComponent *animation_component, - const aiScene *scene + const aiScene *scene, + BoneMatrixPool *bone_matrix_pool ); void load_mesh( Mesh *mesh, @@ -104,7 +105,8 @@ namespace Models { glm::mat4 accumulated_transform, Pack indices_pack ); void load_model( - EntityLoader *entity_loader + EntityLoader *entity_loader, + BoneMatrixPool *bone_matrix_pool ); void create_entities( EntityLoader *entity_loader, @@ -125,7 +127,8 @@ namespace Models { SpatialComponentSet *spatial_component_set, LightComponentSet *light_component_set, BehaviorComponentSet *behavior_component_set, - AnimationComponentSet *animation_component_set + AnimationComponentSet *animation_component_set, + BoneMatrixPool *bone_matrix_pool ); EntityLoader* init_entity_loader( EntityLoader *entity_loader, diff --git a/src/peony.cpp b/src/peony.cpp index 6183300..41def89 100644 --- a/src/peony.cpp +++ b/src/peony.cpp @@ -194,7 +194,7 @@ int main() { MemoryPool state_memory_pool = {}; state_memory_pool.size = sizeof(State); - MemoryPool asset_memory_pool = {.size = MB_TO_B(4096)}; + MemoryPool asset_memory_pool = {.size = MB_TO_B(256)}; WindowInfo window_info; Renderer::init_window(&window_info); diff --git a/src/state.cpp b/src/state.cpp index 87289fa..caccfa4 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -64,6 +64,18 @@ State* init_state( asset_memory_pool, MAX_N_ENTITIES, "animation_components", true ) }; + state->bone_matrix_pool.bone_matrices = Array( + asset_memory_pool, + MAX_N_ANIMATED_MODELS * MAX_N_BONES * MAX_N_ANIMATIONS * MAX_N_ANIM_KEYS, + "bone_matrices", + true + ); + state->bone_matrix_pool.times = Array( + asset_memory_pool, + MAX_N_ANIMATED_MODELS * MAX_N_BONES * MAX_N_ANIMATIONS * MAX_N_ANIM_KEYS, + "bone_matrix_times", + true + ); return state; } diff --git a/src/state.hpp b/src/state.hpp index 3cba381..7021000 100644 --- a/src/state.hpp +++ b/src/state.hpp @@ -17,6 +17,12 @@ struct PerfCounters { uint32 last_fps; }; +struct BoneMatrixPool { + Array bone_matrices; + Array times; + uint32 n_bone_matrix_sets; +}; + struct State { bool32 is_manual_frame_advance_enabled; bool32 should_manually_advance_to_next_frame; @@ -54,6 +60,8 @@ struct State { BehaviorComponentSet behavior_component_set; AnimationComponentSet animation_component_set; + BoneMatrixPool bone_matrix_pool; + real32 heading_opacity; const char *heading_text; real32 heading_fadeout_duration; diff --git a/src/tasks.cpp b/src/tasks.cpp index f12ab38..e61fc2c 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -12,7 +12,10 @@ void Tasks::run_task(Task *task) { auto t0 = debug_start_timer(); if (task->type == TaskType::load_model) { - Models::load_model(task->target.entity_loader); + Models::load_model( + task->target.entity_loader, + task->bone_matrix_pool + ); } else if (task->type == TaskType::copy_textures_to_pbo) { Materials::copy_textures_to_pbo(task->target.material, task->persistent_pbo); } else { diff --git a/src/tasks.hpp b/src/tasks.hpp index ed87809..869acd2 100644 --- a/src/tasks.hpp +++ b/src/tasks.hpp @@ -2,6 +2,7 @@ #define TASK_HPP struct EntityLoader; +struct BoneMatrixPool; enum class TaskType { load_model, @@ -17,6 +18,7 @@ struct Task { TaskType type; TaskTarget target; PersistentPbo *persistent_pbo; + BoneMatrixPool *bone_matrix_pool; }; namespace Tasks { diff --git a/src/world.cpp b/src/world.cpp index f0a554f..894e533 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -734,7 +734,8 @@ bool32 World::check_all_entities_loaded(State *state) { &state->spatial_component_set, &state->light_component_set, &state->behavior_component_set, - &state->animation_component_set + &state->animation_component_set, + &state->bone_matrix_pool ); if (!is_done_loading) { are_all_done_loading = false; @@ -756,7 +757,8 @@ void World::update(State *state) { EntitySets::update_animation_components( &state->animation_component_set, &state->spatial_component_set, - state->t + state->t, + &state->bone_matrix_pool ); EntitySets::update_behavior_components(