diff --git a/samples/basic/viewer/main.cc b/samples/basic/viewer/main.cc index c5e50e483965b9c3480810c639ac43262573ecbb..cd57e408dc4c3b42f0826692ce5ee881066a2c9d 100644 --- a/samples/basic/viewer/main.cc +++ b/samples/basic/viewer/main.cc @@ -1,4 +1,5 @@ #include <imgui/imgui.h> +#include <imgui/imguizmo.h> #include <glow/common/str_utils.hh> #include <glow/data/TextureData.hh> @@ -15,7 +16,8 @@ #include <glow-extras/viewer/canvas.hh> #include <glow-extras/viewer/view.hh> -#include <polymesh/objects/cube.hh> +#include <GLFW/glfw3.h> + #include <typed-geometry/tg.hh> #include <polymesh/copy.hh> @@ -172,6 +174,117 @@ void advanced_objects(pm::vertex_attribute<tg::pos3> const& pos) // TODO: images } +void imguizmo(pm::vertex_attribute<tg::pos3> const& pos) +{ + // This sample shows how to use ImGuizmo, a very useful addon for ImGui for editing 4x4 affine transformation matrices using the viewer + // For additional information refer to https://github.com/CedricGuillemet/ImGuizmo/ + + // your own camera is required to access the camera's view and projection matrix + auto cam = gv::CameraController::create(); + + // cache the renderable + auto r = gv::make_renderable(pos); + + // local vs global coordinate system + auto currentGizmoMode = ImGuizmo::LOCAL; + + // the operation that we want to perform (rotate, scale, or translate) + auto currentGizmoOperation = ImGuizmo::ROTATE; + + // do we want to snap to specific values + bool useSnap = false; + + // the values that we want to snap to, i.e. 1 degree steps + float snap[3] = {1.f, 1.f, 1.f}; + + // the transformation that we want to edit + auto transform = tg::mat4::identity; + + // an interactive view is required + gv::interactive([&](float) { + // you can define keybindings to select an operation + if (ImGui::IsKeyPressed(GLFW_KEY_T)) + currentGizmoOperation = ImGuizmo::TRANSLATE; + if (ImGui::IsKeyPressed(GLFW_KEY_R)) + currentGizmoOperation = ImGuizmo::ROTATE; + if (ImGui::IsKeyPressed(GLFW_KEY_S)) + currentGizmoOperation = ImGuizmo::SCALE; + + // you can also choose the correct operation using gui elements + if (ImGui::RadioButton("Translate", currentGizmoOperation == ImGuizmo::TRANSLATE)) + currentGizmoOperation = ImGuizmo::TRANSLATE; + ImGui::SameLine(); + if (ImGui::RadioButton("Rotate", currentGizmoOperation == ImGuizmo::ROTATE)) + currentGizmoOperation = ImGuizmo::ROTATE; + ImGui::SameLine(); + if (ImGui::RadioButton("Scale", currentGizmoOperation == ImGuizmo::SCALE)) + currentGizmoOperation = ImGuizmo::SCALE; + + // ImGuizmo decomposes your input transform into translation, rotation, and scaling components + float matrixTranslation[3], matrixRotation[3], matrixScale[3]; + ImGuizmo::DecomposeMatrixToComponents(&transform[0][0], matrixTranslation, matrixRotation, matrixScale); + + // show the three components individually + ImGui::InputFloat3("Tr", matrixTranslation); + ImGui::InputFloat3("Rt", matrixRotation); + ImGui::InputFloat3("Sc", matrixScale); + + // rebuild your 4x4 transform matrix + ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, &transform[0][0]); + + // only show the radio button to choose local/global coordinate system when NOT scaling + // as scaling is always in local coordinates + if (currentGizmoOperation != ImGuizmo::SCALE) + { + if (ImGui::RadioButton("Local", currentGizmoMode == ImGuizmo::LOCAL)) + currentGizmoMode = ImGuizmo::LOCAL; + ImGui::SameLine(); + if (ImGui::RadioButton("World", currentGizmoMode == ImGuizmo::WORLD)) + currentGizmoMode = ImGuizmo::WORLD; + } + + // toggle snap on p key + if (ImGui::IsKeyPressed(GLFW_KEY_P)) + useSnap = !useSnap; + + // ... or use gui elements + ImGui::Checkbox("", &useSnap); + ImGui::SameLine(); + + // name the snap input accordingly + switch (currentGizmoOperation) + { + case ImGuizmo::TRANSLATE: + ImGui::InputFloat3("Snap", snap); + break; + case ImGuizmo::ROTATE: + ImGui::InputFloat("Angle Snap", snap); + break; + case ImGuizmo::SCALE: + ImGui::InputFloat("Scale Snap", snap); + break; + default: // silence warnings as there are quite a few more possible operations supported by ImGuizmo + break; // do nothing + } + + // ImGuizmo requires view and projection matrix for rendering. + // However, it does not work with a reversed z projection matrix, which is used by the viewer by default + // So we need to disable it temporarily, generate a projection matrix without it, and then reactivate it + auto view = cam->computeViewMatrix(); + auto const rev_z_enabled = cam->reverseZEnabled(); + cam->setReverseZEnabled(false); // + auto project = cam->computeProjMatrix(); + cam->setReverseZEnabled(rev_z_enabled); + + // This is where all the ImGuizmo magic comes together + ImGuizmo::Manipulate(&view[0][0], &project[0][0], currentGizmoOperation, currentGizmoMode, &transform[0][0], nullptr, useSnap ? &snap[0] : nullptr); + + // Because the view is resized when we change the object, it might be a good idea to pass the viewer a fixed bounding box + auto const aabb = tg::aabb3(tg::pos3(-2), tg::pos3(2)); + gv::view(r, cam, transform, aabb); + }); +} + void advanced_visualization(pm::vertex_attribute<tg::pos3> const& pos) { // the args in gv::view(obj, args) do not only configure the viewer but also change how the renderable is visualized @@ -405,6 +518,7 @@ void advanced_configs(pm::vertex_attribute<tg::pos3> const& pos) gv::view(pos, gv::tonemap_exposure(1.5f), "tonemapping"); gv::view(pos, tg::aabb3(-2, 2), "custom scene AABB"); gv::view(pos, gv::grid_size(0.3f), gv::grid_center({1, 2, 3}), "custom grid configuration"); + gv::view(pos, gv::shadow_screen_fadeout_distance(100.f), gv::print_mode, gv::no_grid, gv::total_shadow_samples(4096), "100px shadow fadeout"); gv::view(pos, gv::print_mode, "print-friendly mode"); gv::view(pos, gv::background_color(tg::color3::blue), "custom BG color"); @@ -1342,6 +1456,8 @@ int main() subtle_cases(pos); headless_screenshot(pos); + + imguizmo(pos); } // known issues