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