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