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 }