diff --git a/src/main.cc b/src/main.cc index f3bfb9b..e0d8e72 100644 --- a/src/main.cc +++ b/src/main.cc @@ -15,10 +15,11 @@ #define GLFW_INCLUDE_NONE #include +#include "3rdparty/json/json.hpp" #include "Camera.h" +#include "GLFW/glfw3.h" #include "SkinnedMesh.h" #include "src/AnimGraph/AnimGraphEditor.h" -#include "GLFW/glfw3.h" const int Width = 1024; const int Height = 768; @@ -26,11 +27,6 @@ const int MaxVertices = (1 << 16); const int MaxIndices = MaxVertices * 3; uint64_t last_time = 0; -bool show_imgui_demo_window = false; -bool show_another_window = false; -bool show_graph_editor = false; -bool show_camera_dialog = false; -bool show_skinned_mesh_dialog = false; sg_pass_action pass_action; sg_pipeline pip; @@ -48,7 +44,8 @@ static void draw_imgui(ImDrawData*); #include "camera.h" // ozz-animation headers -#include // fmodf +#include // fmodf +#include #include // std::unique_ptr, std::make_unique #include "SkinnedMeshRenderer.h" @@ -105,13 +102,92 @@ typedef struct { GuiInputState gGuiInputState = {0, 0, 0, 0, 0, 0, 0}; -enum class ControlMode { - ControlModeNone, - ControlModeFPS -}; +enum class ControlMode { ControlModeNone, ControlModeFPS }; ControlMode gControlMode = ControlMode::ControlModeNone; +struct ApplicationConfig { + int window_position[2] = {100, 30}; + int window_size[2] = {1000, 600}; + + struct GraphEditor { + bool visible = false; + int position[2] = {20, 20}; + int size[2] = {800, 500}; + }; + + GraphEditor graph_editor; + + bool show_imgui_demo_window = false; + bool show_another_window = false; + bool show_camera_widget = false; + bool show_skinned_mesh_widget = false; +}; + +void to_json(nlohmann::json& j, const ApplicationConfig& config) { + std::cerr << "converting to json" << std::endl; + j["type"] = "AnimTestbedConfig"; + + j["main_window"]["position"][0] = config.window_position[0]; + j["main_window"]["position"][1] = config.window_position[1]; + + j["main_window"]["size"][0] = config.window_size[0]; + j["main_window"]["size"][1] = config.window_size[1]; + + j["graph_editor"]["visible"] = config.graph_editor.visible; + j["graph_editor"]["position"][0] = config.graph_editor.position[0]; + j["graph_editor"]["position"][1] = config.graph_editor.position[1]; + j["graph_editor"]["size"][0] = config.graph_editor.size[0]; + j["graph_editor"]["size"][1] = config.graph_editor.size[1]; + + j["skinned_mesh_widget"]["visible"] = config.show_skinned_mesh_widget; + j["camera_widget"]["visible"] = config.show_camera_widget; +} + +void from_json(const nlohmann::json& j, ApplicationConfig& config) { + std::cerr << "converting from json" << std::endl; + + if (j.contains("main_window")) { + if (j["main_window"].contains("position") + and j["main_window"]["position"].size() == 2) { + config.window_position[0] = j["main_window"]["position"].at(0); + config.window_position[1] = j["main_window"]["position"].at(1); + } + + if (j["main_window"].contains("size") + and j["main_window"]["size"].size() == 2) { + config.window_size[0] = j["main_window"]["size"].at(0); + config.window_size[1] = j["main_window"]["size"].at(1); + } + } + + if (j.contains("graph_editor")) { + if (j["graph_editor"].contains("visible")) { + config.graph_editor.visible = j["graph_editor"]["visible"]; + } + + if (j["graph_editor"].contains("position") and j["graph_editor"]["position"].size() == 2) { + config.graph_editor.position[0] = j["graph_editor"]["position"].at(0); + config.graph_editor.position[1] = j["graph_editor"]["position"].at(1); + } + + if (j["graph_editor"].contains("size") and j["graph_editor"]["size"].size() == 2) { + config.graph_editor.size[0] = j["graph_editor"]["size"].at(0); + config.graph_editor.size[1] = j["graph_editor"]["size"].at(1); + } + } + + if (j.contains("skinned_mesh_widget") and j["skinned_mesh_widget"].contains("visible")) { + config.show_skinned_mesh_widget = j["skinned_mesh_widget"]["visible"]; + } + + if (j.contains("camera_widget") and j["camera_widget"].contains("visible")) { + config.show_camera_widget = j["camera_widget"]["visible"]; + } +} + +ApplicationConfig gApplicationConfig; + // io buffers for skeleton and animation data files, we know the max file size upfront static uint8_t skel_data_buffer[4 * 1024]; static uint8_t anim_data_buffer[32 * 1024]; @@ -145,6 +221,41 @@ void handle_mouse(GLFWwindow* w, GuiInputState* io_input_state) { + (glfwGetMouseButton(w, 2) << 2); } +void load_application_config(const char* filename) { + if (std::filesystem::exists(filename)) { + std::ifstream input_file; + input_file.open(filename); + std::stringstream buffer; + buffer << input_file.rdbuf(); + nlohmann::json application_config = + nlohmann::json::parse(buffer.str(), nullptr, false); + + if (application_config.is_discarded()) { + std::cerr << "Error parsing json of file '" << filename << "'." + << std::endl; + } + + if (application_config.value("type", "undefined") != "AnimTestbedConfig") { + std::cerr + << "Invalid json object. Expected type 'AnimTestbedConfig' but got '" + << application_config["type"] << "'." << std::endl; + application_config["type"] = "AnimTestbedConfig"; + } + + gApplicationConfig = application_config.get(); + } +} + +void save_application_config(const char* filename) { + std::ofstream output_file; + + nlohmann::json application_config = gApplicationConfig; + + output_file.open(filename); + output_file << to_string(application_config) << std::endl; + output_file.close(); +} + int main() { // window and GL context via GLFW and flextGL glfwInit(); @@ -158,6 +269,23 @@ int main() { glfwMakeContextCurrent(w); glfwSwapInterval(1); + load_application_config("animtestbed_config.json"); + if (gApplicationConfig.window_position[0] != 0 + || gApplicationConfig.window_position[1] != 0) { + glfwSetWindowPos( + w, + gApplicationConfig.window_position[0], + gApplicationConfig.window_position[1]); + } + + if (gApplicationConfig.window_size[0] != 0 + || gApplicationConfig.window_size[1] != 0) { + glfwSetWindowSize( + w, + gApplicationConfig.window_size[0], + gApplicationConfig.window_size[1]); + } + // GLFW to ImGui input forwarding glfwSetMouseButtonCallback( w, @@ -206,32 +334,50 @@ int main() { int anim_idx = 0; float idle_markers[] = {0.f, 0.5}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, idle_markers); + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( + skinned_mesh.m_animations[anim_idx]->duration(), + 2, + idle_markers); anim_idx = 1; skinned_mesh.LoadAnimation("../media/Walking-loop.ozz"); float walking_markers[] = {0.293, 0.762}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, walking_markers); + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( + skinned_mesh.m_animations[anim_idx]->duration(), + 2, + walking_markers); anim_idx = 2; skinned_mesh.LoadAnimation("../media/RunningSlow-loop.ozz"); float running_slow_markers[] = {0.315, 0.834}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, running_slow_markers); + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( + skinned_mesh.m_animations[anim_idx]->duration(), + 2, + running_slow_markers); anim_idx = 3; skinned_mesh.LoadAnimation("../media/RunningFast-loop.ozz"); float running_fast_markers[] = {0.403, 0.818}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, running_fast_markers); + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( + skinned_mesh.m_animations[anim_idx]->duration(), + 2, + running_fast_markers); anim_idx = 4; skinned_mesh.LoadAnimation("../media/Limping-loop.ozz"); float limping_markers[] = {0.757, 0.044}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, limping_markers); + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( + skinned_mesh.m_animations[anim_idx]->duration(), + 2, + limping_markers); anim_idx = 5; skinned_mesh.LoadAnimation("../media/ZombieWalk-loop.ozz"); float zombiewalk_markers[] = {0.619, 0.}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, zombiewalk_markers); + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( + skinned_mesh.m_animations[anim_idx]->duration(), + 2, + zombiewalk_markers); skinned_mesh.SetCurrentAnimation(0); @@ -346,6 +492,16 @@ int main() { // draw loop while (!glfwWindowShouldClose(w)) { + glfwGetWindowPos( + w, + &gApplicationConfig.window_position[0], + &gApplicationConfig.window_position[1]); + + glfwGetWindowSize( + w, + &gApplicationConfig.window_size[0], + &gApplicationConfig.window_size[1]); + state.time.frame = stm_sec( stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime))); @@ -361,7 +517,7 @@ int main() { ImGui::NewFrame(); // handle input - handle_mouse (w, &gGuiInputState); + handle_mouse(w, &gGuiInputState); if (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_RIGHT)) { if (gControlMode == ControlMode::ControlModeNone) { @@ -422,21 +578,25 @@ int main() { } if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) - { + if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Quit")) { glfwSetWindowShouldClose(w, true); } ImGui::EndMenu(); } - if (ImGui::BeginMenu("View")) - { - ImGui::Checkbox("Graph Editor", &show_graph_editor); - ImGui::Checkbox("Camera Settings", &show_camera_dialog); - ImGui::Checkbox("Skinned Mesh", &show_skinned_mesh_dialog); + if (ImGui::BeginMenu("View")) { + ImGui::Checkbox("Graph Editor", &gApplicationConfig.graph_editor.visible); + ImGui::Checkbox( + "Camera Settings", + &gApplicationConfig.show_camera_widget); + ImGui::Checkbox( + "Skinned Mesh", + &gApplicationConfig.show_skinned_mesh_widget); ImGui::Separator(); - ImGui::Checkbox("ImGui Demo", &show_imgui_demo_window); + ImGui::Checkbox( + "ImGui Demo", + &gApplicationConfig.show_imgui_demo_window); ImGui::EndMenu(); } @@ -446,11 +606,11 @@ int main() { // Animation Runtime { - if (show_camera_dialog) { + if (gApplicationConfig.show_camera_widget) { ImGui::SetNextWindowPos(ImVec2(600, 30), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(300, 170), ImGuiCond_FirstUseEver); - ImGui::Begin("Camera", &show_camera_dialog); + ImGui::Begin("Camera", &gApplicationConfig.show_camera_widget); ImGui::SliderFloat3("pos", state.camera.pos, -100.f, 100.f); ImGui::SliderFloat("near", &state.camera.near, 0.001f, 10.f); ImGui::SliderFloat("far", &state.camera.far, 1.0f, 10000.f); @@ -464,7 +624,7 @@ int main() { draw_grid(); - if (show_skinned_mesh_dialog) { + if (gApplicationConfig.show_skinned_mesh_widget) { skinned_mesh.DrawDebugUi(); } @@ -480,21 +640,30 @@ int main() { } // Animation Graph Editor - if (show_graph_editor) { - ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); + if (gApplicationConfig.graph_editor.visible) { + ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.graph_editor.position[0], gApplicationConfig.graph_editor.position[1]), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.graph_editor.size[0], gApplicationConfig.graph_editor.size[1]), ImGuiCond_FirstUseEver); ImGui::Begin( "Graph Editor", - &show_graph_editor, + &gApplicationConfig.graph_editor.visible, ImGuiWindowFlags_MenuBar); + + ImVec2 graph_editor_position = ImGui::GetWindowPos(); + gApplicationConfig.graph_editor.position[0] = graph_editor_position.x; + gApplicationConfig.graph_editor.position[1] = graph_editor_position.y; + + ImVec2 graph_editor_size = ImGui::GetWindowSize(); + gApplicationConfig.graph_editor.size[0] = graph_editor_size.x; + gApplicationConfig.graph_editor.size[1] = graph_editor_size.y; + AnimGraphEditorUpdate(); + ImGui::End(); } - // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow() - if (show_imgui_demo_window) { + if (gApplicationConfig.show_imgui_demo_window) { ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver); ImGui::ShowDemoWindow(); } @@ -510,6 +679,8 @@ int main() { glfwPollEvents(); } + save_application_config("animtestbed_config.json"); + /* cleanup */ ImNodes::DestroyContext(); ImGui::DestroyContext(); @@ -609,7 +780,6 @@ static void draw_line( draw_vec(v1); } - static void draw_grid(void) { sgl_defaults(); sgl_matrix_mode_projection(); @@ -625,20 +795,24 @@ static void draw_grid(void) { if (i == 0) { continue; } - ozz::math::SimdFloat4 p0 = ozz::math::simd_float4::Load(i * 1.0f, 0.f, -grid_size * 1.0f, 1.f); - ozz::math::SimdFloat4 p1 = ozz::math::simd_float4::Load(i * 1.0f, 0.f, grid_size * 1.0f, 1.f); + ozz::math::SimdFloat4 p0 = + ozz::math::simd_float4::Load(i * 1.0f, 0.f, -grid_size * 1.0f, 1.f); + ozz::math::SimdFloat4 p1 = + ozz::math::simd_float4::Load(i * 1.0f, 0.f, grid_size * 1.0f, 1.f); draw_line(p0, p1); p0 = ozz::math::simd_float4::Load(-grid_size * 1.0f, 0.f, i * 1.0f, 1.f); p1 = ozz::math::simd_float4::Load(grid_size * 1.0f, 0.f, i * 1.0f, 1.f); draw_line(p0, p1); } - sgl_c3f (0.7f, 0.4f, 0.2f); - ozz::math::SimdFloat4 p0 = ozz::math::simd_float4::Load(0, 0.f, -grid_size * 1.0f, 1.f); - ozz::math::SimdFloat4 p1 = ozz::math::simd_float4::Load(0, 0.f, grid_size * 1.0f, 1.f); + sgl_c3f(0.7f, 0.4f, 0.2f); + ozz::math::SimdFloat4 p0 = + ozz::math::simd_float4::Load(0, 0.f, -grid_size * 1.0f, 1.f); + ozz::math::SimdFloat4 p1 = + ozz::math::simd_float4::Load(0, 0.f, grid_size * 1.0f, 1.f); draw_line(p0, p1); - sgl_c3f (0.2f, 0.4f, 0.7f); + sgl_c3f(0.2f, 0.4f, 0.7f); p0 = ozz::math::simd_float4::Load(-grid_size * 1.0f, 0.f, 0.f, 1.f); p1 = ozz::math::simd_float4::Load(grid_size * 1.0f, 0.f, 0.f, 1.f); draw_line(p0, p1); @@ -646,7 +820,6 @@ static void draw_grid(void) { sgl_end(); } - // draw ImGui draw lists via sokol-gfx void draw_imgui(ImDrawData* draw_data) { assert(draw_data);