diff --git a/src/main.cc b/src/main.cc index d06f4f2..3a069e4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -103,6 +103,61 @@ enum class ControlMode { ControlModeNone, ControlModeFPS }; ControlMode gControlMode = ControlMode::ControlModeNone; +struct Viewport { + sg_pass_action pass_action = {}; + sg_pass pass = {}; + sg_pipeline pip = {}; + sg_bindings bind = {}; + sg_image color_image = {}; + sg_image depth_image = {}; + int size[2] = {400, 400}; + + void Resize(int width, int height) { + width = width < 1 ? 1 : width; + height = height < 1 ? 1 : height; + + this->pass_action.colors[0].action = SG_ACTION_CLEAR; + this->pass_action.colors[0].value = {0.1f, 0.1f, 0.1f, 1.0f}; + + if (this->size[0] == width && this->size[1] == height + && this->pass.id != 0) { + return; + } + + this->size[0] = width; + this->size[1] = height; + + if (this->pass.id != 0) { + sg_destroy_pass(this->pass); + sg_destroy_image(this->depth_image); + sg_destroy_image(this->color_image); + } + + sg_image_desc img_desc = { + .render_target = true, + .width = this->size[0], + .height = this->size[1], + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 4, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_REPEAT, + .wrap_v = SG_WRAP_REPEAT, + .label = "color-image"}; + this->color_image = sg_make_image(&img_desc); + img_desc.pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL; + img_desc.label = "depth-image"; + this->depth_image = sg_make_image(&img_desc); + + sg_pass_desc offscreen_pass_desc{}; + offscreen_pass_desc.color_attachments[0].image = this->color_image; + offscreen_pass_desc.depth_stencil_attachment.image = this->depth_image; + offscreen_pass_desc.label = "offscreen-pass"; + + this->pass = sg_make_pass(&offscreen_pass_desc); + } +}; + struct ApplicationConfig { int window_position[2] = {100, 30}; int window_size[2] = {1000, 600}; @@ -128,6 +183,13 @@ struct ApplicationConfig { }; AnimationPlayerWidget animation_player_widget; + struct ViewportWidget { + bool visible = false; + int position[2] = {20, 20}; + int size[2] = {800, 500}; + }; + ViewportWidget viewport_widget; + bool show_imgui_demo_window = false; bool show_another_window = false; bool show_camera_widget = false; @@ -150,16 +212,29 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) { j["graph_editor"]["size"][1] = config.graph_editor.size[1]; j["skinned_mesh_widget"]["visible"] = config.skinned_mesh_widget.visible; - j["skinned_mesh_widget"]["position"][0] = config.skinned_mesh_widget.position[0]; - j["skinned_mesh_widget"]["position"][1] = config.skinned_mesh_widget.position[1]; + j["skinned_mesh_widget"]["position"][0] = + config.skinned_mesh_widget.position[0]; + 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["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["viewport_widget"]["visible"] = config.viewport_widget.visible; + j["viewport_widget"]["position"][0] = config.viewport_widget.position[0]; + j["viewport_widget"]["position"][1] = config.viewport_widget.position[1]; + j["viewport_widget"]["size"][0] = config.viewport_widget.size[0]; + j["viewport_widget"]["size"][1] = config.viewport_widget.size[1]; j["camera_widget"]["visible"] = config.show_camera_widget; } @@ -186,12 +261,14 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) { config.graph_editor.visible = j["graph_editor"]["visible"]; } - if (j["graph_editor"].contains("position") and j["graph_editor"]["position"].size() == 2) { + 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) { + 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); } @@ -202,33 +279,66 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) { 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); + 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); } - if (j["skinned_mesh_widget"].contains("size") and j["skinned_mesh_widget"]["size"].size() == 2) { - config.skinned_mesh_widget.size[0] = j["skinned_mesh_widget"]["size"].at(0); - config.skinned_mesh_widget.size[1] = j["skinned_mesh_widget"]["size"].at(1); + if (j["skinned_mesh_widget"].contains("size") + and j["skinned_mesh_widget"]["size"].size() == 2) { + config.skinned_mesh_widget.size[0] = + j["skinned_mesh_widget"]["size"].at(0); + 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.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["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.contains("viewport_widget")) { + if (j["viewport_widget"].contains("visible")) { + config.viewport_widget.visible = j["viewport_widget"]["visible"]; + } + + if (j["viewport_widget"].contains("position") + and j["viewport_widget"]["position"].size() == 2) { + config.viewport_widget.position[0] = + j["viewport_widget"]["position"].at(0); + config.viewport_widget.position[1] = + j["viewport_widget"]["position"].at(1); + } + + if (j["viewport_widget"].contains("size") + and j["viewport_widget"]["size"].size() == 2) { + config.viewport_widget.size[0] = j["viewport_widget"]["size"].at(0); + config.viewport_widget.size[1] = j["viewport_widget"]["size"].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"]; } @@ -371,8 +481,9 @@ int main() { assert(sg_isvalid()); // setup sokol-gl - sgl_desc_t sgldesc = {}; - sgldesc.sample_count = 0; + sgl_desc_t sgldesc = { + .sample_count = 4 + }; sgl_setup(&sgldesc); SkinnedMeshResource skinned_mesh_resource; @@ -385,7 +496,8 @@ int main() { AnimGraph anim_graph; AnimGraphContext anim_graph_context; AnimData anim_graph_output; - anim_graph_output.m_local_matrices.resize(skinned_mesh.m_skeleton.num_soa_joints()); + anim_graph_output.m_local_matrices.resize( + skinned_mesh.m_skeleton.num_soa_joints()); state.time.factor = 1.0f; @@ -496,18 +608,11 @@ int main() { pass_action.colors[0].action = SG_ACTION_CLEAR; pass_action.colors[0].value = {0.1f, 0.1f, 0.1f, 1.0f}; + Viewport offscreen_viewport; + // 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]); - + // Update time state.time.frame = stm_sec( stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime))); @@ -519,9 +624,21 @@ int main() { } if (state.ozz.animation != nullptr) { - state.time.absolute = fmodf (state.time.absolute, state.ozz.animation->duration()); + state.time.absolute = + fmodf(state.time.absolute, state.ozz.animation->duration()); } + // Update window state + glfwGetWindowPos( + w, + &gApplicationConfig.window_position[0], + &gApplicationConfig.window_position[1]); + + glfwGetWindowSize( + w, + &gApplicationConfig.window_size[0], + &gApplicationConfig.window_size[1]); + int cur_width, cur_height; glfwGetFramebufferSize(w, &cur_width, &cur_height); @@ -545,8 +662,8 @@ int main() { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); Camera_Update( &state.camera, - cur_width, - cur_height, + offscreen_viewport.size[0], + offscreen_viewport.size[1], state.time.frame, 0, 0, @@ -601,7 +718,12 @@ int main() { } if (ImGui::BeginMenu("View")) { - ImGui::Checkbox("Graph Editor", &gApplicationConfig.graph_editor.visible); + ImGui::Checkbox( + "Viewport", + &gApplicationConfig.viewport_widget.visible); + ImGui::Checkbox( + "Graph Editor", + &gApplicationConfig.graph_editor.visible); ImGui::Checkbox( "Camera Settings", &gApplicationConfig.show_camera_widget); @@ -628,7 +750,8 @@ int main() { anim_graph.init(anim_graph_context); // For simplicity use first animation data output - const std::vector& graph_output_sockets = anim_graph.getGraphOutputs(); + const std::vector& graph_output_sockets = + anim_graph.getGraphOutputs(); for (int i = 0; i < graph_output_sockets.size(); i++) { const Socket& output = graph_output_sockets[i]; if (output.m_type == SocketType::SocketTypeAnimation) { @@ -642,6 +765,51 @@ int main() { // Animation Runtime { + // Viewport + if (gApplicationConfig.viewport_widget.visible) { + ImGui::SetNextWindowPos( + ImVec2( + gApplicationConfig.viewport_widget.position[0], + gApplicationConfig.viewport_widget.position[1]), + ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize( + ImVec2( + gApplicationConfig.viewport_widget.size[0], + gApplicationConfig.viewport_widget.size[1]), + ImGuiCond_FirstUseEver); + + ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible); + + ImVec2 viewport_widget_size = ImGui::GetWindowSize(); + gApplicationConfig.viewport_widget.size[0] = + viewport_widget_size.x; + gApplicationConfig.viewport_widget.size[1] = + viewport_widget_size.y; + + ImGui::Text( + "Viewport size: %d, %d", + offscreen_viewport.size[0], + offscreen_viewport.size[1]); + + ImVec2 content_size = ImGui::GetContentRegionAvail(); + + int* current_size = offscreen_viewport.size; + + if (current_size[0] != content_size[0] || current_size[1] != content_size[1]) { + offscreen_viewport.Resize(content_size[0], content_size[1]); + } + + ImGui::Image( + (ImTextureID)(uintptr_t)offscreen_viewport.color_image.id, + ImVec2( + offscreen_viewport.size[0], + offscreen_viewport.size[1]), + ImVec2(0.0f, 1.0f), + ImVec2(1.0f, 0.0f)); + + ImGui::End(); + } + if (gApplicationConfig.show_camera_widget) { ImGui::SetNextWindowPos(ImVec2(600, 30), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(300, 170), ImGuiCond_FirstUseEver); @@ -655,24 +823,33 @@ int main() { ImGui::End(); } - ImGui::SetNextWindowPos(ImVec2(600, 60), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(300, 400), ImGuiCond_FirstUseEver); - - draw_grid(); - if (gApplicationConfig.skinned_mesh_widget.visible) { - ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.skinned_mesh_widget.position[0], gApplicationConfig.skinned_mesh_widget.position[1]), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.skinned_mesh_widget.size[0], gApplicationConfig.skinned_mesh_widget.size[1]), ImGuiCond_FirstUseEver); - - ImGui::Begin("SkinnedMesh", &gApplicationConfig.skinned_mesh_widget.visible); - + ImGui::SetNextWindowPos( + ImVec2( + gApplicationConfig.skinned_mesh_widget.position[0], + gApplicationConfig.skinned_mesh_widget.position[1]), + ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize( + ImVec2( + gApplicationConfig.skinned_mesh_widget.size[0], + gApplicationConfig.skinned_mesh_widget.size[1]), + ImGuiCond_FirstUseEver); + + ImGui::Begin( + "SkinnedMesh", + &gApplicationConfig.skinned_mesh_widget.visible); + ImVec2 skinned_mesh_widget_position = ImGui::GetWindowPos(); - gApplicationConfig.skinned_mesh_widget.position[0] = skinned_mesh_widget_position.x; - gApplicationConfig.skinned_mesh_widget.position[1] = skinned_mesh_widget_position.y; - + gApplicationConfig.skinned_mesh_widget.position[0] = + skinned_mesh_widget_position.x; + gApplicationConfig.skinned_mesh_widget.position[1] = + skinned_mesh_widget_position.y; + ImVec2 skinned_mesh_widget_size = ImGui::GetWindowSize(); - gApplicationConfig.skinned_mesh_widget.size[0] = skinned_mesh_widget_size.x; - gApplicationConfig.skinned_mesh_widget.size[1] = skinned_mesh_widget_size.y; + gApplicationConfig.skinned_mesh_widget.size[0] = + skinned_mesh_widget_size.x; + gApplicationConfig.skinned_mesh_widget.size[1] = + skinned_mesh_widget_size.y; SkinnedMeshWidget(&skinned_mesh); @@ -680,18 +857,32 @@ int main() { } if (gApplicationConfig.animation_player_widget.visible) { - ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.animation_player_widget.position[0], gApplicationConfig.animation_player_widget.position[1]), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.animation_player_widget.size[0], gApplicationConfig.animation_player_widget.size[1]), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos( + ImVec2( + gApplicationConfig.animation_player_widget.position[0], + gApplicationConfig.animation_player_widget.position[1]), + ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize( + ImVec2( + gApplicationConfig.animation_player_widget.size[0], + gApplicationConfig.animation_player_widget.size[1]), + ImGuiCond_FirstUseEver); - ImGui::Begin("Animation Player", &gApplicationConfig.animation_player_widget.visible); + 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; + 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; + gApplicationConfig.animation_player_widget.size[0] = + animation_player_widget_size.x; + gApplicationConfig.animation_player_widget.size[1] = + animation_player_widget_size.y; if (anim_graph.m_nodes.size() > 0) { ImGui::Checkbox("Use Graph", &state.time.use_graph); @@ -753,9 +944,10 @@ int main() { state.ozz.sampling_job.ratio = state.time.absolute / state.ozz.animation->duration(); state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context; - state.ozz.sampling_job.output = ozz::make_span(skinned_mesh.m_local_matrices); + state.ozz.sampling_job.output = + ozz::make_span(skinned_mesh.m_local_matrices); - if(!state.ozz.sampling_job.Run()) { + if (!state.ozz.sampling_job.Run()) { ozz::log::Err() << "Error sampling animation." << std::endl; } @@ -763,7 +955,8 @@ int main() { skinned_mesh.CalcModelMatrices(); } - if (state.time.use_graph && anim_graph.m_nodes.size() > 0 && state.time.anim_update_time > 0.) { + if (state.time.use_graph && anim_graph.m_nodes.size() > 0 + && state.time.anim_update_time > 0.) { anim_graph.markActiveNodes(); anim_graph.updateTime(state.time.anim_update_time); anim_graph.evaluate(anim_graph_context); @@ -777,13 +970,23 @@ int main() { sgl_load_matrix((const float*)&state.camera.mtxProj); sgl_matrix_mode_modelview(); sgl_load_matrix((const float*)&state.camera.mtxView); + + draw_grid(); RenderSkinnedMesh(skinned_mesh); } // Animation Graph Editor 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::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", @@ -809,12 +1012,17 @@ int main() { ImGui::ShowDemoWindow(); } - // the sokol_gfx draw pass - sg_begin_default_pass(&pass_action, cur_width, cur_height); + // Rendering of the offscreen scene + sg_begin_pass(offscreen_viewport.pass, &offscreen_viewport.pass_action); sgl_draw(); + sg_end_pass(); + + // Rendering of the main gui + sg_begin_default_pass(&pass_action, cur_width, cur_height); ImGui::Render(); draw_imgui(ImGui::GetDrawData()); sg_end_pass(); + sg_commit(); glfwSwapBuffers(w); glfwPollEvents(); @@ -937,6 +1145,8 @@ void draw_imgui(ImDrawData* draw_data) { return; } + sg_image default_image = bind.fs_images[0]; + // render the command list sg_apply_pipeline(pip); vs_params_t vs_params; @@ -972,14 +1182,25 @@ void draw_imgui(ImDrawData* draw_data) { if (pcmd.UserCallback) { pcmd.UserCallback(cl, &pcmd); } else { + uint32_t prev_fs_image_id = bind.fs_images[0].id; + if (pcmd.TextureId != nullptr) { + bind.fs_images[0].id = (uint32_t)(uintptr_t)pcmd.TextureId; + sg_apply_bindings(&bind); + } + const int scissor_x = (int)(pcmd.ClipRect.x); const int scissor_y = (int)(pcmd.ClipRect.y); const int scissor_w = (int)(pcmd.ClipRect.z - pcmd.ClipRect.x); const int scissor_h = (int)(pcmd.ClipRect.w - pcmd.ClipRect.y); sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true); sg_draw(base_element, pcmd.ElemCount, 1); + + if (pcmd.TextureId != nullptr) { + bind.fs_images[0].id = prev_fs_image_id; + sg_apply_bindings(&bind); + } } base_element += pcmd.ElemCount; } } -} +} \ No newline at end of file