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 }