A little toolkit for single-quad fragment shader demos

165 lines
5.6 KiB

  1. #pragma once
  2. #include <string>
  3. #include <vector>
  4. #define IMGUI_DEFINE_MATH_OPERATORS
  5. #include <imgui.h>
  6. #include <imgui_internal.h>
  7. #include "../geom.h"
  8. class GuiNode {
  9. public:
  10. GuiNode(std::string name, Rect bbox, uint n_slots_in, uint n_slots_out)
  11. : name(name), bbox(bbox), n_slots_in(n_slots_in), n_slots_out(n_slots_out)
  12. {}
  13. enum SlotDirection {
  14. IN,
  15. OUT,
  16. };
  17. ImVec2 slot_location(SlotDirection direction, uint i) const {
  18. uint n_slots;
  19. switch (direction) {
  20. case IN:
  21. n_slots = n_slots_in; break;
  22. case OUT:
  23. n_slots = n_slots_out; break;
  24. }
  25. auto x_stride = bbox.size.y/(n_slots+1);
  26. ImVec2 base;
  27. switch(direction) {
  28. case IN:
  29. base = bbox.pos; break;
  30. case OUT:
  31. base = bbox.pos + ImVec2(bbox.size.x, 0); break;
  32. }
  33. return base + ImVec2(0, (i + 1) * x_stride);
  34. }
  35. uint n_slots_in;
  36. uint n_slots_out;
  37. std::string name;
  38. Rect bbox;
  39. };
  40. class GuiEdge {
  41. public:
  42. GuiEdge(GuiNode *lhs, GuiNode *rhs, uint fromSlot, uint toSlot)
  43. : lhs(lhs), rhs(rhs), fromSlot(fromSlot), toSlot(toSlot) {}
  44. GuiNode *lhs;
  45. GuiNode *rhs;
  46. uint fromSlot;
  47. uint toSlot;
  48. };
  49. class NodeEditor {
  50. public:
  51. NodeEditor() {
  52. nodeList.emplace_back("hi", Rect(10, 10, 60, 60), 3, 1);
  53. nodeList.emplace_back("hi2", Rect(80, 10, 60, 60), 1, 1);
  54. edgeList.emplace_back(&nodeList[0], &nodeList[1], 0, 0);
  55. }
  56. void draw_window(const char *title, bool *opened) {
  57. ImGui::SetNextWindowSize(ImVec2(700,600), ImGuiSetCond_FirstUseEver);
  58. if (ImGui::Begin(title, opened)) {
  59. // -- Create child canvas
  60. ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1,1));
  61. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0,0));
  62. ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, ImColor(60,60,70,200));
  63. ImGui::BeginChild("nodes_region", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoMove);
  64. // -- Prepare to draw
  65. // Get the draw list for this canvas
  66. ImDrawList* draw_list = ImGui::GetWindowDrawList();
  67. draw_list->ChannelsSplit(2);
  68. // this gets the offset to the upper-right corner of the child canvas.
  69. // Note: here, cursor means "draw cursor", not "mouse pointing cursor".
  70. ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
  71. ImVec2 canvas_size = ImGui::GetWindowSize();
  72. ImVec2 offset = canvas_pos - scrolling; // TODO: add scrolling
  73. // -- Draw the grid
  74. draw_list->ChannelsSetCurrent(0);
  75. if (show_grid) {
  76. ImVec2 local_offset = ImGui::GetCursorPos() - scrolling;
  77. float spacing = 32.0f; ImU32 color = IM_COL32(200, 200, 200, 40);
  78. for (float x = fmodf(local_offset.x, spacing); x < canvas_size.x; x += spacing)
  79. draw_list->AddLine(ImVec2(x, 0.0f)+canvas_pos,
  80. ImVec2(x, canvas_size.y)+canvas_pos, color);
  81. for (float y = fmodf(local_offset.y, spacing); y < canvas_size.y; y += spacing)
  82. draw_list->AddLine(ImVec2(0.0f, y)+canvas_pos,
  83. ImVec2(canvas_size.x, y)+canvas_pos, color);
  84. }
  85. draw_list->ChannelsSetCurrent(0);
  86. for (const auto &edge : edgeList) {
  87. auto head = offset + edge.lhs->slot_location(GuiNode::OUT, edge.fromSlot);
  88. auto tail = offset + edge.rhs->slot_location(GuiNode::IN, edge.fromSlot);
  89. draw_list->AddBezierCurve(head, head+ImVec2(50, 0),
  90. tail+ImVec2(-50, 0), tail,
  91. ImColor(200, 200, 200), 3.0f);
  92. }
  93. Rect viewportRect = Rect(scrolling, canvas_size);
  94. for (const auto &node : nodeList) {
  95. bool visible = viewportRect.intersects(node.bbox);
  96. if (visible) {
  97. // -- Draw the node boxes
  98. draw_list->ChannelsSetCurrent(0); // Switch to the background layer
  99. ImVec2 upper_left = offset + node.bbox.pos;
  100. ImVec2 lower_right = upper_left + node.bbox.size;
  101. draw_list->AddRectFilled(upper_left, lower_right, ImColor(60, 60, 60), 4.0f);
  102. draw_list->AddRect(upper_left, lower_right, ImColor(100, 100, 100), 4.0f);
  103. // for (int i=0; i<N; i++) {
  104. // auto position = node.bbox.pos;
  105. // position.y += (i+1) * (node.bbox.size.y)/(N+1);
  106. // draw_list->AddCircleFilled(offset + position, 4.0f, ImColor(150, 150, 150, 150));
  107. // }
  108. }
  109. }
  110. // -- merge channels back together
  111. draw_list->ChannelsMerge();
  112. // -- scrolling!
  113. // [ pointer over neditor ] [ not interacting ctrls ] [ middle mouse dragging ]
  114. if (ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() && ImGui::IsMouseDragging(2, 0.0f))
  115. scrolling = scrolling - ImGui::GetIO().MouseDelta;
  116. // -- clean up
  117. ImGui::EndChild(); // end :nodes_region
  118. ImGui::PopStyleColor(); // pop ChildWindowBg
  119. ImGui::PopStyleVar(2); // pop FramePadding, WindowPadding
  120. }
  121. ImGui::End();
  122. }
  123. private:
  124. std::vector<GuiNode> nodeList;
  125. std::vector<GuiEdge> edgeList;
  126. ImVec2 scrolling = ImVec2(0.0f, 0.0f);
  127. bool show_grid = true;
  128. };