source: trunk/src/gui/gui_gfx_renderer.cc @ 480

Last change on this file since 480 was 472, checked in by epyon, 10 years ago
  • additional uniforms
  • R16F and R32F format support
  • error fixes (clang)
File size: 12.2 KB
Line 
1// Copyright (C) 2015 ChaosForge Ltd
2// http://chaosforge.org/
3//
4// This file is part of Nova libraries.
5// For conditions of distribution and use, see copying.txt file in root folder.
6
7#include "nv/gui/gui_gfx_renderer.hh"
8
9#include "nv/interface/device.hh"
10#include "nv/interface/context.hh"
11#include "nv/core/logging.hh"
12
13static const char *nv_gui_vertex_shader = R"(
14#version 130
15in vec2 nv_position;
16in vec2 nv_texcoord;
17in vec4 nv_color;
18out vec4 v_color;
19out vec2 v_texcoord;
20uniform mat4 nv_m_projection;
21void main(void)
22{
23        gl_Position = nv_m_projection * vec4(nv_position.x, nv_position.y, 0.0, 1.0);
24        v_texcoord  = nv_texcoord;
25        v_color     = nv_color;
26}
27)";
28
29static const char *nv_gui_fragment_shader = R"(
30#version 130
31in vec4 v_color;
32in vec2 v_texcoord;
33uniform sampler2D nv_t_diffuse;
34void main(void)
35{
36        vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );
37        gl_FragColor   = v_color * tex_color;
38}
39)";
40
41
42using namespace nv;
43using namespace nv::gui;
44
45struct vertex
46{
47        ivec2 position;
48        vec2  texcoord;
49        vec4  color;
50        vertex() {}
51        vertex( const nv::ivec2& cr, const nv::vec2& tc, const nv::vec4& c )
52                : position( cr ), texcoord( tc ), color( c )
53        {
54        }
55};
56
57const ivec2 atlas_size = ivec2( 1024, 1024 );
58
59struct gui_quad
60{
61        vertex vtx[6];
62        gui_quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::vec4& color )
63        {
64                set_color( color );
65                vtx[0].position = coorda;
66                vtx[1].position = nv::ivec2( coorda.x, coordb.y );
67                vtx[2].position = coordb;
68                vtx[3].position = coordb;
69                vtx[4].position = nv::ivec2( coordb.x, coorda.y );
70                vtx[5].position = coorda;
71        }
72        gui_quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::vec4& color, const nv::vec2& tcoorda, const nv::vec2& tcoordb )
73        {
74                set_color( color );
75                vtx[0].position = coorda;
76                vtx[1].position = nv::ivec2( coorda.x, coordb.y );
77                vtx[2].position = coordb;
78                vtx[3].position = coordb;
79                vtx[4].position = nv::ivec2( coordb.x, coorda.y );
80                vtx[5].position = coorda;
81                vtx[0].texcoord = tcoorda;
82                vtx[1].texcoord = nv::vec2( tcoorda.x, tcoordb.y );
83                vtx[2].texcoord = tcoordb;
84                vtx[3].texcoord = tcoordb;
85                vtx[4].texcoord = nv::vec2( tcoordb.x, tcoorda.y );
86                vtx[5].texcoord = tcoorda;
87        }
88        gui_quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::ivec2& coordc, const nv::ivec2& coordd, const nv::vec4& color )
89        {
90                set_color( color );
91                vtx[0].position = coorda;
92                vtx[1].position = coordb;
93                vtx[2].position = coordc;
94                vtx[3].position = coordc;
95                vtx[4].position = coordd;
96                vtx[5].position = coorda;
97        }
98        inline void set_color( const nv::vec4& color )
99        {
100                vtx[0].color = color; vtx[1].color = color;     vtx[2].color = color;
101                vtx[3].color = color; vtx[4].color = color;     vtx[5].color = color;
102        }
103};
104
105
106class screen_render_data : public render_data
107{
108public:
109        screen_render_data( context* actx, nv::size_t initial_size )
110                : buffer( actx, VERTEX_BUFFER, DYNAMIC_DRAW, initial_size ), ctx( actx ), varray(), shader()
111        {
112
113        }
114        ~screen_render_data()
115        {
116                ctx->get_device()->release( shader );
117                ctx->release( varray );
118        }
119
120        nv::sliced_buffer<gui_quad> buffer;
121        nv::context*      ctx;
122        nv::texture       tex;
123        nv::vertex_array  varray;
124        nv::program       shader;
125};
126
127class element_render_data : public render_data
128{
129public:
130        element_render_data( nv::sliced_buffer<gui_quad>* cbuffer )
131                : buffer( cbuffer )
132        {
133        }
134
135        nv::buffer_slice< gui_quad > buffer;
136};
137
138gfx_renderer::gfx_renderer( window* w )
139        : m_window( w )
140        , m_atlas( atlas_size, 4 )
141        , m_reupload( true )
142{
143        NV_LOG_TRACE( "Creating GUI renderer..." );
144        m_context = w->get_context();
145        m_area.dim( dimension( w->get_width(), w->get_height() ) );
146        region white = m_atlas.get_region( ivec2( 3, 3 ) );
147        size_t wsize = m_atlas.get_depth() * 4 * 4;
148        uint8* wfill = new uint8[m_atlas.get_depth() * 4 * 4];
149        raw_fill( wfill, wfill + wsize, 255 );
150        white.pos = ivec2();
151        m_atlas.set_region( white, wfill );
152        delete[] wfill;
153
154        NV_LOG_TRACE( "Creating render data..." );
155        screen_render_data* sr = new screen_render_data( w->get_context(), 1024 );
156        m_render_data = sr;
157        sr->shader = m_window->get_device()->create_program( nv_gui_vertex_shader, nv_gui_fragment_shader );
158        m_scene_state.get_camera().set_ortho( 0.0f, float( m_window->get_width() ), float( m_window->get_height() ), 0.0f );
159
160        sr->varray = m_window->get_context()->create_vertex_array();
161        buffer vb = sr->buffer.get_buffer();
162        m_window->get_context()->add_vertex_buffers< vertex >( sr->varray, vb, false );
163
164        nv::sampler sampler( nv::sampler::LINEAR, nv::sampler::CLAMP_TO_EDGE );
165        sr->tex = m_window->get_device()->create_texture( m_atlas.get_size(), image_format( nv::RGBA, nv::UBYTE ), sampler, nullptr );
166
167        m_render_state.depth_test.enabled = false;
168        m_render_state.culling.enabled = false;
169        m_render_state.blending.enabled = true;
170        m_render_state.blending.src_rgb_factor = blending::SRC_ALPHA;
171        m_render_state.blending.dst_rgb_factor = blending::ONE_MINUS_SRC_ALPHA;
172        m_render_state.blending.src_alpha_factor = blending::SRC_ALPHA;
173        m_render_state.blending.dst_alpha_factor = blending::ONE_MINUS_SRC_ALPHA;
174        NV_LOG_TRACE( "GUI Renderer created" );
175}
176
177texture_font* gfx_renderer::get_font( nv::size_t name ) const
178{
179        if ( name >= m_fonts.size() ) return nullptr;
180        return m_fonts[name];
181}
182
183const image_info* gfx_renderer::get_image( nv::size_t name ) const
184{
185        if ( name >= m_images.size() ) return nullptr;
186        return &m_images[name];
187}
188
189nv::size_t gfx_renderer::load_font( const string_view& filename, nv::size_t size )
190{
191        string128 id( filename );
192        id.append( size );
193        auto i = m_font_names.find( id );
194        if ( i != m_font_names.end() )
195        {
196                return i->second;
197        }
198        size_t result = m_fonts.size();
199        texture_font* f = new texture_font( &m_atlas, filename.data(), static_cast<float>( size ) );
200        f->load_glyphs( "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ " );
201        m_fonts.push_back( f );
202        m_reupload = true;
203        m_font_names[ id ] = result;
204        return result;
205}
206
207nv::size_t gfx_renderer::load_image( const string_view& filename )
208{
209        auto i = m_image_names.find( filename );
210        if ( i != m_image_names.end() )
211        {
212                return i->second;
213        }
214        size_t result = m_images.size();
215        image_data* data = m_window->get_device()->create_image_data( filename );
216        // TODO: Repitching
217        NV_ASSERT( data->get_depth() == 4, "depth != 4" );
218        region r = m_atlas.get_region( data->get_size() );
219        m_atlas.set_region( r, data->get_data() );
220        m_images.emplace_back( vec2( r.pos ) / vec2( atlas_size ), vec2( r.size + r.pos ) / vec2( atlas_size ), r.size );
221        delete data;
222        m_reupload = true;
223        m_image_names[filename] = result;
224        return result;
225}
226
227void gfx_renderer::redraw( element* e, uint32 )
228{
229        screen_render_data* sr = reinterpret_cast< screen_render_data* >( m_render_data );
230        if ( e->m_render_data == nullptr )
231        {
232                e->m_render_data = new element_render_data( &sr->buffer );
233        }
234        element_render_data* er = reinterpret_cast< element_render_data* >( e->m_render_data );
235        size_t size_before = er->buffer.data().size();
236
237        vector< gui_quad >& qvec = er->buffer.lock();
238
239        qvec.clear();
240        rectangle abs = e->m_absolute;
241        if ( e->m_absolute != m_area )
242        {
243                int border = 0;
244                vec4 color;
245                string128 path;
246                const char* stext[] = { "", "selected", "hover" };
247                const char* selector = stext[border];
248                if ( e->m_flags[HOVER] )    selector = stext[2];
249                if ( e->m_flags[SELECTED] ) selector = stext[1];
250
251                if ( m_style.get( e, "skin", selector, path ) )
252                {
253                        size_t image_id = load_image( path );
254                        const image_info* image = get_image( image_id );
255                        if ( image )
256                        {
257                                color = vec4( 2, 2, 2, 1 );
258                                ivec2 isize3 = image->size / 3;
259                                ivec2 isize3x = ivec2( isize3.x, 0 );
260                                ivec2 isize3y = ivec2( 0, isize3.y );
261                                vec2 tsize = ( image->t2 - image->t1 );
262                                vec2 tsize3 = ( image->t2 - image->t1 ) / 3.0f;
263                                vec2 tsize32 = ( image->t2 - image->t1 ) * ( 2.0f / 3.0f );
264                                vec2 tsizex = vec2( tsize.x, 0.0f );
265                                //vec2 tsizey = vec2( 0.0f, tsize.y );
266                                vec2 tsize3x = vec2( tsize3.x, 0.0f );
267                                vec2 tsize3y = vec2( 0.0f, tsize3.y );
268                                vec2 tsize3x2 = vec2( tsize32.x, 0.0f );
269                                vec2 tsize3y2 = vec2( 0.0f, tsize32.y );
270
271                                rectangle inner = abs.shrinked( isize3 );
272                                qvec.emplace_back( abs.ul, inner.ul, color, image->t1, image->t1 + tsize3 );
273                                qvec.emplace_back( abs.ul + isize3x, inner.ur(), color, image->t1 + tsize3x, image->t1 + tsize3x2 + tsize3y );
274                                qvec.emplace_back( abs.ur() - isize3x, inner.ur() + isize3x, color, image->t1 + tsize3x2, image->t1 + tsizex + tsize3y );
275
276                                qvec.emplace_back( abs.ul + isize3y, inner.ll(), color, image->t1 + tsize3y, image->t1 + tsize3y2 + tsize3x );
277                                qvec.emplace_back( inner.ul, inner.lr, color, image->t1 + tsize3, image->t1 + tsize32 );
278                                qvec.emplace_back( inner.ur(), inner.lr + isize3x, color, image->t1 + tsize3 + tsize3x, image->t1 + tsize32 + tsize3x );
279
280
281                                qvec.emplace_back( abs.ll() - isize3y, inner.ll() + isize3y, color, image->t1 + tsize3y2, image->t1 + tsize3y2 + tsize3 );
282                                qvec.emplace_back( inner.ll(), abs.lr - isize3x, color, image->t1 + tsize3y2 + tsize3x, image->t1 + tsize32 + tsize3y );
283                                qvec.emplace_back( inner.lr, abs.lr, color, image->t1 + tsize32, image->t1 + tsize );
284
285
286                                //                              qvec.emplace_back( abs.ul, abs.ll(), inner.ll(), inner.ul, color );
287                                //                              qvec.emplace_back( inner.ur(), inner.lr, abs.lr, abs.ur(), color );
288                                //                              qvec.emplace_back( inner.ll(), abs.ll(), abs.lr, inner.lr, color );
289                                abs = inner;
290
291                        }
292
293                }
294                else
295                {
296
297                        if ( m_style.get( e, "border", selector, border ) && m_style.get( e, "border_color", selector, color ) )
298                        {
299                                rectangle inner = abs.shrinked( border );
300                                qvec.emplace_back( abs.ul, inner.ul, inner.ur(), abs.ur(), color );
301                                qvec.emplace_back( abs.ul, abs.ll(), inner.ll(), inner.ul, color );
302                                qvec.emplace_back( inner.ur(), inner.lr, abs.lr, abs.ur(), color );
303                                qvec.emplace_back( inner.ll(), abs.ll(), abs.lr, inner.lr, color );
304                                abs = inner;
305                        }
306
307                        if ( m_style.get( e, "background_color", selector, color ) )
308                        {
309                                qvec.emplace_back( abs.ul, abs.lr, color );
310                        }
311                }
312
313                e->m_text;
314                if ( !e->m_text.empty() )
315                {
316                        if ( m_style.get( e, "text_color", selector, color ) && m_style.get( e, "text_font", selector, path ) && m_style.get( e, "text_size", selector, border ) )
317                        {
318                                size_t font_id = load_font( path, size_t( border ) );
319                                texture_font* font = get_font( font_id );
320                                position p = abs.ul + position( 0, border );
321                                for ( char c : e->m_text )
322                                {
323                                        const texture_glyph* g = font->get_glyph( static_cast<uint16>( c ) );
324                                        if ( g )
325                                        {
326                                                position gp = position( g->offset.x, -g->offset.y );
327                                                position p2 = p + g->size + gp;
328                                                qvec.emplace_back( p + gp, p2, color, g->tl, g->br );
329                                                p += ivec2( g->advance );
330                                        }
331                                }
332                        }
333                }
334        }
335
336        if ( size_before != er->buffer.data().size() )
337        {
338                sr->buffer.reset();
339        }
340}
341
342void gfx_renderer::on_hover_change( element* e )
343{
344        if ( e->m_flags[DIRTY_HOVER] )
345        {
346                NV_LOG_DEBUG( "on_hover_change" );
347                e->m_flags[DIRTY] = true;
348        }
349}
350
351void gfx_renderer::on_select_change( element* e )
352{
353        if ( e->m_flags[DIRTY_SELECT] )
354        {
355                NV_LOG_DEBUG( "on_select_change" );
356                e->m_flags[DIRTY] = true;
357        }
358}
359
360
361void gfx_renderer::draw( element* e )
362{
363        element_render_data* er = reinterpret_cast< element_render_data* >( e->m_render_data );
364        er->buffer.commit();
365}
366
367void gfx_renderer::draw()
368{
369        screen_render_data* sr = reinterpret_cast< screen_render_data* >( m_render_data );
370
371        if ( m_reupload )
372        {
373                m_context->update( sr->tex, m_atlas.get_data() );
374                m_reupload = false;
375        }
376
377        if ( sr->buffer.commit() )
378        {
379                buffer vb = sr->buffer.get_buffer();
380                m_context->replace_vertex_buffer( sr->varray, vb, false );
381        }
382        m_context->bind( sr->tex, TEX_DIFFUSE );
383        m_context->draw( TRIANGLES, m_render_state, m_scene_state, sr->shader, sr->varray, sr->buffer.get_size() * 6 );
384}
385
386gfx_renderer::~gfx_renderer()
387{
388        for ( auto p : m_fonts )
389        {
390                delete p;
391        }
392        if ( m_render_data )
393        {
394                m_context->get_device()->release( reinterpret_cast< screen_render_data* >( m_render_data )->tex );
395                delete m_render_data;
396        }
397}
398
399void nv::gui::gfx_renderer::on_style_change( element* e )
400{
401        m_style.load_flags( e );
402}
Note: See TracBrowser for help on using the repository browser.