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.integration;
19 import inochi2d.core.dbg;
20 
21 import bindbc.opengl;
22 import inochi2d.math;
23 import std.stdio;
24 
25 version(Windows) {
26     // Ask Windows nicely to use dedicated GPUs :)
27     export extern(C) int NvOptimusEnablement = 0x00000001;
28     export extern(C) int AmdPowerXpressRequestHighPerformance = 0x00000001;
29 }
30 
31 // Internal rendering constants
32 private {
33     // Viewport
34     int inViewportWidth;
35     int inViewportHeight;
36 
37     Shader sceneShader;
38     GLuint sceneVAO;
39     GLuint sceneVBO;
40     GLint sceneMVP;
41 
42     GLuint fBuffer;
43     GLuint fColor;
44     GLuint fStencil;
45 
46     GLuint cfBuffer;
47     GLuint cfColor;
48     GLuint cfStencil;
49 
50     vec4 inClearColor;
51 
52 
53     Shader[] blendingShaders;
54 
55     // Camera
56     Camera inCamera;
57 
58     bool isCompositing;
59 }
60 
61 // Things only available internally for Inochi2D rendering
62 package(inochi2d) {
63     
64     /**
65         Initializes the renderer
66     */
67     void initRenderer() {
68 
69         // Set the viewport and by extension set the textures
70         inSetViewport(640, 480);
71         
72         // Initialize dynamic meshes
73         inInitNodes();
74         inInitPathDeform();
75         inInitDrawable();
76         inInitPart();
77         inInitMask();
78         inInitComposite();
79         version(InDoesRender) inInitDebug();
80 
81         // Some defaults that should be changed by app writer
82         inCamera = new Camera;
83 
84         inClearColor = vec4(0, 0, 0, 0);
85 
86         version (InDoesRender) {
87             
88             // Shader for scene
89             sceneShader = new Shader(import("scene.vert"), import("scene.frag"));
90             sceneMVP = sceneShader.getUniformLocation("mvp");
91             glGenVertexArrays(1, &sceneVAO);
92             glGenBuffers(1, &sceneVBO);
93 
94             // Generate the framebuffer we'll be using to render the model and composites
95             glGenFramebuffers(1, &fBuffer);
96             glGenFramebuffers(1, &cfBuffer);
97             
98             // Generate the color and stencil-depth textures needed
99             // Note: we're not using the depth buffer but OpenGL 3.4 does not support stencil-only buffers
100             glGenTextures(1, &fColor);
101             glGenTextures(1, &fStencil);
102             glGenTextures(1, &cfColor);
103             glGenTextures(1, &cfStencil);
104 
105             // Attach textures to framebuffer
106             glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
107             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fColor, 0);
108             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fStencil, 0);
109             glBindFramebuffer(GL_FRAMEBUFFER, cfBuffer);
110             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cfColor, 0);
111             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, cfStencil, 0);
112 
113             // go back to default fb
114             glBindFramebuffer(GL_FRAMEBUFFER, 0);
115         }
116     }
117 }
118 
119 /**
120     Begins rendering to the framebuffer
121 */
122 void inBeginScene() {
123     glEnable(GL_BLEND);
124     glDisable(GL_DEPTH_TEST);
125 
126     // Make sure to reset our viewport if someone has messed with it
127     glViewport(0, 0, inViewportWidth, inViewportHeight);
128 
129     // Bind our framebuffer
130     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fBuffer);
131     glClearColor(inClearColor.r, inClearColor.g, inClearColor.b, inClearColor.a);
132     glClear(GL_COLOR_BUFFER_BIT);
133 
134     // Everything else is the actual texture used by the meshes at id 0
135     glActiveTexture(GL_TEXTURE0);
136 
137     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
138 }
139 
140 /**
141     Begins a composition step
142 */
143 void inBeginComposite() {
144 
145     // We don't allow recursive compositing
146     if (isCompositing) return;
147     isCompositing = true;
148 
149     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cfBuffer);
150     glClearColor(0, 0, 0, 0);
151     glClear(GL_COLOR_BUFFER_BIT);
152 
153     // Everything else is the actual texture used by the meshes at id 0
154     glActiveTexture(GL_TEXTURE0);
155     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
156 }
157 
158 /**
159     Ends a composition step, re-binding the internal framebuffer
160 */
161 void inEndComposite() {
162 
163     // We don't allow recursive compositing
164     if (!isCompositing) return;
165     isCompositing = false;
166 
167     glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
168     glFlush();
169 }
170 
171 
172 /**
173     Ends rendering to the framebuffer
174 */
175 void inEndScene() {
176     glBindFramebuffer(GL_FRAMEBUFFER, 0);
177 
178     glDisable(GL_BLEND);
179     glEnable(GL_DEPTH_TEST);
180     glFlush();
181 }
182 
183 /**
184     Gets the global camera
185 */
186 Camera inGetCamera() {
187     return inCamera;
188 }
189 
190 /**
191     Sets the global camera, allows switching between cameras
192 */
193 void inSetCamera(Camera camera) {
194     inCamera = camera;
195 }
196 
197 /**
198     Draw scene to area
199 */
200 void inDrawScene(vec4 area) {
201     glBindFramebuffer(GL_FRAMEBUFFER, 0);
202     glViewport(0, 0, cast(int)area.z, cast(int)area.w);
203 
204     // Bind our vertex array
205     glBindVertexArray(sceneVAO);
206     
207     glDisable(GL_CULL_FACE);
208     glDisable(GL_DEPTH_TEST);
209     glEnable(GL_BLEND);
210     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
211 
212     sceneShader.use();
213     sceneShader.setUniform(sceneMVP, 
214         mat4.orthographic(0, area.z, area.w, 0, 0, max(area.z, area.w)) * 
215         mat4.translation(area.x, area.y, 0)
216     );
217 
218     // Bind the texture
219     glBindTexture(GL_TEXTURE_2D, fColor);
220     glActiveTexture(GL_TEXTURE0);
221 
222     // Enable points array
223     glEnableVertexAttribArray(0); // verts
224     float[] data = [
225         area.x,         area.y+area.w,          0, 0,
226         area.x,         area.y,                 0, 1,
227         area.x+area.z,  area.y+area.w,          1, 0,
228         
229         area.x+area.z,  area.y+area.w,          1, 0,
230         area.x,         area.y,                 0, 1,
231         area.x+area.z,  area.y,                 1, 1,
232     ];
233 
234     glBindBuffer(GL_ARRAY_BUFFER, sceneVBO);
235     glBufferData(GL_ARRAY_BUFFER, 24*float.sizeof, data.ptr, GL_DYNAMIC_DRAW);
236     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*float.sizeof, null);
237 
238     // Enable UVs array
239     glEnableVertexAttribArray(1); // uvs
240     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*float.sizeof, cast(float*)(2*float.sizeof));
241 
242     // Draw
243     glDrawArrays(GL_TRIANGLES, 0, 6);
244 
245     // Disable the vertex attribs after use
246     glDisableVertexAttribArray(0);
247     glDisableVertexAttribArray(1);
248 
249     glDisable(GL_BLEND);
250 }
251 
252 /**
253     Gets the Inochi2D framebuffer render image
254 
255     DO NOT MODIFY THIS IMAGE!
256 */
257 GLuint inGetRenderImage() {
258     return fColor;
259 }
260 
261 /**
262     Gets the Inochi2D composite render image
263 
264     DO NOT MODIFY THIS IMAGE!
265 */
266 GLuint inGetCompositeImage() {
267     return cfColor;
268 }
269 
270 /**
271     Sets the viewport area to render to
272 */
273 void inSetViewport(int width, int height) nothrow {
274 
275     inViewportWidth = width;
276     inViewportHeight = height;
277 
278     version(InDoesRender) {
279         // Render Framebuffer
280         glBindTexture(GL_TEXTURE_2D, fColor);
281         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
282         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
283         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
284         
285         glBindTexture(GL_TEXTURE_2D, fStencil);
286         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
287 
288         glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
289         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fColor, 0);
290         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fStencil, 0);
291         
292 
293         // Composite framebuffer
294         glBindTexture(GL_TEXTURE_2D, cfColor);
295         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
296         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
297         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
298         
299         glBindTexture(GL_TEXTURE_2D, cfStencil);
300         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
301 
302         glBindFramebuffer(GL_FRAMEBUFFER, cfBuffer);
303         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cfColor, 0);
304         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, cfStencil, 0);
305         
306         glBindFramebuffer(GL_FRAMEBUFFER, 0);
307     }
308 }
309 
310 /**
311     Gets the viewport
312 */
313 void inGetViewport(out int width, out int height) nothrow {
314     width = inViewportWidth;
315     height = inViewportHeight;
316 }
317 
318 /**
319     Returns length of viewport data for extraction
320 */
321 size_t inViewportDataLength() {
322     return inViewportWidth * inViewportHeight * 4;
323 }
324 
325 /**
326     Dumps viewport data to texture stream
327 */
328 void inDumpViewport(ref ubyte[] dumpTo) {
329     import std.exception : enforce;
330     enforce(dumpTo.length >= inViewportDataLength(), "Invalid data destination length for inDumpViewport");
331     glBindTexture(GL_TEXTURE_2D, fColor);
332     glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, dumpTo.ptr);
333 
334     // We need to flip it because OpenGL renders stuff with a different coordinate system
335     ubyte[] tmpLine = new ubyte[inViewportWidth * 4];
336     size_t ri = 0;
337     foreach_reverse(i; inViewportHeight/2..inViewportHeight) {
338         size_t lineSize = inViewportWidth*4;
339         size_t oldLineStart = (lineSize*ri);
340         size_t newLineStart = (lineSize*i);
341         import core.stdc.string : memcpy;
342 
343         memcpy(tmpLine.ptr, dumpTo.ptr+oldLineStart, lineSize);
344         memcpy(dumpTo.ptr+oldLineStart, dumpTo.ptr+newLineStart, lineSize);
345         memcpy(dumpTo.ptr+newLineStart, tmpLine.ptr, lineSize);
346         
347         ri++;
348     }
349 }
350 
351 /**
352     Sets the background clear color
353 */
354 void inSetClearColor(float r, float g, float b, float a) {
355     inClearColor = vec4(r, g, b, a);
356 }
357 
358 /**
359     UDA for sub-classable parts of the spec
360     eg. Nodes and Automation can be extended by
361     adding new subclasses that aren't in the base spec.
362 */
363 struct TypeId { string id; }