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 }