1 /** 2 Copyright: Copyright (c) 2016-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.graphics.batch2d; 7 8 import voxelman.container.buffer; 9 import voxelman.model.vertex; 10 import voxelman.math; 11 import voxelman.graphics; 12 13 alias ColoredVertex2d = VertexPosColor!(float, 2, ubyte, 4); 14 15 struct Batch2d 16 { 17 Buffer!ColoredVertex2d triBuffer; 18 Buffer!ColoredVertex2d lineBuffer; 19 Buffer!ColoredVertex2d pointBuffer; 20 21 void putRect(vec2 pos, vec2 size, Color4ub color, bool fill) 22 { 23 enum vec2 offset = vec2(0, 0);// 0.375 24 if (fill) { 25 triBuffer.put( 26 ColoredVertex2d(offset + pos, color), 27 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y), color), 28 ColoredVertex2d(offset + vec2(pos.x, pos.y + size.y), color), 29 ColoredVertex2d(offset + vec2(pos.x, pos.y + size.y), color), 30 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y), color), 31 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y + size.y), color)); 32 } else { 33 lineBuffer.put( 34 ColoredVertex2d(offset + pos, color), 35 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y), color), 36 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y), color), 37 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y + size.y), color), 38 ColoredVertex2d(offset + vec2(pos.x + size.x, pos.y + size.y), color), 39 ColoredVertex2d(offset + vec2(pos.x, pos.y + size.y), color), 40 ColoredVertex2d(offset + vec2(pos.x, pos.y + size.y), color), 41 ColoredVertex2d(offset + vec2(pos.x, pos.y - 1), color)); 42 } 43 } 44 45 void putLine(vec2 start, vec2 end, Color4ub color) 46 { 47 lineBuffer.put( 48 ColoredVertex2d(start, color), 49 ColoredVertex2d(end, color)); 50 } 51 52 void putPoint(vec2 pos, Color4ub color) 53 { 54 pointBuffer.put(ColoredVertex2d(pos, color)); 55 } 56 57 void reset() 58 { 59 triBuffer.clear(); 60 lineBuffer.clear(); 61 pointBuffer.clear(); 62 } 63 } 64 65 enum CommandType { 66 batch, 67 clipRect 68 } 69 70 struct Command 71 { 72 this(Texture texture, size_t numVertices) { 73 type = CommandType.batch; 74 this.texture = texture; 75 this.numVertices = numVertices; 76 } 77 this(irect clipRect) { 78 type = CommandType.clipRect; 79 this.clipRect = clipRect; 80 } 81 CommandType type; 82 union 83 { 84 struct // CommandType.batch 85 { 86 Texture texture; 87 size_t numVertices; 88 } 89 struct // CommandType.clipRect 90 { 91 irect clipRect; 92 } 93 } 94 } 95 96 struct BatchRenderCommand 97 { 98 Texture texture; 99 size_t numVertices; 100 } 101 102 struct ClipRectCommand 103 { 104 irect rect; 105 } 106 107 import voxelman.model.vertex; 108 alias UvColVertex2d = VertexPosUvColor!(float, 3, float, 2, ubyte, 4); 109 struct TexturedBatch2d 110 { 111 Buffer!UvColVertex2d buffer; 112 Buffer!Command commands; 113 Buffer!irect rectStack; 114 private bool isCurrentClipRectEmpty; 115 116 void putRect(frect target, frect source, float depth, Color4ub color, Texture tex) 117 { 118 auto pos0 = vec3(target.position.x, target.position.y, depth); 119 auto pos1 = vec3(target.position.x, target.position.y + target.size.y, depth); 120 auto pos2 = vec3(target.position.x + target.size.x, target.position.y + target.size.y, depth); 121 auto pos3 = vec3(target.position.x + target.size.x, target.position.y, depth); 122 123 auto tex0 = source.position; 124 auto tex1 = vec2(source.position.x, source.position.y + source.size.y); 125 auto tex2 = vec2(source.position.x + source.size.x, source.position.y + source.size.y); 126 auto tex3 = vec2(source.position.x + source.size.x, source.position.y); 127 128 auto vert0 = UvColVertex2d(pos0, tex0, color); 129 auto vert1 = UvColVertex2d(pos1, tex1, color); 130 auto vert2 = UvColVertex2d(pos2, tex2, color); 131 auto vert3 = UvColVertex2d(pos3, tex3, color); 132 133 buffer.put(vert0, vert3, vert1, vert1, vert2, vert3); 134 putNVerticies(6, tex); 135 } 136 137 void addOffsetToLastRects(vec2 offset, size_t numLastRects) 138 { 139 addOffsetToLastVerticies(offset, numLastRects * 6); 140 } 141 142 void addOffsetToLastVerticies(vec2 offset, size_t numLastVerticies) 143 { 144 vec3 effectiveOffset = vec3(offset.x, offset.y, 0); 145 foreach (ref vert; buffer.data[$-numLastVerticies..$]) 146 { 147 vert.position += effectiveOffset; 148 } 149 } 150 151 void pushClipRect(irect rect) 152 { 153 rectStack.put(rect); 154 if (!rectStack.empty) 155 { 156 rect = rectIntersection(rect, rectStack.back); 157 } 158 setClipRect(rect); 159 } 160 161 void popClipRect() 162 { 163 // there must be always rect in the stack since we pushed one after reset 164 rectStack.unput(1); 165 irect rect = rectStack.data[$-1]; 166 setClipRect(rect); 167 } 168 169 void setClipRect(irect rect) 170 { 171 assert(rect.width >= 0, "width is negative"); 172 assert(rect.height >= 0, "height is negative"); 173 isCurrentClipRectEmpty = rect.empty; 174 if (commands.data.length) 175 { 176 Command* command = &commands.data[$-1]; 177 178 // replace clip rect of last command if it sets clip rect 179 if (command.type == CommandType.clipRect) 180 { 181 command.clipRect = rect; 182 return; 183 } 184 } 185 commands.put(Command(rect)); 186 } 187 188 private void putNVerticies(size_t verticies, Texture tex) 189 { 190 if (isCurrentClipRectEmpty) return; 191 if (commands.data.length) 192 { 193 Command* command = &commands.data[$-1]; 194 195 // append vertices to the last command if texture is teh same 196 if (command.type == CommandType.batch && command.texture == tex) 197 { 198 command.numVertices += verticies; 199 return; 200 } 201 } 202 commands.put(Command(tex, verticies)); 203 } 204 205 void reset(irect initialClipRect) 206 { 207 buffer.clear(); 208 commands.clear(); 209 rectStack.clear(); 210 pushClipRect(initialClipRect); 211 } 212 }