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