1 /* 2 * DSFML - The Simple and Fast Multimedia Library for D 3 * 4 * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com) 5 * 6 * This software is provided 'as-is', without any express or implied warranty. 7 * In no event will the authors be held liable for any damages arising from the 8 * use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not claim 15 * that you wrote the original software. If you use this software in a product, 16 * an acknowledgment in the product documentation would be appreciated but is 17 * not required. 18 * 19 * 2. Altered source versions must be plainly marked as such, and must not be 20 * misrepresented as being the original software. 21 * 22 * 3. This notice may not be removed or altered from any source distribution 23 */ 24 25 /** 26 * $(U RenderTexture) is the little brother of $(RENDERWINDOW_LINK). It 27 * implements the same 2D drawing and OpenGL-related functions (see their base 28 * class $(RENDERTARGET_LINK) for more details), the difference is that the 29 * result is stored in an off-screen texture rather than being show in a window. 30 * 31 * Rendering to a texture can be useful in a variety of situations: 32 * $(UL 33 * $(LI precomputing a complex static texture (like a level's background from 34 * multiple tiles)) 35 * $(LI applying post-effects to the whole scene with shaders) 36 * $(LI creating a sprite from a 3D object rendered with OpenGL) 37 * $(LI etc.)) 38 * 39 * Example: 40 * --- 41 * // Create a new render-window 42 * auto window = new RenderWindow(VideoMode(800, 600), "DSFML window"); 43 * 44 * // Create a new render-texture 45 * auto texture = new RenderTexture(); 46 * if (!texture.create(500, 500)) 47 * return -1; 48 * 49 * // The main loop 50 * while (window.isOpen()) 51 * { 52 * // Event processing 53 * // ... 54 * 55 * // Clear the whole texture with red color 56 * texture.clear(Color.Red); 57 * 58 * // Draw stuff to the texture 59 * texture.draw(sprite); 60 * texture.draw(shape); 61 * texture.draw(text); 62 * 63 * // We're done drawing to the texture 64 * texture.display(); 65 * 66 * // Now we start rendering to the window, clear it first 67 * window.clear(); 68 * 69 * // Draw the texture 70 * auto sprite = new Sprite(texture.getTexture()); 71 * window.draw(sprite); 72 * 73 * // End the current frame and display its contents on screen 74 * window.display(); 75 * } 76 * --- 77 * 78 * $(PARA Like $(RENDERWINDOW_LINK), $(U RenderTexture) is still able to render 79 * direct OpenGL stuff. It is even possible to mix together OpenGL calls and 80 * regular DSFML drawing commands. If you need a depth buffer for 3D rendering, 81 * don't forget to request it when calling `RenderTexture.create`.) 82 * 83 * See_Also: 84 * $(RENDERTARGET_LINK), $(RENDERWINDOW_LINK), $(VIEW_LINK), $(TEXTURE_LINK) 85 */ 86 module dsfml.graphics.rendertexture; 87 88 import dsfml.graphics.rendertarget; 89 import dsfml.graphics.view; 90 import dsfml.graphics.rect; 91 import dsfml.graphics.drawable; 92 import dsfml.graphics.texture; 93 import dsfml.graphics.renderstates; 94 import dsfml.graphics.vertex; 95 import dsfml.graphics.primitivetype; 96 97 import dsfml.graphics.text; 98 import dsfml.graphics.shader; 99 100 import dsfml.graphics.color; 101 102 import dsfml.system.vector2; 103 104 105 import dsfml.system.err; 106 107 /** 108 * Target for off-screen 2D rendering into a texture. 109 */ 110 class RenderTexture : RenderTarget 111 { 112 package sfRenderTexture* sfPtr; 113 private Texture m_texture; 114 private View m_currentView, m_defaultView; 115 116 /// Default constructor. 117 this() 118 { 119 sfPtr = sfRenderTexture_construct(); 120 m_texture = new Texture(sfRenderTexture_getTexture(sfPtr)); 121 } 122 123 /// Desructor. 124 ~this() 125 { 126 import dsfml.system.config; 127 mixin(destructorOutput); 128 sfRenderTexture_destroy(sfPtr); 129 } 130 131 /** 132 * Create the render-texture. 133 * 134 * Before calling this function, the render-texture is in an invalid state, 135 * thus it is mandatory to call it before doing anything with the 136 * render-texture. 137 * 138 * The last parameter, depthBuffer, is useful if you want to use the 139 * render-texture for 3D OpenGL rendering that requires a depth-buffer. 140 * Otherwise it is unnecessary, and you should leave this parameter to false 141 * (which is its default value). 142 * 143 * Params: 144 * width = Width of the render-texture 145 * height = Height of the render-texture 146 * depthBuffer = Do you want this render-texture to have a depth buffer? 147 * 148 */ 149 void create(uint width, uint height, bool depthBuffer = false) 150 { 151 import dsfml.system..string; 152 153 sfRenderTexture_create(sfPtr, width, height, depthBuffer); 154 err.write(dsfml.system..string.toString(sfErr_getOutput())); 155 } 156 157 @property 158 { 159 /** 160 * Enable or disable texture smoothing. 161 */ 162 bool smooth(bool newSmooth) 163 { 164 sfRenderTexture_setSmooth(sfPtr, newSmooth); 165 return newSmooth; 166 } 167 /// ditto 168 bool smooth() 169 { 170 return (sfRenderTexture_isSmooth(sfPtr)); 171 } 172 } 173 174 @property 175 { 176 /** 177 * Change the current active view. 178 * 179 * The view is like a 2D camera, it controls which part of the 2D scene 180 * is visible, and how it is viewed in the render-target. The new view 181 * will affect everything that is drawn, until another view is set. 182 * 183 * The render target keeps its own copy of the view object, so it is not 184 * necessary to keep the original one alive after calling this function. 185 * To restore the original view of the target, you can pass the result 186 * of `getDefaultView()` to this function. 187 */ 188 override View view(View newView) 189 { 190 sfRenderTexture_setView(sfPtr, newView.center.x, newView.center.y, newView.size.x, newView.size.y, newView.rotation, 191 newView.viewport.left, newView.viewport.top, newView.viewport.width, newView.viewport.height); 192 return newView; 193 } 194 /// ditto 195 override View view() const 196 { 197 View currentView; 198 199 Vector2f currentCenter, currentSize; 200 float currentRotation; 201 FloatRect currentViewport; 202 203 sfRenderTexture_getView(sfPtr, ¤tCenter.x, ¤tCenter.y, ¤tSize.x, ¤tSize.y, ¤tRotation, 204 ¤tViewport.left, ¤tViewport.top, ¤tViewport.width, ¤tViewport.height); 205 206 currentView.center = currentCenter; 207 currentView.size = currentSize; 208 currentView.rotation = currentRotation; 209 currentView.viewport = currentViewport; 210 211 return currentView; 212 } 213 } 214 215 /** 216 * Get the default view of the render target. 217 * 218 * The default view has the initial size of the render target, and never 219 * changes after the target has been created. 220 * 221 * Returns: The default view of the render target. 222 */ 223 View getDefaultView() const 224 { 225 View currentView; 226 227 Vector2f currentCenter, currentSize; 228 float currentRotation; 229 FloatRect currentViewport; 230 231 sfRenderTexture_getDefaultView(sfPtr, ¤tCenter.x, ¤tCenter.y, ¤tSize.x, ¤tSize.y, ¤tRotation, 232 ¤tViewport.left, ¤tViewport.top, ¤tViewport.width, ¤tViewport.height); 233 234 currentView.center = currentCenter; 235 currentView.size = currentSize; 236 currentView.rotation = currentRotation; 237 currentView.viewport = currentViewport; 238 239 return currentView; 240 } 241 242 /** 243 * Return the size of the rendering region of the target. 244 * 245 * Returns: Size in pixels. 246 */ 247 Vector2u getSize() const 248 { 249 Vector2u temp; 250 sfRenderTexture_getSize(sfPtr, &temp.x, &temp.y); 251 return temp; 252 } 253 254 /** 255 * Get a read-only reference to the target texture. 256 * 257 * After drawing to the render-texture and calling Display, you can retrieve 258 * the updated texture using this function, and draw it using a sprite 259 * (for example). 260 * 261 * The internal Texture of a render-texture is always the same instance, so 262 * that it is possible to call this function once and keep a reference to 263 * the texture even after it is modified. 264 * 265 * Returns: Const reference to the texture. 266 */ 267 const(Texture) getTexture() 268 { 269 return m_texture; 270 } 271 272 /** 273 * Activate or deactivate the render-texture for rendering. 274 * 275 * This function makes the render-texture's context current for future 276 * OpenGL rendering operations (so you shouldn't care about it if you're not 277 * doing direct OpenGL stuff). 278 * 279 * Only one context can be current in a thread, so if you want to draw 280 * OpenGL geometry to another render target (like a $(RENDERWINDOW_LINK)) 281 * don't forget to activate it again. 282 * 283 * Params: 284 * active = true to activate, false to deactivate 285 */ 286 void setActive(bool active = true) 287 { 288 sfRenderTexture_setActive(sfPtr, active); 289 } 290 291 /** 292 * Clear the entire target with a single color. 293 * 294 * This function is usually called once every frame, to clear the previous 295 * contents of the target. 296 * 297 * Params: 298 * color = Fill color to use to clear the render target 299 */ 300 void clear(Color color = Color.Black) 301 { 302 sfRenderTexture_clear(sfPtr, color.r,color.g, color.b, color.a); 303 } 304 305 /** 306 * Update the contents of the target texture. 307 * 308 * This function updates the target texture with what has been drawn so far. 309 * Like for windows, calling this function is mandatory at the end of 310 * rendering. Not calling it may leave the texture in an undefined state. 311 */ 312 void display() 313 { 314 sfRenderTexture_display(sfPtr); 315 } 316 317 /** 318 * Draw a drawable object to the render target. 319 * 320 * Params: 321 * drawable = Object to draw 322 * states = Render states to use for drawing 323 */ 324 override void draw(Drawable drawable, RenderStates states = RenderStates.init) 325 { 326 drawable.draw(this, states); 327 } 328 329 /** 330 * Draw primitives defined by an array of vertices. 331 * 332 * Params: 333 * vertices = Array of vertices to draw 334 * type = Type of primitives to draw 335 * states = Render states to use for drawing 336 */ 337 override void draw(const(Vertex)[] vertices, PrimitiveType type, RenderStates states = RenderStates.init) 338 { 339 import std.algorithm; 340 341 sfRenderTexture_drawPrimitives(sfPtr, vertices.ptr, cast(uint)min(uint.max, vertices.length),type,states.blendMode.colorSrcFactor, states.blendMode.alphaDstFactor, 342 states.blendMode.colorEquation, states.blendMode.alphaSrcFactor, states.blendMode.alphaDstFactor, states.blendMode.alphaEquation, 343 states.transform.m_matrix.ptr, states.texture?states.texture.sfPtr:null, states.shader?states.shader.sfPtr:null); 344 } 345 346 /** 347 * Restore the previously saved OpenGL render states and matrices. 348 * 349 * See the description of pushGLStates to get a detailed description of 350 * these functions. 351 */ 352 void popGLStates() 353 { 354 sfRenderTexture_popGLStates(sfPtr); 355 } 356 357 /** 358 * Save the current OpenGL render states and matrices. 359 * 360 * This function can be used when you mix SFML drawing and direct OpenGL 361 * rendering. Combined with PopGLStates, it ensures that: 362 * $(UL 363 * $(LI DSFML's internal states are not messed up by your OpenGL code) 364 * $(LI your OpenGL states are not modified by a call to an SFML function)) 365 * 366 * $(PARA More specifically, it must be used around the code that calls 367 * `draw` functions. 368 * 369 * Note that this function is quite expensive: it saves all the possible 370 * OpenGL states and matrices, even the ones you don't care about.Therefore 371 * it should be used wisely. It is provided for convenience, but the best 372 * results will be achieved if you handle OpenGL states yourself (because 373 * you know which states have really changed, and need to be saved and 374 * restored). Take a look at the `resetGLStates` function if you do so.) 375 */ 376 void pushGLStates() 377 { 378 import dsfml.system..string; 379 sfRenderTexture_pushGLStates(sfPtr); 380 err.write(dsfml.system..string.toString(sfErr_getOutput())); 381 } 382 383 /** 384 * Reset the internal OpenGL states so that the target is ready for drawing. 385 * 386 * This function can be used when you mix DSFML drawing and direct OpenGL 387 * rendering, if you choose not to use pushGLStates/popGLStates. It makes 388 * sure that all OpenGL states needed by DSFML are set, so that subsequent 389 * `draw()` calls will work as expected. 390 */ 391 void resetGLStates() 392 { 393 sfRenderTexture_resetGLStates(sfPtr); 394 } 395 } 396 397 unittest 398 { 399 version(DSFML_Unittest_Graphics) 400 { 401 import std.stdio; 402 import dsfml.graphics.sprite; 403 404 writeln("Unit tests for RenderTexture"); 405 406 auto renderTexture = new RenderTexture(); 407 408 renderTexture.create(100,100); 409 410 //doesn't need a texture for this unit test 411 Sprite testSprite = new Sprite(); 412 413 //clear before doing anything 414 renderTexture.clear(); 415 416 renderTexture.draw(testSprite); 417 418 //prepare the RenderTexture for usage after drawing 419 renderTexture.display(); 420 421 //grab that texture for usage 422 auto texture = renderTexture.getTexture(); 423 424 writeln(); 425 426 } 427 } 428 429 package extern(C) struct sfRenderTexture; 430 431 private extern(C): 432 433 //Construct a new render texture 434 sfRenderTexture* sfRenderTexture_construct(); 435 436 //Construct a new render texture 437 void sfRenderTexture_create(sfRenderTexture* renderTexture, uint width, uint height, bool depthBuffer); 438 439 //Destroy an existing render texture 440 void sfRenderTexture_destroy(sfRenderTexture* renderTexture); 441 442 //Get the size of the rendering region of a render texture 443 void sfRenderTexture_getSize(const sfRenderTexture* renderTexture, uint* x, uint* y); 444 445 //Activate or deactivate a render texture as the current target for rendering 446 bool sfRenderTexture_setActive(sfRenderTexture* renderTexture, bool active); 447 448 //Update the contents of the target texture 449 void sfRenderTexture_display(sfRenderTexture* renderTexture); 450 451 //Clear the rendertexture with the given color 452 void sfRenderTexture_clear(sfRenderTexture* renderTexture, ubyte r, ubyte g, ubyte b, ubyte a); 453 454 //Change the current active view of a render texture 455 void sfRenderTexture_setView(sfRenderTexture* renderTexture, float centerX, float centerY, float sizeX, 456 float sizeY, float rotation, float viewportLeft, float viewportTop, float viewportWidth, 457 float viewportHeight); 458 459 //Get the current active view of a render texture 460 void sfRenderTexture_getView(const sfRenderTexture* renderTexture, float* centerX, float* centerY, float* sizeX, 461 float* sizeY, float* rotation, float* viewportLeft, float* viewportTop, float* viewportWidth, 462 float* viewportHeight); 463 464 //Get the default view of a render texture 465 void sfRenderTexture_getDefaultView(const sfRenderTexture* renderTexture, float* centerX, float* centerY, float* sizeX, 466 float* sizeY, float* rotation, float* viewportLeft, float* viewportTop, float* viewportWidth, 467 float* viewportHeight); 468 469 //Draw primitives defined by an array of vertices to a render texture 470 void sfRenderTexture_drawPrimitives(sfRenderTexture* renderTexture, const void* vertices, uint vertexCount, int type, int colorSrcFactor, int colorDstFactor, int colorEquation, 471 int alphaSrcFactor, int alphaDstFactor, int alphaEquation, const float* transform, const sfTexture* texture, const sfShader* shader); 472 473 //Save the current OpenGL render states and matrices 474 void sfRenderTexture_pushGLStates(sfRenderTexture* renderTexture); 475 476 //Restore the previously saved OpenGL render states and matrices 477 void sfRenderTexture_popGLStates(sfRenderTexture* renderTexture); 478 479 //Reset the internal OpenGL states so that the target is ready for drawing 480 void sfRenderTexture_resetGLStates(sfRenderTexture* renderTexture); 481 482 //Get the target texture of a render texture 483 sfTexture* sfRenderTexture_getTexture(const sfRenderTexture* renderTexture); 484 485 //Enable or disable the smooth filter on a render texture 486 void sfRenderTexture_setSmooth(sfRenderTexture* renderTexture, bool smooth); 487 488 //Tell whether the smooth filter is enabled or not for a render texture 489 bool sfRenderTexture_isSmooth(const sfRenderTexture* renderTexture); 490 491 492 const(char)* sfErr_getOutput();