1 module moggle.xxx.vertices;
2 
3 import std.string;
4 import std.format;
5 import std.conv;
6 
7 import moggle.core.gl;
8 import moggle.core.vao;
9 import moggle.xxx.buffer;
10 
11 /++ Manages an OpenGL vertex array object (Vao).
12 
13 Keeps track of attributes of Vertices stored inside Buffers.
14 +/
15 final class Vertices {
16 
17 	private Vao vao_;
18 	@property auto ref vao() inout { return vao_; }
19 
20 	/// A vertex attribute.
21 	final class Attribute {
22 
23 		/// The name of the attribute.
24 		immutable string name;
25 
26 		/// The Buffer in which the data is stored.
27 		GenericBuffer buffer;
28 
29 		/// How and where the data is stored in the Buffer. (See moggle.core.vao.)
30 		AttributeParameters parameters;
31 
32 		private GLuint index_ = GLuint.max;
33 
34 		/// The index of the attribute if it is enabled, or GLuint.max if it is not.
35 		@property GLuint index() const { return index_; }
36 
37 		/// Check whether the attribute is enabled or not.
38 		@property bool enabled() const { return index_ != GLuint.max; }
39 
40 		/++ Enable the attribute.
41 
42 		Calls vao.setAttribute(index, buffer.vbo, parameters),
43 		which will call glEnableVertexAttribArray and glVertexAttribPointer.
44 		+/
45 		void enable(GLuint index) {
46 			vao.setAttribute(index, buffer.vbo, parameters);
47 			if (auto a = index in enabled_attributes_) a.index_ = GLuint.max;
48 			enabled_attributes_[index] = this;
49 			index_ = index;
50 		}
51 
52 		/++ Disable the attribute.
53 
54 		If enabled, calls vao.disableAttribute(index),
55 		which will call glDisableVertexAttribArray.
56 		+/
57 		void disable() {
58 			if (enabled) {
59 				vao.disableAttribute(index_);
60 				enabled_attributes_.remove(index_);
61 				index_ = GLuint.max;
62 			}
63 		}
64 
65 		/// Get a string representation of this attribute (just the name).
66 		override string toString() const {
67 			return name;
68 		}
69 
70 		private this(string n, GenericBuffer b, AttributeParameters p) {
71 			name = n; buffer = b; parameters = p;
72 		}
73 
74 	}
75 
76 	private Attribute[string] attributes_;
77 	private Attribute[GLuint] enabled_attributes_;
78 
79 	/++ The attributes of the Vertices.
80 	Returns: A delegate that can be foreach'ed over.
81 	+/
82 	@property auto attributes() { return attributes_.byValue(); }
83 
84 	/++ The attributes that are enabled.
85 	Returns: A delegate that can be foreach'ed over.
86 	+/
87 	@property auto enabledAttributes() { return enabled_attributes_.byValue(); }
88 
89 	/++ Gives you the vertex Attribute with the given name or index.
90 
91 	Throws: AttributeError when there is no such attribute.
92 	The exception message doesn't only tell the name/index of the attribute that doesn't exist,
93 	but also lists the attributes that do exist.
94 	+/
95 	auto attribute(string name, string file = __FILE__, size_t line = __LINE__) inout {
96 		if (auto a = name in attributes_) return *a;
97 		throw new AttributeError(format("No such attribute name: %s (Available are: %-(%s, %).)", name, attributes_.byKey()), file, line);
98 	}
99 
100 	/// ditto
101 	auto attribute(GLuint index, string file = __FILE__, size_t line = __LINE__) inout {
102 		if (auto a = index in enabled_attributes_) return *a;
103 		throw new AttributeError(format("No such attribute index: %s (Available are: %-(%s, %).)", index, enabled_attributes_.byKey()), file, line);
104 	}
105 
106 	/// Gives you a pointer to the Attribute with the given name or index, or null if it doesn't exist.
107 	auto hasAttribute(string name) inout {
108 		return name in attributes_;
109 	}
110 
111 	/// ditto
112 	auto hasAttribute(GLuint index) inout {
113 		return index in enabled_attributes_;
114 	}
115 
116 	/++ Add or change an attribute.
117 
118 	Does not yet call vao.setAttribute(...).
119 	It only stores the information in the attributes map, such that you can later
120 	call attribute(name).enable(index), which will call vao.setAttribute.
121 
122 	The second version automatically deduces the AttributeParameters
123 	using attributeParametersFor!T. (Defined in moggle.core.vao.)
124 	+/
125 	Attribute setAttribute()(in string name, GenericBuffer buffer, in AttributeParameters parameters) {
126 		return attributes_[name] = new Attribute(name, buffer, parameters);
127 	}
128 
129 	/// ditto
130 	Attribute setAttribute(T)(in string name, Buffer!T buffer) {
131 		return setAttribute(name, buffer, attributeParametersFor!T);
132 	}
133 
134 	/++ Automatically calls setAttribute for each member of the struct T.
135 
136 	The second version takes a map that maps the names of the members
137 	of T to the names of the attributes.
138 	If a name is mapped to null, the member is ignored.
139 
140 	Example:
141 	---
142 	struct Vertex { HVector4f position; Vector3f normal; HVector4 color; }
143 	auto b = new Buffer!Vertex(...);
144 	auto v = new Vertices;
145 
146 	// This:
147 	v.setAttributes(b);
148 	// does the same as:
149 	v.setAttribute("position", b, attributeParametersFor!(Vertex, "position"));
150 	v.setAttribute("normal", b, attributeParametersFor!(Vertex, "normal"));
151 	v.setAttribute("color", b, attributeParametersFor!(Vertex, "color"));
152 
153 	// Also, this:
154 	v.setAttributes(b, ["position":"pos", "normal":null]);
155 	// does the same as:
156 	v.setAttribute("pos", b, attributeParametersFor!(Vertex, "position"));
157 	v.setAttribute("color", b, attributeParametersFor!(Vertex, "color"));
158 	---
159 	+/
160 	void setAttributes(T)(Buffer!T buffer) if (is(T == struct)) {
161 		foreach (i, _; T.init.tupleof) {
162 			enum m = T.tupleof[i].stringof;
163 			enum member = m[m.lastIndexOf('.') + 1 .. $];
164 			setAttribute(member, buffer, attributeParametersFor!(T, member));
165 		}
166 	}
167 
168 	/// ditto
169 	void setAttributes(T)(Buffer!T buffer, string[string] names) if (is(T == struct)) {
170 		foreach (i, _; T.init.tupleof) {
171 			enum m = T.tupleof[i].stringof;
172 			enum member = m[m.lastIndexOf('.') + 1 .. $];
173 			string name = member;
174 			if (auto p = name in names) name = *p;
175 			if (name) setAttribute(name, buffer, attributeParametersFor!(T, member));
176 		}
177 	}
178 
179 	/// Bind the Vao. (Calls vao.bind().)
180 	void bind() {
181 		vao_.bind();
182 	}
183 
184 }
185 
186 /// The error that is thrown when Vertices.attribute(name) doesn't find an attribute with that name.
187 class AttributeError : Exception {
188 	this(string what, string file = __FILE__, size_t line = __LINE__) {
189 		super(what, file, line);
190 	}
191 };
192