1 module ws.gl.batch;
2 
3 import
4 	std.algorithm,
5 	std.conv,
6 	ws.gl.gl,
7 	ws.gl.context,
8 	ws.exception,
9 	ws.io;
10 
11 __gshared:
12 
13 
14 struct Layout {
15 	uint target;
16 	uint size;
17 }
18 
19 
20 class Batch {
21 
22 	enum tex2 = [Layout(gl.attributeTexture, 2)];
23 	enum vert3 = [Layout(gl.attributeVertex, 3)];
24 	enum color4 = [Layout(gl.attributeColor, 4)];
25 	enum normal3 = [Layout(gl.attributeNormal, 3)];
26 
27 	alias float[2] Tex;
28 	alias float[3] Vec;
29 	alias float[4] Color;
30 	
31 	this(GlContext context, uint type, Layout[] layout, float[] data){
32 		this.context = context;
33 		uint line;
34 		foreach(l; layout)
35 			line += l.size;
36 		assert(data.length % line == 0, "Batch data is not divisible by row length");
37 		begin(cast(uint)(data.length/line), type);
38 		foreach(i; 0..vertexCount){
39 			auto d = data[i*line .. (i+1)*line];
40 			int consumed = 0;
41 			foreach(l; layout){
42 				add(l.target, d[consumed..consumed+l.size]);
43 				consumed += l.size;
44 			}
45 			currentVert++;
46 		}
47 		finish;
48 	}
49 
50 	void begin(int verts, uint type = GL_TRIANGLES){
51 		vertexCount = verts;
52 		currentVert = 0;
53 		this.type = type;
54 		context.genVertexArrays(1, cast(uint*)&vao);
55 		context.bindVertexArray(vao);
56 	}
57 	
58 	void finish(){
59 		assert(!done);
60 		foreach(array; arrays){
61 			context.bindBuffer(GL_ARRAY_BUFFER, array.globj);
62 			context.unmapBuffer(GL_ARRAY_BUFFER);
63 		}
64 
65 		context.bindVertexArray(vao);
66 
67 		foreach(array; arrays){
68 			context.bindBuffer(GL_ARRAY_BUFFER, array.globj);
69 			context.enableVertexAttribArray(array.attributeId),
70 			context.vertexAttribPointer(array.attributeId, array.size, GL_FLOAT, GL_FALSE, 0, null);
71 		}
72 
73 		done = true;
74 		context.bindVertexArray(0);
75 	}
76 	
77 	void draw(){
78 		if(!done)
79 			return;
80 		context.bindVertexArray(vao);
81 		context.drawArrays(type, 0, vertexCount);
82 		//glBindVertexArray(0);
83 	}
84 
85 	void add(Vec pos){
86 		add(gl.attributeVertex, pos[]);
87 		currentVert++;
88 	}
89 
90 	void addPoint(Vec pos, Color col){
91 		add(gl.attributeVertex, pos[]);
92 		add(gl.attributeColor, col);
93 		currentVert++;
94 	}
95 
96 	void addPoint(Vec pos, Vec normal){
97 		add(gl.attributeVertex, pos[]);
98 		add(gl.attributeNormal, normal[]);
99 		currentVert++;
100 	}
101 
102 	void addPoint(Vec pos, Vec normal, Color col){
103 		add(gl.attributeVertex, pos[]);
104 		add(gl.attributeNormal, normal[]);
105 		add(gl.attributeColor, col);
106 		currentVert++;
107 	}
108 
109 	void addPoint(Vec pos, Tex t){
110 		add(gl.attributeVertex, pos[]);
111 		add(gl.attributeTexture, t);
112 		currentVert++;
113 	}
114 
115 	void addPoint(Vec pos, Vec normal, Tex t){
116 		add(gl.attributeVertex, pos[]);
117 		add(gl.attributeNormal, normal[]);
118 		add(gl.attributeTexture, t);
119 		currentVert++;
120 	}
121 
122 	/*~this(){
123 		glDeleteVertexArrays(1, &vao);
124 	}*/
125 	
126 	void updateVertices(float[] data, size_t pos = 0, size_t length = 1){
127 		context.bindBuffer(GL_ARRAY_BUFFER, arrays[gl.attributeVertex].globj);
128 		context.bufferSubData(GL_ARRAY_BUFFER, pos*3*float.sizeof, length*3*float.sizeof, data.ptr);
129 	} 
130 	
131 	protected:
132 		
133 		GlContext context;
134 
135 		uint vao;
136 		uint type;
137 		
138 		bool done = false;
139 		int vertexCount = 0;
140 		int currentVert = 0;
141 		
142 		class Array {
143 			this(uint size, uint attributeId){
144 				assert(!done);
145 				assert(vao);
146 				this.size = size;
147 				this.attributeId = attributeId;
148 				context.genBuffers(1, cast(uint*)&globj);
149 				context.bindBuffer(GL_ARRAY_BUFFER, globj);
150 				context.bufferData(GL_ARRAY_BUFFER, float.sizeof*size*vertexCount, null, GL_DYNAMIC_DRAW);
151 				context.bindBuffer(GL_ARRAY_BUFFER, globj);
152 				array = cast(float*)context.mapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
153 				arrays[attributeId] = this;
154 				if(!array)
155 					exception("Failed to create array buffer");
156 			}
157 			float* array = null;
158 			uint globj = 0;
159 			uint size;
160 			uint attributeId;
161 			/*~this(){
162 				glDeleteBuffers(1, &globj);
163 			}*/
164 		}
165 		
166 		Array[uint] arrays;
167 	
168 		void add(uint target, float[] data){
169 			if(target !in arrays)
170 				arrays[target] = new Array(data.length.to!uint, target);
171 			float* o = arrays[target].array+currentVert*data.length;
172 			o[0..data.length] = data;
173 		}
174 
175 }