1 module prova.graphics.sprites.spritebatch;
2 
3 import prova.graphics,
4        prova.math,
5        prova.util,
6        std.typecons;
7 
8 ///
9 final class SpriteBatch
10 {
11   static ShaderProgram defaultShaderProgram;
12   /// Copy of defaultShaderProgram
13   ShaderProgram shaderProgram;
14   private bool begun;
15   private Mesh mesh;
16   private RenderTarget renderTarget;
17   private Matrix projection;
18   private LinkedList!(Tuple!(Sprite, Matrix)) sprites;
19 
20   ///
21   this()
22   {
23     if(!defaultShaderProgram)
24       defaultShaderProgram = new SpriteShaderProgram();
25 
26     shaderProgram = defaultShaderProgram;
27 
28     sprites = new LinkedList!(Tuple!(Sprite, Matrix));
29 
30     mesh = new SpriteMesh();
31   }
32 
33   ///
34   void begin(RenderTarget renderTarget, Matrix projection)
35   {
36     if(begun)
37       throw new Exception("Batch already started");
38 
39     this.renderTarget = renderTarget;
40     this.projection = projection;
41     begun = true;
42   }
43 
44   ///
45   void batchSprite(AnimatedSprite sprite, Matrix transform)
46   {
47     sprite.update();
48 
49     batchSprite(cast(Sprite) sprite, transform);
50   }
51 
52   ///
53   void batchSprite(Sprite sprite, Matrix transform)
54   {
55     if(!begun)
56       throw new Exception("Batch not started");
57     
58     sprites.insertBack(tuple(sprite, transform));
59   }
60 
61   /// Draws batched sprites
62   void end()
63   {
64     if(!begun)
65       throw new Exception("Batch not started");
66 
67     Color lastTint;
68     uint lastTexture = -1;
69 
70     // set the inital value for the tint
71     shaderProgram.setVector4("tint", lastTint);
72 
73     foreach(Tuple!(Sprite, Matrix) spriteTuple; sprites) {
74       Sprite sprite = spriteTuple[0];
75       Matrix transform = spriteTuple[1];
76 
77       if(sprite.texture.id != lastTexture) {
78         shaderProgram.setTexture(0, sprite.texture);
79         lastTexture = sprite.texture.id;
80       }
81 
82       if(sprite.tint != lastTint) {
83         shaderProgram.setVector4("tint", sprite.tint);
84         lastTint = sprite.tint;
85       }
86       
87       drawSprite(sprite, transform);
88     }
89 
90     sprites.clear();
91     begun = false;
92   }
93 
94   ///
95   void drawSprite(Sprite sprite, Matrix transform)
96   {
97     Vector2 size = sprite.clip.getSize();
98     Vector3 center = size / 2;
99 
100     Rect clip;
101     clip.left = sprite.clip.left / sprite.texture.width;
102     clip.top = 1 - (sprite.clip.top + sprite.clip.height) / sprite.texture.height;
103     clip.width = sprite.clip.width / sprite.texture.width;
104     clip.height = sprite.clip.height / sprite.texture.height;
105 
106     Matrix offset = Matrix.identity;
107     offset = offset.scale(size);
108     offset = offset.translate(-sprite.origin - center);
109 
110     shaderProgram.setMatrix("transform", projection * (transform * offset));
111     shaderProgram.setVector4("clip", clip);
112     shaderProgram.drawMesh(mesh, renderTarget, DrawMode.TRIANGLE_FAN);
113   }
114 }