1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.gl.base.object;
26 
27 import std.c..string;
28 
29 import derelict.opengl3.gl3;
30 
31 import des.gl.base.type;
32 
33 import des.util.data.type;
34 
35 ///
36 class GLObjException : DesGLException 
37 { 
38     ///
39     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
40     { super( msg, file, line ); } 
41 }
42 
43 ///
44 class GLBuffer : DesObject
45 {
46     mixin DES;
47     mixin ClassLogger;
48 
49 protected:
50     uint _id;
51     Target type;
52 
53     ///
54     size_t data_size;
55     ///
56     uint element_count;
57 
58     ///
59     GLenum gltype() const nothrow @property { return cast(GLenum)type; }
60 
61 public:
62 
63     ///
64     enum Usage
65     {
66         STREAM_DRAW  = GL_STREAM_DRAW,  /// `GL_STREAM_DRAW`
67         STREAM_READ  = GL_STREAM_READ,  /// `GL_STREAM_READ`
68         STREAM_COPY  = GL_STREAM_COPY,  /// `GL_STREAM_COPY`
69         STATIC_DRAW  = GL_STATIC_DRAW,  /// `GL_STATIC_DRAW`
70         STATIC_READ  = GL_STATIC_READ,  /// `GL_STATIC_READ`
71         STATIC_COPY  = GL_STATIC_COPY,  /// `GL_STATIC_COPY`
72         DYNAMIC_DRAW = GL_DYNAMIC_DRAW, /// `GL_DYNAMIC_DRAW`
73         DYNAMIC_READ = GL_DYNAMIC_READ, /// `GL_DYNAMIC_READ`
74         DYNAMIC_COPY = GL_DYNAMIC_COPY  /// `GL_DYNAMIC_COPY`
75     }
76 
77     ///
78     enum Access
79     {
80         READ_ONLY  = GL_READ_ONLY,  /// `GL_READ_ONLY`
81         WRITE_ONLY = GL_WRITE_ONLY, /// `GL_WRITE_ONLY`
82         READ_WRITE = GL_READ_WRITE  /// `GL_READ_WRITE`
83     }
84 
85     ///
86     enum Target
87     {
88         ARRAY_BUFFER              = GL_ARRAY_BUFFER,              /// `GL_ARRAY_BUFFER`
89         ATOMIC_COUNTER_BUFFER     = GL_ATOMIC_COUNTER_BUFFER,     /// `GL_ATOMIC_COUNTER_BUFFER`
90         COPY_READ_BUFFER          = GL_COPY_READ_BUFFER,          /// `GL_COPY_READ_BUFFER`
91         COPY_WRITE_BUFFER         = GL_COPY_WRITE_BUFFER,         /// `GL_COPY_WRITE_BUFFER`
92         DRAW_INDIRECT_BUFFER      = GL_DRAW_INDIRECT_BUFFER,      /// `GL_DRAW_INDIRECT_BUFFER`
93         DISPATCH_INDIRECT_BUFFER  = GL_DISPATCH_INDIRECT_BUFFER,  /// `GL_DISPATCH_INDIRECT_BUFFER`
94         ELEMENT_ARRAY_BUFFER      = GL_ELEMENT_ARRAY_BUFFER,      /// `GL_ELEMENT_ARRAY_BUFFER`
95         PIXEL_PACK_BUFFER         = GL_PIXEL_PACK_BUFFER,         /// `GL_PIXEL_PACK_BUFFER`
96         PIXEL_UNPACK_BUFFER       = GL_PIXEL_UNPACK_BUFFER,       /// `GL_PIXEL_UNPACK_BUFFER`
97         SHADER_STORAGE_BUFFER     = GL_SHADER_STORAGE_BUFFER,     /// `GL_SHADER_STORAGE_BUFFER`
98         TEXTURE_BUFFER            = GL_TEXTURE_BUFFER,            /// `GL_TEXTURE_BUFFER`
99         TRANSFORM_FEEDBACK_BUFFER = GL_TRANSFORM_FEEDBACK_BUFFER, /// `GL_TRANSFORM_FEEDBACK_BUFFER`
100         UNIFORM_BUFFER            = GL_UNIFORM_BUFFER             /// `GL_UNIFORM_BUFFER`  
101     }
102 
103     /// `glBindBuffer( trg, 0 )`
104     static nothrow void unbind( Target trg )
105     { glBindBuffer( cast(GLenum)trg, 0 ); }
106 
107     ///
108     this( Target tp=Target.ARRAY_BUFFER )
109     {
110         checkGLCall!glGenBuffers( 1, &_id );
111         type = tp;
112 
113         logger = new InstanceLogger( this, format( "%d", _id ) );
114 
115         logger.Debug( "type [%s]", type );
116     }
117 
118     final
119     {
120         /// `glBindBuffer`
121         void bind()
122         {
123             checkGLCall!glBindBuffer( gltype, _id );
124             debug logger.trace( "type [%s]", type );
125         }
126 
127         /// `glBindBuffer` with self target to 0
128         void unbind()
129         {
130             checkGLCall!glBindBuffer( gltype, 0 );
131             debug logger.trace( "type [%s]", type );
132         }
133 
134         ///
135         uint id() @property const { return _id; }
136     }
137 
138     /++ calls when element count changed (in setUntypedData)
139      +
140      + See_Also: `setUntypedData`
141      +/
142     Signal!uint elementCountCB;
143 
144     /++ calls when element size changed (in setUntypedData)
145      +
146      + See_Also: `setUntypedData`
147      +/
148     Signal!uint elementSizeCB;
149 
150     /++ calls when data size changed (in setUntypedData)
151      +
152      + See_Also: `setUntypedData`
153      +/
154     Signal!size_t dataSizeCB;
155 
156     /++ `bind`, `glBufferData`, `unbind`
157      +  call signals:
158      +  `elementCountCB`
159      +  `elementSizeCB`
160      +  `dataSizeCB`
161      +/
162     void setUntypedData( in void[] data_arr, size_t element_size, Usage mem=Usage.DYNAMIC_DRAW )
163     {
164         auto size = data_arr.length;
165         if( !size ) throw new GLObjException( "set buffer data size is 0" );
166 
167         bind();
168         checkGLCall!glBufferData( gltype, size, data_arr.ptr, cast(GLenum)mem );
169 
170         element_count = cast(uint)( data_arr.length / element_size );
171         data_size = size;
172 
173         elementCountCB( cast(uint)element_count );
174         dataSizeCB( data_size );
175         elementSizeCB( cast(uint)element_size );
176 
177         debug logger.trace( "[%s]: size [%d], element size [%d], usage [%s]",
178                 type, size, element_size, mem );
179 
180         unbind();
181     }
182 
183     /// `setUntypedData( data_arr, E.sizeof, mem )`
184     void setData(E)( in E[] data_arr, Usage mem=Usage.DYNAMIC_DRAW )
185     { setUntypedData( data_arr, E.sizeof, mem ); }
186 
187     /// `bind`, `glBufferSubData`, `unbind`
188     void setSubUntypedData( size_t offset, in void[] data_arr, size_t element_size )
189     {
190         auto size = data_arr.length;
191 
192         if( !size ) throw new GLObjException( "set sub buffer data size is 0" );
193         if( offset + size > data_size )
194             throw new GLObjException( "set sub buffer data: offset+size > data_size" );
195 
196         bind();
197         checkGLCall!glBufferSubData( gltype, offset, size, data_arr.ptr );
198 
199         debug logger.trace( "[%s]: offset [%d], size [%d], element size [%d]",
200                 type, offset, size, element_size );
201         
202         unbind();
203     }
204 
205     /// `setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof )`
206     void setSubData(E)( size_t offset, in E[] data_arr )
207     { setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof ); }
208 
209     /// return ubtyped copy of buffer data
210     void[] getUntypedData()
211     {
212         auto mp = mapUntypedData( Access.READ_ONLY );
213         auto buf = new void[]( data_size );
214         memcpy( buf.ptr, mp.ptr, data_size );
215         unmap();
216         return buf;
217     }
218 
219     /// cast untyped copy of buffer data to `E[]`
220     E[] getData(E)() { return cast(E[])getUntypedData(); }
221 
222     /// return untyped copy of buffer sub data
223     void[] getSubUntypedData( size_t offset, size_t length )
224     {
225         auto mp = mapUntypedDataRange( offset, length, Access.READ_ONLY );
226         auto buf = new void[]( length );
227         memcpy( buf.ptr, mp.ptr, length );
228         unmap();
229         return buf;
230     }
231 
232     /// cast untyped copy of buffer sub data to `E[]`
233     E[] getSubData(E)( size_t offset, size_t count )
234     { return cast(E[])getSubUntypedData( E.sizeof * offset, E.sizeof * count ); }
235 
236     @property
237     {
238         const final
239         {
240             ///
241             uint elementCount() { return element_count; }
242             ///
243             size_t dataSize() { return data_size; }
244 
245             /// calculated
246             uint elementSize()
247             { return cast(uint)( element_count ? data_size / element_count : 0 ); }
248         }
249     }
250 
251     /// `bind`, `glMapBuffer`, `unbind`
252     ArrayData mapUntypedData( Access access=Access.READ_ONLY )
253     {
254         debug logger.trace( "by access [%s]", access );
255         bind();
256         scope(exit) unbind();
257         return ArrayData( data_size, checkGLCall!glMapBuffer( gltype, cast(GLenum)access ) );
258     }
259 
260     /// `bind`, `glMapBufferRange`, `unbind`
261     ArrayData mapUntypedDataRange( size_t offset, size_t length, Access access=Access.READ_ONLY )
262     { 
263         if( offset + length > data_size )
264             throw new GLObjException( "map buffer range: offset + length > data_size" );
265         debug logger.trace( "by access [%s]: offset [%d], length [%d]", access, offset, length );
266         bind();
267         scope(exit) unbind();
268         return ArrayData( length, checkGLCall!glMapBufferRange( gltype, offset, length, cast(GLenum)access ) );
269     }
270 
271     ///
272     AlienArray!E mapData(E)( Access access )
273     { return getTypedArray!E( mapUntypedData( access ) ); }
274 
275     ///
276     AlienArray!E mapDataRange(E)( size_t offset, size_t length, Access access ) 
277     { return getTypedArray!E( mapUntypedDataRange( offset * E.sizeof, length * E.sizeof, access ) ); }
278 
279     /// `bind`, `glUnmapBuffer`, `unbind`
280     void unmap()
281     {
282         bind();
283         checkGLCall!glUnmapBuffer( gltype );
284         debug logger.trace( "pass" );
285         unbind();
286     }
287 
288 protected:
289 
290     override void selfDestroy()
291     {
292         checkGLCall!glDeleteBuffers( 1, &_id );
293         debug logger.Debug( "pass" );
294     }
295 }
296 
297 /// Vertex Array Object
298 final class GLVAO : DesObject
299 {
300     mixin DES;
301     mixin ClassLogger;
302 
303 protected:
304     uint _id;
305 
306     int[int] aaset;
307 
308 public:
309     ///
310     static nothrow void unbind(){ glBindVertexArray(0); }
311 
312     /// `glGenVertexArrays`
313     this()
314     {
315         checkGLCall!glGenVertexArrays( 1, &_id );
316         logger = new InstanceLogger( this, format( "%d", _id ) );
317         logger.Debug( "pass" );
318     }
319 
320     ///
321     int[] enabled() const @property { return aaset.keys; }
322 
323     nothrow 
324     {
325         /// `glBindVertexArray`
326         void bind() 
327         { 
328             ntCheckGLCall!glBindVertexArray( _id );
329             debug logger.trace( "pass" );
330         }
331 
332         /// `glEnableVertexAttribArray`
333         void enable( int n )
334         {
335             debug scope(exit) logger.Debug( "[%d]", n );
336             if( n < 0 ) return;
337             bind();
338             ntCheckGLCall!glEnableVertexAttribArray( n ); 
339             aaset[n] = n;
340         }
341 
342         /// `glDisableVertexAttribArray`
343         void disable( int n )
344         {
345             debug scope(exit) logger.Debug( "[%d]", n );
346             if( n < 0 ) return;
347             bind();
348             ntCheckGLCall!glDisableVertexAttribArray( n ); 
349             aaset.remove(n);
350         }
351     }
352 
353 protected:
354 
355     /// `glDeleteVertexArrays`
356     override void selfDestroy() { glDeleteVertexArrays( 1, &_id ); }
357 
358 }
359 
360 /// 
361 class GLObject : DesObject
362 {
363     mixin DES;
364     mixin ClassLogger;
365 
366 protected:
367     ///
368     GLVAO vao;
369 
370     final
371     {
372         /// `glVertexAttribPointer`
373         void setAttribPointer( GLBuffer buffer, int index, uint per_element,
374                 GLType attype, size_t stride, size_t offset, bool norm=false )
375         {
376             vao.enable( index );
377 
378             buffer.bind();
379             checkGLCall!glVertexAttribPointer( index, cast(int)per_element,
380                     cast(GLenum)attype, norm, cast(int)stride, cast(void*)offset );
381 
382             logger.Debug( "VAO [%d], buffer [%d], "~
383                             "index [%d], per element [%d][%s]"~
384                             "%s%s",
385                             vao._id, buffer.id,
386                             index, per_element, attype, 
387                             stride != 0 ? ntFormat(", stride [%d], offset [%d]", stride, offset ) : "",
388                             norm ? ntFormat( ", norm [%s]", norm ) : "" );
389 
390             buffer.unbind();
391         }
392 
393         /// ditto
394         void setAttribPointer( GLBuffer buffer, int index, uint per_element,
395                 GLType attype, bool norm=false )
396         { setAttribPointer( buffer, index, per_element, attype, 0, 0, norm ); }
397     }
398 
399     /// override this for any action before draw
400     void preDraw() {}
401 
402     ///
403     void drawArrays( DrawMode mode, uint count, uint start=0 )
404     {
405         vao.bind();
406         preDraw();
407         checkGLCall!glDrawArrays( mode, start, count );
408         debug logger.trace( "mode [%s], count [%d]", mode, count );
409     }
410 
411     ///
412     void drawElements( DrawMode mode, uint count )
413     {
414         vao.bind();
415         preDraw();
416         checkGLCall!glDrawElements( mode, count, GL_UNSIGNED_INT, null );
417         debug logger.trace( "mode [%s], count [%d]", mode, count );
418     }
419 
420 public:
421 
422     ///
423     this()
424     {
425         vao = newEMM!GLVAO;
426         debug checkGL;
427     }
428 
429     ///
430     enum DrawMode
431     {
432         POINTS                   = GL_POINTS,                  /// `GL_POINTS`
433         LINES                    = GL_LINES,                   /// `GL_LINES`
434         LINE_STRIP               = GL_LINE_STRIP,              /// `GL_LINE_STRIP`
435         LINE_LOOP                = GL_LINE_LOOP,               /// `GL_LINE_LOOP`
436         TRIANGLES                = GL_TRIANGLES,               /// `GL_TRIANGLES`
437         TRIANGLE_STRIP           = GL_TRIANGLE_STRIP,          /// `GL_TRIANGLE_STRIP`
438         TRIANGLE_FAN             = GL_TRIANGLE_FAN,            /// `GL_TRIANGLE_FAN`
439         LINES_ADJACENCY          = GL_LINES_ADJACENCY,         /// `GL_LINES_ADJACENCY`
440         LINE_STRIP_ADJACENCY     = GL_LINE_STRIP_ADJACENCY,    /// `GL_LINE_STRIP_ADJACENCY`
441         TRIANGLES_ADJACENCY      = GL_TRIANGLES_ADJACENCY,     /// `GL_TRIANGLES_ADJACENCY`
442         TRIANGLE_STRIP_ADJACENCY = GL_TRIANGLE_STRIP_ADJACENCY /// `GL_TRIANGLE_STRIP_ADJACENCY`
443     }
444 }