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