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 }