1 module evael.graphics.Spritesheet; 2 3 import std.typecons; 4 import std.math; 5 6 import evael.graphics.GL; 7 8 import evael.graphics.GraphicsDevice; 9 import evael.graphics.Texture; 10 import evael.graphics.Vertex; 11 import evael.graphics.shapes.Quad; 12 import evael.graphics.models.Animation; 13 14 import evael.utils.Math; 15 import evael.utils.Color; 16 import evael.utils.Size; 17 import evael.utils.Rectangle; 18 19 import dnogc.DynamicArray; 20 import dnogc.Utils; 21 22 /** 23 * Spritesheet 24 * Draw animated sprite. 25 */ 26 class Spritesheet : Quad!Vertex2PositionColorTexture 27 { 28 /// Sprite size 29 private Size!int m_spriteSize; 30 31 private vec2 m_currentSpritePosition; 32 33 private Animation[string] m_animations; 34 35 private Animation m_currentAnimation; 36 private float m_currentAnimationFrame; 37 38 /** 39 * Spritesheet constructor. 40 */ 41 public this()(GraphicsDevice graphics, Texture texture, in auto ref Size!int spriteSize) 42 { 43 this.m_texture = texture; 44 this.m_spriteSize = spriteSize; 45 this.m_currentSpritePosition = vec2(0, 0); 46 this.m_currentAnimationFrame = 0.0f; 47 48 immutable float v = 1.0f / this.m_texture.size.width; 49 50 super( 51 graphics, 52 [ 53 // Bottom-left vertex 54 Vertex2PositionColorTexture(vec2(0, 0), Color.White, vec2(0, (this.m_currentSpritePosition.y + spriteSize.height) * v)), 55 // Top-left vertex 56 Vertex2PositionColorTexture(vec2(0, spriteSize.height), Color.White, vec2(0, 0)), 57 // Bottom-right vertex 58 Vertex2PositionColorTexture(vec2(spriteSize.width, 0), Color.White, vec2((this.m_currentSpritePosition.x + spriteSize.width) * v, (this.m_currentSpritePosition.y + spriteSize.height) * v)), 59 // Top-right vertex 60 Vertex2PositionColorTexture(vec2(spriteSize.width, spriteSize.height), Color.White, vec2((this.m_currentSpritePosition.x + spriteSize.width) * v, 0)) 61 ] 62 , 63 [0, 1, 2, 2, 1, 3] 64 ); 65 } 66 67 public override void draw(in float deltaTime, mat4 view, mat4 projection) 68 { 69 if (this.m_animations.length) 70 { 71 this.update(deltaTime); 72 } 73 74 super.draw(deltaTime, view, projection); 75 } 76 77 public void update(in float deltaTime) 78 { 79 this.m_currentAnimationFrame += this.m_currentAnimation.speed * deltaTime; 80 81 if (this.m_currentAnimationFrame >= this.m_currentAnimation.endFrame) 82 { 83 this.m_currentAnimationFrame = this.m_currentAnimation.startFrame; 84 } 85 86 auto ss = this.m_spriteSize; 87 auto frame = cast(int)this.m_currentAnimationFrame - 1; 88 89 auto framePerWidth = this.m_texture.size.width / cast(float)ss.width; 90 91 auto x = frame % framePerWidth; 92 auto y = floor(frame / framePerWidth); 93 94 this.m_currentSpritePosition.x = x * ss.width; 95 this.m_currentSpritePosition.y = y * ss.height; 96 97 immutable float v = 1.0f / this.m_texture.size.width; 98 99 auto vertices = 100 [ 101 // Bottom-left vertex 102 Vertex2PositionColorTexture(vec2(0, 0), Color.White, 103 vec2(this.m_currentSpritePosition.x * v, (this.m_currentSpritePosition.y + ss.height) * v)), 104 105 // Top-left vertex 106 Vertex2PositionColorTexture(vec2(0, this.m_spriteSize.height), Color.White, 107 vec2(this.m_currentSpritePosition.x * v, this.m_currentSpritePosition.y * v)), 108 109 // Bottom-right vertex 110 Vertex2PositionColorTexture(vec2(this.m_spriteSize.width, 0), Color.White, 111 vec2((this.m_currentSpritePosition.x + ss.width) * v, (this.m_currentSpritePosition.y + ss.height) * v)), 112 113 // Top-right vertex 114 Vertex2PositionColorTexture(vec2(this.m_spriteSize.width, this.m_spriteSize.height), Color.White, 115 vec2((this.m_currentSpritePosition.x + ss.width) * v, this.m_currentSpritePosition.y * v)) 116 ]; 117 118 this.m_graphicsDevice.sendVertexBufferData(this.m_vertexBuffer, 0, Vertex2PositionColorTexture.sizeof * vertices.length, vertices.ptr); 119 } 120 121 /** 122 * Adds animation. 123 * Params: 124 * name: animation name 125 * animation : animation 126 */ 127 public void addAnimation()(in string name, in auto ref Animation animation) 128 { 129 this.m_animations[name] = animation; 130 } 131 132 /** 133 * Sets current animation. 134 * Params: 135 * name: animation name 136 */ 137 public void setAnimation(in string name) 138 { 139 assert(name in this.m_animations); 140 this.m_currentAnimation = this.m_animations[name]; 141 this.m_currentAnimationFrame = this.m_currentAnimation.startFrame; 142 } 143 144 /** 145 * Sets current sprite texture coords. 146 * Params: 147 * rect : texture rect 148 */ 149 public void setTextureCoords()(in auto ref Rectangle!float rect) 150 { 151 immutable float v = 1.0f / this.m_texture.size.width; 152 153 auto vertices = 154 [ 155 // Bottom-left vertex 156 Vertex2PositionColorTexture(vec2(0, 0), Color.White, vec2(rect.left * v, rect.top * v)), 157 158 // Top-left vertex 159 Vertex2PositionColorTexture(vec2(0, rect.size.height), Color.White, vec2(rect.left * v, rect.bottom * v)), 160 161 // Bottom-right vertex 162 Vertex2PositionColorTexture(vec2(rect.size.width, 0), Color.White, vec2(rect.right * v, rect.top * v)), 163 164 // Top-right vertex 165 Vertex2PositionColorTexture(vec2(rect.size.width, rect.size.height), Color.White, vec2(rect.right * v, rect.bottom * v)) 166 ]; 167 168 this.m_graphicsDevice.sendVertexBufferData(this.m_vertexBuffer, 0, Vertex2PositionColorTexture.sizeof * vertices.length, vertices.ptr); 169 } 170 171 @nogc @safe 172 @property pure nothrow 173 { 174 175 } 176 }