1 module polyplex.core.render.gl.objects;
2 import polyplex.core.render.shapes;
3 import polyplex.core.color;
4 import polyplex.math;
5 
6 import derelict.sdl2.sdl;
7 import derelict.opengl;
8 import derelict.opengl.gl;
9 import polyplex.utils.logging;
10 import polyplex.utils.strutils;
11 import std.stdio;
12 import std.conv;
13 import std.format;
14 import std.traits;
15 
16 enum DrawType {
17 	LineStrip = GL_LINE_STRIP,
18 	TriangleStrip = GL_TRIANGLE_STRIP,
19 	Triangles = GL_TRIANGLES,
20 	Points = GL_POINTS,
21 	LineLoop = GL_LINE_LOOP,
22 	Lines = GL_LINES,
23 }
24 
25 enum OptimizeMode {
26 	Mode3D,
27 	Mode2D
28 }
29 
30 enum BufferMode {
31 	Static = GL_STATIC_DRAW,
32 	Dynamic = GL_DYNAMIC_DRAW
33 }
34 
35 
36 alias Buffer = GLfloat[];
37 
38 /**
39 	Layout is the way data is layed out in a buffer object.
40 */
41 enum Layout {
42 	/**
43 		A layout where each element is seperated out into multiple buffers.
44 		[XXX], [YYY], [ZZZ]
45 	*/
46 	Seperated,
47 
48 	/**
49 		A layout where each element is clustered into larger groups in one buffer.
50 		[XXXYYYZZZ]
51 	*/
52 	Batched,
53 
54 	/**
55 		A layout where each element is clustered into smaller groups in one buffer.
56 		[XYZXYZXYZ]
57 	*/
58 	Interleaved
59 }
60 
61 //Vertex Array Object contains state information to be sent to the GPU
62 class VertexArray(T, Layout layout) {
63 
64 	private GLuint id;
65 	public @property uint Id() { return cast(uint)id; }
66 
67 	this() {
68 		glGenVertexArrays(1, &id);
69 	}
70 
71 	~this() {
72 		glDeleteVertexArrays(1, &id);
73 	}
74 
75 	void Bind() {
76 		glBindVertexArray(id);
77 	}
78 
79 	void Unbind() {
80 		glBindVertexArray(0);
81 	}
82 }
83 
84 // Make sure valid types are in the struct
85 // TODO: Add type support for ints, and such.
86 enum ValidBufferType(T) = (is(T == float)) || (IsVector!T && is(T.Type == float));
87 
88 struct VertexBuffer(T, Layout layout) {
89 	VertexArray!(T, layout) vao;
90 	private GLuint[] gl_buffers;
91 	public T[] Data;
92 
93 
94 	this(T input) {
95 		this([input]);
96 	}
97 
98 	this(T[] input) {
99 		vao = new VertexArray!(T, layout)();
100 		this.Data = input;
101 
102 		vao.Bind();
103 
104 		// Generate GL buffers.
105 		static if(layout == Layout.Seperated) {
106 			int struct_member_count = cast(int)__traits(allMembers, T).length;
107 			gl_buffers.length = struct_member_count;
108 			glGenBuffers(struct_member_count, gl_buffers.ptr);
109 		} else {
110 			gl_buffers.length = 1;
111 			glGenBuffers(1, gl_buffers.ptr);
112 			UpdateBuffer();
113 		}
114 	}
115 
116 	~this() {
117 		foreach(int iterator, string member; __traits(allMembers, T)) {
118 			glDisableVertexAttribArray(iterator);
119 		}
120 		glDeleteBuffers(cast(GLsizei)gl_buffers.length, gl_buffers.ptr);
121 		destroy(vao);
122 	}
123 
124 	public void UpdateAttribPointers() {
125 		if (Data.length == 0) return;
126 		foreach(int iterator, string member; __traits(allMembers, T)) {
127 			// Use a mixin to get the offset values.
128 			mixin(q{int field_size = T.{0}.sizeof/4;}.Format(member));
129 			mixin(q{void* field_t = cast(void*)T.{0}.offsetof;}.Format(member));
130 
131 			Logger.VerboseDebug("{0}", T.sizeof);
132 
133 			// Check if it's a valid type for the VBO buffer.
134 			mixin(q{ alias M = T.{0}; }.Format(member));
135 			static assert(ValidBufferType!(typeof(M)), "Invalid buffer value <{0}>, may only contain: float, vector2, vector3 and vector4s!".Format(member));
136 
137 			if (layout == Layout.Seperated) {
138 				Bind(iterator+1);
139 				UpdateBuffer(iterator+1);
140 				glVertexAttribPointer(iterator, field_size, GL_FLOAT, GL_FALSE, 0, null);
141 				glEnableVertexAttribArray(iterator);
142 			} else {
143 				Bind();
144 				Logger.VerboseDebug("glVertexAttribPointer({0}, {1}, GL_FLOAT, GL_FALSE, {2}, {3})", iterator, field_size, T.sizeof, field_t);
145 				glVertexAttribPointer(cast(GLuint)iterator, field_size, GL_FLOAT, GL_FALSE, T.sizeof, field_t);
146 				Logger.VerboseDebug("glEnableVertexAttribArray({0})", iterator);
147 				glEnableVertexAttribArray(iterator);
148 			}
149 		}
150 	}
151 
152 	public void Bind(int index = 0) {
153 		Logger.VerboseDebug("glBindBuffer(GL_ARRAY_BUFFER, gl_buffers[{0}]) <ptr {1}>", index, gl_buffers[index]);
154 		vao.Bind();
155 		glBindBuffer(GL_ARRAY_BUFFER, gl_buffers[index]);
156 	}
157 
158 	public void Unbind() {
159 		glBindBuffer(GL_ARRAY_BUFFER, 0);
160 		vao.Unbind();
161 	}
162 
163 	public void UpdateBuffer(int index = 0, BufferMode mode = BufferMode.Dynamic) {
164 		Bind(index);
165 		Logger.VerboseDebug("glBufferData(GL_ARRAY_BUFFER, {0}, {1}, {2})", Data.sizeof, Data.ptr, mode);
166 		glBufferData(GL_ARRAY_BUFFER, Data.length * T.sizeof, Data.ptr, mode);
167 		UpdateAttribPointers();
168 	}
169 
170 	public void UpdateSubData(int index = 0, GLintptr offset = 0, GLsizeiptr size = 0) {
171 		Bind(index);
172 		glBufferSubData(GL_ARRAY_BUFFER, offset, T.sizeof*size, cast(void*)Data.ptr);
173 		UpdateAttribPointers();
174 	}
175 
176 	public void Draw(int amount = 0, DrawType dt = DrawType.Triangles) {
177 		vao.Bind();
178 		if (amount == 0) glDrawArrays(dt, 0, cast(GLuint)this.Data.length);
179 		else glDrawArrays(dt, 0, amount);
180 		uint err = glGetError();
181 		if (err != GL_NO_ERROR) {
182 			Logger.Debug("{0}", err);
183 		}
184 	}
185 }
186 
187 struct IndexBuffer {
188 	public GLuint[] Indices;
189 	public GLuint Id;
190 
191 	this(GLuint[] indices) {
192 		glGenBuffers(1, &Id);
193 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Id);
194 		this.Indices = indices;
195 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.sizeof, indices.ptr, GL_DYNAMIC_DRAW);
196 	}
197 
198 	public void Bind() {
199 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Id);
200 	}
201 
202 	public void Unbind() {
203 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
204 	}
205 
206 	public void UpdateBuffer(int index = 0, BufferMode mode = BufferMode.Dynamic) {
207 		Bind();
208 		Logger.VerboseDebug("glBufferData(GL_ARRAY_BUFFER, {0}, {1}, {2})", Indices.sizeof, Indices.ptr, mode);
209 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, Indices.sizeof, Indices.ptr, mode);
210 	}
211 
212 	public void UpdateSubData(int index = 0, GLintptr offset = 0, GLsizeiptr size = 0) {
213 		Bind();
214 		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, cast(void*)Indices.ptr);
215 	}
216 }
217 
218 public enum FBOTextureType {
219 	Color,
220 	Depth,
221 	Stencil,
222 	DepthStencil
223 }
224 
225 /// WORK IN PROGRESS!
226 
227 class FrameBuffer {
228 	// Internal managment value to make sure that the IsComplete function can revert to the userbound FBO.
229 	private static GLuint current_attch;
230 
231 	private GLuint id;
232 
233 	private GLuint[FBOTextureType] render_textures;
234 
235 	private int width;
236 	private int height;
237 
238 	public int Width() { return width; }
239 	public int Height() { return height; }
240 
241 	public bool IsComplete() {
242 		Bind();
243 		bool status = (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
244 		glBindFramebuffer(GL_FRAMEBUFFER, current_attch);
245 		return status;
246 	}
247 
248 	this() {
249 		glGenFramebuffers(1, &id);
250 	}
251 
252 	~this() {
253 		glDeleteFramebuffers(1, &id);
254 	}
255 
256 	public void SetTexture(FBOTextureType type) {
257 		if (render_textures[type] != 0) {
258 			// Delete previous texture.
259 			glBindTexture(GL_TEXTURE, render_textures[type]);
260 			glDeleteTextures(1, &render_textures[type]);
261 		}
262 
263 		render_textures[type] = 0;
264 		glGenTextures(1, &render_textures[type]);
265 		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, null);
266 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
267 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
268 
269 	}
270 
271 	public void Rebuild(int width, int height) {
272 		this.width = width;
273 		this.height = height;
274 	}
275 
276 	public void Bind() {
277 		// Make sure IsComplete state can be reversed to this again.
278 		current_attch = id;
279 		glBindFramebuffer(GL_FRAMEBUFFER, id);
280 	}
281 
282 	public void Unbind() {
283 		// Make sure IsComplete state can be reversed to this again.
284 		current_attch = id;
285 		glBindFramebuffer(GL_FRAMEBUFFER, 0);
286 	}
287 
288 
289 }
290 
291 /*
292 class IBO : BufferObject {
293 	this(Layout layout) {
294 		super(GL_ELEMENT_ARRAY_BUFFER, layout, null);
295 	}
296 
297 	public override void Draw(int amount = 0) {
298 		throw new Exception("You can't draw an IBO, attach the IBO to a VBO instead.");
299 	}
300 }*/