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