1 module des.gl.draw;
2 
3 import des.gl.general;
4 import des.gl.buffer;
5 import des.gl.vao;
6 
7 ///
8 class GLObjException : DesGLException
9 {
10     ///
11     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
12     { super( msg, file, line ); }
13 }
14 
15 ///
16 class GLDrawObject : DesObject
17 {
18     mixin DES;
19     mixin ClassLogger;
20 
21 protected:
22     ///
23     GLVAO vao;
24 
25     /// override this for any action before draw
26     void preDraw() {}
27 
28 public:
29 
30     ///
31     this()
32     {
33         vao = newEMM!GLVAO;
34         debug checkGL;
35     }
36 
37     ///
38     enum DrawMode
39     {
40         POINTS                   = GL_POINTS,                  /// `GL_POINTS`
41         LINES                    = GL_LINES,                   /// `GL_LINES`
42         LINE_STRIP               = GL_LINE_STRIP,              /// `GL_LINE_STRIP`
43         LINE_LOOP                = GL_LINE_LOOP,               /// `GL_LINE_LOOP`
44         TRIANGLES                = GL_TRIANGLES,               /// `GL_TRIANGLES`
45         TRIANGLE_STRIP           = GL_TRIANGLE_STRIP,          /// `GL_TRIANGLE_STRIP`
46         TRIANGLE_FAN             = GL_TRIANGLE_FAN,            /// `GL_TRIANGLE_FAN`
47         LINES_ADJACENCY          = GL_LINES_ADJACENCY,         /// `GL_LINES_ADJACENCY`
48         LINE_STRIP_ADJACENCY     = GL_LINE_STRIP_ADJACENCY,    /// `GL_LINE_STRIP_ADJACENCY`
49         TRIANGLES_ADJACENCY      = GL_TRIANGLES_ADJACENCY,     /// `GL_TRIANGLES_ADJACENCY`
50         TRIANGLE_STRIP_ADJACENCY = GL_TRIANGLE_STRIP_ADJACENCY /// `GL_TRIANGLE_STRIP_ADJACENCY`
51     }
52 
53 protected:
54 
55     /// `glVertexAttribPointer`
56     void setAttribPointer( GLArrayBuffer buffer, int index, uint per_element,
57             GLType attype, size_t stride, size_t offset, bool norm=false )
58     {
59         vao.enable( index );
60 
61         buffer.bind(); scope(exit) buffer.unbind();
62 
63         checkGLCall!glVertexAttribPointer( index, cast(int)per_element,
64                 cast(GLenum)attype, norm, cast(int)stride, cast(void*)offset );
65 
66         logger.Debug( "VAO [%d], buffer [%d], "~
67                         "index [%d], per element [%d][%s]"~
68                         "%s%s",
69                         vao.id, buffer.id,
70                         index, per_element, attype,
71                         stride != 0 ? ntFormat(", stride [%d], offset [%d]", stride, offset ) : "",
72                         norm ? ntFormat( ", norm [%s]", norm ) : "" );
73     }
74 
75     /// ditto
76     void setAttribPointer( GLArrayBuffer buffer, int index, uint per_element,
77             GLType attype, bool norm=false )
78     { setAttribPointer( buffer, index, per_element, attype, 0, 0, norm ); }
79 
80     /// ditto
81     void setAttribPointer( GLArrayBuffer buffer, in GLAttrib attr )
82     {
83         setAttribPointer( buffer, attr.location, attr.elements,
84                             attr.type, attr.stride, attr.offset, attr.norm );
85     }
86 
87     /// `glDrawArraysInstancedBaseInstance`
88     void drawArrays( DrawMode mode, uint start, uint count,
89                      uint instcount=1, uint baseinst=0 )
90     {
91         vao.bind();
92         preDraw();
93         checkGLCall!glDrawArraysInstancedBaseInstance( mode,
94                 start, count, instcount, baseinst );
95         debug logger.trace( "mode [%s], start [%d], count [%d], " ~
96                             "instance count [%d], base instance [%d]",
97                             mode, start, count, instcount, baseinst );
98     }
99 
100     ///
101     void multiDrawArraysIndirect( DrawMode mode, GLDrawIndirectBuffer dib,
102                                   size_t offset=0, uint count=0 )
103     {
104         enforce( dib !is null, new GLObjException( "draw indirect buffer is null" ) );
105         vao.bind();
106         preDraw();
107         dib.bind();
108         auto dibes = dib.elementSize;
109         checkGLCall!glMultiDrawArraysIndirect( mode,
110                 cast(const(void)*)(offset*dibes), count, dibes );
111         debug logger.trace( "mode [%s]", mode );
112     }
113 
114     /// `glDrawElementsInstancedBaseVertexBaseInstance`
115     void drawElements( DrawMode mode, GLElementArrayBuffer eab,
116                        uint instcount=1, uint basevert=0, uint baseinst=0 )
117     {
118         preElementDraw( eab );
119         checkGLCall!glDrawElementsInstancedBaseVertexBaseInstance( mode,
120                         eab.elementCount, cast(GLenum)eab.type, null,
121                         instcount, basevert, baseinst );
122         debug logger.trace( "mode [%s]", mode );
123     }
124 
125     ///
126     void multiDrawElementsIndirect( DrawMode mode, GLElementArrayBuffer eab,
127                                     GLDrawIndirectBuffer dib, uint offset=0,
128                                     uint count=0 )
129     {
130         enforce( dib !is null, new GLObjException( "draw indirect buffer is null" ) );
131         preElementDraw( eab );
132         dib.bind();
133         auto dibes = dib.elementSize;
134         checkGLCall!glMultiDrawElementsIndirect( mode, eab.type,
135                 cast(void*)(offset*dibes),
136                 (count?count:(dib.elementCount-offset)), dibes );
137         debug logger.trace( "mode [%s]", mode );
138     }
139 
140 private:
141 
142     void preElementDraw( GLElementArrayBuffer eab )
143     {
144         enforce( eab !is null, new GLObjException( "element array buffer is null" ) );
145 
146         vao.bind();
147         eab.bind();
148 
149         preDraw();
150     }
151 }
152 
153 ///
154 struct GLMeshData
155 {
156     ///
157     GLDrawObject.DrawMode draw_mode;
158 
159     ///
160     uint num_vertices;
161 
162     ///
163     uint[] indices;
164 
165     ///
166     GLAttrib[] attribs;
167 
168     ///
169     static struct Buffer
170     {
171         ///
172         void[] data;
173         /// numbers of attributes in `GLMeshData.attribs` array
174         uint[] attribs;
175     }
176 
177     ///
178     Buffer[] buffers;
179 }
180 
181 ///
182 class GLMeshObject : GLDrawObject
183 {
184 protected:
185 
186     ///
187     uint num_vertices;
188 
189     ///
190     GLElementArrayBuffer indices;
191 
192     ///
193     GLArrayBuffer[] arrays;
194 
195     DrawMode draw_mode;
196 
197 public:
198 
199     ///
200     this( in GLMeshData md ) { prepareMesh( md ); }
201 
202 protected:
203 
204     /// with `draw_mode` and `num_vertices`
205     void drawArrays() { super.drawArrays( draw_mode, 0, num_vertices ); }
206 
207     /// with `draw_mode` and `indices.elementCount`
208     void drawElements() { super.drawElements( draw_mode, indices ); }
209 
210     /// creates buffers, set vertices count, etc
211     void prepareMesh( in GLMeshData data )
212     {
213         draw_mode = data.draw_mode;
214 
215         num_vertices = data.num_vertices;
216 
217         if( data.indices.length )
218         {
219             indices = newEMM!GLElementArrayBuffer();
220             indices.set( data.indices );
221             logger.Debug( "indices count: ", data.indices.length );
222             import std.algorithm;
223             logger.Debug( "indices max: ", reduce!max( data.indices ) );
224         }
225 
226         foreach( bufdata; data.buffers )
227             if( auto buf = prepareBuffer( bufdata, data.attribs ) )
228                 arrays ~= buf;
229     }
230 
231     /// create buffer, set attrib pointer, set data if exists
232     GLArrayBuffer prepareBuffer( in GLMeshData.Buffer bd, in GLAttrib[] attrlist )
233     {
234         if( bd.data is null )
235         {
236             logger.warn( "buffer is defined, but has no data" );
237             return null;
238         }
239 
240         if( bd.attribs is null )
241         {
242             logger.warn( "buffer is defined, but has no attribs" );
243             return null;
244         }
245 
246         auto buf = createArrayBuffer();
247         buf.setRaw( bd.data, attrlist[bd.attribs[0]].dataSize,
248                     GLBuffer.Usage.STATIC_DRAW );
249 
250         foreach( attr_no; bd.attribs )
251         {
252             auto attr = attrlist[attr_no];
253             setAttribPointer( buf, attr );
254             logger.Debug( "set attrib '%s' at loc '%d'", attr.name, attr.location );
255         }
256 
257         return buf;
258     }
259 
260     /// override if want to create specific buffers
261     GLArrayBuffer createArrayBuffer() { return newEMM!GLArrayBuffer(); }
262 }