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