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