Better serialization of application state.
parent
8a91c7700a
commit
3d387a5dad
251
src/main.cc
251
src/main.cc
|
@ -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;
|
||||
|
@ -49,6 +45,7 @@ static void draw_imgui(ImDrawData*);
|
|||
|
||||
// ozz-animation headers
|
||||
#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)));
|
||||
|
||||
|
@ -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,8 +795,10 @@ 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);
|
||||
|
@ -634,8 +806,10 @@ static void draw_grid(void) {
|
|||
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);
|
||||
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);
|
||||
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue