1 module unecht.gl.vertexBufferObject;
2 
3 import unecht.gl.shader;
4 
5 import derelict.opengl3.gl3;
6 
7 import gl3n.linalg;
8 
9 enum GLRenderPrimitive
10 {
11 	triangles,
12 	lines,
13 }
14 
15 ///
16 final class GLVertexBufferObject
17 {
18 private:
19 	GLuint vbo;
20 	GLuint boundToIndex = GLuint.max;
21 
22 	int _elementCount;
23 	int _elementSize;
24 	uint _elementBufferType;
25 	ElementType _elementType;
26 	GLRenderPrimitive _primitiveType=GLRenderPrimitive.triangles;
27 
28 	enum ElementType
29 	{
30 		none,
31 		float_,
32 	}
33 
34 public:
35 
36 	///
37 	@property int elementSize() const { return _elementSize; }
38 	///
39 	@property int elementCount() const { return _elementCount; }
40 	///
41 	@property void primitiveType(GLRenderPrimitive _val) { _primitiveType = _val; }
42 
43 	/// create vertex buffer
44 	this(vec3[] _vertexData, bool _static=true)
45 	{
46 		this.create(_vertexData,false,ElementType.float_,_static);
47 	}
48 
49 	/// create uv buffer
50 	this(vec2[] _uvData, bool _static=true)
51 	{
52 		this.create(_uvData,false,ElementType.float_,_static);
53 	}
54 
55 	/// create index buffer
56 	this(uint[] _indexData, bool _static=true)
57 	{
58 		this.create(_indexData,true,ElementType.none,_static);
59 	}
60 
61 	///
62 	~this()
63 	{
64 		//TODO: ensure teardown
65 	}
66 
67 	///
68 	private void create(T)(T[] _data, bool _elementBuffer, ElementType _elementType, bool _static)
69 	{
70 		this._elementBufferType = _elementBuffer?GL_ELEMENT_ARRAY_BUFFER:GL_ARRAY_BUFFER;
71 		this._elementType = _elementType;
72 		this._elementCount = cast(int)_data.length;
73 
74 		glGenBuffers(1, &vbo);
75 		glBindBuffer(_elementBufferType, vbo);
76 		glBufferData(_elementBufferType, _data.length * T.sizeof, _data.ptr, GL_STATIC_DRAW);
77 
78 		if(_elementType != ElementType.none)
79 		{
80 			this._elementSize = cast(int)(T.sizeof / float.sizeof);
81 		}
82 		
83 		checkGLError();
84 	}
85 
86 	///
87 	void destroy()
88 	{
89 		//TODO: destroy vbo and vao
90 	}
91 
92 	///
93 	void bind(GLuint _index)
94 	{
95 		if(_elementType != ElementType.none)
96 		{
97 			assert(boundToIndex == GLuint.max);
98 			boundToIndex = _index;
99 
100 			glEnableVertexAttribArray(boundToIndex);
101 
102 			glBindBuffer(_elementBufferType, vbo);
103 			assert(_elementType == ElementType.float_, "only float supported right now");
104 			glVertexAttribPointer(boundToIndex, _elementSize, GL_FLOAT, GL_FALSE, 0, null);
105 		}
106 		else
107 			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo);
108 
109 		checkGLError();
110 	}
111 
112 	///
113 	void unbind()
114 	{
115 		if(_elementType != ElementType.none)
116 		{
117 			assert(boundToIndex != GLuint.max);
118 
119 			glDisableVertexAttribArray(boundToIndex);
120 
121 			boundToIndex = GLuint.max;
122 		}
123 
124 		checkGLError();
125 	}
126 
127 	///
128 	void renderIndexed()
129 	{
130 		//TODO: save primtype and save the branch here
131 		auto primType = _primitiveType==GLRenderPrimitive.triangles?GL_TRIANGLES:GL_LINES;
132 
133 		glDrawElements(primType, _elementCount, GL_UNSIGNED_INT, null);
134 		
135 		checkGLError();
136 	}
137 }
138 
139 ///
140 static void checkGLError()
141 {
142 	int i;
143 	while(true)
144 	{
145 		auto err = glGetError();
146 		
147 		import unecht.core.logger;
148 		if(err != GL_NO_ERROR)
149 			log.errorf("Error: gl err [%s]: '%s'", i, getGLErrorAsString(err));
150 		else
151 			break;
152 		
153 		i++;
154 	}
155 }
156 
157 ///
158 private static string getGLErrorAsString(int _err)
159 {
160 	switch(_err)
161 	{
162 		case GL_INVALID_ENUM:
163 			return "GL_INVALID_ENUM";
164 		case GL_INVALID_VALUE:
165 			return "GL_INVALID_VALUE";
166 		case GL_INVALID_OPERATION:
167 			return "GL_INVALID_OPERATION";
168 		case GL_INVALID_FRAMEBUFFER_OPERATION:
169 			return "GL_INVALID_FRAMEBUFFER_OPERATION";
170 		case GL_OUT_OF_MEMORY:
171 			return "GL_OUT_OF_MEMORY";
172 		default:
173 			return "";
174 	}
175 }