1 /*
2     Inochi2D Rendering
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;
10 
11 public import inochi2d.core.shader;
12 public import inochi2d.core.texture;
13 public import inochi2d.core.nodes;
14 public import inochi2d.core.puppet;
15 public import inochi2d.core.meshdata;
16 public import inochi2d.core.param;
17 public import inochi2d.core.automation;
18 public import inochi2d.core.animation;
19 public import inochi2d.integration;
20 import inochi2d.core.dbg;
21 
22 import bindbc.opengl;
23 import inochi2d.math;
24 import std.stdio;
25 
26 version(Windows) {
27     // Ask Windows nicely to use dedicated GPUs :)
28     export extern(C) int NvOptimusEnablement = 0x00000001;
29     export extern(C) int AmdPowerXpressRequestHighPerformance = 0x00000001;
30 }
31 
32 struct PostProcessingShader {
33 private:
34     GLint[string] uniformCache;
35 
36 public:
37     Shader shader;
38     this(Shader shader) {
39         this.shader = shader;
40 
41         shader.use();
42         shader.setUniform(shader.getUniformLocation("albedo"), 0);
43         shader.setUniform(shader.getUniformLocation("emissive"), 1);
44         shader.setUniform(shader.getUniformLocation("bumpmap"), 2);
45     }
46 
47     /**
48         Gets the location of the specified uniform
49     */
50     GLuint getUniform(string name) {
51         if (this.hasUniform(name)) return uniformCache[name];
52         GLint element = shader.getUniformLocation(name);
53         uniformCache[name] = element;
54         return element;
55     }
56 
57     /**
58         Returns true if the uniform is present in the shader cache 
59     */
60     bool hasUniform(string name) {
61         return (name in uniformCache) !is null;
62     }
63 }
64 
65 // Internal rendering constants
66 private {
67     // Viewport
68     int inViewportWidth;
69     int inViewportHeight;
70 
71     GLuint sceneVAO;
72     GLuint sceneVBO;
73 
74     GLuint fBuffer;
75     GLuint fAlbedo;
76     GLuint fEmissive;
77     GLuint fBump;
78     GLuint fStencil;
79 
80     GLuint cfBuffer;
81     GLuint cfAlbedo;
82     GLuint cfEmissive;
83     GLuint cfBump;
84     GLuint cfStencil;
85 
86     vec4 inClearColor;
87 
88     PostProcessingShader basicSceneShader;
89     PostProcessingShader basicSceneLighting;
90     PostProcessingShader[] postProcessingStack;
91 
92     // Camera
93     Camera inCamera;
94 
95     bool isCompositing;
96 
97     void renderScene(vec4 area, PostProcessingShader shaderToUse, GLuint albedo, GLuint emissive, GLuint bump) {
98         glViewport(0, 0, cast(int)area.z, cast(int)area.w);
99 
100         // Bind our vertex array
101         glBindVertexArray(sceneVAO);
102         
103         glDisable(GL_CULL_FACE);
104         glDisable(GL_DEPTH_TEST);
105         glEnable(GL_BLEND);
106         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
107 
108         shaderToUse.shader.use();
109         shaderToUse.shader.setUniform(shaderToUse.getUniform("mvp"), 
110             mat4.orthographic(0, area.z, area.w, 0, 0, max(area.z, area.w)) * 
111             mat4.translation(area.x, area.y, 0)
112         );
113 
114         // Ambient light
115         GLint ambientLightUniform = shaderToUse.getUniform("ambientLight");
116         if (ambientLightUniform != -1) shaderToUse.shader.setUniform(ambientLightUniform, inSceneAmbientLight);
117 
118         // framebuffer size
119         GLint fbSizeUniform = shaderToUse.getUniform("fbSize");
120         if (fbSizeUniform != -1) shaderToUse.shader.setUniform(fbSizeUniform, vec2(inViewportWidth, inViewportHeight));
121 
122         // Bind the texture
123         glActiveTexture(GL_TEXTURE0);
124         glBindTexture(GL_TEXTURE_2D, albedo);
125         glActiveTexture(GL_TEXTURE1);
126         glBindTexture(GL_TEXTURE_2D, emissive);
127         glActiveTexture(GL_TEXTURE2);
128         glBindTexture(GL_TEXTURE_2D, bump);
129 
130         // Enable points array
131         glEnableVertexAttribArray(0); // verts
132         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*float.sizeof, null);
133 
134         // Enable UVs array
135         glEnableVertexAttribArray(1); // uvs
136         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*float.sizeof, cast(float*)(2*float.sizeof));
137 
138         // Draw
139         glDrawArrays(GL_TRIANGLES, 0, 6);
140 
141         // Disable the vertex attribs after use
142         glDisableVertexAttribArray(0);
143         glDisableVertexAttribArray(1);
144 
145         glDisable(GL_BLEND);
146     }
147 }
148 
149 // Things only available internally for Inochi2D rendering
150 package(inochi2d) {
151     
152     /**
153         Initializes the renderer
154     */
155     void initRenderer() {
156 
157         // Set the viewport and by extension set the textures
158         inSetViewport(640, 480);
159         
160         // Initialize dynamic meshes
161         inInitBlending();
162         inInitNodes();
163         inInitDrawable();
164         inInitPart();
165         inInitMask();
166         inInitComposite();
167         inInitMeshGroup();
168         version(InDoesRender) inInitDebug();
169 
170         inParameterSetFactory((data) {
171             import fghj : deserializeValue;
172             Parameter param = new Parameter;
173             data.deserializeValue(param);
174             return param;
175         });
176 
177         // Some defaults that should be changed by app writer
178         inCamera = new Camera;
179 
180         inClearColor = vec4(0, 0, 0, 0);
181 
182         version (InDoesRender) {
183             
184             // Shader for scene
185             basicSceneShader = PostProcessingShader(new Shader(import("scene.vert"), import("scene.frag")));
186             glGenVertexArrays(1, &sceneVAO);
187             glGenBuffers(1, &sceneVBO);
188 
189             // Generate the framebuffer we'll be using to render the model and composites
190             glGenFramebuffers(1, &fBuffer);
191             glGenFramebuffers(1, &cfBuffer);
192             
193             // Generate the color and stencil-depth textures needed
194             // Note: we're not using the depth buffer but OpenGL 3.4 does not support stencil-only buffers
195             glGenTextures(1, &fAlbedo);
196             glGenTextures(1, &fEmissive);
197             glGenTextures(1, &fBump);
198             glGenTextures(1, &fStencil);
199 
200             glGenTextures(1, &cfAlbedo);
201             glGenTextures(1, &cfEmissive);
202             glGenTextures(1, &cfBump);
203             glGenTextures(1, &cfStencil);
204 
205             // Attach textures to framebuffer
206             glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
207             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fAlbedo, 0);
208             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fEmissive, 0);
209             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fBump, 0);
210             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fStencil, 0);
211 
212             glBindFramebuffer(GL_FRAMEBUFFER, cfBuffer);
213             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cfAlbedo, 0);
214             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, cfEmissive, 0);
215             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, cfBump, 0);
216             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, cfStencil, 0);
217 
218             // go back to default fb
219             glBindFramebuffer(GL_FRAMEBUFFER, 0);
220         }
221     }
222 }
223 
224 vec3 inSceneAmbientLight = vec3(1, 1, 1);
225 
226 /**
227     Begins rendering to the framebuffer
228 */
229 void inBeginScene() {
230     glBindVertexArray(sceneVAO);
231     glEnable(GL_BLEND);
232     glEnablei(GL_BLEND, 0);
233     glEnablei(GL_BLEND, 1);
234     glEnablei(GL_BLEND, 2);
235     glDisable(GL_DEPTH_TEST);
236     glDisable(GL_CULL_FACE);
237 
238     // Make sure to reset our viewport if someone has messed with it
239     glViewport(0, 0, inViewportWidth, inViewportHeight);
240 
241     // Bind our framebuffer
242     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fBuffer);
243 
244     // First clear buffer 1
245     glDrawBuffers(1, [GL_COLOR_ATTACHMENT0].ptr);
246     glClearColor(inClearColor.r, inClearColor.g, inClearColor.b, inClearColor.a);
247     glClear(GL_COLOR_BUFFER_BIT);
248 
249     // Then clear others with black
250     glDrawBuffers(2, [GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2].ptr);
251     glClearColor(0, 0, 0, 1);
252     glClear(GL_COLOR_BUFFER_BIT);
253 
254     // Everything else is the actual texture used by the meshes at id 0
255     glActiveTexture(GL_TEXTURE0);
256 
257     // Finally we render to all buffers
258     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
259     glDrawBuffers(3, [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2].ptr);
260 }
261 
262 /**
263     Begins a composition step
264 */
265 void inBeginComposite() {
266 
267     // We don't allow recursive compositing
268     if (isCompositing) return;
269     isCompositing = true;
270 
271     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cfBuffer);
272     glDrawBuffers(3, [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2].ptr);
273     glClearColor(0, 0, 0, 0);
274     glClear(GL_COLOR_BUFFER_BIT);
275 
276     // Everything else is the actual texture used by the meshes at id 0
277     glActiveTexture(GL_TEXTURE0);
278     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
279 }
280 
281 /**
282     Ends a composition step, re-binding the internal framebuffer
283 */
284 void inEndComposite() {
285 
286     // We don't allow recursive compositing
287     if (!isCompositing) return;
288     isCompositing = false;
289 
290     glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
291     glDrawBuffers(3, [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2].ptr);
292     glFlush();
293 }
294 
295 /**
296     Ends rendering to the framebuffer
297 */
298 void inEndScene() {
299     glBindFramebuffer(GL_FRAMEBUFFER, 0);
300 
301     glDisablei(GL_BLEND, 0);
302     glDisablei(GL_BLEND, 1);
303     glDisablei(GL_BLEND, 2);
304     glEnable(GL_DEPTH_TEST);
305     glEnable(GL_CULL_FACE);
306     glDisable(GL_BLEND);
307     glFlush();
308     glDrawBuffers(1, [GL_COLOR_ATTACHMENT0].ptr);
309 }
310 
311 /**
312     Runs post processing on the scene
313 */
314 void inPostProcessScene() {
315     bool targetBuffer;
316 
317     float r, g, b, a;
318     inGetClearColor(r, g, b, a);
319 
320     // Render area
321     vec4 area = vec4(
322         0, 0,
323         inViewportWidth, inViewportHeight
324     );
325 
326     // Tell OpenGL the resolution to render at
327     float[] data = [
328         area.x,         area.y+area.w,          0, 0,
329         area.x,         area.y,                 0, 1,
330         area.x+area.z,  area.y+area.w,          1, 0,
331         
332         area.x+area.z,  area.y+area.w,          1, 0,
333         area.x,         area.y,                 0, 1,
334         area.x+area.z,  area.y,                 1, 1,
335     ];
336     glBindBuffer(GL_ARRAY_BUFFER, sceneVBO);
337     glBufferData(GL_ARRAY_BUFFER, 24*float.sizeof, data.ptr, GL_DYNAMIC_DRAW);
338 
339 
340     if (postProcessingStack.length > 0) {
341         glActiveTexture(GL_TEXTURE1);
342         glBindTexture(GL_TEXTURE_2D, fEmissive);
343         glGenerateMipmap(GL_TEXTURE_2D);
344 
345         // We want to be able to post process all the attachments
346         glBindFramebuffer(GL_FRAMEBUFFER, cfBuffer);
347         glDrawBuffers(3, [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2].ptr);
348         glClear(GL_COLOR_BUFFER_BIT);
349 
350         glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
351         glDrawBuffers(3, [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2].ptr);
352 
353         foreach(shader; postProcessingStack) {
354             targetBuffer = !targetBuffer;
355 
356             if (targetBuffer) {
357 
358                 // Main buffer -> Composite buffer
359                 glBindFramebuffer(GL_FRAMEBUFFER, cfBuffer); // dst
360                 renderScene(area, shader, fAlbedo, fEmissive, fBump); // src
361             } else {
362 
363                 // Composite buffer -> Main buffer 
364                 glBindFramebuffer(GL_FRAMEBUFFER, fBuffer); // dst
365                 renderScene(area, shader, cfAlbedo, cfEmissive, cfBump); // src
366             }
367         }
368 
369         if (targetBuffer) {
370             glBindFramebuffer(GL_READ_FRAMEBUFFER, cfBuffer);
371             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fBuffer);
372             glBlitFramebuffer(
373                 0, 0, inViewportWidth, inViewportHeight, // src rect
374                 0, 0, inViewportWidth, inViewportHeight, // dst rect
375                 GL_COLOR_BUFFER_BIT, // blit mask
376                 GL_LINEAR // blit filter
377             );
378         }
379     }
380     
381     glBindFramebuffer(GL_FRAMEBUFFER, 0);
382 }
383 
384 /**
385     Add basic lighting shader to processing stack
386 */
387 void inPostProcessingAddBasicLighting() {
388     postProcessingStack ~= PostProcessingShader(
389         new Shader(
390             import("scene.vert"),
391             import("lighting.frag")
392         )
393     );
394 }
395 
396 /**
397     Clears the post processing stack
398 */
399 ref PostProcessingShader[] inGetPostProcessingStack() {
400     return postProcessingStack;
401 }
402 
403 /**
404     Gets the global camera
405 */
406 Camera inGetCamera() {
407     return inCamera;
408 }
409 
410 /**
411     Sets the global camera, allows switching between cameras
412 */
413 void inSetCamera(Camera camera) {
414     inCamera = camera;
415 }
416 
417 /**
418     Draw scene to area
419 */
420 void inDrawScene(vec4 area) {
421     glBindFramebuffer(GL_FRAMEBUFFER, 0);
422     float[] data = [
423         area.x,         area.y+area.w,          0, 0,
424         area.x,         area.y,                 0, 1,
425         area.x+area.z,  area.y+area.w,          1, 0,
426         
427         area.x+area.z,  area.y+area.w,          1, 0,
428         area.x,         area.y,                 0, 1,
429         area.x+area.z,  area.y,                 1, 1,
430     ];
431 
432     glBindBuffer(GL_ARRAY_BUFFER, sceneVBO);
433     glBufferData(GL_ARRAY_BUFFER, 24*float.sizeof, data.ptr, GL_DYNAMIC_DRAW);
434     renderScene(area, basicSceneShader, fAlbedo, fEmissive, fBump);
435 }
436 
437 void incCompositePrepareRender() {
438     glActiveTexture(GL_TEXTURE0);
439     glBindTexture(GL_TEXTURE_2D, cfAlbedo);
440     glActiveTexture(GL_TEXTURE1);
441     glBindTexture(GL_TEXTURE_2D, cfEmissive);
442     glActiveTexture(GL_TEXTURE2);
443     glBindTexture(GL_TEXTURE_2D, cfBump);
444 }
445 
446 /**
447     Gets the Inochi2D framebuffer 
448 
449     DO NOT MODIFY THIS IMAGE!
450 */
451 GLuint inGetFramebuffer() {
452     return fBuffer;
453 }
454 
455 /**
456     Gets the Inochi2D framebuffer render image
457 
458     DO NOT MODIFY THIS IMAGE!
459 */
460 GLuint inGetRenderImage() {
461     return fAlbedo;
462 }
463 
464 /**
465     Gets the Inochi2D composite render image
466 
467     DO NOT MODIFY THIS IMAGE!
468 */
469 GLuint inGetCompositeImage() {
470     return cfAlbedo;
471 }
472 
473 /**
474     Sets the viewport area to render to
475 */
476 void inSetViewport(int width, int height) nothrow {
477 
478     // Skip resizing when not needed.
479     if (width == inViewportWidth && height == inViewportHeight) return;
480 
481     inViewportWidth = width;
482     inViewportHeight = height;
483 
484     version(InDoesRender) {
485         // Render Framebuffer
486         glBindTexture(GL_TEXTURE_2D, fAlbedo);
487         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
488         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
489         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
490         
491         glBindTexture(GL_TEXTURE_2D, fEmissive);
492         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, null);
493         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
494         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
495         
496         glBindTexture(GL_TEXTURE_2D, fBump);
497         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
498         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
499         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
500         
501         glBindTexture(GL_TEXTURE_2D, fStencil);
502         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
503 
504         glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
505         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fAlbedo, 0);
506         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fEmissive, 0);
507         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fBump, 0);
508         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fStencil, 0);
509         
510 
511         // Composite framebuffer
512         glBindTexture(GL_TEXTURE_2D, cfAlbedo);
513         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
514         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
515         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
516         
517         glBindTexture(GL_TEXTURE_2D, cfEmissive);
518         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, null);
519         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
520         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
521         
522         glBindTexture(GL_TEXTURE_2D, cfBump);
523         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
524         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
525         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
526         
527         glBindTexture(GL_TEXTURE_2D, cfStencil);
528         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
529 
530         glBindFramebuffer(GL_FRAMEBUFFER, cfBuffer);
531         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cfAlbedo, 0);
532         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, cfEmissive, 0);
533         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, cfBump, 0);
534         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, cfStencil, 0);
535         
536         glBindFramebuffer(GL_FRAMEBUFFER, 0);
537 
538         glViewport(0, 0, width, height);
539     }
540 }
541 
542 /**
543     Gets the viewport
544 */
545 void inGetViewport(out int width, out int height) nothrow {
546     width = inViewportWidth;
547     height = inViewportHeight;
548 }
549 
550 /**
551     Returns length of viewport data for extraction
552 */
553 size_t inViewportDataLength() {
554     return inViewportWidth * inViewportHeight * 4;
555 }
556 
557 /**
558     Dumps viewport data to texture stream
559 */
560 void inDumpViewport(ref ubyte[] dumpTo) {
561     import std.exception : enforce;
562     enforce(dumpTo.length >= inViewportDataLength(), "Invalid data destination length for inDumpViewport");
563     glBindTexture(GL_TEXTURE_2D, fAlbedo);
564     glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, dumpTo.ptr);
565 
566     // We need to flip it because OpenGL renders stuff with a different coordinate system
567     ubyte[] tmpLine = new ubyte[inViewportWidth * 4];
568     size_t ri = 0;
569     foreach_reverse(i; inViewportHeight/2..inViewportHeight) {
570         size_t lineSize = inViewportWidth*4;
571         size_t oldLineStart = (lineSize*ri);
572         size_t newLineStart = (lineSize*i);
573         import core.stdc.string : memcpy;
574 
575         memcpy(tmpLine.ptr, dumpTo.ptr+oldLineStart, lineSize);
576         memcpy(dumpTo.ptr+oldLineStart, dumpTo.ptr+newLineStart, lineSize);
577         memcpy(dumpTo.ptr+newLineStart, tmpLine.ptr, lineSize);
578         
579         ri++;
580     }
581 }
582 
583 /**
584     Sets the background clear color
585 */
586 void inSetClearColor(float r, float g, float b, float a) {
587     inClearColor = vec4(r, g, b, a);
588 }
589 
590 /**
591 
592 */
593 void inGetClearColor(out float r, out float g, out float b, out float a) {
594     r = inClearColor.r;
595     g = inClearColor.g;
596     b = inClearColor.b;
597     a = inClearColor.a;
598 }
599 
600 /**
601     UDA for sub-classable parts of the spec
602     eg. Nodes and Automation can be extended by
603     adding new subclasses that aren't in the base spec.
604 */
605 struct TypeId { string id; }
606 
607 /**
608     Different modes of interpolation between values.
609 */
610 enum InterpolateMode {
611 
612     /**
613         Round to nearest
614     */
615     Nearest,
616     
617     /**
618         Linear interpolation
619     */
620     Linear,
621 
622     /**
623         Round to nearest
624     */
625     Stepped,
626 
627     /**
628         Cubic interpolation
629     */
630     Cubic,
631 
632     /**
633         Interpolation using beziér splines
634     */
635     Bezier,
636 
637     COUNT
638 }