1 /**
2  * Copyright:   Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:     $(Gabriel Gheorghe)
4  * License:     $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:      $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/graphics/opengl/vertex.d, _vertex.d)
6  * Documentation:
7  * Coverage:
8  */
9 module liberty.graphics.opengl.vertex;
10 version (__OpenGL__) :
11 import derelict.opengl;
12 import liberty.graphics.renderer;
13 import liberty.graphics.opengl.traits;
14 import std.string, std.typetuple, std.typecons, std.traits;
15 import liberty.math.vector;
16 import liberty.graphics.opengl.shader;
17 import liberty.graphics.video.vertex : VertexSpec;
18 /// Specify an attribute which has to be normalized.
19 struct Normalized;
20 /// Describe a Vertex structure.
21 /// You must instantiate it with a compile-time struct describing your vertex format.
22 final class GLVertexSpec(VERTEX) : VertexSpec!VERTEX {
23 	private {
24 		VertexAttribute[] _attributes;
25 	}
26 	/// Creates a vertex specification.
27 	this(GLShaderProgram shader_program) {
28 		alias TT = FieldTypeTuple!VERTEX;
29 		foreach (member; __traits(allMembers, VERTEX)) { // TODO: static foreach?
30 			static assert(member != "this", `Found a 'this' member in vertex struct. Use a 'static struct' instead.`);
31 			enum fullName = "VERTEX." ~ member;
32 			mixin("alias T = typeof(" ~ fullName ~ ");");
33 			static if (staticIndexOf!(T, TT) != -1) {
34 				int location = shader_program.attribute(member).location;
35 				mixin("enum size_t offset = VERTEX." ~ member ~ ".offsetof;");
36 				enum UDAs = __traits(getAttributes, member);
37 				bool normalize = false; //(staticIndexOf!(Normalized, UDAs) == -1); // TODO: -> not working @Normalized.
38 				int n;
39 				uint type;
40 				toGLTypeAndSize!T(type, n);
41 				_attributes ~= VertexAttribute(location, n, type, normalize ? 1 : 0, offset);
42 			}
43 		}
44 	}
45 	/// Use this vertex specification.
46 	override void use(uint divisor = 0) {
47 		for (uint i = 0; i < _attributes.length; i++) {
48 			_attributes[i].use(cast(int)vertexSize, divisor);
49 		}
50 	}
51 	/// Unuse this vertex specification.
52 	/// If you are using a VAO, you don't need to call it, since the attributes would be tied to the VAO activation.
53     /// Throws: $(D GLException) on error.
54 	override void unuse() {
55 	    for (uint i = 0; i < _attributes.length; i++) {
56 	        _attributes[i].unuse();
57 	    }
58 	}
59 }
60 /// Represent an OpenGL program attribute.
61 final class GLAttribute {
62 	private {
63         int _location;
64         uint _type;
65         int _size;
66         string _name;
67         bool _disabled;
68     }
69     ///
70     enum int fakeLocation = -1;
71     ///
72     this(string name, int location, uint type, GLsizei size) {
73         _name = name;
74         _location = location;
75         _type = type;
76         _size = size;
77         _disabled = false;
78     }
79     /// Creates a fake disabled attribute, designed to cope with attribute
80     /// that have been optimized out by the OpenGL driver, or those which do not exist.
81     this(string name) {
82         _disabled = true;
83         _location = fakeLocation;
84         _type = GL_FLOAT; // whatever
85         _size = 1;
86         //s_gl._logger.warningf("Faking attribute '%s' which either does not exist in the shader program, or was discarded by the driver as unused", name);
87     }
88     ///
89     int location() pure nothrow const @safe @nogc @property {
90         return _location;
91     }
92 	///
93     string name() pure nothrow const @safe @nogc @property {
94         return _name;
95     }
96 }
97 /// Describes a single attribute in a vertex entry.
98 struct VertexAttribute {
99 	private {
100 		int _location;
101 		int _n;
102 		uint _type;
103 		ubyte _normalize; // boolean
104 		size_t _offset;
105 		bool _divisorSet;
106 	}
107 	/// Use this attribute.
108     /// Throws: $(D GLException) on error.
109 	void use(int vertex_size, uint divisor) {
110 		if (_location == GLAttribute.fakeLocation) {
111 			return;
112 		}
113 		if (divisor != 0) {
114 			_divisorSet = true;
115 		}
116 		glEnableVertexAttribArray(_location);
117 		if (isVideoIntegerType(_type)) {
118 			glVertexAttribIPointer(_location, _n, _type, vertex_size, cast(void*)_offset);
119 		} else {
120 			glVertexAttribPointer(_location, _n, _type, _normalize, vertex_size, cast(void*)_offset);
121 		}
122 		GraphicsEngine.get.backend.runtimeCheck();
123 	}
124 	/// Unuse this attribute.
125     /// Throws: $(D GLException) on error.
126     void unuse() {
127         if (_divisorSet) {
128             glVertexAttribDivisor(_location, 0);
129         }
130         _divisorSet = false;
131         glDisableVertexAttribArray(_location);
132         GraphicsEngine.get.backend.runtimeCheck();
133     }
134 }
135 /// Define a simple vertex type from a vector type.
136 align(1) struct VertexPosition(VECTOR, string FIELD_NAME = "position") if (isVector!VECTOR) {
137 	align(1):
138 	mixin("VECTOR " ~ FIELD_NAME ~ ";");
139 	static assert(VertexPosition.sizeof == VECTOR.sizeof);
140 }
141 ///
142 unittest {
143 	static struct VertexTest {
144 		Vector3F position;
145 		Vector3F normal;
146 		Vector4F color;
147 		@Normalized Vector2I uv;
148 		float intensity;
149 	}
150 	alias Test1 = GLVertexSpec!VertexTest;
151 	alias Test2 = GLVertexSpec!(VertexPosition!Vector3F);
152 }