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 /++ 37 + Drawable representation of a texture, with its own transformations, color, etc. 38 + 39 + Sprite is a drawable class that allows to easily display a texture (or a part of it) on a render target. 40 + 41 + 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. 42 + 43 + Sprite works in combination with the Texture class, which loads and provides the pixel data of a given texture. 44 + 45 + 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. 46 + 47 + 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). 48 + 49 + Authors: Laurent Gomila, Jeremy DeHaan 50 + See_Also: http://sfml-dev.org/documentation/2.0/classsf_1_1Sprite.php#details 51 +/ 52 class Sprite : Drawable, Transformable 53 { 54 mixin NormalTransformable; 55 56 private 57 { 58 Vertex[4] m_vertices; 59 Rebindable!(const(Texture)) m_texture; 60 IntRect m_textureRect; 61 62 } 63 64 this() 65 { 66 m_texture = null; 67 m_textureRect = IntRect(); 68 } 69 70 this(const(Texture) texture) 71 { 72 // Constructor code 73 m_textureRect = IntRect(); 74 setTexture(texture); 75 } 76 77 ~this() 78 { 79 import dsfml.system.config; 80 mixin(destructorOutput); 81 } 82 83 /** 84 * The sub-rectangle of the texture that the sprite will display. 85 * 86 * 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. 87 */ 88 @property 89 { 90 IntRect textureRect(IntRect rect) 91 { 92 if (rect != m_textureRect) 93 { 94 m_textureRect = rect; 95 updatePositions(); 96 updateTexCoords(); 97 } 98 return rect; 99 } 100 IntRect textureRect() 101 { 102 return m_textureRect; 103 } 104 } 105 106 /** 107 * The global color of the sprite. 108 * 109 * 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. 110 */ 111 @property 112 { 113 Color color(Color newColor) 114 { 115 // Update the vertices' color 116 m_vertices[0].color = newColor; 117 m_vertices[1].color = newColor; 118 m_vertices[2].color = newColor; 119 m_vertices[3].color = newColor; 120 return newColor; 121 } 122 Color color() 123 { 124 return m_vertices[0].color; 125 } 126 127 } 128 129 /** 130 * Get the global bounding rectangle of the entity. 131 * 132 * 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. 133 * 134 * Returns: Global bounding rectangle of the entity 135 */ 136 FloatRect getGlobalBounds() 137 { 138 return getTransform().transformRect(getLocalBounds()); 139 } 140 141 /** 142 * Get the local bounding rectangle of the entity. 143 * 144 * 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. 145 * 146 * Returns: Local bounding rectangle of the entity 147 */ 148 FloatRect getLocalBounds() 149 { 150 float width = (abs(m_textureRect.width)); 151 float height = (abs(m_textureRect.height)); 152 return FloatRect(0f, 0f, width, height); 153 } 154 155 /** 156 * Get the source texture of the sprite. 157 * 158 * 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. 159 * 160 * Returns: The sprite's texture 161 */ 162 const(Texture) getTexture() 163 { 164 return m_texture; 165 } 166 167 /** 168 * Change the source texture of the shape. 169 * 170 * 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. 171 * 172 * 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. 173 * 174 * Params: 175 * texture = New texture 176 * resetRect = Should the texture rect be reset to the size of the new texture? 177 */ 178 void setTexture(const(Texture) texture, bool rectReset = false) 179 { 180 if(rectReset || ((m_texture is null) && (m_textureRect == IntRect()))) 181 { 182 textureRect(IntRect(0,0,texture.getSize().x,texture.getSize().y)); 183 } 184 185 m_texture = texture; 186 } 187 188 /** 189 * Draw the sprite to a render target. 190 * 191 * Params: 192 * renderTarget = Target to draw to 193 * renderStates = Current render states 194 */ 195 override void draw(RenderTarget renderTarget, RenderStates renderStates) 196 { 197 if (m_texture) 198 { 199 renderStates.transform *= getTransform(); 200 renderStates.texture = m_texture; 201 renderTarget.draw(m_vertices, PrimitiveType.Quads, renderStates); 202 } 203 } 204 205 /** 206 * Create a new Sprite with the same data. Note that the texture is not copied, only its reference. 207 * 208 * Returns: A new Sprite object with the same data. 209 */ 210 @property 211 Sprite dup() const 212 { 213 Sprite temp = new Sprite(); 214 // properties from Transformable 215 temp.origin = origin; 216 temp.position = position; 217 temp.rotation = rotation; 218 temp.scale = scale; 219 // properties from Sprite: 220 temp.setTexture(m_texture); 221 temp.color = m_vertices[0].color; 222 temp.textureRect = m_textureRect; 223 return temp; 224 } 225 226 //TODO: should these be protected? 227 void updatePositions() 228 { 229 FloatRect bounds = getLocalBounds(); 230 231 m_vertices[0].position = Vector2f(0, 0); 232 m_vertices[1].position = Vector2f(0, bounds.height); 233 m_vertices[2].position = Vector2f(bounds.width, bounds.height); 234 m_vertices[3].position = Vector2f(bounds.width, 0); 235 } 236 237 void updateTexCoords() 238 { 239 float left = (m_textureRect.left); 240 float right = left + m_textureRect.width; 241 float top = (m_textureRect.top); 242 float bottom = top + m_textureRect.height; 243 244 m_vertices[0].texCoords = Vector2f(left, top); 245 m_vertices[1].texCoords = Vector2f(left, bottom); 246 m_vertices[2].texCoords = Vector2f(right, bottom); 247 m_vertices[3].texCoords = Vector2f(right, top); 248 } 249 250 } 251 252 unittest 253 { 254 version(DSFML_Unittest_Graphics) 255 { 256 import std.stdio; 257 258 import dsfml.graphics.rendertexture; 259 260 writeln("Unit test for Sprite"); 261 262 auto texture = new Texture(); 263 264 assert(texture.loadFromFile("res/TestImage.png")); 265 266 auto sprite = new Sprite(texture); 267 268 269 auto renderTexture = new RenderTexture(); 270 271 renderTexture.create(100,100); 272 273 renderTexture.clear(); 274 275 renderTexture.draw(sprite); 276 277 renderTexture.display(); 278 279 writeln(); 280 } 281 }