1 /*
2     Inochi2D Drawable base class
3 
4     Copyright © 2020, Inochi2D Project
5     Distributed under the 2-Clause BSD License, see LICENSE file.
6     
7     Authors: Luna Nielsen
8 */
9 module inochi2d.core.nodes.drawable;
10 public import inochi2d.core.nodes.defstack;
11 import inochi2d.integration;
12 import inochi2d.fmt.serialize;
13 import inochi2d.math;
14 import bindbc.opengl;
15 import std.exception;
16 import inochi2d.core.dbg;
17 import inochi2d.core;
18 import std.string;
19 
20 private GLuint drawableVAO;
21 
22 package(inochi2d) {
23     void inInitDrawable() {
24         version(InDoesRender) glGenVertexArrays(1, &drawableVAO);
25     }
26 
27 
28     /**
29         Binds the internal vertex array for rendering
30     */
31     void incDrawableBindVAO() {
32 
33         // Bind our vertex array
34         glBindVertexArray(drawableVAO);
35     }
36 
37     bool doGenerateBounds = false;
38 }
39 
40 /**
41     Sets whether Inochi2D should keep track of the bounds
42 */
43 void inSetUpdateBounds(bool state) {
44     doGenerateBounds = state;
45 }
46 
47 /**
48     Nodes that are meant to render something in to the Inochi2D scene
49     Other nodes don't have to render anything and serve mostly other 
50     purposes.
51 
52     The main types of Drawables are Parts and Masks
53 */
54 
55 @TypeId("Drawable")
56 abstract class Drawable : Node {
57 private:
58 
59     void updateIndices() {
60         version (InDoesRender) {
61             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
62             glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indices.length*ushort.sizeof, data.indices.ptr, GL_STATIC_DRAW);
63         }
64     }
65 
66     void updateVertices() {
67         version (InDoesRender) {
68 
69             // Important check since the user can change this every frame
70             glBindBuffer(GL_ARRAY_BUFFER, vbo);
71             glBufferData(GL_ARRAY_BUFFER, data.vertices.length*vec2.sizeof, data.vertices.ptr, GL_DYNAMIC_DRAW);
72         }
73 
74         // Zero-fill the deformation delta
75         this.deformation.length = vertices.length;
76         foreach(i; 0..deformation.length) {
77             this.deformation[i] = vec2(0, 0);
78         }
79         this.updateDeform();
80     }
81 
82 protected:
83     void updateDeform() {
84         // Important check since the user can change this every frame
85         enforce(
86             deformation.length == vertices.length, 
87             "Data length mismatch for %s, deformation length=%d whereas vertices.length=%d, if you want to change the mesh you need to change its data with Part.rebuffer.".format(name, deformation.length, vertices.length)
88         );
89         postProcess();
90 
91         version (InDoesRender) {
92             glBindBuffer(GL_ARRAY_BUFFER, dbo);
93             glBufferData(GL_ARRAY_BUFFER, deformation.length*vec2.sizeof, deformation.ptr, GL_DYNAMIC_DRAW);
94         }
95 
96         this.updateBounds();
97     }
98 
99     /**
100         OpenGL Index Buffer Object
101     */
102     GLuint ibo;
103 
104     /**
105         OpenGL Vertex Buffer Object
106     */
107     GLuint vbo;
108 
109     /**
110         OpenGL Vertex Buffer Object for deformation
111     */
112     GLuint dbo;
113 
114     /**
115         The mesh data of this part
116 
117         NOTE: DO NOT MODIFY!
118         The data in here is only to be used for reference.
119     */
120     MeshData data;
121 
122     /**
123         Binds Index Buffer for rendering
124     */
125     final void bindIndex() {
126         version (InDoesRender) {
127             // Bind element array and draw our mesh
128             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
129             glDrawElements(GL_TRIANGLES, cast(int)data.indices.length, GL_UNSIGNED_SHORT, null);
130         }
131     }
132 
133     /**
134         Allows serializing self data (with pretty serializer)
135     */
136     override
137     void serializeSelfImpl(ref InochiSerializer serializer, bool recursive=true) {
138         super.serializeSelfImpl(serializer, recursive);
139         serializer.putKey("mesh");
140         serializer.serializeValue(data);
141     }
142 
143     override
144     SerdeException deserializeFromFghj(Fghj data) {
145         import std.stdio : writeln;
146         super.deserializeFromFghj(data);
147         if (auto exc = data["mesh"].deserializeValue(this.data)) return exc;
148 
149         this.vertices = this.data.vertices.dup;
150 
151         // Update indices and vertices
152         this.updateIndices();
153         this.updateVertices();
154         return null;
155     }
156 
157     void onDeformPushed(ref Deformation deform) { }
158 
159     override
160     void preProcess() {
161         if (preProcessed)
162             return;
163         preProcessed = true;
164         if (preProcessFilter !is null) {
165             overrideTransformMatrix = null;
166             mat4 matrix = this.transform.matrix;
167             auto filterResult = preProcessFilter(vertices, deformation, &matrix);
168             if (filterResult[0] !is null) {
169                 deformation = filterResult[0];
170             } 
171             if (filterResult[1] !is null) {
172                 overrideTransformMatrix = new MatrixHolder(*filterResult[1]);
173             }
174         }
175     }
176 
177     override
178     void postProcess() {
179         if (postProcessed)
180             return;
181         postProcessed = true;
182         if (postProcessFilter !is null) {
183             overrideTransformMatrix = null;
184             mat4 matrix = this.transform.matrix;
185             auto filterResult = postProcessFilter(vertices, deformation, &matrix);
186             if (filterResult[0] !is null) {
187                 deformation = filterResult[0];
188             } 
189             if (filterResult[1] !is null) {
190                 overrideTransformMatrix = new MatrixHolder(*filterResult[1]);
191             }
192         }
193     }
194 
195 package(inochi2d):
196     final void notifyDeformPushed(ref Deformation deform) {
197         onDeformPushed(deform);
198     }
199 
200 public:
201 
202     abstract void renderMask(bool dodge = false);
203 
204     /**
205         Constructs a new drawable surface
206     */
207     this(Node parent = null) {
208         super(parent);
209 
210         version(InDoesRender) {
211 
212             // Generate the buffers
213             glGenBuffers(1, &vbo);
214             glGenBuffers(1, &ibo);
215             glGenBuffers(1, &dbo);
216         }
217 
218         // Create deformation stack
219         this.deformStack = DeformationStack(this);
220     }
221 
222     /**
223         Constructs a new drawable surface
224     */
225     this(MeshData data, Node parent = null) {
226         this(data, inCreateUUID(), parent);
227     }
228 
229     /**
230         Constructs a new drawable surface
231     */
232     this(MeshData data, uint uuid, Node parent = null) {
233         super(uuid, parent);
234         this.data = data;
235         this.deformStack = DeformationStack(this);
236 
237         // Set the deformable points to their initial position
238         this.vertices = data.vertices.dup;
239 
240         version(InDoesRender) {
241             
242             // Generate the buffers
243             glGenBuffers(1, &vbo);
244             glGenBuffers(1, &ibo);
245             glGenBuffers(1, &dbo);
246         }
247 
248         // Update indices and vertices
249         this.updateIndices();
250         this.updateVertices();
251     }
252 
253     ref vec2[] vertices() {
254         return data.vertices;
255     }
256 
257     /**
258         Deformation offset to apply
259     */
260     vec2[] deformation;
261 
262     /**
263         The bounds of this drawable
264     */
265     vec4 bounds;
266 
267     /**
268         Deformation stack
269     */
270     DeformationStack deformStack;
271 
272     /**
273         Refreshes the drawable, updating its vertices
274     */
275     final void refresh() {
276         this.updateVertices();
277     }
278     
279     /**
280         Refreshes the drawable, updating its deformation deltas
281     */
282     final void refreshDeform() {
283         this.updateDeform();
284     }
285 
286     override
287     void beginUpdate() {
288         deformStack.preUpdate();
289         super.beginUpdate();
290     }
291 
292     /**
293         Updates the drawable
294     */
295     override
296     void update() {
297         preProcess();
298         deformStack.update();
299         super.update();
300         this.updateDeform();
301     }
302 
303     /**
304         Draws the drawable
305     */
306     override
307     void drawOne() {
308         super.drawOne();
309     }
310 
311     /**
312         Draws the drawable without any processing
313     */
314     void drawOneDirect(bool forMasking) { }
315 
316     override
317     string typeId() { return "Drawable"; }
318 
319     /**
320         Updates the drawable's bounds
321     */
322     void updateBounds() {
323         if (!doGenerateBounds) return;
324 
325         // Calculate bounds
326         Transform wtransform = transform;
327         bounds = vec4(wtransform.translation.xyxy);
328         mat4 matrix = getDynamicMatrix();
329         foreach(i, vertex; vertices) {
330             vec2 vertOriented = vec2(matrix * vec4(vertex+deformation[i], 0, 1));
331             if (vertOriented.x < bounds.x) bounds.x = vertOriented.x;
332             if (vertOriented.y < bounds.y) bounds.y = vertOriented.y;
333             if (vertOriented.x > bounds.z) bounds.z = vertOriented.x;
334             if (vertOriented.y > bounds.w) bounds.w = vertOriented.y;
335         }
336     }
337 
338     /**
339         Draws bounds
340     */
341     override
342     void drawBounds() {
343         if (!doGenerateBounds) return;
344         if (vertices.length == 0) return;
345         
346         float width = bounds.z-bounds.x;
347         float height = bounds.w-bounds.y;
348         inDbgSetBuffer([
349             vec3(bounds.x, bounds.y, 0),
350             vec3(bounds.x + width, bounds.y, 0),
351             
352             vec3(bounds.x + width, bounds.y, 0),
353             vec3(bounds.x + width, bounds.y+height, 0),
354             
355             vec3(bounds.x + width, bounds.y+height, 0),
356             vec3(bounds.x, bounds.y+height, 0),
357             
358             vec3(bounds.x, bounds.y+height, 0),
359             vec3(bounds.x, bounds.y, 0),
360         ]);
361         inDbgLineWidth(3);
362         inDbgDrawLines(vec4(.5, .5, .5, 1));
363         inDbgLineWidth(1);
364     }
365     
366     version (InDoesRender) {
367         /**
368             Draws line of mesh
369         */
370         void drawMeshLines() {
371             if (vertices.length == 0) return;
372 
373             auto trans = getDynamicMatrix();
374 
375             ushort[] indices = data.indices;
376 
377             vec3[] points = new vec3[indices.length*2];
378             foreach(i; 0..indices.length/3) {
379                 size_t ix = i*3;
380                 size_t iy = ix*2;
381                 auto indice = indices[ix];
382 
383                 points[iy+0] = vec3(vertices[indice]-data.origin+deformation[indice], 0);
384                 points[iy+1] = vec3(vertices[indices[ix+1]]-data.origin+deformation[indices[ix+1]], 0);
385 
386                 points[iy+2] = vec3(vertices[indices[ix+1]]-data.origin+deformation[indices[ix+1]], 0);
387                 points[iy+3] = vec3(vertices[indices[ix+2]]-data.origin+deformation[indices[ix+2]], 0);
388 
389                 points[iy+4] = vec3(vertices[indices[ix+2]]-data.origin+deformation[indices[ix+2]], 0);
390                 points[iy+5] = vec3(vertices[indice]-data.origin+deformation[indice], 0);
391             }
392 
393             inDbgSetBuffer(points);
394             inDbgDrawLines(vec4(.5, .5, .5, 1), trans);
395         }
396 
397         /**
398             Draws the points of the mesh
399         */
400         void drawMeshPoints() {
401             if (vertices.length == 0) return;
402 
403             auto trans = getDynamicMatrix();
404             vec3[] points = new vec3[vertices.length];
405             foreach(i, point; vertices) {
406                 points[i] = vec3(point-data.origin+deformation[i], 0);
407             }
408 
409             inDbgSetBuffer(points);
410             inDbgPointsSize(8);
411             inDbgDrawPoints(vec4(0, 0, 0, 1), trans);
412             inDbgPointsSize(4);
413             inDbgDrawPoints(vec4(1, 1, 1, 1), trans);
414         }
415     }
416 
417     /**
418         Returns the mesh data for this Part.
419     */
420     final ref MeshData getMesh() {
421         return this.data;
422     }
423 
424     /**
425         Changes this mesh's data
426     */
427     void rebuffer(ref MeshData data) {
428         this.data = data;
429         this.updateIndices();
430         this.updateVertices();
431     }
432     
433     /**
434         Resets the vertices of this drawable
435     */
436     final void reset() {
437         vertices[] = data.vertices;
438     }
439 }
440 
441 version (InDoesRender) {
442     /**
443         Begins a mask
444 
445         This causes the next draw calls until inBeginMaskContent/inBeginDodgeContent or inEndMask 
446         to be written to the current mask.
447 
448         This also clears whatever old mask there was.
449     */
450     void inBeginMask(bool hasMasks) {
451 
452         // Enable and clear the stencil buffer so we can write our mask to it
453         glEnable(GL_STENCIL_TEST);
454         glClearStencil(hasMasks ? 0 : 1);
455         glClear(GL_STENCIL_BUFFER_BIT);
456     }
457 
458     /**
459         End masking
460 
461         Once masking is ended content will no longer be masked by the defined mask.
462     */
463     void inEndMask() {
464 
465         // We're done stencil testing, disable it again so that we don't accidentally mask more stuff out
466         glStencilMask(0xFF);
467         glStencilFunc(GL_ALWAYS, 1, 0xFF);   
468         glDisable(GL_STENCIL_TEST);
469     }
470 
471     /**
472         Starts masking content
473 
474         NOTE: This have to be run within a inBeginMask and inEndMask block!
475     */
476     void inBeginMaskContent() {
477 
478         glStencilFunc(GL_EQUAL, 1, 0xFF);
479         glStencilMask(0x00);
480     }
481 }