1 module gfm.opengl.vertex; 2 3 4 // Describing vertex, submitting geometry. 5 // Implement so called Vertex Arrays and VBO (which are Vertex Arrays + OpenGL buffers) 6 7 import std.string, 8 std.typetuple, 9 std.typecons, 10 std.traits; 11 12 import bindbc.opengl; 13 14 import gfm.math.vector, 15 gfm.opengl.opengl, 16 gfm.opengl.program, 17 gfm.opengl.buffer; 18 19 20 /// An UDA to specify an attribute which has to be normalized 21 struct Normalized 22 { 23 } 24 25 /** 26 * A VertexSpecification's role is to describe a Vertex structure. 27 * You must instantiate it with a compile-time struct describing your vertex format. 28 * 29 * Examples: 30 * -------------------- 31 * struct MyVertex 32 * { 33 * vec3f position; 34 * vec4f diffuse; 35 * float shininess; 36 * @Normalized vec2i uv; 37 * vec3f normal; 38 * } 39 * -------------------- 40 * Member names MUST match those used in the vertex shader as attributes. 41 */ 42 final class VertexSpecification(Vertex) 43 { 44 public 45 { 46 /// Creates a vertex specification. 47 /// The program is used to find the attribute location. 48 this(GLProgram program) 49 { 50 _program = program; 51 52 template isRWField(T, string M) 53 { 54 enum isRWField = __traits(compiles, __traits(getMember, Tgen!T(), M) = __traits(getMember, Tgen!T(), M)); 55 pragma(msg, T.stringof~"."~M~": "~(isRWField?"1":"0")); 56 } 57 58 alias TT = FieldTypeTuple!Vertex; 59 60 // Create all attribute description 61 foreach (member; __traits(allMembers, Vertex)) 62 { 63 // Nested struct have a context pointer 64 static assert(member != "this", 65 `Found a 'this' member in vertex struct. Use a 'static struct' instead.`); 66 67 enum fullName = "Vertex." ~ member; 68 mixin("alias T = typeof(" ~ fullName ~ ");"); 69 70 static if (staticIndexOf!(T, TT) != -1) 71 { 72 int location = program.attrib(member).location; 73 mixin("enum size_t offset = Vertex." ~ member ~ ".offsetof;"); 74 75 enum UDAs = __traits(getAttributes, member); 76 bool normalize = (staticIndexOf!(Normalized, UDAs) == -1); 77 78 // detect suitable type 79 int n; 80 GLenum glType; 81 toGLTypeAndSize!T(glType, n); 82 _attributes ~= VertexAttribute(n, offset, glType, location, normalize ? GL_TRUE : GL_FALSE); 83 84 } 85 } 86 } 87 88 /** 89 * Use this vertex specification. 90 * 91 * Divisor is the value passed to glVertexAttribDivisor. 92 * See $(WEB www.opengl.org/wiki/Vertex_Specification#Instanced_arrays) for details. 93 * 94 * Throws: $(D OpenGLException) on error. 95 */ 96 void use(GLuint divisor = 0) 97 { 98 // use every attribute 99 for (uint i = 0; i < _attributes.length; ++i) 100 _attributes[i].use(cast(GLsizei) vertexSize(), divisor); 101 } 102 103 /// Unuse this vertex specification. If you are using a VAO, you don't need to call it, 104 /// since the attributes would be tied to the VAO activation. 105 /// Throws: $(D OpenGLException) on error. 106 void unuse() 107 { 108 // unuse all the attributes 109 for (uint i = 0; i < _attributes.length; ++i) 110 _attributes[i].unuse(); 111 } 112 113 /// Returns the size of the Vertex; this size can be computer 114 /// after you added all your attributes 115 size_t vertexSize() pure const nothrow 116 { 117 return Vertex.sizeof; 118 } 119 } 120 121 private 122 { 123 GLProgram _program; 124 VertexAttribute[] _attributes; 125 } 126 } 127 128 /// Describes a single attribute in a vertex entry. 129 struct VertexAttribute 130 { 131 private 132 { 133 int n; 134 size_t offset; 135 GLenum glType; 136 GLint location; 137 GLboolean normalize; 138 bool divisorSet; 139 140 141 /// Use this attribute. 142 /// Throws: $(D OpenGLException) on error. 143 void use(GLsizei sizeOfVertex, GLuint divisor) 144 { 145 // fake attribute, do not enable 146 if (location == GLAttribute.fakeLocation) 147 return ; 148 149 if (divisor != 0) 150 divisorSet = true; 151 152 glEnableVertexAttribArray(location); 153 if (isIntegerType(glType)) 154 glVertexAttribIPointer(location, n, glType, sizeOfVertex, cast(GLvoid*)offset); 155 else 156 glVertexAttribPointer(location, n, glType, normalize, sizeOfVertex, cast(GLvoid*)offset); 157 if(divisorSet) 158 glVertexAttribDivisor(location, divisor); 159 runtimeCheck(); 160 } 161 162 /// Unuse this attribute. 163 /// Throws: $(D OpenGLException) on error. 164 void unuse() 165 { 166 // couldn't figure out if glDisableVertexAttribArray resets this, so play it safe 167 if(divisorSet) 168 glVertexAttribDivisor(location, 0); 169 divisorSet = false; 170 glDisableVertexAttribArray(location); 171 runtimeCheck(); 172 } 173 } 174 } 175 176 private 177 { 178 bool isIntegerType(GLenum t) 179 { 180 return (t == GL_BYTE 181 || t == GL_UNSIGNED_BYTE 182 || t == GL_SHORT 183 || t == GL_UNSIGNED_SHORT 184 || t == GL_INT 185 || t == GL_UNSIGNED_INT); 186 } 187 188 alias VectorTypes = TypeTuple!(byte, ubyte, short, ushort, int, uint, float, double); 189 enum GLenum[] VectorTypesGL = 190 [ 191 GL_BYTE, 192 GL_UNSIGNED_BYTE, 193 GL_SHORT, 194 GL_UNSIGNED_SHORT, 195 GL_INT, 196 GL_UNSIGNED_INT, 197 GL_FLOAT, 198 GL_DOUBLE 199 ]; 200 201 template isSupportedScalarType(T) 202 { 203 enum isSupportedScalarType = staticIndexOf!(Unqual!T, VectorTypes) != -1; 204 } 205 206 template typeToGLScalar(T) 207 { 208 alias U = Unqual!T; 209 enum index = staticIndexOf!(U, VectorTypes); 210 static if (index == -1) 211 { 212 static assert(false, "Could not use " ~ T.stringof ~ " in a vertex description"); 213 } 214 else 215 enum typeToGLScalar = VectorTypesGL[index]; 216 } 217 218 void toGLTypeAndSize(T)(out GLenum type, out int n) 219 { 220 static if (isSupportedScalarType!T) 221 { 222 type = typeToGLScalar!T; 223 n = 1; 224 } 225 else static if (isStaticArray!T) 226 { 227 type = typeToGLScalar!(typeof(T.init[0])); 228 n = T.length; 229 } 230 else 231 { 232 alias U = Unqual!T; 233 234 // is it a gfm.vector.Vector? 235 foreach(int t, S ; VectorTypes) 236 { 237 static if (is (U == Vector!(S, 2))) 238 { 239 type = VectorTypesGL[t]; 240 n = 2; 241 return; 242 } 243 244 static if (is (U == Vector!(S, 3))) 245 { 246 type = VectorTypesGL[t]; 247 n = 3; 248 return; 249 } 250 251 static if (is (U == Vector!(S, 4))) 252 { 253 type = VectorTypesGL[t]; 254 n = 4; 255 return; 256 } 257 } 258 259 assert(false, "Could not use " ~ T.stringof ~ " in a vertex description"); 260 } 261 } 262 } 263 264 265 /// A helper template to define a simple vertex type from a vector type. 266 /// By default the unique field will be name "position". 267 /// Note: it's important the struct isn't larger than the vector itself. 268 /// 269 /// Example: 270 /// VertexSpecification!(VertexPosition!vec3f); 271 /// 272 align(1) struct VertexPosition(Vec, string fieldName = "position") if (isVector!Vec) 273 { 274 align(1): 275 mixin("Vec " ~ fieldName ~ ";"); 276 static assert(VertexPosition.sizeof == Vec.sizeof); 277 } 278 279 unittest 280 { 281 static struct MyVertex 282 { 283 vec3f position; 284 vec4f diffuse; 285 float shininess; 286 @Normalized vec2i uv; 287 vec3f normal; 288 } 289 alias Test = VertexSpecification!MyVertex; 290 291 alias Test2 = VertexSpecification!(VertexPosition!vec3f); 292 }