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, 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 enum fullName = "Vertex." ~ member; 65 mixin("alias T = typeof(" ~ fullName ~ ");"); 66 67 static if (staticIndexOf!(T, TT) != -1) 68 { 69 int location = program.attrib(member).location; 70 mixin("enum size_t offset = Vertex." ~ member ~ ".offsetof;"); 71 72 enum UDAs = __traits(getAttributes, member); 73 bool normalize = (staticIndexOf!(Normalized, UDAs) == -1); 74 75 // detect suitable type 76 int n; 77 GLenum glType; 78 toGLTypeAndSize!T(glType, n); 79 _attributes ~= VertexAttribute(n, offset, glType, location, normalize ? GL_TRUE : GL_FALSE); 80 81 } 82 } 83 } 84 85 /** 86 * Use this vertex specification. 87 * 88 * Divisor is the value passed to glVertexAttribDivisor. 89 * See $(WEB www.opengl.org/wiki/Vertex_Specification#Instanced_arrays) for details. 90 * 91 * Throws: $(D OpenGLException) on error. 92 */ 93 void use(GLuint divisor = 0) 94 { 95 // use every attribute 96 for (uint i = 0; i < _attributes.length; ++i) 97 _attributes[i].use(_gl, cast(GLsizei) vertexSize(), divisor); 98 } 99 100 /// Unuse this vertex specification. If you are using a VAO, you don't need to call it, 101 /// since the attributes would be tied to the VAO activation. 102 /// Throws: $(D OpenGLException) on error. 103 void unuse() 104 { 105 // unuse all the attributes 106 for (uint i = 0; i < _attributes.length; ++i) 107 _attributes[i].unuse(_gl); 108 } 109 110 /// Returns the size of the Vertex; this size can be computer 111 /// after you added all your attributes 112 size_t vertexSize() pure const nothrow 113 { 114 return Vertex.sizeof; 115 } 116 } 117 118 private 119 { 120 OpenGL _gl; 121 GLProgram _program; 122 VertexAttribute[] _attributes; 123 } 124 } 125 126 /// Describes a single attribute in a vertex entry. 127 struct VertexAttribute 128 { 129 private 130 { 131 int n; 132 size_t offset; 133 GLenum glType; 134 GLint location; 135 GLboolean normalize; 136 bool divisorSet; 137 138 139 /// Use this attribute. 140 /// Throws: $(D OpenGLException) on error. 141 void use(OpenGL gl, GLsizei sizeOfVertex, GLuint divisor) 142 { 143 // fake attribute, do not enable 144 if (location == GLAttribute.fakeLocation) 145 return ; 146 147 if (divisor != 0) 148 divisorSet = true; 149 150 glEnableVertexAttribArray(location); 151 if (isIntegerType(glType)) 152 glVertexAttribIPointer(location, n, glType, sizeOfVertex, cast(GLvoid*)offset); 153 else 154 glVertexAttribPointer(location, n, glType, normalize, sizeOfVertex, cast(GLvoid*)offset); 155 if(divisorSet) 156 glVertexAttribDivisor(location, divisor); 157 gl.runtimeCheck(); 158 } 159 160 /// Unuse this attribute. 161 /// Throws: $(D OpenGLException) on error. 162 void unuse(OpenGL gl) 163 { 164 // couldn't figure out if glDisableVertexAttribArray resets this, so play it safe 165 if(divisorSet) 166 glVertexAttribDivisor(location, 0); 167 divisorSet = false; 168 glDisableVertexAttribArray(location); 169 gl.runtimeCheck(); 170 } 171 } 172 } 173 174 private 175 { 176 bool isIntegerType(GLenum t) 177 { 178 return (t == GL_BYTE 179 || t == GL_UNSIGNED_BYTE 180 || t == GL_SHORT 181 || t == GL_UNSIGNED_SHORT 182 || t == GL_INT 183 || t == GL_UNSIGNED_INT); 184 } 185 186 alias VectorTypes = TypeTuple!(byte, ubyte, short, ushort, int, uint, float, double); 187 enum GLenum[] VectorTypesGL = 188 [ 189 GL_BYTE, 190 GL_UNSIGNED_BYTE, 191 GL_SHORT, 192 GL_UNSIGNED_SHORT, 193 GL_INT, 194 GL_UNSIGNED_INT, 195 GL_FLOAT, 196 GL_DOUBLE 197 ]; 198 199 template typeToGLScalar(T) 200 { 201 alias U = Unqual!T; 202 enum index = staticIndexOf!(U, VectorTypes); 203 static if (index == -1) 204 { 205 static assert(false, "Could not use " ~ T.stringof ~ " in a vertex description"); 206 } 207 else 208 enum typeToGLScalar = VectorTypesGL[index]; 209 } 210 211 void toGLTypeAndSize(T)(out GLenum type, out int n) 212 { 213 static if (isStaticArray!T) 214 { 215 type = typeToGLScalar(typeof(T[0])); 216 n = U.length; 217 } 218 else 219 { 220 alias U = Unqual!T; 221 222 // is it a gfm.vector.Vector? 223 foreach(int t, S ; VectorTypes) 224 { 225 static if (is (U == Vector!(S, 2))) 226 { 227 type = VectorTypesGL[t]; 228 n = 2; 229 return; 230 } 231 232 static if (is (U == Vector!(S, 3))) 233 { 234 type = VectorTypesGL[t]; 235 n = 3; 236 return; 237 } 238 239 static if (is (U == Vector!(S, 4))) 240 { 241 type = VectorTypesGL[t]; 242 n = 4; 243 return; 244 } 245 } 246 247 assert(false, "Could not use " ~ T.stringof ~ " in a vertex description"); 248 } 249 } 250 }