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