-/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
+/* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
#ifndef VG_AUDIO_H
#define VG_AUDIO_H
#endif
#endif
+#define AUDIO_FRAME_SIZE 512
+#define AUDIO_MIX_FRAME_SIZE 256
+
#define AUDIO_CHANNELS 32
#define AUDIO_LFOS 8
+#define AUDIO_FILTERS 16
#define AUDIO_FLAG_LOOP 0x1
-#define AUDIO_FLAG_SPACIAL_3D 0x2
+#define AUDIO_FLAG_SPACIAL_3D 0x4
+#define AUDIO_FLAG_AUTO_START 0x8
/* Vorbis will ALWAYS use the maximum amount of channels it can */
//#define AUDIO_FLAG_MONO 0x100 NOTE: This is the default, so its not used
}
vg_audio = { .volume_console = 1.0f };
+#include "vg/vg_audio_dsp.h"
static struct vg_profile
_vg_prof_audio_decode = {.mode = k_profile_mode_accum,
.name = "[T2] audio_decode()"},
_vg_prof_audio_mix = {.mode = k_profile_mode_accum,
.name = "[T2] audio_mix()"},
+ _vg_prof_dsp = {.mode = k_profile_mode_accum,
+ .name = "[T2] dsp_process()"},
vg_prof_audio_decode,
- vg_prof_audio_mix;
+ vg_prof_audio_mix,
+ vg_prof_audio_dsp;
/*
* These functions are called from the main thread and used to prevent bad
u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size );
+ vg_dsp_init();
+
SDL_AudioSpec spec_desired, spec_got;
spec_desired.callback = audio_mixer_callback;
spec_desired.channels = 2;
spec_desired.format = AUDIO_F32;
spec_desired.freq = 44100;
spec_desired.padding = 0;
- spec_desired.samples = 512;
+ spec_desired.samples = AUDIO_FRAME_SIZE;
spec_desired.silence = 0;
spec_desired.size = 0;
spec_desired.userdata = NULL;
vg_audio.sdl_output_device =
- SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,
- SDL_AUDIO_ALLOW_SAMPLES_CHANGE );
+ SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,0 );
if( vg_audio.sdl_output_device )
{
VG_STATIC void vg_audio_free(void)
{
+ vg_dsp_free();
SDL_CloseAudioDevice( vg_audio.sdl_output_device );
}
return n;
}
-static float audio_lfo_pull_sample( audio_lfo *lfo )
+static inline float audio_lfo_pull_sample( audio_lfo *lfo )
{
lfo->time ++;
vg_profile_end( &_vg_prof_audio_decode );
}
-static void audio_channel_mix( audio_channel *ch,
- float *buffer, u32 frame_count )
+static void audio_channel_mix( audio_channel *ch, float *buffer )
{
- u32 frames_write = frame_count;
-
- u32 buffer_length = frame_count;
+ u32 buffer_length = AUDIO_MIX_FRAME_SIZE;
if( ch->_.sampling_rate != 1.0f )
{
- buffer_length = ceilf( (float)frame_count * ch->_.sampling_rate ) + 1;
+ float l = ceilf( (float)(AUDIO_MIX_FRAME_SIZE) * ch->_.sampling_rate );
+ buffer_length = l+1;
}
- float *pcf = alloca( buffer_length * 2 * sizeof(float) );
+ float pcf[ AUDIO_MIX_FRAME_SIZE * 2 * 2 ];
audio_channel_get_samples( ch, buffer_length, pcf );
- vg_profile_begin( &_vg_prof_audio_mix );
-
- if( ch->_.lfo )
- ch->_.lfo->time = ch->_.lfo->time_startframe;
float framevol_l = 1.0f,
framevol_r = 1.0f;
framevol_r *= (vol * 0.5f) * (1.0f + pan);
}
- for( u32 j=0; j<frame_count; j++ )
- {
- if( ch->volume_movement < ch->_.volume_rate )
- {
- ch->volume_movement ++;
+ vg_profile_begin( &_vg_prof_audio_mix );
- float movement_t = ch->volume_movement;
- movement_t /= (float)ch->_.volume_rate;
+ float volume_movement = ch->volume_movement;
+ float const fvolume_rate = vg_maxf( 1.0f, ch->_.volume_rate );
+ const float inv_volume_rate = 1.0f/fvolume_rate;
- ch->_.volume = vg_lerpf( ch->volume_movement_start,
- ch->_.volume_target,
- movement_t );
- }
+ float volume = ch->_.volume;
+ const float volume_start = ch->volume_movement_start;
+ const float volume_target = ch->_.volume_target;
- float vol_norm = ch->_.volume * ch->_.volume;
+ for( u32 j=0; j<AUDIO_MIX_FRAME_SIZE; j++ )
+ {
+ /*
+ * there is some REALLY weird behaviour with minss,
+ * i cannot begin to guess what the cause is, but the bahaviour when
+ * the second argument is not 1.0 would seemingly tripple or up to
+ * eight times this routine.
+ *
+ * the times it would happen are when moving from empty space into areas
+ * with geometry. in the bvh for skate rift.
+ *
+ * it should be completely unrelated to this, but somehow -- it is
+ * effecting the speed of minss. and severely at that too.
+ **/
+
+ volume_movement += 1.0f;
+ float movement_t = volume_movement * inv_volume_rate;
+ movement_t = vg_minf( volume_movement, 1.0f );
+ volume = vg_lerpf( volume_start, volume_target, movement_t );
+
+ float vol_norm = volume * volume;
if( ch->_.lfo )
vol_norm *= 1.0f + audio_lfo_pull_sample(ch->_.lfo) * ch->_.lfo_amount;
- float vol_l = vol_norm * framevol_l,
- vol_r = vol_norm * framevol_r,
+ float vol_l = vol_norm * framevol_l,
+ vol_r = vol_norm * framevol_r,
sample_l,
sample_r;
buffer[ j*2+1 ] += sample_r * vol_r;
}
+ ch->volume_movement += AUDIO_MIX_FRAME_SIZE;
+ ch->volume_movement = VG_MIN( ch->volume_movement, ch->_.volume_rate );
+ ch->_.volume = volume;
+
vg_profile_end( &_vg_prof_audio_mix );
}
lfo->editble_state_write_mask = 0x00;
}
-
+ dsp_update_tunings();
audio_unlock();
/*
audio_channel *ch = &vg_audio.channels[i];
if( ch->activity == k_channel_activity_alive )
- audio_channel_mix( ch, pOut32F, frame_count );
+ {
+ if( ch->_.lfo )
+ ch->_.lfo->time = ch->_.lfo->time_startframe;
+
+ u32 remaining = frame_count,
+ subpos = 0;
+
+ while( remaining )
+ {
+ audio_channel_mix( ch, pOut32F+subpos );
+ remaining -= AUDIO_MIX_FRAME_SIZE;
+ subpos += AUDIO_MIX_FRAME_SIZE*2;
+ }
+ }
}
+ vg_profile_begin( &_vg_prof_dsp );
+
+ for( int i=0; i<frame_count; i++ )
+ vg_dsp_process( pOut32F + i*2, pOut32F + i*2 );
+
+ vg_profile_end( &_vg_prof_dsp );
+
/*
* Relinquishing conditions
* ------------------------------------------------------------------
* ----------------------------------------------- */
vg_profile_increment( &_vg_prof_audio_decode );
vg_profile_increment( &_vg_prof_audio_mix );
+ vg_profile_increment( &_vg_prof_dsp );
+
vg_prof_audio_mix = _vg_prof_audio_mix;
vg_prof_audio_decode = _vg_prof_audio_decode;
+ vg_prof_audio_dsp = _vg_prof_dsp;
+
vg_audio.samples_last = frame_count;
+ if( vg_audio.debug_ui )
+ {
+ vg_dsp_update_texture();
+ }
+
audio_unlock();
}
audio_lock();
+ glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture );
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 256, 256,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ vg_dsp.view_texture_buffer );
+
/*
* Profiler
* -----------------------------------------------------------------------
float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
- &vg_prof_audio_mix }, 2,
+ &vg_prof_audio_mix,
+ &vg_prof_audio_dsp}, 3,
budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
- 250, 0 }, 3 );
+ 512, 0 }, 3 );
char perf[128];
/* Draw UI */
- vg_uictx.cursor[0] = 258;
- vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12;
+ vg_uictx.cursor[0] = 512 + 8;
+ vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12+12;
vg_uictx.cursor[2] = 150;
vg_uictx.cursor[3] = 12;
+
+ ui_rect view_thing = { 4, vg.window_y-512-4, 512, 512 };
+ ui_push_image( view_thing, vg_dsp.view_texture );
float mb1 = 1024.0f*1024.0f,
usage = vg_linear_get_cur( vg_audio.audio_pool ) / mb1,
--- /dev/null
+#ifndef VG_AUDIO_DSP_H
+#define VG_AUDIO_DSP_H
+
+#define VG_GAME
+#include "vg/vg.h"
+
+static struct vg_dsp
+{
+ float *buffer;
+ u32 allocations;
+
+ u8 *view_texture_buffer;
+ GLuint view_texture;
+
+ float echo_distances[14],
+ echo_tunings[8],
+ reverb_wet_mix,
+ reverb_dry_mix;
+}
+vg_dsp;
+
+static float *dsp_allocate( u32 samples )
+{
+ samples = vg_align4( samples );
+
+ if( vg_dsp.allocations + samples > (1024*1024)/4 )
+ vg_fatal_exit_loop( "too much dsp" );
+
+ float *buf = &vg_dsp.buffer[ vg_dsp.allocations ];
+ vg_dsp.allocations += samples;
+
+ return buf;
+}
+
+
+/*
+ * filters
+ * ----------------------------------------------
+ */
+
+struct dsp_delay
+{
+ u32 length, cur;
+ float *buffer;
+};
+
+struct dsp_lpf
+{
+ float exponent;
+ float *buffer;
+};
+
+struct dsp_schroeder
+{
+ struct dsp_delay M;
+ float gain;
+};
+
+static inline void dsp_read_delay( struct dsp_delay *delay, float *s )
+{
+ u32 index = delay->cur+1;
+
+ if( index >= delay->length )
+ index = 0;
+
+ *s = delay->buffer[ index ];
+}
+
+static inline void dsp_write_delay( struct dsp_delay *delay, float *s )
+{
+ u32 index = delay->cur;
+ delay->buffer[ index ] = *s;
+
+ delay->cur ++;
+
+ if( delay->cur >= delay->length )
+ delay->cur = 0;
+}
+
+static void dsp_init_delay( struct dsp_delay *delay, float length )
+{
+ delay->length = 44100.0f * length;
+ delay->cur = 0;
+ delay->buffer = dsp_allocate( delay->length );
+
+ for( int i=0; i<delay->length; i++ )
+ delay->buffer[i] = 0.0f;
+}
+
+static void dsp_update_lpf( struct dsp_lpf *lpf, float freq )
+{
+ lpf->exponent = 1.0f-expf( -(1.0f/44100.0f) * 2.0f * VG_PIf * freq );
+}
+
+static void dsp_init_lpf( struct dsp_lpf *lpf, float freq )
+{
+ lpf->buffer = dsp_allocate( 4 );
+ lpf->buffer[0] = 0.0f;
+ dsp_update_lpf( lpf, freq );
+}
+
+static inline void dsp_write_lpf( struct dsp_lpf *lpf, float *s )
+{
+ float diff = *s - lpf->buffer[0];
+ lpf->buffer[0] += diff * lpf->exponent;
+}
+
+static inline void dsp_read_lpf( struct dsp_lpf *lpf, float *s )
+{
+ *s = lpf->buffer[0];
+}
+
+static void dsp_init_schroeder( struct dsp_schroeder *sch, float length,
+ float gain )
+{
+ dsp_init_delay( &sch->M, length );
+ sch->gain = gain;
+}
+
+static inline void dsp_process_schroeder( struct dsp_schroeder *sch,
+ float *input, float *output )
+{
+ float dry = *input;
+
+ float delay_output;
+ dsp_read_delay( &sch->M, &delay_output );
+
+ float feedback_attenuated = delay_output * sch->gain,
+ input_feedback_sum = dry + feedback_attenuated;
+
+ dsp_write_delay( &sch->M, &input_feedback_sum );
+
+ *output = delay_output - input_feedback_sum*sch->gain;
+}
+
+/* temporary global design */
+static struct dsp_lpf __lpf_mud_free;
+static struct dsp_delay __echos[8];
+static struct dsp_lpf __echos_lpf[8];
+static struct dsp_schroeder __diffusion_chain[8];
+
+static void vg_dsp_init( void )
+{
+ vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 );
+ vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 );
+
+ vg_acquire_thread_sync();
+ glGenTextures( 1, &vg_dsp.view_texture );
+ glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, vg_dsp.view_texture_buffer );
+ vg_tex2d_nearest();
+ vg_release_thread_sync();
+
+
+ /* temporary global design */
+
+
+ dsp_init_lpf( &__lpf_mud_free, 125.0f );
+
+ float sizes[] =
+ { 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f, 256.0f };
+
+ float reflection_variance = 0.1f;
+
+ for( int i=0; i<8; i++ )
+ {
+ float reflection_time = ((sizes[i])/343.0f) * 1000.0f;
+
+ float var = 1.0f + (vg_randf()*2.0f - 1.0f) * reflection_variance,
+ total = reflection_time * var;
+
+ dsp_init_delay( &__echos[i], total / 1000.0f );
+
+ float freq = vg_lerpf( 800.0f, 350.0f, sizes[i] / 256.0f );
+ dsp_init_lpf( &__echos_lpf[i], freq );
+ }
+
+ float diffusions[] = { 187.0f, 159.0f, 143.0f, 121.0f,
+ 79.0f, 57.0f, 27.0f, 11.0f };
+
+ for( int i=0; i<8; i++ )
+ {
+ dsp_init_schroeder( __diffusion_chain+i, diffusions[i]/1000.0f, 0.7f );
+ }
+}
+
+static void vg_dsp_process( float *stereo_in, float *stereo_out )
+{
+ float in_total = (stereo_in[0]+stereo_in[1])*0.5f;
+ float recieved = 0.0f;
+
+ for( int i=0; i<8; i++ )
+ {
+ float echo;
+ dsp_read_delay( __echos+i, &echo );
+ dsp_write_lpf( __echos_lpf+i, &echo );
+ dsp_read_lpf( __echos_lpf+i, &echo );
+
+ recieved += echo * vg_dsp.echo_tunings[i]*0.98;
+ }
+
+ float diffused = recieved;
+
+ for( int i=0; i<8; i++ )
+ {
+ dsp_process_schroeder( __diffusion_chain+i, &diffused, &diffused );
+ }
+
+ float diffuse_mix = vg_dsp.reverb_wet_mix;
+ diffuse_mix = vg_lerpf( recieved, diffused, diffuse_mix );
+ float total = in_total + diffuse_mix;
+
+ float low_mud;
+ dsp_write_lpf( &__lpf_mud_free, &total );
+ dsp_read_lpf( &__lpf_mud_free, &low_mud );
+
+ total -= low_mud;
+
+ for( int i=0; i<8; i++ )
+ dsp_write_delay( __echos+i, &total );
+
+ stereo_out[0] = stereo_in[0]*vg_dsp.reverb_dry_mix;
+ stereo_out[1] = stereo_in[1]*vg_dsp.reverb_dry_mix;
+ stereo_out[0] += diffuse_mix*2.0f*vg_dsp.reverb_wet_mix;
+ stereo_out[1] += diffuse_mix*2.0f*vg_dsp.reverb_wet_mix;
+}
+
+static void dsp_update_tunings(void)
+{
+ float sizes[] =
+ { 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f, 256.0f };
+
+ float avg_distance = 0.0f;
+
+ for( int i=0; i<8; i++ )
+ vg_dsp.echo_tunings[i] = 0.5f;
+
+ for( int j=0; j<14; j++ )
+ {
+ float d = vg_dsp.echo_distances[j];
+
+ for( int i=0; i<7; i++ )
+ {
+ if( d < sizes[i+1] )
+ {
+ float range = sizes[i+1]-sizes[i];
+ float t = vg_clampf( (d - sizes[i])/range, 0.0f, 1.0f );
+
+ vg_dsp.echo_tunings[i ] += 1.0f-t;
+ vg_dsp.echo_tunings[i+1] += t;
+
+ break;
+ }
+ }
+
+ avg_distance += d;
+ }
+ avg_distance /= 14.0f;
+
+
+ vg_dsp.reverb_wet_mix =1.0f-vg_clampf((avg_distance-30.0f)/200.0f,0.0f,1.0f);
+ vg_dsp.reverb_dry_mix =1.0f-vg_dsp.reverb_wet_mix*0.4f;
+
+ float total = 0.0f;
+ for( int i=0; i<8; i++ )
+ total += vg_dsp.echo_tunings[i];
+
+ if( total > 0.0f )
+ {
+ float inverse = 1.0f/total;
+
+ for( int i=0;i<8; i++ )
+ {
+ vg_dsp.echo_tunings[i] *= inverse;
+ }
+ }
+
+ for( int i=0; i<8; i++ )
+ {
+ float freq = vg_lerpf( 200.0f, 500.0f, vg_dsp.echo_tunings[i] );
+ dsp_update_lpf( &__echos_lpf[i], freq );
+ }
+}
+
+static void vg_dsp_free( void )
+{
+ glDeleteTextures( 1, &vg_dsp.view_texture );
+}
+
+static void vg_dsp_update_texture( void )
+{
+ for( int i=0; i<512*512; i++ )
+ {
+ float v = vg_clampf( vg_dsp.buffer[i] * 0.5f + 0.5f, 0.0f, 1.0f );
+ vg_dsp.view_texture_buffer[i] = v * 255.0f;
+ }
+}
+
+#endif /* VG_AUDIO_DSP_H */
return m << 3;
}
+VG_STATIC u32 vg_align4( u32 s )
+{
+ u32 m = (s + 3) >> 2;
+ return m << 2;
+}
+
/* Returns allocator structure from data pointer */
static vg_linear_allocator *vg_linear_header( void *data )
{
{
const char *name;
- float samples[ VG_PROFILE_SAMPLE_COUNT ];
- u32 buffer_count, buffer_current;
+ u64 samples[ VG_PROFILE_SAMPLE_COUNT ];
+ u32 buffer_count, buffer_current;
enum profile_mode
{
if( profile->buffer_current >= VG_PROFILE_SAMPLE_COUNT )
profile->buffer_current = 0;
- profile->samples[ profile->buffer_current ] = 0.0f;
+ profile->samples[ profile->buffer_current ] = 0;
}
VG_STATIC void vg_profile_end( struct vg_profile *profile )
if( profile->mode == k_profile_mode_frame )
{
- profile->samples[ profile->buffer_current ] = (float)delta;
+ profile->samples[ profile->buffer_current ] = delta;
vg_profile_increment( profile );
}
else
}
}
-VG_STATIC void vg_profile_graph_sample( struct vg_profile *profile, float s )
-{
- profile->samples[ profile->buffer_current ] = s;
- vg_profile_increment( profile );
-}
-
VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count,
float budget, ui_rect panel, u32 colour_offset )
{
ui_fill_rect( panel, 0xa0000000 );
assert( count <= 8 );
- float avgs[8];
- int ptrs[8];
+ double avgs[8];
+ int ptrs[8];
for( int i=0; i<count; i++ )
{
u32 colours[] = { 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000,
0xffff00ff, 0xffffff00 };
- float rate_mul = 1000.0f / (float)SDL_GetPerformanceFrequency();
+ double rate_mul = 1000.0 / (double)SDL_GetPerformanceFrequency();
for( int i=0; i<VG_PROFILE_SAMPLE_COUNT-1; i++ )
{
- float total = 0.0f;
+ double total = 0.0;
for( int j=0; j<count; j++ )
{
if( ptrs[j] < 0 )
ptrs[j] = VG_PROFILE_SAMPLE_COUNT-1;
- float sample = profiles[j]->samples[ptrs[j]] * rate_mul,
- px = (total / (budget)) * sw,
- wx = (sample / (budget)) * sw;
+ double sample = (double)profiles[j]->samples[ptrs[j]] * rate_mul,
+ px = (total / (budget)) * sw,
+ wx = (sample / (budget)) * sw;
ui_rect block = { panel[0] + px, panel[1] + (float)i*sh,
wx, sh };
char infbuf[64];
+ snprintf( infbuf, 64, "accuracy: %.7fms", rate_mul );
+ ui_text( (ui_rect){ panel[0] + 4,
+ panel[1] + 0 * 14, 0, 0 },
+ infbuf,
+ 1,
+ k_text_align_left );
+
for( int i=0; i<count; i++ )
{
snprintf( infbuf, 64, "%.4fms %s",
}
};
+static struct vg_shader _shader_ui_image =
+{
+ .name = "[vg] ui_image",
+ .link = NULL,
+ .vs =
+ {
+ .orig_file = NULL,
+ .static_src =
+ "layout (location=0) in vec2 a_co;"
+ "layout (location=1) in vec2 a_uv;"
+ "layout (location=2) in vec4 a_colour;"
+ "layout (location=3) in vec4 a_clip;"
+ "uniform mat3 uPv;"
+ ""
+ "out vec2 aTexCoords;"
+ "out vec4 aColour;"
+ "out vec2 aWsp;"
+ "out vec4 aClip;"
+ ""
+ "void main()"
+ "{"
+ "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
+ "aTexCoords = a_uv * 0.0078125;"
+ "aColour = a_colour;"
+
+ "aWsp = a_co;"
+ "aClip = a_clip;"
+ "}",
+ },
+ .fs =
+ {
+ .orig_file = NULL,
+ .static_src =
+ "uniform sampler2D uTexImage;"
+ "out vec4 FragColor;"
+ ""
+ "in vec2 aTexCoords;"
+ "in vec4 aColour;"
+ ""
+ "in vec2 aWsp;"
+ "in vec4 aClip;"
+
+ "void main()"
+ "{"
+ "float clip_blend = step( aWsp.x, aClip.z ) *"
+ "step( aWsp.y, aClip.w ) *"
+ "step( aClip.x, aWsp.x ) *"
+ "step( aClip.y, aWsp.y ); "
+
+ "vec4 colour = texture( uTexImage, aTexCoords );"
+ "float value = dot(vec4(1.0),colour)*0.25;"
+
+ "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
+ "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
+
+ "FragColor = vec4( col*4.0, clip_blend );"
+ "}"
+ }
+};
+
typedef i16 ui_px;
typedef u32 ui_colour;
typedef ui_px ui_rect[4];
VG_STATIC void _vg_ui_init(void)
{
- if( !vg_shader_compile( &_shader_ui ) )
+ if( !vg_shader_compile( &_shader_ui ) ||
+ !vg_shader_compile( &_shader_ui_image ) )
vg_fatal_exit_loop( "Failed to compile ui shader" );
/*
glDrawElements( GL_TRIANGLES, num_indices_normal,
GL_UNSIGNED_SHORT, (void*)(0) );
+
+
+ /* images */
+ glUseProgram( _shader_ui_image.id );
+ glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1,
+ GL_FALSE, (float *)view_override );
+
+ glActiveTexture( GL_TEXTURE1 );
+ glUniform1i( glGetUniformLocation( _shader_ui_image.id, "uTexImage" ), 1 );
/* Draw image elements */
for( int i = 0; i < vg_uictx.image_count; i ++ )