1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module atelier.render.sprite; 7 8 import std.conv; 9 10 import bindbc.sdl, bindbc.sdl.image; 11 12 import atelier.core; 13 import atelier.render.window; 14 import atelier.render.drawable; 15 import atelier.render.drawable; 16 17 /// Renders a **Texture** with its own properties. 18 final class Sprite { 19 @property { 20 /// Is the drawable loaded ? 21 bool isValid() const { 22 return drawable !is null; 23 } 24 25 /// Get the anchored and scaled center of the sprite. 26 Vec2f center() const { 27 return anchor * size * scale; 28 } 29 } 30 31 /// Texture being used. 32 Drawable drawable; 33 34 /// Mirroring property. 35 Flip flip = Flip.none; 36 37 /// Scale of the sprite. 38 Vec2f scale = Vec2f.one; 39 40 /// Size of the sprite. 41 Vec2f size = Vec2f.zero; 42 43 /// Relative center of the sprite. 44 Vec2f anchor = Vec2f.half; 45 46 /// Texture region being rendered (the source size). 47 Vec4i clip; 48 49 /// Angle in which the sprite will be rendered. 50 float angle = 0f; 51 52 /// Color added to the sprite. 53 Color color = Color.white; 54 55 /// Alpha 56 float alpha = 1f; 57 58 /// Blending algorithm. 59 Blend blend = Blend.alpha; 60 61 /// Default ctor. 62 this() { 63 } 64 65 /// Copy another sprite. 66 this(Sprite sprite) { 67 drawable = sprite.drawable; 68 flip = sprite.flip; 69 scale = sprite.scale; 70 size = sprite.size; 71 anchor = sprite.anchor; 72 clip = sprite.clip; 73 angle = sprite.angle; 74 color = sprite.color; 75 alpha = sprite.alpha; 76 blend = sprite.blend; 77 } 78 79 /// Default sprite that takes the whole Texture. 80 this(Drawable drawable_, Flip flip_ = Flip.none) { 81 drawable = drawable_; 82 if (!drawable) { 83 clip = Vec4i.zero; 84 size = Vec2f.zero; 85 } 86 else { 87 clip = Vec4i(0, 0, drawable.width, drawable.height); 88 size = to!Vec2f(clip.zw); 89 } 90 flip = flip_; 91 } 92 93 /// Sprite that takes a clipped region of a Texture. 94 this(Drawable drawable_, Vec4i clip_, Flip flip_ = Flip.none) { 95 drawable = drawable_; 96 clip = clip_; 97 size = to!Vec2f(clip.zw); 98 flip = flip_; 99 } 100 101 /// Reset the sprite to take the whole specified Texture. 102 Sprite opAssign(Drawable drawable_) { 103 drawable = drawable_; 104 if (!drawable) { 105 clip = Vec4i.zero; 106 size = Vec2f.zero; 107 } 108 else { 109 clip = Vec4i(0, 0, drawable.width, drawable.height); 110 size = to!Vec2f(clip.zw); 111 } 112 return this; 113 } 114 115 /// Set the sprite's size to fit inside the specified size. 116 void fit(Vec2f size_) { 117 size = to!Vec2f(clip.zw).fit(size_); 118 } 119 120 /// Set the sprite's size to contain the specified size. 121 void contain(Vec2f size_) { 122 size = to!Vec2f(clip.zw).contain(size_); 123 } 124 125 /// Render the sprite there. 126 void draw(const Vec2f position) { 127 assert(drawable, "Texture is null"); 128 Vec2f finalSize = size * scale * transformScale(); 129 //if (isVisible(position, finalSize)) { 130 drawable.color = color; 131 drawable.blend = blend; 132 drawable.alpha = alpha; 133 drawable.draw(transformRenderSpace(position), finalSize, clip, angle, flip, anchor); 134 //} 135 } 136 137 /// Ditto 138 void drawUnchecked(const Vec2f position) { 139 assert(drawable, "Texture is null"); 140 Vec2f finalSize = size * scale * transformScale(); 141 drawable.color = color; 142 drawable.blend = blend; 143 drawable.alpha = alpha; 144 drawable.draw(transformRenderSpace(position), finalSize, clip, angle, flip, anchor); 145 } 146 147 /// Ditto 148 void drawRotated(const Vec2f position) { 149 assert(drawable, "Texture is null"); 150 Vec2f finalSize = size * scale * transformScale(); 151 Vec2f dist = (anchor - Vec2f.half) * size * scale; 152 dist.rotate(angle); 153 drawable.color = color; 154 drawable.blend = blend; 155 drawable.alpha = alpha; 156 drawable.draw(transformRenderSpace(position - dist), finalSize, clip, angle, flip); 157 } 158 159 /// Ditto 160 void draw(const Vec2f pivot, float pivotDistance, float pivotAngle) { 161 assert(drawable, "Texture is null"); 162 Vec2f finalSize = size * scale * transformScale(); 163 drawable.color = color; 164 drawable.blend = blend; 165 drawable.alpha = alpha; 166 drawable.draw(transformRenderSpace(pivot + Vec2f.angled(pivotAngle) * pivotDistance), 167 finalSize, clip, angle, flip, anchor); 168 } 169 170 /// Ditto 171 void draw(const Vec2f pivot, const Vec2f pivotOffset, float pivotAngle) { 172 assert(drawable, "Texture is null"); 173 Vec2f finalSize = size * scale * transformScale(); 174 drawable.color = color; 175 drawable.blend = blend; 176 drawable.alpha = alpha; 177 drawable.draw(transformRenderSpace(pivot + pivotOffset.rotated(pivotAngle)), 178 finalSize, clip, angle, flip, anchor); 179 } 180 181 /// Is this inside the sprite region ? \ 182 /// Note: Does not take angle into account. may not work properly. 183 bool isInside(const Vec2f position) const { 184 Vec2f halfSize = size * scale * transformScale() * 0.5f; 185 return position.isBetween(-halfSize, halfSize); 186 } 187 }