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; }