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, &currentCenter.x, &currentCenter.y, &currentSize.x, &currentSize.y, &currentRotation,
204                                     &currentViewport.left, &currentViewport.top, &currentViewport.width, &currentViewport.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, &currentCenter.x, &currentCenter.y, &currentSize.x, &currentSize.y, &currentRotation,
232                                 &currentViewport.left, &currentViewport.top, &currentViewport.width, &currentViewport.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();