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 Sprite) is a drawable class that allows to easily display a texture (or a
27  * part of it) on a render target.
28  *
29  * It inherits all the functions from $(TRANSFORMABLE_LINK): position, rotation,
30  * scale, origin. It also adds sprite-specific properties such as the texture to
31  * use, the part of it to display, and some convenience functions to change the
32  * overall color of the sprite, or to get its bounding rectangle.
33  *
34  * $(U Sprite) works in combination with the $(TEXTURE_LINK) class, which loads
35  * and provides the pixel data of a given texture.
36  *
37  * The separation of $(U Sprite) and $(TEXTURE_LINK) allows more flexibility and
38  * better performances: indeed a $(TEXTURE_LINK) is a heavy resource, and any
39  * operation on it is slow (often too slow for real-time applications). On the
40  * other side, a $(U Sprite) is a lightweight object which can use the pixel
41  * data of a $(TEXTURE_LINK) and draw it with its own
42  * transformation/color/blending attributes.
43  *
44  * It is important to note that the $(U Sprite) instance doesn't copy the
45  * texture that it uses, it only keeps a reference to it. Thus, a
46  * $(TEXTURE_LINK) must not be destroyed while it is used by a $(U Sprite)
47  * (i.e. never write a function that uses a local Texture instance for creating
48  * a sprite).
49  *
50  * See also the note on coordinates and undistorted rendering in
51  * $(TRANSFORMABLE_LINK).
52  *
53  * example:
54  * ---
55  * // Declare and load a texture
56  * auto texture = new Texture();
57  * texture.loadFromFile("texture.png");
58  *
59  * // Create a sprite
60  * auto sprite = new Sprite();
61  * sprite.setTexture(texture);
62  * sprite.textureRect = IntRect(10, 10, 50, 30);
63  * sprite.color = Color(255, 255, 255, 200);
64  * sprite.position = Vector2f(100, 25);
65  *
66  * // Draw it
67  * window.draw(sprite);
68  * ---
69  *
70  * See_Also:
71  * $(TEXTURE_LINK), $(TRANSFORMABLE_LINK)
72  */
73 module dsfml.graphics.sprite;
74 
75 import dsfml.graphics.drawable;
76 import dsfml.graphics.transformable;
77 import dsfml.graphics.transform;
78 import dsfml.graphics.texture;
79 import dsfml.graphics.rect;
80 import dsfml.graphics.vertex;
81 
82 import dsfml.graphics.color;
83 import dsfml.graphics.rendertarget;
84 import dsfml.graphics.renderstates;
85 import dsfml.graphics.primitivetype;
86 
87 import dsfml.system.vector2;
88 import std.typecons:Rebindable;
89 
90 /**
91  * Drawable representation of a texture, with its own transformations, color,
92  * etc.
93  */
94 class Sprite : Drawable, Transformable
95 {
96     mixin NormalTransformable;
97 
98     private
99     {
100         Vertex[4] m_vertices;
101         Rebindable!(const(Texture)) m_texture;
102         IntRect m_textureRect;
103 
104     }
105 
106     /**
107      * Default constructor
108      *
109      * Creates an empty sprite with no source texture.
110      */
111     this()
112     {
113         m_texture = null;
114         m_textureRect = IntRect();
115     }
116 
117     /**
118      * Construct the sprite from a source texture
119      *
120      * Params:
121      * texture = Source texture
122      */
123     this(const(Texture) texture)
124     {
125         // Constructor code
126         m_textureRect = IntRect();
127         setTexture(texture);
128     }
129 
130     /// Destructor.
131     ~this()
132     {
133         import dsfml.system.config;
134         mixin(destructorOutput);
135     }
136 
137     @property
138     {
139         /**
140          * The sub-rectangle of the texture that the sprite will display.
141          *
142          * The texture rect is useful when you don't want to display the whole
143          * texture, but rather a part of it. By default, the texture rect covers
144          * the entire texture.
145          */
146         IntRect textureRect(IntRect rect)
147         {
148             if (rect != m_textureRect)
149             {
150                 m_textureRect = rect;
151                 updatePositions();
152                 updateTexCoords();
153             }
154             return rect;
155         }
156         /// ditto
157         IntRect textureRect()
158         {
159             return m_textureRect;
160         }
161     }
162 
163     @property
164     {
165         /**
166          * The global color of the sprite.
167          *
168          * This color is modulated (multiplied) with the sprite's texture. It can be
169          * used to colorize the sprite, or change its global opacity. By default,
170          * the sprite's color is opaque white.
171          */
172         Color color(Color newColor)
173         {
174             // Update the vertices' color
175             m_vertices[0].color = newColor;
176             m_vertices[1].color = newColor;
177             m_vertices[2].color = newColor;
178             m_vertices[3].color = newColor;
179             return newColor;
180         }
181         /// ditto
182         Color color()
183         {
184             return m_vertices[0].color;
185         }
186 
187     }
188 
189     /**
190      * Get the global bounding rectangle of the entity.
191      *
192      * The returned rectangle is in global coordinates, which means that it
193      * takes in account the transformations (translation, rotation, scale, ...)
194      * that are applied to the entity. In other words, this function returns the
195      * bounds of the sprite in the global 2D world's coordinate system.
196      *
197      * Returns: Global bounding rectangle of the entity.
198      */
199     FloatRect getGlobalBounds()
200     {
201         return getTransform().transformRect(getLocalBounds());
202     }
203 
204     /**
205      * Get the local bounding rectangle of the entity.
206      *
207      * The returned rectangle is in local coordinates, which means that it
208      * ignores the transformations (translation, rotation, scale, ...) that are
209      * applied to the entity. In other words, this function returns the bounds
210      * of the entity in the entity's coordinate system.
211      *
212      * Returns: Local bounding rectangle of the entity.
213      */
214     FloatRect getLocalBounds()
215     {
216         float width = (abs(m_textureRect.width));
217         float height = (abs(m_textureRect.height));
218         return FloatRect(0f, 0f, width, height);
219     }
220 
221     /**
222      * Get the source texture of the sprite.
223      *
224      * If the sprite has no source texture, a NULL pointer is returned. The
225      * returned pointer is const, which means that you can't modify the texture
226      * when you retrieve it with this function.
227      *
228      * Returns: The sprite's texture.
229      */
230     const(Texture) getTexture()
231     {
232         return m_texture;
233     }
234 
235     /**
236      * Change the source texture of the shape.
237      *
238      * The texture argument refers to a texture that must exist as long as the
239      * sprite uses it. Indeed, the sprite doesn't store its own copy of the
240      * texture, but rather keeps a pointer to the one that you passed to this
241      * function. If the source texture is destroyed and the sprite tries to use
242      * it, the behaviour is undefined. texture can be NULL to disable texturing.
243      *
244      * If resetRect is true, the TextureRect property of the sprite is
245      * automatically adjusted to the size of the new texture. If it is false,
246      * the texture rect is left unchanged.
247      *
248      * Params:
249      * 	texture	  = New texture
250      * 	rectReset = Should the texture rect be reset to the size of the new
251      *              texture?
252      */
253     void setTexture(const(Texture) texture, bool rectReset = false)
254     {
255         if(rectReset || ((m_texture is null) && (m_textureRect == IntRect())))
256         {
257             textureRect(IntRect(0,0,texture.getSize().x,texture.getSize().y));
258         }
259 
260         m_texture = texture;
261     }
262 
263     /**
264      * Draw the sprite to a render target.
265      *
266      * Params:
267      * 		renderTarget	= Target to draw to
268      * 		renderStates	= Current render states
269      */
270     override void draw(RenderTarget renderTarget, RenderStates renderStates)
271     {
272         if (m_texture)
273         {
274             renderStates.transform *= getTransform();
275             renderStates.texture = m_texture;
276             renderTarget.draw(m_vertices, PrimitiveType.Quads, renderStates);
277         }
278     }
279 
280     /**
281      * Create a new Sprite with the same data. Note that the texture is not
282      * copied, only its reference.
283      *
284      * Returns: A new Sprite object with the same data.
285      */
286     @property
287     Sprite dup() const
288     {
289         Sprite temp = new Sprite();
290         // properties from Transformable
291         temp.origin = origin;
292         temp.position = position;
293         temp.rotation = rotation;
294         temp.scale = scale;
295         // properties from Sprite:
296         temp.setTexture(m_texture);
297         temp.color = m_vertices[0].color;
298         temp.textureRect = m_textureRect;
299         return temp;
300     }
301 
302     //TODO: should these be protected?
303     void updatePositions()
304     {
305         FloatRect bounds = getLocalBounds();
306 
307         m_vertices[0].position = Vector2f(0, 0);
308         m_vertices[1].position = Vector2f(0, bounds.height);
309         m_vertices[2].position = Vector2f(bounds.width, bounds.height);
310         m_vertices[3].position = Vector2f(bounds.width, 0);
311     }
312 
313     void updateTexCoords()
314     {
315         float left = (m_textureRect.left);
316         float right = left + m_textureRect.width;
317         float top = (m_textureRect.top);
318         float bottom = top + m_textureRect.height;
319 
320         m_vertices[0].texCoords = Vector2f(left, top);
321         m_vertices[1].texCoords = Vector2f(left, bottom);
322         m_vertices[2].texCoords = Vector2f(right, bottom);
323         m_vertices[3].texCoords = Vector2f(right, top);
324     }
325 }
326 
327 unittest
328 {
329     version(DSFML_Unittest_Graphics)
330     {
331         import std.stdio;
332 
333         import dsfml.graphics.rendertexture;
334 
335         writeln("Unit test for Sprite");
336 
337         auto texture = new Texture();
338 
339         assert(texture.loadFromFile("res/TestImage.png"));
340 
341         auto sprite = new Sprite(texture);
342 
343 
344         auto renderTexture = new RenderTexture();
345 
346         renderTexture.create(100,100);
347 
348         renderTexture.clear();
349 
350         renderTexture.draw(sprite);
351 
352         renderTexture.display();
353 
354         writeln();
355     }
356 }