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