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