1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.System.VertexBufferObject;
25 
26 private {
27 	import derelict.opengl3.gl;
28 	
29 	import Dgame.Internal.Log;
30 	import Dgame.Internal.core;
31 	import Dgame.System.VertexRenderer;
32 	import Dgame.Graphics.Texture;
33 	import Dgame.Graphics.Shape;
34 }
35 
36 public import Dgame.System.VertexRenderer : Target;
37 
38 /**
39  * Usage methods
40  */
41 final abstract class Usage {
42 public:
43 	/**
44 	 * Stream usage
45 	 */
46 	enum Stream {
47 		/** 
48 		 * The contents of the data memory is determined once by the application
49 		 * and rarely used as the source for GL rendering command.
50 		 */
51 		Draw = GL_STREAM_DRAW,
52 		/**
53 		 * The contents of the data memory is determined once for reading data 
54 		 * and rarely queried by the application.
55 		 */
56 		Read = GL_STREAM_READ,
57 		/**
58 		 * The contents of the data memory is determined once for reading data 
59 		 * and rarely used as the source for GL rendering command.
60 		 */
61 		Copy = GL_STREAM_COPY,
62 	}
63 	
64 	/**
65 	 * Static usage.
66 	 */
67 	enum Static {
68 		/**
69 		 * The contents of the data memory is determined once by the application 
70 		 * and often used as a source for a GL rendering command.
71 		 */
72 		Draw = GL_STATIC_DRAW,
73 		/**
74 		 * The contents of the data memory is determined once for reading data 
75 		 * and often queried by the application.
76 		 */
77 		Read = GL_STATIC_READ,
78 		/**
79 		 * The contents of the data memory is determined once for reading data 
80 		 * and often used as a source for a GL rendering command.
81 		 */
82 		Copy = GL_STATIC_COPY,
83 	}
84 	
85 	/**
86 	 * Dynamic usage.
87 	 */
88 	enum Dynamic {
89 		/**
90 		 * The contents of the data memory is repeatedly determined by the application
91 		 * and often used as a source for a GL rendering command.
92 		 */
93 		Draw = GL_DYNAMIC_DRAW,
94 		/**
95 		 * The content of the data memory is repeatedly set for reading out data 
96 		 * and frequently requested by the application.
97 		 */
98 		Read = GL_DYNAMIC_READ,
99 		/**
100 		 * The contents of the data memory is set repeatedly for reading data 
101 		 * and often used as a source for a GL rendering command.
102 		 */
103 		Copy = GL_DYNAMIC_COPY,
104 	}
105 }
106 
107 /**
108  * Buffer is a object oriented wrapper for a Vertex Buffer Object.
109  * VertexRenderer is public imported. See there for more details, like PointerTarget.
110  *
111  * Author: rschuett
112  */
113 class VertexBufferObject {
114 	/**
115 	 * The access type.
116 	 */
117 	enum Access {
118 		Read  = GL_READ_ONLY,	/** Read only. */
119 		Write = GL_WRITE_ONLY,	/** Write only. */
120 		ReadWrite = GL_READ_WRITE /** Read and write. */
121 	}
122 	
123 	/**
124 	 * Declare which Buffer Type is stored.
125 	 */
126 	enum Type {
127 		/** The currently bound buffer object stores vertex array data. */
128 		Array = GL_ARRAY_BUFFER,
129 		/** The currently bound buffer object stores index values ​​for vertex arrays. */
130 		Element = GL_ELEMENT_ARRAY_BUFFER
131 	}
132 
133 	const Type type;
134 	const Target targets;
135 	const ubyte numTargets;
136 	
137 private:
138 	GLuint[3] _vboId;
139 	Target _curTarget;
140 	ubyte[Target] _targetIds;
141 	bool[Target] _dataAssigned;
142 	
143 public:
144 final:
145 	/**
146 	 * CTor
147 	 */
148 	this(Target trg, Type type = Type.Array) {
149 		if (trg == Target.None)
150 			Log.error("Invalid PointerTarget.");
151 		
152 		ubyte num_targets = 0;
153 		if (Target.Vertex & trg)
154 			this._targetIds[Target.Vertex] = num_targets++;
155 		if (Target.Color & trg)
156 			this._targetIds[Target.Color] = num_targets++;
157 		if (Target.TexCoords & trg)
158 			this._targetIds[Target.TexCoords] = num_targets++;
159 		
160 		this.type = type;
161 		this.targets = trg;
162 		this.numTargets = num_targets;
163 		this._curTarget = Target.None;
164 		
165 		glGenBuffers(this.numTargets, &this._vboId[0]);
166 		
167 		foreach (Target id, _; this._targetIds) {
168 			this.bind(id);
169 			
170 			this._dataAssigned[id] = false;
171 		}
172 		
173 		this.unbind();
174 	}
175 	
176 	/**
177 	 * Binds the a specific VBO PointerTarget.
178 	 * If the target is invalid (because no such buffer exist)
179 	 * nothing happens.
180 	 * 
181 	 * See: PointerTarget enum
182 	 */
183 	void bind(Target trg) {
184 		if ((trg & this.targets) == 0)
185 			return;
186 		
187 		this._curTarget = trg;
188 		
189 		const ubyte id = this._targetIds[trg];
190 		glBindBuffer(this.type, this._vboId[id]);
191 	}
192 	
193 	/**
194 	 * Unbind the current VBO.
195 	 */
196 	void unbind() {
197 		this._curTarget = Target.None;
198 		
199 		glBindBuffer(this.type, 0);
200 	}
201 	
202 	/**
203 	 * Returns the current PointerTarget
204 	 * 
205 	 * See: PointerTarget enum
206 	 */
207 	Target getBound() const pure nothrow {
208 		return this._curTarget;
209 	}
210 	
211 	/**
212 	 * Returns if some PointerTarget is currently bound
213 	 */
214 	bool isSomethingBound() const pure nothrow {
215 		return this._curTarget != Target.None;
216 	}
217 	
218 	/**
219 	 * Checks whether the current buffer has already content, or not
220 	 */
221 	bool isCurrentEmpty() const pure nothrow {
222 		if (!this.isSomethingBound())
223 			return false;
224 		
225 		return this._dataAssigned[this._curTarget] == false;
226 	}
227 	
228 	/**
229 	 * Checks whether a specific buffer has already content, or not.
230 	 * If the target is invalid (because no such buffer exist)
231 	 * an Exception is thrown.
232 	 */
233 	bool isEmpty(Target trg) const {
234 		if ((trg & this.targets) == 0)
235 			Log.error("%s is not a valid target of this buffer.", trg);
236 		
237 		return this._dataAssigned[trg] == false;
238 	}
239 	
240 	/**
241 	 * Reset the current buffer state
242 	 * 
243 	 * See: isEmpty
244 	 */
245 	void deplete() {
246 		if (!this.isSomethingBound())
247 			return;
248 		
249 		this._dataAssigned[this._curTarget] = false;
250 	}
251 	
252 	/**
253 	 * Reset all buffer states
254 	 * 
255 	 * See: isEmpty
256 	 */
257 	void depleteAll() {
258 		foreach (Target id, _; this._targetIds) {
259 			this.bind(id);
260 			this.deplete();
261 		}
262 	}
263 	
264 	/**
265 	 * Stores data in the current VBO.
266 	 * 
267 	 * See: glBufferData
268 	 */
269 	void cache(const void* ptr, size_t totalSize, uint usage = Usage.Static.Draw) {
270 		this._dataAssigned[this._curTarget] = true;
271 		
272 		glBufferData(this.type, totalSize, ptr, usage);
273 	}
274 	
275 	/**
276 	 * Modify existing buffer data
277 	 * 
278 	 * See: glBufferSubData
279 	 */
280 	void modify(const void* ptr, size_t totalSize, uint offset = 0) const {
281 		glBufferSubData(this.type, offset, totalSize, ptr); 
282 	}
283 	
284 	/**
285 	 * The internal buffer memory is transferred to the memory of the client
286 	 * with a specific access.
287 	 * Before the buffer can be reused, <code>unmap</code> must be called.
288 	 * 
289 	 * See: Access enum
290 	 * See: glMapBuffer
291 	 */
292 	void* map(Access access) const {
293 		return glMapBuffer(this.type, access);
294 	}
295 	
296 	/**
297 	 * Allows other commands buffer access, in which it retrieves the memory from the client.
298 	 * 
299 	 * See: map method
300 	 */
301 	void unmap() const {
302 		glUnmapBuffer(this.type);
303 	}
304 	
305 	/**
306 	 * Points to the current VBO with a specific PointerTarget.
307 	 * 
308 	 * See: glVertexPointer
309 	 * See: glColorPointer
310 	 * See: glTexCoordPointer
311 	 * See: PointerTarget enum.
312 	 */
313 	void pointTo(Target trg, ubyte stride = 0, ubyte offset = 0) {
314 		this.bind(trg);
315 		
316 		VertexRenderer.pointTo(trg, null, stride, offset);
317 	}
318 	
319 	/**
320 	 * Enable a specific client state (with glEnableClientState)
321 	 * like GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
322 	 * with the corresponding PointerTarget.
323 	 */
324 	void enableState(Target trg) const {
325 		VertexRenderer.enableState(trg);
326 	}
327 	
328 	/**
329 	 * Enable all client states
330 	 */
331 	void enableAllStates() const {
332 		VertexRenderer.enableAllStates();
333 	}
334 	
335 	/**
336 	 * Disable all client states
337 	 */
338 	void disableAllStates() const {
339 		VertexRenderer.disableAllStates();
340 	}
341 	
342 	/**
343 	 * Disable a specific client state (with glDisableClientState)
344 	 */
345 	void disableState(Target trg) const {
346 		VertexRenderer.disableState(trg);
347 	}
348 	
349 	/**
350 	 * Draw shapes of the specific type from the current VBO data.
351 	 * It will use count vertices.
352 	 */
353 	void drawArrays(Shape.Type ptype, size_t count, uint start = 0) const {
354 		VertexRenderer.drawArrays(ptype, count, start);
355 	}
356 	
357 	/**
358 	 * Draw shapes of the specific type from the current VBO data.
359 	 * It will use count vertices and indices for the correct index per vertex.
360 	 */
361 	void drawElements(Shape.Type ptype, size_t count, uint[] indices) const {
362 		VertexRenderer.drawElements(ptype, count, indices);
363 	}
364 	
365 	/**
366 	 * Draw shapes of the specific type from the current VBO data.
367 	 * It will use count vertices and indices for the correct index per vertex.
368 	 *
369 	 * Note: If start or end are -1 or below, 0 and indices.length are used.
370 	 */
371 	void drawRangeElements(Shape.Type ptype, size_t count, uint[] indices, int start = -1, int end = -1) const {
372 		VertexRenderer.drawRangeElements(ptype, count, indices, start, end);
373 	}
374 	
375 	/**
376 	 * Bind a texture to this Buffer.
377 	 * It's a shortcut for:
378 	 * ----
379 	 * buf.pointTo(Target.TexCoords);
380 	 * buf.pointTo(Target.Vertex);
381 	 * 
382 	 * tex.bind();
383 	 * ----
384 	 * 
385 	 * Note: You should clean up with:
386 	 * tex.unbind(); and buf.disableAllStates();
387 	 */
388 	void bindTexture(const Texture tex) {
389 		this.pointTo(Target.TexCoords);
390 		this.pointTo(Target.Vertex);
391 		
392 		tex.bind();
393 	}
394 }