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 dsfml.graphics.rendertexture;
90 
91 import dsfml.graphics.color;
92 import dsfml.graphics.drawable;
93 import dsfml.graphics.primitivetype;
94 import dsfml.graphics.rect;
95 import dsfml.graphics.renderstates;
96 import dsfml.graphics.rendertarget;
97 import dsfml.graphics.shader;
98 import dsfml.graphics.text;
99 import dsfml.graphics.texture;
100 import dsfml.graphics.vertex;
101 import dsfml.graphics.view;
102 
103 import dsfml.system.err;
104 import dsfml.system.vector2;
105 
106 /**
107  * Target for off-screen 2D rendering into a texture.
108  */
109 class RenderTexture : RenderTarget
110 {
111     package sfRenderTexture* sfPtr;
112     private Texture m_texture;
113     private View m_currentView, m_defaultView;
114 
115     /// Default constructor.
116     this()
117     {
118         sfPtr = sfRenderTexture_construct();
119         m_texture = new Texture(sfRenderTexture_getTexture(sfPtr));
120     }
121 
122     /// Desructor.
123     ~this()
124     {
125         import dsfml.system.config;
126         mixin(destructorOutput);
127         sfRenderTexture_destroy(sfPtr);
128     }
129 
130     /**
131      * Create the render-texture.
132      *
133      * Before calling this function, the render-texture is in an invalid state,
134      * thus it is mandatory to call it before doing anything with the
135      * render-texture.
136      *
137      * The last parameter, depthBuffer, is useful if you want to use the
138      * render-texture for 3D OpenGL rendering that requires a depth-buffer.
139      * Otherwise it is unnecessary, and you should leave this parameter to false
140      * (which is its default value).
141      *
142      * Params:
143      * 	width		= Width of the render-texture
144      * 	height		= Height of the render-texture
145      * 	depthBuffer	= Do you want this render-texture to have a depth buffer?
146      *
147      * Returns: True if creation has been successful.
148      */
149     bool create(uint width, uint height, bool depthBuffer = false)
150     {
151         return sfRenderTexture_create(sfPtr, width, height, depthBuffer);
152     }
153 
154     @property
155     {
156         /**
157          * Enable or disable texture smoothing.
158          */
159         bool smooth(bool newSmooth)
160         {
161             sfRenderTexture_setSmooth(sfPtr, newSmooth);
162             return newSmooth;
163         }
164 
165         /// ditto
166         bool smooth() const
167         {
168             return (sfRenderTexture_isSmooth(sfPtr));
169         }
170     }
171 
172     @property
173     {
174         /**
175          * Change the current active view.
176          *
177          * The view is like a 2D camera, it controls which part of the 2D scene
178          * is visible, and how it is viewed in the render-target. The new view
179          * will affect everything that is drawn, until another view is set.
180          *
181          * The render target keeps its own copy of the view object, so it is not
182          * necessary to keep the original one alive after calling this function.
183          * To restore the original view of the target, you can pass the result
184          * of `getDefaultView()` to this function.
185          */
186         override View view(View newView)
187         {
188             sfRenderTexture_setView(sfPtr, newView.center.x, newView.center.y, newView.size.x, newView.size.y, newView.rotation,
189                                     newView.viewport.left, newView.viewport.top, newView.viewport.width, newView.viewport.height);
190             return newView;
191         }
192 
193         /// ditto
194         override View view() const
195         {
196             View currentView;
197 
198             Vector2f currentCenter, currentSize;
199             float currentRotation;
200             FloatRect currentViewport;
201 
202             sfRenderTexture_getView(sfPtr, &currentCenter.x, &currentCenter.y, &currentSize.x, &currentSize.y, &currentRotation,
203                                     &currentViewport.left, &currentViewport.top, &currentViewport.width, &currentViewport.height);
204 
205             currentView.center = currentCenter;
206             currentView.size = currentSize;
207             currentView.rotation = currentRotation;
208             currentView.viewport = currentViewport;
209 
210             return currentView;
211         }
212     }
213 
214     /**
215      * Get the default view of the render target.
216      *
217      * The default view has the initial size of the render target, and never
218      * changes after the target has been created.
219      *
220      * Returns: The default view of the render target.
221      */
222     View getDefaultView() const
223     {
224         View currentView;
225 
226         Vector2f currentCenter, currentSize;
227         float currentRotation;
228         FloatRect currentViewport;
229 
230         sfRenderTexture_getDefaultView(sfPtr, &currentCenter.x, &currentCenter.y, &currentSize.x, &currentSize.y, &currentRotation,
231                                 &currentViewport.left, &currentViewport.top, &currentViewport.width, &currentViewport.height);
232 
233         currentView.center = currentCenter;
234         currentView.size = currentSize;
235         currentView.rotation = currentRotation;
236         currentView.viewport = currentViewport;
237 
238         return currentView;
239     }
240 
241     /**
242      * Return the size of the rendering region of the target.
243      *
244      * Returns: Size in pixels.
245      */
246     Vector2u getSize() const
247     {
248         Vector2u temp;
249         sfRenderTexture_getSize(sfPtr, &temp.x, &temp.y);
250         return temp;
251     }
252 
253     /**
254      * Get a read-only reference to the target texture.
255      *
256      * After drawing to the render-texture and calling Display, you can retrieve
257      * the updated texture using this function, and draw it using a sprite
258      * (for example).
259      *
260      * The internal Texture of a render-texture is always the same instance, so
261      * that it is possible to call this function once and keep a reference to
262      * the texture even after it is modified.
263      *
264      * Returns: Const reference to the texture.
265      */
266     const(Texture) getTexture() const
267     {
268         return m_texture;
269     }
270 
271     /**
272      * Activate or deactivate the render-texture for rendering.
273      *
274      * This function makes the render-texture's context current for future
275      * OpenGL rendering operations (so you shouldn't care about it if you're not
276      * doing direct OpenGL stuff).
277      *
278      * Only one context can be current in a thread, so if you want to draw
279      * OpenGL geometry to another render target (like a $(RENDERWINDOW_LINK))
280      * don't forget to activate it again.
281      *
282      * Params:
283      * 		active	= true to activate, false to deactivate
284      */
285     void setActive(bool active = true)
286     {
287         sfRenderTexture_setActive(sfPtr, active);
288     }
289 
290     /**
291      * Clear the entire target with a single color.
292      *
293      * This function is usually called once every frame, to clear the previous
294      * contents of the target.
295      *
296      * Params:
297      * 		color	= Fill color to use to clear the render target
298      */
299     void clear(Color color = Color.Black)
300     {
301         sfRenderTexture_clear(sfPtr, color.r,color.g, color.b, color.a);
302     }
303 
304     /**
305      * Update the contents of the target texture.
306      *
307      * This function updates the target texture with what has been drawn so far.
308      * Like for windows, calling this function is mandatory at the end of
309      * rendering. Not calling it may leave the texture in an undefined state.
310      */
311     void display()
312     {
313         sfRenderTexture_display(sfPtr);
314     }
315 
316     /**
317      * Draw a drawable object to the render target.
318      *
319      * Params:
320      * 		drawable	= Object to draw
321      * 		states		= Render states to use for drawing
322      */
323     override void draw(Drawable drawable, RenderStates states = RenderStates.init)
324     {
325         drawable.draw(this, states);
326     }
327 
328     /**
329      * Draw primitives defined by an array of vertices.
330      *
331      * Params:
332      * 		vertices	= Array of vertices to draw
333      * 		type		= Type of primitives to draw
334      * 		states		= Render states to use for drawing
335      */
336     override void draw(const(Vertex)[] vertices, PrimitiveType type, RenderStates states = RenderStates.init)
337     {
338         import std.algorithm;
339 
340         sfRenderTexture_drawPrimitives(sfPtr, vertices.ptr, cast(uint)min(uint.max, vertices.length),type,states.blendMode.colorSrcFactor, states.blendMode.alphaDstFactor,
341             states.blendMode.colorEquation, states.blendMode.alphaSrcFactor, states.blendMode.alphaDstFactor, states.blendMode.alphaEquation,
342             states.transform.m_matrix.ptr, states.texture?states.texture.sfPtr:null, states.shader?states.shader.sfPtr:null);
343     }
344 
345     /**
346      * Restore the previously saved OpenGL render states and matrices.
347      *
348      * See the description of pushGLStates to get a detailed description of
349      * these functions.
350      */
351     void popGLStates()
352     {
353         sfRenderTexture_popGLStates(sfPtr);
354     }
355 
356     /**
357      * Save the current OpenGL render states and matrices.
358      *
359      * This function can be used when you mix SFML drawing and direct OpenGL
360      * rendering. Combined with PopGLStates, it ensures that:
361      * $(UL
362      * $(LI DSFML's internal states are not messed up by your OpenGL code)
363      * $(LI your OpenGL states are not modified by a call to an SFML function))
364      *
365      * $(PARA More specifically, it must be used around the code that calls
366      * `draw` functions.
367      *
368      * Note that this function is quite expensive: it saves all the possible
369 	 * OpenGL states and matrices, even the ones you don't care about.Therefore
370 	 * it should be used wisely. It is provided for convenience, but the best
371 	 * results will be achieved if you handle OpenGL states yourself (because
372 	 * you know which states have really changed, and need to be saved and
373 	 * restored). Take a look at the `resetGLStates` function if you do so.)
374      */
375     void pushGLStates()
376     {
377         sfRenderTexture_pushGLStates(sfPtr);
378     }
379 
380     /**
381      * Reset the internal OpenGL states so that the target is ready for drawing.
382      *
383      * This function can be used when you mix DSFML drawing and direct OpenGL
384      * rendering, if you choose not to use pushGLStates/popGLStates. It makes
385      * sure that all OpenGL states needed by DSFML are set, so that subsequent
386      * `draw()` calls will work as expected.
387      */
388     void resetGLStates()
389     {
390         sfRenderTexture_resetGLStates(sfPtr);
391     }
392 }
393 
394 unittest
395 {
396     version(DSFML_Unittest_Graphics)
397     {
398         import std.stdio;
399         import dsfml.graphics.sprite;
400 
401         writeln("Unit tests for RenderTexture");
402 
403         auto renderTexture = new RenderTexture();
404 
405         renderTexture.create(100,100);
406 
407         //doesn't need a texture for this unit test
408         Sprite testSprite = new Sprite();
409 
410         //clear before doing anything
411         renderTexture.clear();
412 
413         renderTexture.draw(testSprite);
414 
415         //prepare the RenderTexture for usage after drawing
416         renderTexture.display();
417 
418         //grab that texture for usage
419         auto texture = renderTexture.getTexture();
420 
421         writeln();
422     }
423 }
424 
425 package extern(C) struct sfRenderTexture;
426 
427 private extern(C):
428 
429 //Construct a new render texture
430 sfRenderTexture* sfRenderTexture_construct();
431 
432 //Construct a new render texture
433 bool sfRenderTexture_create(sfRenderTexture* renderTexture, uint width, uint height, bool depthBuffer);
434 
435 //Destroy an existing render texture
436 void sfRenderTexture_destroy(sfRenderTexture* renderTexture);
437 
438 //Get the size of the rendering region of a render texture
439 void sfRenderTexture_getSize(const sfRenderTexture* renderTexture, uint* x, uint* y);
440 
441 //Activate or deactivate a render texture as the current target for rendering
442 bool sfRenderTexture_setActive(sfRenderTexture* renderTexture, bool active);
443 
444 //Update the contents of the target texture
445 void sfRenderTexture_display(sfRenderTexture* renderTexture);
446 
447 //Clear the rendertexture with the given color
448 void sfRenderTexture_clear(sfRenderTexture* renderTexture, ubyte r, ubyte g, ubyte b, ubyte a);
449 
450 //Change the current active view of a render texture
451 void sfRenderTexture_setView(sfRenderTexture* renderTexture, float centerX, float centerY, float sizeX,
452                                                 float sizeY, float rotation, float viewportLeft, float viewportTop, float viewportWidth,
453                                                 float viewportHeight);
454 
455 //Get the current active view of a render texture
456 void sfRenderTexture_getView(const sfRenderTexture* renderTexture, float* centerX, float* centerY, float* sizeX,
457                                                 float* sizeY, float* rotation, float* viewportLeft, float* viewportTop, float* viewportWidth,
458                                                 float* viewportHeight);
459 
460 //Get the default view of a render texture
461 void sfRenderTexture_getDefaultView(const sfRenderTexture* renderTexture, float* centerX, float* centerY, float* sizeX,
462                                                 float* sizeY, float* rotation, float* viewportLeft, float* viewportTop, float* viewportWidth,
463                                                 float* viewportHeight);
464 
465 //Draw primitives defined by an array of vertices to a render texture
466 void sfRenderTexture_drawPrimitives(sfRenderTexture* renderTexture,  const void* vertices, uint vertexCount, int type, int colorSrcFactor, int colorDstFactor, int colorEquation,
467     int alphaSrcFactor, int alphaDstFactor, int alphaEquation, const float* transform, const sfTexture* texture, const sfShader* shader);
468 
469 //Save the current OpenGL render states and matrices
470 void sfRenderTexture_pushGLStates(sfRenderTexture* renderTexture);
471 
472 //Restore the previously saved OpenGL render states and matrices
473 void sfRenderTexture_popGLStates(sfRenderTexture* renderTexture);
474 
475 //Reset the internal OpenGL states so that the target is ready for drawing
476 void sfRenderTexture_resetGLStates(sfRenderTexture* renderTexture);
477 
478 //Get the target texture of a render texture
479 sfTexture* sfRenderTexture_getTexture(const sfRenderTexture* renderTexture);
480 
481 //Enable or disable the smooth filter on a render texture
482 void sfRenderTexture_setSmooth(sfRenderTexture* renderTexture, bool smooth);
483 
484 //Tell whether the smooth filter is enabled or not for a render texture
485 bool sfRenderTexture_isSmooth(const sfRenderTexture* renderTexture);