A 3D game engine from scratch.
at main 230 lines 7.4 kB view raw
1// (c) 2020 Vlad-Stefan Harbuz <vlad@vladh.net> 2 3#include "util.hpp" 4#include "logs.hpp" 5#include "engine.hpp" 6#include "anim.hpp" 7#include "intrinsics.hpp" 8 9 10anim::State *anim::state = nullptr; 11 12 13bool 14anim::is_animation_component_valid(anim::Component *animation_component) 15{ 16 return animation_component->n_bones > 0 && 17 animation_component->n_animations > 0; 18} 19 20 21u32 22anim::push_to_bone_matrix_pool() 23{ 24 return anim::state->bone_matrix_pool.n_bone_matrix_sets++; 25} 26 27 28m4 * 29anim::get_bone_matrix(u32 idx, u32 idx_bone, u32 idx_anim_key) 30{ 31 return anim::state->bone_matrix_pool.bone_matrices[ 32 idx * MAX_N_ANIM_KEYS * MAX_N_BONES + 33 idx_anim_key * MAX_N_BONES + 34 idx_bone 35 ]; 36} 37 38 39void 40anim::update() 41{ 42 each (animation_component, *get_components()) { 43 if (!is_animation_component_valid(animation_component)) { 44 continue; 45 } 46 47 Animation *animation = &animation_component->animations[0]; 48 49 range_named (idx_bone, 0, animation_component->n_bones) { 50 Bone *bone = &animation_component->bones[idx_bone]; 51 52 // If we have no anim keys, just return the identity matrix. 53 if (bone->n_anim_keys == 0) { 54 animation_component->bone_matrices[idx_bone] = m4(1.0f); 55 56 // If we only have one anim key, just return that. 57 } else if (bone->n_anim_keys == 1) { 58 animation_component->bone_matrices[idx_bone] = *get_bone_matrix( 59 animation->idx_bone_matrix_set, idx_bone, 0); 60 61 // If we have multiple anim keys, find the right ones and interpolate. 62 } else { 63 f64 animation_timepoint = fmod(engine::get_t(), animation->duration); 64 65 u32 idx_anim_key = get_bone_matrix_anim_key_for_timepoint( 66 animation_component, 67 animation_timepoint, animation->idx_bone_matrix_set, 68 idx_bone); 69 70 f64 t0 = *get_bone_matrix_time( 71 animation->idx_bone_matrix_set, idx_bone, idx_anim_key); 72 m4 transform_t0 = *get_bone_matrix( 73 animation->idx_bone_matrix_set, idx_bone, idx_anim_key); 74 75 f64 t1 = *get_bone_matrix_time( 76 animation->idx_bone_matrix_set, idx_bone, idx_anim_key + 1); 77 m4 transform_t1 = *get_bone_matrix( 78 animation->idx_bone_matrix_set, idx_bone, idx_anim_key + 1); 79 80 f32 lerp_factor = (f32)((animation_timepoint - t0) / (t1 - t0)); 81 82 // NOTE: This is probably bad if we have scaling in our transform? 83 m4 interpolated_matrix = 84 (transform_t0 * (1.0f - lerp_factor)) + (transform_t1 * lerp_factor); 85 86 animation_component->bone_matrices[idx_bone] = interpolated_matrix; 87 } 88 } 89 } 90} 91 92 93void 94anim::make_bone_matrices_for_animation_bone( 95 anim::Component *animation_component, 96 aiNodeAnim *ai_channel, 97 u32 idx_animation, 98 u32 idx_bone 99) { 100 assert(ai_channel->mNumPositionKeys == ai_channel->mNumRotationKeys); 101 assert(ai_channel->mNumPositionKeys == ai_channel->mNumScalingKeys); 102 103 Bone *bone = &animation_component->bones[idx_bone]; 104 bone->n_anim_keys = ai_channel->mNumPositionKeys; 105 106 range_named (idx_anim_key, 0, bone->n_anim_keys) { 107 assert(ai_channel->mPositionKeys[idx_anim_key].mTime == 108 ai_channel->mRotationKeys[idx_anim_key].mTime); 109 assert(ai_channel->mPositionKeys[idx_anim_key].mTime == 110 ai_channel->mScalingKeys[idx_anim_key].mTime); 111 f64 anim_key_time = ai_channel->mPositionKeys[idx_anim_key].mTime; 112 113 m4 *bone_matrix = get_bone_matrix( 114 animation_component->animations[idx_animation].idx_bone_matrix_set, 115 idx_bone, idx_anim_key); 116 117 f64 *time = get_bone_matrix_time( 118 animation_component->animations[idx_animation].idx_bone_matrix_set, 119 idx_bone, idx_anim_key); 120 121 m4 parent_transform = m4(1.0f); 122 123 if (idx_bone > 0) { 124 parent_transform = *get_bone_matrix( 125 animation_component->animations[idx_animation].idx_bone_matrix_set, 126 bone->idx_parent, idx_anim_key); 127 } 128 129 m4 translation = glm::translate(m4(1.0f), 130 util::aiVector3D_to_glm(&ai_channel->mPositionKeys[idx_anim_key].mValue)); 131 m4 rotation = glm::toMat4(normalize( 132 util::aiQuaternion_to_glm(&ai_channel->mRotationKeys[idx_anim_key].mValue) 133 )); 134 m4 scaling = glm::scale(m4(1.0f), 135 util::aiVector3D_to_glm(&ai_channel->mScalingKeys[idx_anim_key].mValue)); 136 137 m4 anim_transform = translation * rotation * scaling; 138 *bone_matrix = parent_transform * anim_transform; 139 *time = anim_key_time; 140 } 141} 142 143 144anim::Component * 145anim::find_animation_component(spatial::Component *spatial_component) 146{ 147 anim::Component *animation_component = get_component(spatial_component->entity_handle); 148 149 if (is_animation_component_valid(animation_component)) { 150 return animation_component; 151 } 152 153 if (spatial_component->parent_entity_handle != entities::NO_ENTITY_HANDLE) { 154 spatial::Component *parent = spatial::get_component(spatial_component->parent_entity_handle); 155 return find_animation_component(parent); 156 } 157 158 return nullptr; 159} 160 161 162Array<anim::Component> * 163anim::get_components() 164{ 165 return &anim::state->components; 166} 167 168 169anim::Component * 170anim::get_component(entities::Handle entity_handle) 171{ 172 return anim::state->components[entity_handle]; 173} 174 175 176void 177anim::init(anim::State *anim_state, memory::Pool *asset_memory_pool) 178{ 179 anim::state = anim_state; 180 anim::state->bone_matrix_pool.bone_matrices = Array<m4>(asset_memory_pool, 181 MAX_N_ANIMATED_MODELS * MAX_N_BONES * MAX_N_ANIMATIONS * MAX_N_ANIM_KEYS, 182 "bone_matrices", true); 183 anim::state->bone_matrix_pool.times = Array<f64>(asset_memory_pool, 184 MAX_N_ANIMATED_MODELS * MAX_N_BONES * MAX_N_ANIMATIONS * MAX_N_ANIM_KEYS, 185 "bone_matrix_times", true); 186 anim::state->components = Array<anim::Component>( 187 asset_memory_pool, MAX_N_ENTITIES, "animation_components", true, 1); 188} 189 190 191f64 * 192anim::get_bone_matrix_time( 193 u32 idx, 194 u32 idx_bone, 195 u32 idx_anim_key 196) { 197 return anim::state->bone_matrix_pool.times[ 198 idx * MAX_N_ANIM_KEYS * MAX_N_BONES + 199 idx_anim_key * MAX_N_BONES + 200 idx_bone 201 ]; 202} 203 204 205u32 206anim::get_bone_matrix_anim_key_for_timepoint( 207 anim::Component *animation_component, 208 f64 animation_timepoint, 209 u32 idx_bone_matrix_set, 210 u32 idx_bone 211) { 212 Bone *bone = &animation_component->bones[idx_bone]; 213 assert(bone->n_anim_keys > 1); 214 u32 idx_anim_key = bone->last_anim_key; 215 do { 216 f64 t0 = *get_bone_matrix_time(idx_bone_matrix_set, idx_bone, idx_anim_key); 217 f64 t1 = *get_bone_matrix_time(idx_bone_matrix_set, idx_bone, idx_anim_key + 1); 218 if (animation_timepoint > t0 && animation_timepoint < t1) { 219 bone->last_anim_key = idx_anim_key; 220 return idx_anim_key; 221 } 222 idx_anim_key++; 223 assert(idx_anim_key < bone->n_anim_keys); 224 if (idx_anim_key == bone->n_anim_keys - 1) { 225 idx_anim_key = 0; 226 } 227 } while (idx_anim_key != bone->last_anim_key); 228 logs::fatal("Could not find anim key."); 229 return 0; 230}