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