diff --git a/src/SkinnedMeshResource.cc b/src/SkinnedMeshResource.cc index 141cec4..e9caf3f 100644 --- a/src/SkinnedMeshResource.cc +++ b/src/SkinnedMeshResource.cc @@ -105,7 +105,7 @@ void SkinnedMeshResource::createInstance(SkinnedMesh& skinnedMesh) const { skinnedMesh.LoadSkeleton(m_skeleton_file.c_str()); for (int i = 0; i < m_animation_files.size(); i++) { skinnedMesh.LoadAnimation(m_animation_files[i].c_str()); - skinnedMesh.m_animation_sync_track.push_back(m_sync_tracks[i]); + skinnedMesh.m_animation_sync_track.back() = m_sync_tracks[i]; } } diff --git a/src/main.cc b/src/main.cc index 9cde157..32a3995 100644 --- a/src/main.cc +++ b/src/main.cc @@ -61,18 +61,12 @@ static void draw_imgui(ImDrawData*); #include "ozz/base/maths/soa_transform.h" #include "ozz/base/maths/vec_float.h" -// wrapper struct for managed ozz-animation C++ objects, mUST BE DEleted -// before shutdown, otherwise ozz-animation will report a memory leak -typedef struct { - ozz::animation::Skeleton skeleton; - ozz::animation::Animation animation; - ozz::animation::SamplingJob::Context sampling_context; - ozz::vector local_matrices; - ozz::vector model_matrices; -} ozz_t; - static struct { - std::unique_ptr ozz; + struct { + ozz::animation::Animation* animation = nullptr; + ozz::animation::SamplingJob sampling_job; + ozz::animation::SamplingJob::Context* m_sampling_context = nullptr; + } ozz; sg_pass_action pass_action; Camera camera; struct { @@ -82,7 +76,7 @@ static struct { } loaded; struct { double frame; - double absolute; + float absolute; uint64_t laptime; float factor; float anim_ratio; @@ -124,7 +118,14 @@ struct ApplicationConfig { int size[2] = {800, 500}; }; SkinnedMeshWidget skinned_mesh_widget; - + + struct AnimationPlayerWidget { + bool visible = false; + int position[2] = {20, 20}; + int size[2] = {800, 500}; + }; + AnimationPlayerWidget animation_player_widget; + bool show_imgui_demo_window = false; bool show_another_window = false; bool show_camera_widget = false; @@ -151,7 +152,13 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) { j["skinned_mesh_widget"]["position"][1] = config.skinned_mesh_widget.position[1]; j["skinned_mesh_widget"]["size"][0] = config.skinned_mesh_widget.size[0]; j["skinned_mesh_widget"]["size"][1] = config.skinned_mesh_widget.size[1]; - + + j["animation_player_widget"]["visible"] = config.animation_player_widget.visible; + j["animation_player_widget"]["position"][0] = config.animation_player_widget.position[0]; + j["animation_player_widget"]["position"][1] = config.animation_player_widget.position[1]; + j["animation_player_widget"]["size"][0] = config.animation_player_widget.size[0]; + j["animation_player_widget"]["size"][1] = config.animation_player_widget.size[1]; + j["camera_widget"]["visible"] = config.show_camera_widget; } @@ -188,12 +195,11 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) { } } - if (j.contains("skinned_mesh_widget")) { if (j["skinned_mesh_widget"].contains("visible")) { config.skinned_mesh_widget.visible = j["skinned_mesh_widget"]["visible"]; } - + if (j["skinned_mesh_widget"].contains("position") and j["skinned_mesh_widget"]["position"].size() == 2) { config.skinned_mesh_widget.position[0] = j["skinned_mesh_widget"]["position"].at(0); config.skinned_mesh_widget.position[1] = j["skinned_mesh_widget"]["position"].at(1); @@ -204,7 +210,23 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) { config.skinned_mesh_widget.size[1] = j["skinned_mesh_widget"]["size"].at(1); } } - + + +if (j.contains("animation_player_widget")) { + if (j["animation_player_widget"].contains("visible")) { + config.animation_player_widget.visible = j["animation_player_widget"]["visible"]; + } + + if (j["animation_player_widget"].contains("position") and j["animation_player_widget"]["position"].size() == 2) { + config.animation_player_widget.position[0] = j["animation_player_widget"]["position"].at(0); + config.animation_player_widget.position[1] = j["animation_player_widget"]["position"].at(1); + } + + if (j["animation_player_widget"].contains("size") and j["animation_player_widget"]["size"].size() == 2) { + config.animation_player_widget.size[0] = j["animation_player_widget"]["size"].at(0); + config.animation_player_widget.size[1] = j["animation_player_widget"]["size"].at(1); + } +} if (j.contains("camera_widget") and j["camera_widget"].contains("visible")) { config.show_camera_widget = j["camera_widget"]["visible"]; } @@ -480,10 +502,16 @@ int main() { &gApplicationConfig.window_size[0], &gApplicationConfig.window_size[1]); - state.time.frame = stm_sec( - stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime))); + if (!state.time.paused) { + state.time.frame = stm_sec( + stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime))); - state.time.absolute += state.time.frame * state.time.factor; + state.time.absolute += state.time.frame * state.time.factor; + + if (state.ozz.animation != nullptr) { + state.time.absolute = fmodf (state.time.absolute, state.ozz.animation->duration()); + } + } int cur_width, cur_height; glfwGetFramebufferSize(w, &cur_width, &cur_height); @@ -571,6 +599,10 @@ int main() { ImGui::Checkbox( "Skinned Mesh", &gApplicationConfig.skinned_mesh_widget.visible); + ImGui::Checkbox( + "Animation Player", + &gApplicationConfig.animation_player_widget.visible); + ImGui::Separator(); ImGui::Checkbox( "ImGui Demo", @@ -621,6 +653,55 @@ int main() { ImGui::End(); } + if (gApplicationConfig.animation_player_widget.visible) { + ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.animation_player_widget.position[0], gApplicationConfig.skinned_mesh_widget.position[1]), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.animation_player_widget.size[0], gApplicationConfig.skinned_mesh_widget.size[1]), ImGuiCond_FirstUseEver); + + ImGui::Begin("Animation Player", &gApplicationConfig.animation_player_widget.visible); + + ImVec2 animation_player_widget_position = ImGui::GetWindowPos(); + gApplicationConfig.animation_player_widget.position[0] = animation_player_widget_position.x; + gApplicationConfig.animation_player_widget.position[1] = animation_player_widget_position.y; + + ImVec2 animation_player_widget_size = ImGui::GetWindowSize(); + gApplicationConfig.animation_player_widget.size[0] = animation_player_widget_size.x; + gApplicationConfig.animation_player_widget.size[1] = animation_player_widget_size.y; + + ImGui::Text("Animation"); + + const char* items[255] = {0}; + static int selected = -1; + for (int i = 0; i < skinned_mesh.m_animations.size(); i++) { + items[i] = skinned_mesh.m_animation_names[i].c_str(); + } + + if (ImGui::Combo("Animation", &selected, items, skinned_mesh.m_animations.size())) { + state.ozz.animation = skinned_mesh.m_animations[selected]; + } + + if (state.time.paused) { + if (ImGui::Button("Play")) { + state.time.paused = false; + } + } else { + if (ImGui::Button("Pause")) { + state.time.paused = true; + } + } + + if (state.ozz.animation != nullptr) { + ImGui::SliderFloat( + "Time", + &state.time.absolute, + 0, + state.ozz.animation->duration(), + "%.3f", + 0); + } + + ImGui::End(); + } + // TODO: add AnimGraph to calculate pose // skinned_mesh.CalcModelMatrices();