1 module moggle.core.vbo;
2 
3 import moggle.core.gl;
4 
5 /++ A vertex buffer object.
6 
7 This is a wrapper around a GLuint generated by glGenBuffers(1, &id).
8 Initially, this id is 0.
9 glGenBuffers is automatically called the first time bind() is called.
10 
11 Vbo!void is an alias of this.
12 +/
13 struct GenericVbo {
14 
15 	private GLuint id_ = 0;
16 
17 	/// The id of this Vao, or 0 if it is not yet created in OpenGL.
18 	@property GLuint id() const { return id_; }
19 
20 	/// Check if this Vao is already created in OpenGL.
21 	@property bool created() const { return id != 0; }
22 
23 	/// ditto
24 	bool opCast(T : bool)() const { return created; }
25 
26 	/// Force the creation of a OpenGL Vbo, or do nothing if already created. (Calls glGenBuffers.)
27 	void create() {
28 		if (!id_) glGenBuffers(1, &id_);
29 		assert(id_, "glGenBuffers did not generate a buffer.");
30 	}
31 
32 	/// Destroy the OpenGL Vbo and reset the id back to 0. (Calls glDeleteBuffers.)
33 	void destroy() {
34 		glDeleteBuffers(1, &id_);
35 		id_ = 0;
36 	}
37 
38 	// ditto
39 	~this() {
40 		destroy();
41 	}
42 
43 	/// Create the OpenGL Vbo, if needed, and bind it. (Calls glBindBuffer.)
44 	void bind(GLenum buffer) {
45 		create();
46 		glBindBuffer(buffer, id_);
47 	}
48 
49 	/// Cast this GenericVbo to a specific SpecificVbo!T.
50 	ref inout(SpecificVbo!(T)) opCast(T : SpecificVbo!(T))() inout {
51 		return *cast(typeof(return)*)&this;
52 	}
53 
54 	@disable this(this);
55 
56 }
57 
58 /++ A Vbo that knows what type of elements are stored in it.
59 
60 Vbo!T is an alias of this, except for Vbo!void (which is an alias for GenericVbo).
61 
62 Contains a single GenericVbo, which is aliased as this.
63 (Which basically means that any SpecificVbo!T is also a GenericVbo.)
64 +/
65 struct SpecificVbo(T) {
66 
67 	GenericVbo vbo_;
68 
69 	alias vbo_ this;
70 
71 	/++ Store the given data in the Vbo. (Calls glBufferData.)
72 
73 	Examples:
74 	---
75 	auto v = Vbo!int([1, 2, 3, 4, 5]);
76 	v.data([2, 1, 0]); // Updates the Vbo with new data.
77 	---
78 	+/
79 	this(T[] data_, GLenum usage = GL_STATIC_DRAW) {
80 		data(data_, usage);
81 	}
82 	/// ditto
83 	void data(T[] data_, GLenum usage = GL_STATIC_DRAW) {
84 		bind(GL_ARRAY_BUFFER);
85 		glBufferData(GL_ARRAY_BUFFER, data_.length * T.sizeof, data_.ptr, usage);
86 	}
87 
88 	/++ Allocate a OpenGL Vbo with space for n elements. (Calls glBufferData with null.)
89 
90 	The contents of the Vbo are uninitialized and thus undefined.
91 	+/
92 	this(size_t n, GLenum usage = GL_STATIC_DRAW) {
93 		resize(n, usage);
94 	}
95 	/// ditto
96 	void resize(size_t size_, GLenum usage = GL_STATIC_DRAW) {
97 		bind(GL_ARRAY_BUFFER);
98 		glBufferData(GL_ARRAY_BUFFER, size_ * T.sizeof, null, usage);
99 	}
100 
101 	/// The number of elements stored in this Vbo. (Calls glGetBufferParameteriv with GL_BUFFER_SIZE.)
102 	size_t size() {
103 		GLint s;
104 		bind(GL_ARRAY_BUFFER);
105 		glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &s);
106 		return s / T.sizeof;
107 	}
108 
109 	/// Calls resize(0).
110 	void clear(GLenum usage = GL_STATIC_DRAW) {
111 		resize(0, usage);
112 	}
113 
114 	/++ Map the contents of the Vbo in our own memory, temporarily. (Calls glMapBuffer.)
115 
116 	Returns: An object that behaves like a T[] (or const(T)[], for the read-only version),
117 	and reflects the contents of the Vbo.
118 	After this object is destructed, slices in that piece of memory are no longer valid
119 	(because glUnmapBuffer is then called).
120 
121 	Examples:
122 	---
123 	auto v = Vbo!int(3);
124 	{
125 		auto m = v.mapWriteOnly();
126 		m[] = 5;
127 		m[1] = 2;
128 	}
129 	assert(v.mapReadOnly()[] == [5, 2, 5]);
130 	---
131 	+/
132 	auto mapReadOnly() {
133 		bind(GL_ARRAY_BUFFER);
134 		auto p = cast(const T *) glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
135 		return VboMapping!(const T)(p[0..size()]);
136 	}
137 
138 	/// ditto
139 	auto mapWriteOnly() {
140 		bind(GL_ARRAY_BUFFER);
141 		auto p = cast(T *) glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
142 		return VboMapping!(T)(p[0..size()]);
143 	}
144 
145 	/// ditto
146 	auto mapReadWrite() {
147 		bind(GL_ARRAY_BUFFER);
148 		auto p = cast(T *) glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
149 		return VboMapping!(T)(p[0..size()]);
150 	}
151 
152 }
153 
154 /// An alias for SpecificVbo!T, except that Vbo!void is an alias for GenericVbo.
155 template Vbo(T) {
156 	static if (is(T == void)) {
157 		alias GenericVbo Vbo;
158 	} else {
159 		alias SpecificVbo!T Vbo;
160 	}
161 }
162 
163 struct VboMapping(T) {
164 
165 	private T[] data_;
166 
167 	@property T[] data() { return data_; }
168 
169 	alias data this;
170 
171 	@disable this(this);
172 
173 	~this() {
174 		glUnmapBuffer(GL_ARRAY_BUFFER);
175 	}
176 }
177