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 bindbc.sdl;
7 import bindbc.opengl;
8 import bindbc.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 XBuffer = 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 XVertexArray(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 	XVertexArray!(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 XVertexArray!(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 			// Check if it's a valid type for the VBO buffer.
132 			mixin(q{ alias M = T.{0}; }.Format(member));
133 			static assert(ValidBufferType!(typeof(M)), "Invalid buffer value <{0}>, may only contain: float, vector2, vector3 and vector4s! (contains {1})".Format(member, typeof(M).stringof));
134 
135 			if (layout == Layout.Seperated) {
136 				Bind(iterator+1);
137 				UpdateBuffer(iterator+1);
138 				glVertexAttribPointer(iterator, field_size, GL_FLOAT, GL_FALSE, 0, null);
139 				glEnableVertexAttribArray(iterator);
140 			} else {
141 				Bind();
142 				glVertexAttribPointer(cast(GLuint)iterator, field_size, GL_FLOAT, GL_FALSE, T.sizeof, field_t);
143 				glEnableVertexAttribArray(iterator);
144 			}
145 		}
146 	}
147 
148 	public void Bind(int index = 0) {
149 		vao.Bind();
150 		glBindBuffer(GL_ARRAY_BUFFER, gl_buffers[index]);
151 	}
152 
153 	public void Unbind() {
154 		glBindBuffer(GL_ARRAY_BUFFER, 0);
155 		vao.Unbind();
156 	}
157 
158 	public void UpdateBuffer(int index = 0, BufferMode mode = BufferMode.Dynamic) {
159 		Bind(index);
160 		glBufferData(GL_ARRAY_BUFFER, Data.length * T.sizeof, Data.ptr, mode);
161 		UpdateAttribPointers();
162 	}
163 
164 	public void UpdateSubData(int index = 0, GLintptr offset = 0, GLsizeiptr size = 0) {
165 		Bind(index);
166 		glBufferSubData(GL_ARRAY_BUFFER, offset, T.sizeof*size, cast(void*)Data.ptr);
167 		UpdateAttribPointers();
168 	}
169 
170 	public void Draw(int amount = 0, DrawType dt = DrawType.Triangles) {
171 		vao.Bind();
172 		if (amount == 0) glDrawArrays(dt, 0, cast(GLuint)this.Data.length);
173 		else glDrawArrays(dt, 0, amount);
174 		uint err = glGetError();
175 		import std.format;
176 		if (err != GL_NO_ERROR) {
177 			if (err == 0x500) throw new Exception("GLSL Invalid Enum Error ("~format!"%x"(err)~")!");
178 			if (err == 0x502) throw new Exception("GLSL Invalid Operation Error ("~format!"%x"(err)~")! (Are you sending the right types? Uniforms count!)");
179 			throw new Exception("Unhandled GLSL Exception.");
180 		}
181 	}
182 }
183 
184 struct IndexBuffer {
185 	public GLuint[] Indices;
186 	public GLuint Id;
187 
188 	this(GLuint[] indices) {
189 		glGenBuffers(1, &Id);
190 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Id);
191 		this.Indices = indices;
192 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.sizeof, indices.ptr, GL_DYNAMIC_DRAW);
193 	}
194 
195 	public void Bind() {
196 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Id);
197 	}
198 
199 	public void Unbind() {
200 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
201 	}
202 
203 	public void UpdateBuffer(int index = 0, BufferMode mode = BufferMode.Dynamic) {
204 		Bind();
205 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, Indices.sizeof, Indices.ptr, mode);
206 	}
207 
208 	public void UpdateSubData(int index = 0, GLintptr offset = 0, GLsizeiptr size = 0) {
209 		Bind();
210 		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, cast(void*)Indices.ptr);
211 	}
212 }
213 
214 public enum FBOTextureType {
215 	Color,
216 	Depth,
217 	Stencil,
218 	DepthStencil
219 }
220 
221 /// WORK IN PROGRESS!
222 
223 class FrameBuffer {
224 	// Internal managment value to make sure that the IsComplete function can revert to the userbound FBO.
225 	private static GLuint current_attch;
226 
227 	private GLuint id;
228 
229 	private GLuint[FBOTextureType] render_textures;
230 
231 	private int width;
232 	private int height;
233 
234 	public int Width() { return width; }
235 	public int Height() { return height; }
236 
237 	public bool IsComplete() {
238 		Bind();
239 		bool status = (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
240 		glBindFramebuffer(GL_FRAMEBUFFER, current_attch);
241 		return status;
242 	}
243 
244 	this() {
245 		glGenFramebuffers(1, &id);
246 	}
247 
248 	~this() {
249 		glDeleteFramebuffers(1, &id);
250 	}
251 
252 	public void SetTexture(FBOTextureType type) {
253 		if (render_textures[type] != 0) {
254 			// Delete previous texture.
255 			glBindTexture(GL_TEXTURE, render_textures[type]);
256 			glDeleteTextures(1, &render_textures[type]);
257 		}
258 
259 		render_textures[type] = 0;
260 		glGenTextures(1, &render_textures[type]);
261 		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, null);
262 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
263 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
264 
265 	}
266 
267 	public void Rebuild(int width, int height) {
268 		this.width = width;
269 		this.height = height;
270 	}
271 
272 	public void Bind() {
273 		// Make sure IsComplete state can be reversed to this again.
274 		current_attch = id;
275 		glBindFramebuffer(GL_FRAMEBUFFER, id);
276 	}
277 
278 	public void Unbind() {
279 		// Make sure IsComplete state can be reversed to this again.
280 		current_attch = id;
281 		glBindFramebuffer(GL_FRAMEBUFFER, 0);
282 	}
283 
284 
285 }
286 
287 /*
288 class IBO : BufferObject {
289 	this(Layout layout) {
290 		super(GL_ELEMENT_ARRAY_BUFFER, layout, null);
291 	}
292 
293 	public override void Draw(int amount = 0) {
294 		throw new Exception("You can't draw an IBO, attach the IBO to a VBO instead.");
295 	}
296 }*/