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 }