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 ///
34 class GLObjException : DesGLException 
35 { 
36     ///
37     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
38     { super( msg, file, line ); } 
39 }
40 
41 ///
42 class GLBuffer : DesObject
43 {
44     mixin DES;
45     mixin ClassLogger;
46 
47 protected:
48     uint _id;
49     Target type;
50 
51     ///
52     size_t data_size;
53     ///
54     size_t element_count;
55 
56     ///
57     GLenum gltype() const nothrow @property { return cast(GLenum)type; }
58 
59 public:
60 
61     ///
62     enum Usage
63     {
64         STREAM_DRAW  = GL_STREAM_DRAW,  /// `GL_STREAM_DRAW`
65         STREAM_READ  = GL_STREAM_READ,  /// `GL_STREAM_READ`
66         STREAM_COPY  = GL_STREAM_COPY,  /// `GL_STREAM_COPY`
67         STATIC_DRAW  = GL_STATIC_DRAW,  /// `GL_STATIC_DRAW`
68         STATIC_READ  = GL_STATIC_READ,  /// `GL_STATIC_READ`
69         STATIC_COPY  = GL_STATIC_COPY,  /// `GL_STATIC_COPY`
70         DYNAMIC_DRAW = GL_DYNAMIC_DRAW, /// `GL_DYNAMIC_DRAW`
71         DYNAMIC_READ = GL_DYNAMIC_READ, /// `GL_DYNAMIC_READ`
72         DYNAMIC_COPY = GL_DYNAMIC_COPY  /// `GL_DYNAMIC_COPY`
73     }
74 
75     ///
76     enum Access
77     {
78         READ_ONLY  = GL_READ_ONLY,  /// `GL_READ_ONLY`
79         WRITE_ONLY = GL_WRITE_ONLY, /// `GL_WRITE_ONLY`
80         READ_WRITE = GL_READ_WRITE  /// `GL_READ_WRITE`
81     }
82 
83     ///
84     enum Target
85     {
86         ARRAY_BUFFER              = GL_ARRAY_BUFFER,              /// `GL_ARRAY_BUFFER`
87         ATOMIC_COUNTER_BUFFER     = GL_ATOMIC_COUNTER_BUFFER,     /// `GL_ATOMIC_COUNTER_BUFFER`
88         COPY_READ_BUFFER          = GL_COPY_READ_BUFFER,          /// `GL_COPY_READ_BUFFER`
89         COPY_WRITE_BUFFER         = GL_COPY_WRITE_BUFFER,         /// `GL_COPY_WRITE_BUFFER`
90         DRAW_INDIRECT_BUFFER      = GL_DRAW_INDIRECT_BUFFER,      /// `GL_DRAW_INDIRECT_BUFFER`
91         DISPATCH_INDIRECT_BUFFER  = GL_DISPATCH_INDIRECT_BUFFER,  /// `GL_DISPATCH_INDIRECT_BUFFER`
92         ELEMENT_ARRAY_BUFFER      = GL_ELEMENT_ARRAY_BUFFER,      /// `GL_ELEMENT_ARRAY_BUFFER`
93         PIXEL_PACK_BUFFER         = GL_PIXEL_PACK_BUFFER,         /// `GL_PIXEL_PACK_BUFFER`
94         PIXEL_UNPACK_BUFFER       = GL_PIXEL_UNPACK_BUFFER,       /// `GL_PIXEL_UNPACK_BUFFER`
95         SHADER_STORAGE_BUFFER     = GL_SHADER_STORAGE_BUFFER,     /// `GL_SHADER_STORAGE_BUFFER`
96         TEXTURE_BUFFER            = GL_TEXTURE_BUFFER,            /// `GL_TEXTURE_BUFFER`
97         TRANSFORM_FEEDBACK_BUFFER = GL_TRANSFORM_FEEDBACK_BUFFER, /// `GL_TRANSFORM_FEEDBACK_BUFFER`
98         UNIFORM_BUFFER            = GL_UNIFORM_BUFFER             /// `GL_UNIFORM_BUFFER`  
99     }
100 
101     /// glBindBuffer( trg, 0 )
102     static nothrow void unbind( Target trg )
103     { glBindBuffer( cast(GLenum)trg, 0 ); }
104 
105     ///
106     this( Target tp=Target.ARRAY_BUFFER )
107     {
108         checkGLCall!glGenBuffers( 1, &_id );
109         type = tp;
110 
111         logger = new InstanceLogger( this, format( "%d", _id ) );
112 
113         logger.Debug( "with type [%s]", type );
114     }
115 
116     final
117     {
118         /// glBindBuffer
119         void bind()
120         {
121             checkGLCall!glBindBuffer( gltype, _id );
122             debug logger.trace( "with type [%s]", type );
123         }
124 
125         /// glBindBuffer with self target to 0
126         void unbind()
127         {
128             checkGLCall!glBindBuffer( gltype, 0 );
129             debug logger.trace( "type [%s]", type );
130         }
131         @property uint id() const { return _id; }
132     }
133 
134     /++ calls when element count changed (in setUntypedData)
135      +
136      + See_Also: [setUntypedData](des/gl/base/object/GLBuffer.setUntypedData.html)
137      +/
138     Signal!size_t elementCountCB;
139 
140     /++ calls when element size changed (in setUntypedData)
141      +
142      + See_Also: [setUntypedData](des/gl/base/object/GLBuffer.setUntypedData.html)
143      +/
144     Signal!size_t elementSizeCB;
145 
146     /++ calls when data size changed (in setUntypedData)
147      +
148      + See_Also: [setUntypedData](des/gl/base/object/GLBuffer.setUntypedData.html)
149      +/
150     Signal!size_t dataSizeCB;
151 
152     /++ glBufferData, call signals 
153      +
154      + See_Also: 
155      + * [Signal!size_t elementCountCB](des/gl/base/object/GLBuffer.elementCountCB.html)
156      + * [Signal!size_t elementSizeCB](des/gl/base/object/GLBuffer.elementSizeCB.html)
157      + * [Signal!size_t dataSizeCB](des/gl/base/object/GLBuffer.dataSizeCB.html)
158      +/
159     void setUntypedData( in void[] data_arr, size_t element_size, Usage mem=Usage.DYNAMIC_DRAW )
160     {
161         auto size = data_arr.length;
162         if( !size ) throw new GLObjException( "set buffer data size is 0" );
163 
164         bind();
165         checkGLCall!glBufferData( gltype, size, data_arr.ptr, cast(GLenum)mem );
166         unbind();
167 
168         element_count = data_arr.length / element_size;
169         data_size = size;
170 
171         elementCountCB( element_count );
172         dataSizeCB( data_size );
173         elementSizeCB( element_size );
174 
175         debug logger.trace( "[%s]: size [%d], element size [%d], usage [%s]",
176                 type, size, element_size, mem );
177     }
178 
179     /// `setUntypedData( data_arr, E.sizeof, mem )`
180     void setData(E)( in E[] data_arr, Usage mem=Usage.DYNAMIC_DRAW )
181     { setUntypedData( data_arr, E.sizeof, mem ); }
182 
183     /// `glBufferSubData`
184     void setSubUntypedData( size_t offset, in void[] data_arr, size_t element_size )
185     {
186         auto size = data_arr.length;
187 
188         if( !size ) throw new GLObjException( "set sub buffer data size is 0" );
189         if( offset + size > data_size )
190             throw new GLObjException( "set sub buffer data: offset+size > data_size" );
191 
192         bind();
193         checkGLCall!glBufferSubData( gltype, offset, size, data_arr.ptr );
194         unbind();
195 
196         debug logger.trace( "[%s]: offset [%d], size [%d], element size [%d]",
197                 type, offset, size, element_size );
198     }
199 
200     /// `setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof )`
201     void setSubData(E)( size_t offset, in E[] data_arr )
202     { setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof ); }
203 
204     /// return ubtyped copy of buffer data
205     void[] getUntypedData()
206     {
207         bind();
208         auto mp = map( Access.READ_ONLY );
209         auto buf = new void[]( data_size );
210         memcpy( buf.ptr, mp, data_size );
211         unmap();
212         unbind();
213         return buf;
214     }
215 
216     /// cast untyped copy of buffer data to `E[]`
217     E[] getData(E)() { return cast(E[])getUntypedData(); }
218 
219     /// return untyped copy of buffer sub data
220     void[] getSubUntypedData( size_t offset, size_t length )
221     {
222         bind();
223         auto mp = mapRange( offset, length, Access.READ_ONLY );
224         auto buf = new void[]( length );
225         memcpy( buf.ptr, mp, length );
226         unmap();
227         unbind();
228         return buf;
229     }
230 
231     /// cast untyped copy of buffer sub data to `E[]`
232     E[] getSubData(E)( size_t offset, size_t count )
233     { return cast(E[])getSubUntypedData( E.sizeof * offset, E.sizeof * count ); }
234 
235     @property
236     {
237         const final
238         {
239             ///
240             size_t elementCount() { return element_count; }
241             ///
242             size_t dataSize() { return data_size; }
243 
244             /// calculated
245             size_t elementSize()
246             { return element_count ? data_size / element_count : 0; }
247         }
248     }
249 
250     /// glMapBuffer
251     void* map( Access access=Access.READ_ONLY )
252     {
253         debug logger.trace( "by access [%s]", access );
254         return checkGLCall!glMapBuffer( gltype, cast(GLenum)access );
255     }
256 
257     /// glMapBufferRange
258     void* mapRange( size_t offset, size_t length, Access access=Access.READ_ONLY )
259     { 
260         if( offset + length > data_size )
261             throw new GLObjException( "map buffer range: offset + length > data_size" );
262         debug logger.trace( "by access [%s]: offset [%d], length [%d]", access, offset, length );
263         return checkGLCall!glMapBufferRange( gltype, offset, length, cast(GLenum)access );
264     }
265 
266     /// glUnmapBuffer
267     void unmap()
268     {
269         checkGLCall!glUnmapBuffer( gltype );
270         debug logger.trace( "pass" );
271     }
272 
273 protected:
274 
275     override void selfDestroy()
276     {
277         checkGLCall!glDeleteBuffers( 1, &_id );
278         debug logger.Debug( "pass" );
279     }
280 }
281 
282 /// Vertex Array Object
283 final class GLVAO : DesObject
284 {
285     mixin DES;
286     mixin ClassLogger;
287 
288 protected:
289     uint _id;
290 
291 public:
292     ///
293     static nothrow void unbind(){ glBindVertexArray(0); }
294 
295     /// glGenVertexArrays
296     this()
297     {
298         checkGLCall!glGenVertexArrays( 1, &_id );
299         logger = new InstanceLogger( this, format( "%d", _id ) );
300         logger.Debug( "pass" );
301     }
302 
303     nothrow 
304     {
305         /// glBindVertexArray
306         void bind() 
307         { 
308             ntCheckGLCall!glBindVertexArray( _id );
309             debug logger.trace( "pass" );
310         }
311 
312         /// glEnableVertexAttribArray
313         void enable( int n )
314         {
315             debug scope(exit) logger.Debug( "[%d]", n );
316             if( n < 0 ) return;
317             bind();
318             ntCheckGLCall!glEnableVertexAttribArray( n ); 
319         }
320 
321         /// glDisableVertexAttribArray
322         void disable( int n )
323         {
324             debug scope(exit) logger.Debug( "[%d]", n );
325             if( n < 0 ) return;
326             bind();
327             ntCheckGLCall!glDisableVertexAttribArray( n ); 
328         }
329     }
330 
331 protected:
332 
333     /// glDeleteVertexArrays
334     override void selfDestroy() { glDeleteVertexArrays( 1, &_id ); }
335 
336 }
337 
338 /// 
339 class GLObject : DesObject
340 {
341     mixin DES;
342     mixin ClassLogger;
343 
344 protected:
345     ///
346     GLVAO vao;
347 
348     final
349     {
350 
351         /// glVertexAttribPointer
352         void setAttribPointer( GLBuffer buffer, int index, uint per_element,
353                 GLType attype, size_t stride, size_t offset, bool norm=false )
354         {
355             vao.enable( index );
356 
357             buffer.bind();
358             checkGLCall!glVertexAttribPointer( index, cast(int)per_element,
359                     cast(GLenum)attype, norm, cast(int)stride, cast(void*)offset );
360             buffer.unbind();
361 
362             logger.Debug( "VAO [%d], buffer [%d], "~
363                             "index [%d], per element [%d][%s]"~
364                             "%s%s",
365                             vao._id, buffer.id,
366                             index, per_element, attype, 
367                             stride != 0 ? ntFormat(", stride [%d], offset [%d]", stride, offset ) : "",
368                             norm ? ntFormat( ", norm [%s]", norm ) : "" );
369         }
370 
371         /// ditto
372         void setAttribPointer( GLBuffer buffer, int index, uint per_element,
373                 GLType attype, bool norm=false )
374         { setAttribPointer( buffer, index, per_element, attype, 0, 0, norm ); }
375     }
376 
377 public:
378 
379     ///
380     this()
381     {
382         vao = newEMM!GLVAO;
383         debug checkGL;
384     }
385 }