1 module meld.mesh;
2 
3 import derelict.opengl3.gl3;
4 import std.stdio;
5 import std.conv;
6 import std.math;
7 import std.file;
8 import std.range;
9 import std..string;
10 import meld.maths;
11 
12 struct Vertex
13 {
14 	this(float x, float y, float z, float nx, float ny, float nz, float u, float v)
15 	{
16 		pos = vec3(x, y, z);
17 		normal = vec3(nx, ny, nz);
18 		uv = vec2(u, v);
19 	}
20 	this(vec3 pos, vec3 normal, vec2 uv)
21 	{
22 		this.pos = pos;
23 		this.normal = normal;
24 		this.uv = uv;
25 	}
26 
27 	vec3 pos;
28 	vec3 normal;
29 	vec2 uv;
30 
31 	static void SetupLayout()
32 	{
33 		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, Vertex.sizeof, cast(char*)(0));
34 		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, Vertex.sizeof, cast(char*)(float.sizeof*3));
35 		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, Vertex.sizeof, cast(char*)(float.sizeof*6));
36 
37 		glEnableVertexAttribArray(0);
38 		glEnableVertexAttribArray(1);
39 		glEnableVertexAttribArray(2);
40 	}
41 }
42 
43 struct MeshData
44 {
45 	Vertex[] vertices;
46 	ushort[] indices;
47 }
48 
49 class Mesh
50 {
51 private:
52 	GLuint m_vertexBuffer, m_indexBuffer, m_vertexLayout;
53 	int m_numIndices;
54 	GLenum m_drawMode;
55 
56 	static void ErrCheck()
57 	{
58 		/*GLenum glErr;
59 	    int    retCode = 0;
60 
61 	    glErr = glGetError();
62 	    if (glErr != GL_NO_ERROR)
63 	    {
64 	        assert(0, "OpenGL error: "~to!string(glErr));
65 	    }*/
66 	}
67 
68 public:
69 	this(V)(ref V[] verts, ref ushort[] indices, GLenum drawMode)
70 	{
71 		//Create the vertex buffer
72 		glGenBuffers(1, &m_vertexBuffer);
73 		glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
74 		glBufferData(GL_ARRAY_BUFFER, V.sizeof*verts.length, &verts[0], GL_STATIC_DRAW);
75 		ErrCheck();
76 
77 		//Create the index buffer
78 		glGenBuffers(1, &m_indexBuffer);
79 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
80 		glBufferData(GL_ELEMENT_ARRAY_BUFFER, ushort.sizeof*indices.length, &indices[0], GL_STATIC_DRAW);
81 		m_numIndices = cast(int)indices.length;
82 		ErrCheck();
83 
84 		//Create the vertex layout
85 		glGenVertexArrays(1, &m_vertexLayout);
86 		glBindVertexArray(m_vertexLayout);
87 
88 		//Bind the vertex buffer to the layout and specify the format
89 		glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
90 		V.SetupLayout();
91 
92 		//Bind the index buffer to the layout
93 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
94 	    m_drawMode = drawMode;
95 		ErrCheck();
96 
97 		//Null out everything
98 		glBindVertexArray(0);
99 		glBindBuffer(GL_ARRAY_BUFFER, 0);
100 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
101 		ErrCheck();
102 	}
103 
104 	~this()
105 	{
106 		//Delete the buffers and vertex arrays
107 		glDeleteBuffers(1, &m_vertexBuffer);
108 		glDeleteBuffers(1, &m_indexBuffer);
109 		glDeleteVertexArrays(1, &m_vertexLayout);
110 	}
111 
112 	void Draw()
113 	{
114 		glBindVertexArray(m_vertexLayout);
115 		glDrawRangeElements(m_drawMode, 0, m_numIndices, m_numIndices, GL_UNSIGNED_SHORT, null);
116 		ErrCheck();
117 	}
118 
119 	static Mesh CreatePlane( float width, float height )
120 	{
121 		float hw = width * 0.5f;
122 		float hh = height * 0.5f;
123 
124 		Vertex verts[] = 
125 		[
126 			Vertex(-hw, 0.0f, -hh, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f ),
127 			Vertex(  hw, 0.0f, -hh, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f ),
128 			Vertex(  hw, 0.0f,  hh, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f ),
129 			Vertex( -hw, 0.0f,  hh, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f ),
130 		];
131 		ushort indices[] =
132 		[
133 			0, 2, 1,
134 			0, 3, 2
135 		];
136 
137 		return new Mesh(verts, indices, GL_TRIANGLES);
138 	}
139 
140 	static Mesh CreateSphere( int rings, int segments, bool flip )
141 	{
142 		int numVerts = ((rings+1) * segments);
143 		Vertex[] vertices = new Vertex[numVerts];
144 		
145 		int numIndices = (numVerts-segments) * 6;
146 		ushort[] indices = new ushort[numIndices];
147 		
148 		float ringInterval = PI/cast(float)rings;
149 		float segInterval = (2.0f*PI)/cast(float)segments;
150 		
151 		float xTexInterval = 1.0f/cast(float)rings;
152 		float yTexInterval = 1.0f/cast(float)segments;
153 		float xTex = 0.0f;
154 		
155 		float ringPos = 0.0f;
156 		int i = 0, k = 0;
157 		
158 		for (int ring = 0; ring <= rings; ++ring, ringPos += ringInterval, xTex += xTexInterval)
159 		{
160 			float sinRing = sin(ringPos);
161 			float cosRing = cos(ringPos);
162 			float segPos = 0.0f;
163 			float yTex = 0.0f;
164 			for (int segment = 0; segment < segments; ++segment, segPos += segInterval, yTex += yTexInterval)
165 			{
166 				Vertex vertex = Vertex
167 				(
168 					cast(float)cos(segPos) * sinRing, cast(float)sin(segPos) * sinRing, cosRing,
169 					cast(float)cos(segPos) * sinRing, cast(float)sin(segPos) * sinRing, cosRing,
170 					xTex, yTex
171 				);
172 				vertices[k] = vertex;
173 				k++;
174 				
175 				if (ring < rings)
176 				{
177 					int j = i*6;
178 					indices[j+0] = cast(ushort)i;
179 					indices[j+1] = cast(ushort)(i+segments);
180 					indices[j+2] = cast(ushort)(segment == segments-1 ? i-segments+1 : i+1);
181 					
182 					indices[j+3] = cast(ushort)(segment == segments-1 ? i-segments+1 : i+1);
183 					indices[j+4] = cast(ushort)(i+segments);
184 					indices[j+5] = cast(ushort)(segment == segments-1 ? i+1 : i+segments+1);
185 					++i;
186 				}
187 			}
188 		}
189 		
190 		if (flip)
191 		{
192 			for (i = 0; i<numIndices; i+=3)
193 			{
194 				ushort tmp = indices[i+2];
195 				indices[i+2] = indices[i+1];
196 				indices[i+1] = tmp;
197 			}
198 		}
199 		
200 		Mesh sphere = new Mesh(vertices, indices, GL_TRIANGLES);
201 		delete vertices;
202 		delete indices;
203 		
204 		return sphere;
205 	}
206 	
207 	static Mesh CreateCone( int segments )
208 	{
209 		Vertex[] vertices = new Vertex[segments + 2];
210 		Vertex top = Vertex
211 		(
212 			0.0f, 0.0f, 1.0f,
213 			0.0f, 0.0f, 1.0f,
214 			0.0f, 0.0f
215 		);
216 		Vertex bottom = Vertex
217 		(
218 			0.0f, 0.0f, -1.0f,
219 				0.0f, 0.0f, -1.0f,
220 				0.0f, 1.0f
221 		);
222 		vertices[0] = top;
223 		vertices[1] = bottom;
224 		
225 		ushort[] indices = new ushort[segments * 6];
226 		int j = 2;
227 		int k = 0;
228 		float segPos = 0.0f, segInterval = (2.0f*PI)/cast(float)segments;
229 		for (int i = 0; i<segments; ++i, ++j, segPos += segInterval)
230 		{
231 			Vertex vertex = Vertex
232 			(
233 				cast(float)sin(segPos), cast(float)cos(segPos), -1.0f,
234 				cast(float)sin(segPos), cast(float)cos(segPos), -1.0f,
235 				segPos, 0.5f
236 			);
237 			vertices[j] = vertex;
238 			
239 			indices[k++] = cast(ushort)(i==segments-1? 2 : j+1);
240 			indices[k++] = cast(ushort)j;
241 			indices[k++] = 0;
242 			
243 			indices[k++] = 1;
244 			indices[k++] = cast(ushort)j;
245 			indices[k++] = cast(ushort)(i==segments-1? 2 : j+1);
246 		}
247 		
248 		Mesh cone = new Mesh(vertices, indices, GL_TRIANGLES);
249 		delete indices;
250 		delete vertices;
251 		
252 		return cone;
253 	}
254 
255 	static Mesh CreateCube()
256 	{
257 		const float hw = 1.0f;
258 		const float hy = 1.0f;
259 		const float hh = 1.0f;
260 		
261 		Vertex verts[] = 
262 		[
263 			//Top Face (+y)
264 			Vertex(-hw, hy, -hh, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f ),
265 			Vertex(-hw, hy,  hh, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f ),
266 			Vertex( hw, hy,  hh, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f ),
267 	        Vertex( hw, hy, -hh, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f ),
268 				
269 			//Bottom Face (-y)
270 			Vertex( -hw, -hy, -hh, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f ),
271 			Vertex(  hw, -hy, -hh, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f ),
272 			Vertex(  hw, -hy,  hh, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f ),
273 			Vertex( -hw, -hy,  hh, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f ),
274 				
275 			//Left Face (-x)
276 			Vertex( -hw, hy, -hh, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f ),
277 			Vertex( -hw, -hy,-hh, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f ),
278 			Vertex( -hw, -hy, hh, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f ),
279 			Vertex( -hw, hy,  hh, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f ),
280 				
281 			//Right Face (+x)
282 			Vertex( hw, hy, -hh, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f ),
283 			Vertex( hw, hy,  hh, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f ),
284 			Vertex( hw, -hy, hh, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f ),
285 			Vertex( hw, -hy,-hh, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f ),
286 				
287 			//Front Face (-z)
288 			Vertex( -hw, hy, -hh, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f ),
289 			Vertex(  hw, hy, -hh, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f ),
290 			Vertex(  hw,-hy, -hh, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f ),
291 			Vertex( -hw,-hy, -hh, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f ),
292 				
293 			//Back Face (+z)
294 			Vertex( -hw, hy, hh, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ),
295 			Vertex( -hw,-hy, hh, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f ),
296 			Vertex(  hw,-hy, hh, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f ),
297 			Vertex(  hw, hy, hh, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f )
298 		];
299 		ushort indices[] =
300 		[
301 			//Top Face (+y)
302 			0, 1, 2,
303 			0, 2, 3,
304 			//Bottom Face (-y)
305 			4, 5, 6,
306 			4, 6, 7,
307 			//Left Face (-x)
308 			8, 9, 10,
309 			8, 10, 11,
310 			//Right Face (+x)
311 			12, 13, 14,
312 			12, 14, 15,
313 			//Front Face (-z)
314 			16, 17, 18,
315 			16, 18, 19,
316 			//Back Face (+z)
317 			20, 21, 22,
318 			20, 22, 23
319 		];
320 		
321 		return new Mesh(verts, indices, GL_TRIANGLES);
322 	}
323 	
324 	static Mesh CreateCylinder(int segments)
325 	{
326 		int numVerts = (segments * 4) + 2;
327 		int numIndices = segments * 12;
328 		Vertex[] verts = new Vertex[numVerts];
329 		ushort[] indices = new ushort[numIndices];
330 		
331 		float segmentInterval = 2.0f*PI/cast(float)segments;
332 		float texIntervalIncr = 1.0f/cast(float)segments;
333 		float interval = 0.0f, texInterval = 0.0f;
334 		int ind = 0;
335 		for (int i = 0; i<segments; ++i, interval += segmentInterval, texInterval += texIntervalIncr)
336 		{
337 			float cosI = cos(interval);
338 			float sinI = sin(interval);
339 			
340 			//Cylinder caps
341 			{
342 				Vertex vert = Vertex
343 				(
344 					sinI, cosI, -1.0f,
345 					0.0f, 0.0f, -1.0f,
346 					sinI*0.5f, cosI*0.5f
347 				);
348 				verts[i] = vert;
349 				
350 				indices[ind++] = cast(ushort)i;
351 				indices[ind++] = cast(ushort)(i+1 == segments ? 0 : i+1);
352 				indices[ind++] = cast(ushort)(numVerts - 1);
353 				
354 				vert.normal.z = 1.0f;
355 				vert.pos.z = 1.0f;
356 				verts[i+segments] = vert;
357 				
358 				indices[ind++] = cast(ushort)(numVerts - 2);
359 				indices[ind++] = cast(ushort)(i+1 == segments ? segments : i+segments+1);
360 				indices[ind++] = cast(ushort)(i+segments);
361 			}
362 			//Cylinder sides
363 			{
364 				Vertex vert = Vertex
365 				(
366 					sinI, cosI, -1.0f,
367 						sinI, cosI, 0.0f,
368 						texInterval, 0.0f
369 				);
370 				int f = segments*2;
371 				verts[i+f] = vert;
372 				
373 				vert.uv.y = 1.0f;
374 				vert.pos.z = 1.0f;
375 				int s = segments*3;
376 				verts[i+s] = vert;
377 				
378 				indices[ind++] = cast(ushort)(i+f);
379 				indices[ind++] = cast(ushort)(i+s);
380 				indices[ind++] = cast(ushort)(i+1 == segments ? f : i+f+1);
381 				
382 				indices[ind++] = cast(ushort)(i+1 == segments ? s : i+s+1);
383 				indices[ind++] = cast(ushort)(i+1 == segments ? f : i+f+1);
384 				indices[ind++] = cast(ushort)(i+s);
385 			}
386 		}
387 		
388 		//Cylinder ends
389 		Vertex vertEnd = Vertex
390 		(
391 			0.0f, 0.0f, -1.0f,
392 				0.0f, 0.0f, -1.0f,
393 				0.0f, 0.0f
394 		);
395 		verts[numVerts-1] = vertEnd;
396 		vertEnd.pos.z = 1.0f;
397 		vertEnd.normal.z = 1.0f;
398 		verts[numVerts-2] = vertEnd;
399 		
400 		Mesh mesh = new Mesh(verts, indices, GL_TRIANGLES);
401 		delete verts;
402 		delete indices;
403 		
404 		return mesh;
405 	}
406 
407 	static Mesh LoadMesh(in string path)
408 	{
409 		import msgpack;
410 		import std.file;
411 
412 		ubyte[] data = cast(ubyte[])read(path);
413 		MeshData meshData = data.unpack!MeshData();
414 
415 		return new Mesh(meshData.vertices, meshData.indices, GL_TRIANGLES);
416 	}
417 
418 	/*static Mesh LoadMeshObj(string path)
419 	{
420 		import meld.maths;
421 		vec3[] positions = [];
422 		vec2[] uvs = [];
423 		vec3[] normals = [];
424 
425 		ushort[] indices = [];
426 		Vertex[] verts = [];
427 		ushort[Vertex] vertToInd;
428 
429 		auto file = File(path);
430 		foreach (char[] line; file.byLine())
431 		{
432 			if (line.empty) continue;
433 
434 			char[][] parts = strip(line[2..$]).split(' ');
435 
436 			switch (line[0..2])
437 			{
438 				case "v ":
439 					positions ~= vec3(to!float(parts[0]), to!float(parts[1]), to!float(parts[2]));
440 					break;
441 
442 				case "vt":
443 					uvs ~= vec2(to!float(parts[0]), to!float(parts[1]));
444 					break;
445 
446 				case "vn":
447 					normals ~= vec3(to!float(parts[0]), to!float(parts[1]), to!float(parts[2]));
448 					break;
449 
450 				case "f ":
451 					Vertex Parse(char[][] bits)
452 					{
453 						//writeln("'" ~ bits[0] ~ "' (" ~ strip(line[2..$]) ~ ")");
454 						vec3 pos = positions[to!int(bits[0]) - 1];
455 						vec2 uv = bits.length >= 2 ? uvs[to!int(bits[1]) - 1] : vec2.zero;
456 						vec3 normal = bits.length >= 3 ? normals[to!int(bits[2]) - 1] : vec3.zero;
457 						return Vertex(pos.x, pos.y, pos.z, normal.x, normal.y, normal.z, uv.x, uv.y);
458 					}
459 
460 					for (int i = 0; i<parts.length; i++)
461 					{
462 						Vertex vert = Parse(parts[i].split('/'));
463 						ushort* index = vert in vertToInd;
464 						ushort ind;
465 						if (index is null)
466 						{
467 							verts ~= vert;
468 							ind = to!ushort(verts.length - 1);
469 							//vertToInd[vert] = ind;
470 						}
471 
472 						indices ~= ind;
473 					}
474 					break;
475 
476 				default:
477     				continue;
478 			}
479 		}
480 
481 		Mesh mesh = new Mesh(verts, indices, GL_TRIANGLES);
482 		delete verts;
483 		delete indices;
484 		delete positions;
485 		delete uvs;
486 		delete normals;
487 		return mesh;
488 	}*/
489 }