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;