1 /* 2 Copyright © 2020, Inochi2D Project 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 5 Authors: Luna Nielsen 6 */ 7 module creator.panels.viewport; 8 import creator.widgets; 9 import creator.core; 10 import creator.core.colorbleed; 11 import creator.panels; 12 import creator.core.input; 13 import creator.actions; 14 import creator; 15 import inochi2d; 16 import inochi2d.core.dbg; 17 import bindbc.imgui; 18 import std.string; 19 import i18n; 20 21 /** 22 A viewport 23 */ 24 class ViewportPanel : Panel { 25 private: 26 ImVec2 lastSize; 27 float zoom = 1; 28 29 bool isMovingViewport; 30 float sx, sy; 31 float csx, csy; 32 33 bool isMovingPart; 34 35 void _updateMode() { 36 ImGuiIO* io = igGetIO(); 37 vec2 mpos = incInputGetMousePosition(); 38 39 switch(incEditMode) { 40 case EditMode.ModelEdit: 41 if (igIsMouseClicked(ImGuiMouseButton.Left)) { 42 // TODO: allow selecting 43 } 44 45 if (igIsMouseClicked(ImGuiMouseButton.Right)) { 46 // TODO: Allow selecting from everything in the area of the click 47 } 48 49 // TODO: Allow dragging elements 50 if (!isMovingPart && igIsMouseDragging(ImGuiMouseButton.Left)) { 51 isMovingPart = true; 52 } 53 if (isMovingPart) { 54 // TODO: Show drag 55 } 56 if (igIsMouseReleased(ImGuiMouseButton.Left)) { 57 // TODO: settle dragged element 58 } 59 break; 60 61 case EditMode.DeformEdit: 62 break; 63 64 case EditMode.VertexEdit: 65 break; 66 67 default: assert(0); 68 } 69 } 70 71 void _drawMode() { 72 73 switch(incEditMode) { 74 case EditMode.ModelEdit: 75 break; 76 77 case EditMode.DeformEdit: 78 break; 79 80 case EditMode.VertexEdit: 81 break; 82 83 default: assert(0); 84 } 85 } 86 protected: 87 override 88 void onBeginUpdate() { 89 90 ImGuiWindowClass wmclass; 91 wmclass.DockNodeFlagsOverrideSet = ImGuiDockNodeFlagsI.NoTabBar; 92 igSetNextWindowClass(&wmclass); 93 igPushStyleVar(ImGuiStyleVar.WindowPadding, ImVec2(1, 2)); 94 igSetNextWindowDockID(incGetViewportDockSpace(), ImGuiCond.Always); 95 super.onBeginUpdate(); 96 } 97 98 override void onEndUpdate() { 99 super.onEndUpdate(); 100 igPopStyleVar(); 101 } 102 103 override 104 void onUpdate() { 105 106 auto io = igGetIO(); 107 auto camera = inGetCamera(); 108 109 // Draw viewport itself 110 ImVec2 currSize; 111 igGetContentRegionAvail(&currSize); 112 113 // We do not want the viewport to be NaN 114 // That will crash the app 115 if (currSize.x.isNaN || currSize.y.isNaN) { 116 currSize = ImVec2(0, 0); 117 } 118 119 // Resize Inochi2D viewport according to frame 120 // Also viewport of 0 is too small, minimum 128. 121 currSize = ImVec2(clamp(currSize.x, 128, float.max), clamp(currSize.y, 128, float.max)); 122 123 124 igBeginChild("##ViewportView", ImVec2(0, -30)); 125 igGetContentRegionAvail(&currSize); 126 currSize = ImVec2( 127 clamp(currSize.x, 128, float.max), 128 clamp(currSize.y, 128, float.max)-4 129 ); 130 131 if (currSize != lastSize) { 132 inSetViewport(cast(int)currSize.x, cast(int)currSize.y); 133 } 134 135 if (igIsWindowHovered(ImGuiHoveredFlags.ChildWindows)) { 136 137 // HANDLE MOVE VIEWPORT 138 if (!isMovingViewport && io.MouseDown[1]) { 139 isMovingViewport = true; 140 sx = io.MousePos.x; 141 sy = io.MousePos.y; 142 csx = camera.position.x; 143 csy = camera.position.y; 144 } 145 146 if (isMovingViewport && !io.MouseDown[1]) { 147 isMovingViewport = false; 148 } 149 150 if (isMovingViewport) { 151 152 camera.position = vec2( 153 csx+((io.MousePos.x-sx)/zoom), 154 csy+((io.MousePos.y-sy)/zoom) 155 ); 156 157 incTargetPosition = camera.position; 158 } 159 160 // HANDLE ZOOM 161 if (io.MouseWheel != 0) { 162 zoom += (io.MouseWheel/50)*zoom; 163 zoom = clamp(zoom, incVIEWPORT_ZOOM_MIN, incVIEWPORT_ZOOM_MAX); 164 camera.scale = vec2(zoom); 165 incTargetZoom = zoom; 166 } 167 } 168 169 auto style = igGetStyle(); 170 inSetClearColor(style.Colors[ImGuiCol.WindowBg].x, style.Colors[ImGuiCol.WindowBg].y, style.Colors[ImGuiCol.WindowBg].z, 1); 171 incBeginUpdate(); 172 incInputBegin(); 173 ImVec2 pos; 174 ImVec2 mpos; 175 igGetItemRectMin(&pos); 176 igGetMousePos(&mpos); 177 incInputSetViewportMouse(pos.x-mpos.x, pos.y-mpos.y); 178 179 _updateMode(); 180 incUpdateActiveProject(); 181 _drawMode(); 182 183 // NOTE: No End Needed 184 incEndUpdate(); 185 186 int width, height; 187 inGetViewport(width, height); 188 189 // Render our viewport 190 ImVec2 sPos; 191 ImVec2 sPosA; 192 igGetCursorScreenPos(&sPos); 193 194 igImage( 195 cast(void*)inGetRenderImage(), 196 ImVec2(width, height), 197 ImVec2(0, 1), 198 ImVec2(1, 0), 199 ImVec4(1, 1, 1, 1), ImVec4(0, 0, 0, 0) 200 ); 201 igGetCursorScreenPos(&sPosA); 202 203 // Render our fancy in-viewport buttons 204 igSetCursorScreenPos(ImVec2(sPos.x+8, sPos.y+8)); 205 igSetItemAllowOverlap(); 206 207 igPushStyleVar(ImGuiStyleVar.FrameRounding, 0); 208 igBeginChild("##ViewportMainControls", ImVec2(128, 28)); 209 igPushStyleVar_Vec2(ImGuiStyleVar.FramePadding, ImVec2(6, 6)); 210 211 igPushFont(incIconFont()); 212 if (igButton("", ImVec2(0, 0))) { 213 incShowVertices = !incShowVertices; 214 } 215 igPopFont(); 216 incTooltip(_("Show/hide Vertices")); 217 218 igPushFont(incIconFont()); 219 igSameLine(0, 0); 220 if (igButton("", ImVec2(0, 0))) { 221 incShowBounds = !incShowBounds; 222 } 223 igPopFont(); 224 incTooltip(_("Show/hide Bounds")); 225 226 igPushFont(incIconFont()); 227 igSameLine(0, 0); 228 if (igButton("", ImVec2(0, 0))) { 229 incShowOrientation = !incShowOrientation; 230 } 231 igPopFont(); 232 incTooltip(_("Show/hide Orientation Gizmo")); 233 234 igPopStyleVar(); 235 igEndChild(); 236 igPopStyleVar(); 237 238 igSetCursorScreenPos(sPosA); 239 240 lastSize = currSize; 241 igEndChild(); 242 243 if (igBeginDragDropTarget()) { 244 ImGuiPayload* payload = igAcceptDragDropPayload("__PARTS_DROP"); 245 if (payload !is null) { 246 string[] files = *cast(string[]*)payload.Data; 247 import std.path : baseName, extension; 248 import std.uni : toLower; 249 mainLoop: foreach(file; files) { 250 string fname = file.baseName; 251 252 switch(fname.extension.toLower) { 253 case ".png", ".tga", ".jpeg", ".jpg": 254 255 auto tex = new ShallowTexture(file); 256 incColorBleedPixels(tex); 257 inTexPremultiply(tex.data); 258 incAddChildWithHistory( 259 inCreateSimplePart(*tex, null, fname), 260 incSelectedNode(), 261 fname 262 ); 263 264 // We've added new stuff, rescan nodes 265 incActivePuppet().rescanNodes(); 266 267 foreach(Part part; incActivePuppet().getRootParts()) { 268 import std.stdio : writeln; 269 writeln(part); 270 } 271 break; 272 273 // Allow dragging PSD in to main window 274 case ".psd": 275 incImportPSD(file); 276 break mainLoop; 277 278 default: break; 279 } 280 } 281 282 // Finish the file drag 283 incFinishFileDrag(); 284 } 285 286 igEndDragDropTarget(); 287 } 288 289 igGetContentRegionAvail(&currSize); 290 igBeginChild("##ViewportControls", ImVec2(0, currSize.y), false, flags.NoScrollbar); 291 igPushItemWidth(72); 292 igSpacing(); 293 igSameLine(0, 8); 294 if (igSliderFloat( 295 "##Zoom", 296 &zoom, 297 incVIEWPORT_ZOOM_MIN, 298 incVIEWPORT_ZOOM_MAX, 299 "%s%%\0".format(cast(int)(zoom*100)).ptr, 300 ImGuiSliderFlags.NoRoundToFormat) 301 ) { 302 camera.scale = vec2(zoom); 303 incTargetZoom = zoom; 304 } 305 if (incTargetZoom != 1) { 306 igPushFont(incIconFont()); 307 igSameLine(0, 8); 308 if (igButton("", ImVec2(0, 0))) { 309 incTargetZoom = 1; 310 } 311 igPopFont(); 312 } 313 igSameLine(0, 8); 314 igSeparatorEx(ImGuiSeparatorFlags.Vertical); 315 316 igSameLine(0, 8); 317 igText("x = %.2f y = %.2f", incTargetPosition.x, incTargetPosition.y); 318 if (incTargetPosition != vec2(0)) { 319 igSameLine(0, 8); 320 igPushFont(incIconFont()); 321 if (igButton("##2", ImVec2(0, 0))) { 322 incTargetPosition = vec2(0, 0); 323 } 324 igPopFont(); 325 } 326 327 328 igPopItemWidth(); 329 igEndChild(); 330 331 // Handle smooth move 332 zoom = dampen(zoom, incTargetZoom, deltaTime, 1); 333 camera.scale = vec2(zoom, zoom); 334 camera.position = vec2(dampen(camera.position, incTargetPosition, deltaTime, 1.5)); 335 } 336 337 public: 338 this() { 339 super("Viewport", _("Viewport"), true); 340 this.alwaysVisible = true; 341 } 342 343 } 344 345 mixin incPanel!ViewportPanel;