Better serialization of application state.

AnimGraphEditor
Martin Felis 2023-03-25 23:30:13 +01:00
parent 8a91c7700a
commit 3d387a5dad
1 changed files with 216 additions and 43 deletions

View File

@ -15,10 +15,11 @@
#define GLFW_INCLUDE_NONE
#include <iostream>
#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 <cmath> // fmodf
#include <cmath> // fmodf
#include <fstream>
#include <memory> // 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<ApplicationConfig>();
}
}
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);