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