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 }*/