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 }