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