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