tune reverb
authorhgn <hgodden00@gmail.com>
Tue, 11 Apr 2023 23:14:26 +0000 (00:14 +0100)
committerhgn <hgodden00@gmail.com>
Tue, 11 Apr 2023 23:14:26 +0000 (00:14 +0100)
vg.h
vg_audio.h
vg_audio_dsp.h
vg_m.h

diff --git a/vg.h b/vg.h
index 64b6f6fc16dcf3c797d93c357cef6d5d4dd6f526..40767a2a0746cf20fce277d0eb8d658f7c936eb0 100644 (file)
--- a/vg.h
+++ b/vg.h
@@ -669,13 +669,15 @@ VG_STATIC void _vg_init_window( const char *window_name )
    }
 
 
-
-
+#ifdef VG_DEVWINDOW
+   vg.refresh_rate = 60;
+   vg.window_x = 1000;
+   vg.window_y = 800;
+#else
    /* TODO: Allow chosing the modes at startup */
    vg_info( "Getting default display mode\n" );
    SDL_DisplayMode vm_ideal = { 0, 0, 0, 0, 0 };
-   if( SDL_GetDisplayMode( display_index, mode_index, &vm_ideal ) != 0 )
-   {
+   if( SDL_GetDisplayMode( display_index, mode_index, &vm_ideal ) != 0 ){
       vg_error( "SDL_GetDisplayMode failed: %s", SDL_GetError() );
       exit(0);
    }
@@ -688,8 +690,7 @@ VG_STATIC void _vg_init_window( const char *window_name )
 
 
    SDL_DisplayMode vm_best;
-   if( !SDL_GetClosestDisplayMode( display_index, &vm_ideal, &vm_best ) )
-   {
+   if( !SDL_GetClosestDisplayMode( display_index, &vm_ideal, &vm_best ) ){
       vg_error( "SDL_GetClosestDisplayMode failed: %s", SDL_GetError() );
       exit(0);
    }
@@ -697,29 +698,36 @@ VG_STATIC void _vg_init_window( const char *window_name )
    vg.refresh_rate = vm_best.refresh_rate;
    vg.window_x = vm_best.w;
    vg.window_y = vm_best.h;
+#endif
 
    vg_info( "CreateWindow( %d %d @%.2fhz )\n", vg.window_x, vg.window_y,
                                                vg.refresh_rate );
 
    /* TODO: Allow selecting closest video mode from launch opts */
    if((vg.window = SDL_CreateWindow( window_name,
+
+#ifdef VG_DEVWINDOW
+         0, 0, vg.window_x, vg.window_y, 
+         SDL_WINDOW_BORDERLESS|SDL_WINDOW_OPENGL|SDL_WINDOW_INPUT_GRABBED
+      ))){}
+#else
                                      SDL_WINDOWPOS_UNDEFINED,
                                      SDL_WINDOWPOS_UNDEFINED,
                                      vg.window_x, vg.window_y,
 
                                      SDL_WINDOW_FULLSCREEN_DESKTOP | 
                                      SDL_WINDOW_OPENGL |
-                                     SDL_WINDOW_INPUT_GRABBED )))
+                                     SDL_WINDOW_INPUT_GRABBED 
+                                     )))
    {
-      if( SDL_SetWindowDisplayMode( vg.window, &vm_best ) )
-      {
+      if( SDL_SetWindowDisplayMode( vg.window, &vm_best ) ){
          vg_error( "SDL_SetWindowDisplayMode failed: %s", SDL_GetError() );
          SDL_Quit();
          exit(0);
       }
    }
-   else
-   {
+#endif
+   else{
       vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
       exit(0);
    }
index 75a8c7ea97cb829a42a6aa839b77d1f90e52eb0e..3ba7093030c57547c25be468f2bf5097fa479f13 100644 (file)
@@ -193,7 +193,7 @@ static struct vg_audio_system{
    }
    channels[ AUDIO_CHANNELS ];
 
-   int               debug_ui, debug_ui_3d;
+   int               debug_ui, debug_ui_3d, debug_dsp;
 
    v3f               internal_listener_pos,
                      internal_listener_ears,
@@ -274,6 +274,14 @@ VG_STATIC void vg_audio_init(void)
       .persistent = 1
    });
 
+   vg_var_push( (struct vg_var){
+      .name = "debug_dsp",
+      .data = &vg_audio.debug_dsp,
+      .data_type = k_var_dtype_i32,
+      .opt_i32 = { .min=0, .max=1, .clamp=1 },
+      .persistent = 1
+   });
+
    vg_var_push( (struct vg_var){
       .name = "volume",
       .data = &vg_audio.external_global_volume,
@@ -1139,7 +1147,7 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count )
 
    vg_audio.samples_last = frame_count;
 
-   if( vg_audio.debug_ui ){
+   if( vg_audio.debug_dsp ){
       vg_dsp_update_texture();
    }
 
@@ -1297,8 +1305,10 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv )
    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 );
+   if( vg_audio.debug_dsp ){
+      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,
index 4c7d85c931907f4e52b37a2c94c6543fbe469bab..56d3b20c655cb8050999f87287531bf76cf59a41 100644 (file)
@@ -163,8 +163,7 @@ static void vg_dsp_init( void )
 
    float reflection_variance = 0.1f;
 
-   for( int i=0; i<8; i++ )
-   {
+   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,
@@ -179,8 +178,7 @@ static void vg_dsp_init( void )
    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++ )
-   {
+   for( int i=0; i<8; i++ ){
       dsp_init_schroeder( __diffusion_chain+i, diffusions[i]/1000.0f, 0.7f );
    }
 }
@@ -190,8 +188,7 @@ 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++ )
-   {
+   for( int i=0; i<8; i++ ){
       float echo;
       dsp_read_delay(  __echos+i, &echo );
       dsp_write_lpf( __echos_lpf+i, &echo );
@@ -202,8 +199,7 @@ static void vg_dsp_process( float *stereo_in, float *stereo_out )
 
    float diffused = recieved;
 
-   for( int i=0; i<8; i++ )
-   {
+   for( int i=0; i<8; i++ ){
       dsp_process_schroeder( __diffusion_chain+i, &diffused, &diffused );
    }
 
@@ -230,20 +226,19 @@ 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 volumes[] = 
+         { 0.2f, 0.3f, 0.5f, 0.7f,   0.8f, 0.9f, 1.0f, 1.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++ )
-   {
+   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] )
-         {
+      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 );
 
@@ -266,21 +261,22 @@ static void dsp_update_tunings(void)
    for( int i=0; i<8; i++ )
       total += vg_dsp.echo_tunings[i];
 
-   if( total > 0.0f )
-   {
+   if( total > 0.0f ){
       float inverse = 1.0f/total;
 
-      for( int i=0;i<8; i++ )
-      {
+      for( int i=0;i<8; i++ ){
          vg_dsp.echo_tunings[i] *= inverse;
       }
    }
 
-   for( int i=0; i<8; i++ )
-   {
+   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 );
    }
+
+   for( int i=0;i<8; i++ ){
+      vg_dsp.echo_tunings[i] *= volumes[i];
+   }
 }
 
 static void vg_dsp_free( void )
@@ -290,8 +286,7 @@ static void vg_dsp_free( void )
 
 static void vg_dsp_update_texture( void )
 {
-   for( int i=0; i<512*512; i++ )
-   {
+   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;
    }
diff --git a/vg_m.h b/vg_m.h
index ca3308b959be06bb9156666ef6814b50a160e43d..e75e8dc38ce7cb49d25e025ec4fa27b6d147d605 100644 (file)
--- a/vg_m.h
+++ b/vg_m.h
@@ -745,213 +745,6 @@ static inline void m3x3_rotate( m3x3f m, float angle )
    m[1][2] = m02 * -s + m12 * c;
 }
 
-/*
- * Matrix 4x3
- */
-
-#define M4X3_IDENTITY   {{1.0f, 0.0f, 0.0f, },\
-                        { 0.0f, 1.0f, 0.0f, },\
-                        { 0.0f, 0.0f, 1.0f, },\
-                        { 0.0f, 0.0f, 0.0f }}
-
-static inline void m4x3_to_3x3( m4x3f a, m3x3f b )
-{
-   v3_copy( a[0], b[0] );
-   v3_copy( a[1], b[1] );
-   v3_copy( a[2], b[2] );
-}
-
-static inline void m4x3_invert_affine( m4x3f a, m4x3f b )
-{
-   m3x3_transpose( a, b );
-   m3x3_mulv( b, a[3], b[3] );
-   v3_negate( b[3], b[3] );
-}
-
-static void m4x3_invert_full( m4x3f src, m4x3f dst )
-{
-  float t2, t4, t5,
-        det,
-        a = src[0][0], b = src[0][1], c = src[0][2],
-        e = src[1][0], f = src[1][1], g = src[1][2],
-        i = src[2][0], j = src[2][1], k = src[2][2],
-        m = src[3][0], n = src[3][1], o = src[3][2];
-
-   t2 = j*o - n*k;
-   t4 = i*o - m*k;
-   t5 = i*n - m*j;
-   
-   dst[0][0] =  f*k - g*j;
-   dst[1][0] =-(e*k - g*i);
-   dst[2][0] =  e*j - f*i;
-   dst[3][0] =-(e*t2 - f*t4 + g*t5);
-   
-   dst[0][1] =-(b*k - c*j);
-   dst[1][1] =  a*k - c*i;
-   dst[2][1] =-(a*j - b*i);
-   dst[3][1] =  a*t2 - b*t4 + c*t5;
-   
-   t2 = f*o - n*g;
-   t4 = e*o - m*g; 
-   t5 = e*n - m*f;
-   
-   dst[0][2] =  b*g - c*f ;
-   dst[1][2] =-(a*g - c*e );
-   dst[2][2] =  a*f - b*e ;
-   dst[3][2] =-(a*t2 - b*t4 + c * t5);
-
-   det = 1.0f / (a * dst[0][0] + b * dst[1][0] + c * dst[2][0]);
-   v3_muls( dst[0], det, dst[0] );
-   v3_muls( dst[1], det, dst[1] );
-   v3_muls( dst[2], det, dst[2] );
-   v3_muls( dst[3], det, dst[3] );
-}
-
-static inline void m4x3_copy( m4x3f a, m4x3f b )
-{
-   v3_copy( a[0], b[0] );
-   v3_copy( a[1], b[1] );
-   v3_copy( a[2], b[2] );
-   v3_copy( a[3], b[3] );
-}
-
-static inline void m4x3_identity( m4x3f a )
-{
-   m4x3f id = M4X3_IDENTITY;
-   m4x3_copy( id, a );
-}
-
-static inline void m4x3_mul( m4x3f a, m4x3f b, m4x3f d ) 
-{
-   float 
-   a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
-   a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
-   a20 = a[2][0], a21 = a[2][1], a22 = a[2][2],
-   a30 = a[3][0], a31 = a[3][1], a32 = a[3][2],
-   b00 = b[0][0], b01 = b[0][1], b02 = b[0][2],
-   b10 = b[1][0], b11 = b[1][1], b12 = b[1][2],
-   b20 = b[2][0], b21 = b[2][1], b22 = b[2][2],
-   b30 = b[3][0], b31 = b[3][1], b32 = b[3][2];
-   
-   d[0][0] = a00*b00 + a10*b01 + a20*b02;
-   d[0][1] = a01*b00 + a11*b01 + a21*b02;
-   d[0][2] = a02*b00 + a12*b01 + a22*b02;
-   d[1][0] = a00*b10 + a10*b11 + a20*b12;
-   d[1][1] = a01*b10 + a11*b11 + a21*b12;
-   d[1][2] = a02*b10 + a12*b11 + a22*b12;
-   d[2][0] = a00*b20 + a10*b21 + a20*b22;
-   d[2][1] = a01*b20 + a11*b21 + a21*b22;
-   d[2][2] = a02*b20 + a12*b21 + a22*b22;
-   d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30;
-   d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31;
-   d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32;
-}
-
-static inline void m4x3_mulv( m4x3f m, v3f v, v3f d ) 
-{
-   v3f res;
-  
-   res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0];
-   res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1];
-   res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2];
-
-   v3_copy( res, d );
-}
-
-/* 
- * Transform plane ( xyz, distance )
- */
-static inline void m4x3_mulp( m4x3f m, v4f p, v4f d )
-{
-   v3f o;
-
-   v3_muls( p, p[3], o );
-   m4x3_mulv( m, o, o );
-   m3x3_mulv( m, p, d );
-   
-   d[3] = v3_dot( o, d );
-}
-
-/*
- * Affine transforms
- */
-
-static inline void m4x3_translate( m4x3f m, v3f v )
-{
-   v3_muladds( m[3], m[0], v[0], m[3] );
-   v3_muladds( m[3], m[1], v[1], m[3] );
-   v3_muladds( m[3], m[2], v[2], m[3] );
-}
-
-static inline void m4x3_rotate_x( m4x3f m, float angle )
-{
-   m4x3f t = M4X3_IDENTITY;
-   float c, s;
-
-   c = cosf( angle );
-   s = sinf( angle );
-
-   t[1][1] =  c;
-   t[1][2] =  s;
-   t[2][1] = -s;
-   t[2][2] =  c;
-
-   m4x3_mul( m, t, m );
-}
-
-static inline void m4x3_rotate_y( m4x3f m, float angle )
-{
-   m4x3f t = M4X3_IDENTITY;
-   float c, s;
-
-   c = cosf( angle );
-   s = sinf( angle );
-
-   t[0][0] =  c;
-   t[0][2] = -s;
-   t[2][0] =  s;
-   t[2][2] =  c;
-
-   m4x3_mul( m, t, m );
-}
-
-static inline void m4x3_rotate_z( m4x3f m, float angle )
-{
-   m4x3f t = M4X3_IDENTITY;
-   float c, s;
-
-   c = cosf( angle );
-   s = sinf( angle );
-
-   t[0][0] =  c;
-   t[0][1] =  s;
-   t[1][0] = -s;
-   t[1][1] =  c;
-
-   m4x3_mul( m, t, m );
-}
-
-static inline void m4x3_expand( m4x3f m, m4x4f d )
-{
-   v3_copy( m[0], d[0] );
-   v3_copy( m[1], d[1] );
-   v3_copy( m[2], d[2] );
-   v3_copy( m[3], d[3] );
-   d[0][3] = 0.0f;
-   d[1][3] = 0.0f;
-   d[2][3] = 0.0f;
-   d[3][3] = 1.0f;
-}
-
-static inline void m4x3_expand_aabb_point( m4x3f m, boxf box, v3f point )
-{
-   v3f v;
-   m4x3_mulv( m, point, v );
-
-   v3_minv( box[0], v, box[0] );
-   v3_maxv( box[1], v, box[1] );
-}
-
 static inline void box_addpt( boxf a, v3f pt )
 {
    v3_minv( a[0], pt, a[0] );
@@ -1000,26 +793,6 @@ static inline void box_init_inf( boxf box )
    v3_fill( box[1], -INFINITY );
 }
 
-static inline void m4x3_transform_aabb( m4x3f m, boxf box )
-{
-   v3f a; v3f b;
-   
-   v3_copy( box[0], a );
-   v3_copy( box[1], b );
-   v3_fill( box[0],  INFINITY );
-   v3_fill( box[1], -INFINITY );
-
-   m4x3_expand_aabb_point( m, box, (v3f){ a[0], a[1], a[2] } );
-   m4x3_expand_aabb_point( m, box, (v3f){ a[0], b[1], a[2] } );
-   m4x3_expand_aabb_point( m, box, (v3f){ b[0], b[1], a[2] } );
-   m4x3_expand_aabb_point( m, box, (v3f){ b[0], a[1], a[2] } );
-
-   m4x3_expand_aabb_point( m, box, (v3f){ a[0], a[1], b[2] } );
-   m4x3_expand_aabb_point( m, box, (v3f){ a[0], b[1], b[2] } );
-   m4x3_expand_aabb_point( m, box, (v3f){ b[0], b[1], b[2] } );
-   m4x3_expand_aabb_point( m, box, (v3f){ b[0], a[1], b[2] } );
-}
-
 int ray_aabb1( boxf box, v3f co, v3f dir_inv, float dist )
 {
    v3f v0, v1;
@@ -1488,6 +1261,248 @@ enum contact_type
    k_contact_type_edge
 };
 
+/*
+ * Matrix 4x3
+ */
+
+#define M4X3_IDENTITY   {{1.0f, 0.0f, 0.0f, },\
+                        { 0.0f, 1.0f, 0.0f, },\
+                        { 0.0f, 0.0f, 1.0f, },\
+                        { 0.0f, 0.0f, 0.0f }}
+
+static inline void m4x3_to_3x3( m4x3f a, m3x3f b )
+{
+   v3_copy( a[0], b[0] );
+   v3_copy( a[1], b[1] );
+   v3_copy( a[2], b[2] );
+}
+
+static inline void m4x3_invert_affine( m4x3f a, m4x3f b )
+{
+   m3x3_transpose( a, b );
+   m3x3_mulv( b, a[3], b[3] );
+   v3_negate( b[3], b[3] );
+}
+
+static void m4x3_invert_full( m4x3f src, m4x3f dst )
+{
+  float t2, t4, t5,
+        det,
+        a = src[0][0], b = src[0][1], c = src[0][2],
+        e = src[1][0], f = src[1][1], g = src[1][2],
+        i = src[2][0], j = src[2][1], k = src[2][2],
+        m = src[3][0], n = src[3][1], o = src[3][2];
+
+   t2 = j*o - n*k;
+   t4 = i*o - m*k;
+   t5 = i*n - m*j;
+   
+   dst[0][0] =  f*k - g*j;
+   dst[1][0] =-(e*k - g*i);
+   dst[2][0] =  e*j - f*i;
+   dst[3][0] =-(e*t2 - f*t4 + g*t5);
+   
+   dst[0][1] =-(b*k - c*j);
+   dst[1][1] =  a*k - c*i;
+   dst[2][1] =-(a*j - b*i);
+   dst[3][1] =  a*t2 - b*t4 + c*t5;
+   
+   t2 = f*o - n*g;
+   t4 = e*o - m*g; 
+   t5 = e*n - m*f;
+   
+   dst[0][2] =  b*g - c*f ;
+   dst[1][2] =-(a*g - c*e );
+   dst[2][2] =  a*f - b*e ;
+   dst[3][2] =-(a*t2 - b*t4 + c * t5);
+
+   det = 1.0f / (a * dst[0][0] + b * dst[1][0] + c * dst[2][0]);
+   v3_muls( dst[0], det, dst[0] );
+   v3_muls( dst[1], det, dst[1] );
+   v3_muls( dst[2], det, dst[2] );
+   v3_muls( dst[3], det, dst[3] );
+}
+
+static inline void m4x3_copy( m4x3f a, m4x3f b )
+{
+   v3_copy( a[0], b[0] );
+   v3_copy( a[1], b[1] );
+   v3_copy( a[2], b[2] );
+   v3_copy( a[3], b[3] );
+}
+
+static inline void m4x3_identity( m4x3f a )
+{
+   m4x3f id = M4X3_IDENTITY;
+   m4x3_copy( id, a );
+}
+
+static void m4x3_mul( m4x3f a, m4x3f b, m4x3f d ) 
+{
+   float 
+   a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
+   a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
+   a20 = a[2][0], a21 = a[2][1], a22 = a[2][2],
+   a30 = a[3][0], a31 = a[3][1], a32 = a[3][2],
+   b00 = b[0][0], b01 = b[0][1], b02 = b[0][2],
+   b10 = b[1][0], b11 = b[1][1], b12 = b[1][2],
+   b20 = b[2][0], b21 = b[2][1], b22 = b[2][2],
+   b30 = b[3][0], b31 = b[3][1], b32 = b[3][2];
+   
+   d[0][0] = a00*b00 + a10*b01 + a20*b02;
+   d[0][1] = a01*b00 + a11*b01 + a21*b02;
+   d[0][2] = a02*b00 + a12*b01 + a22*b02;
+   d[1][0] = a00*b10 + a10*b11 + a20*b12;
+   d[1][1] = a01*b10 + a11*b11 + a21*b12;
+   d[1][2] = a02*b10 + a12*b11 + a22*b12;
+   d[2][0] = a00*b20 + a10*b21 + a20*b22;
+   d[2][1] = a01*b20 + a11*b21 + a21*b22;
+   d[2][2] = a02*b20 + a12*b21 + a22*b22;
+   d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30;
+   d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31;
+   d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32;
+}
+
+static inline void m4x3_mulv( m4x3f m, v3f v, v3f d ) 
+{
+   v3f res;
+  
+   res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0];
+   res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1];
+   res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2];
+
+   v3_copy( res, d );
+}
+
+/* 
+ * Transform plane ( xyz, distance )
+ */
+static void m4x3_mulp( m4x3f m, v4f p, v4f d )
+{
+   v3f o;
+
+   v3_muls( p, p[3], o );
+   m4x3_mulv( m, o, o );
+   m3x3_mulv( m, p, d );
+   
+   d[3] = v3_dot( o, d );
+}
+
+/*
+ * Affine transforms
+ */
+
+static void m4x3_translate( m4x3f m, v3f v )
+{
+   v3_muladds( m[3], m[0], v[0], m[3] );
+   v3_muladds( m[3], m[1], v[1], m[3] );
+   v3_muladds( m[3], m[2], v[2], m[3] );
+}
+
+static void m4x3_rotate_x( m4x3f m, float angle )
+{
+   m4x3f t = M4X3_IDENTITY;
+   float c, s;
+
+   c = cosf( angle );
+   s = sinf( angle );
+
+   t[1][1] =  c;
+   t[1][2] =  s;
+   t[2][1] = -s;
+   t[2][2] =  c;
+
+   m4x3_mul( m, t, m );
+}
+
+static void m4x3_rotate_y( m4x3f m, float angle )
+{
+   m4x3f t = M4X3_IDENTITY;
+   float c, s;
+
+   c = cosf( angle );
+   s = sinf( angle );
+
+   t[0][0] =  c;
+   t[0][2] = -s;
+   t[2][0] =  s;
+   t[2][2] =  c;
+
+   m4x3_mul( m, t, m );
+}
+
+static void m4x3_rotate_z( m4x3f m, float angle )
+{
+   m4x3f t = M4X3_IDENTITY;
+   float c, s;
+
+   c = cosf( angle );
+   s = sinf( angle );
+
+   t[0][0] =  c;
+   t[0][1] =  s;
+   t[1][0] = -s;
+   t[1][1] =  c;
+
+   m4x3_mul( m, t, m );
+}
+
+static void m4x3_expand( m4x3f m, m4x4f d )
+{
+   v3_copy( m[0], d[0] );
+   v3_copy( m[1], d[1] );
+   v3_copy( m[2], d[2] );
+   v3_copy( m[3], d[3] );
+   d[0][3] = 0.0f;
+   d[1][3] = 0.0f;
+   d[2][3] = 0.0f;
+   d[3][3] = 1.0f;
+}
+
+static void m4x3_decompose( m4x3f m, v3f co, v4f q, v3f s )
+{
+   v3_copy( m[3], co );
+   s[0] = v3_length(m[0]);
+   s[1] = v3_length(m[1]);
+   s[2] = v3_length(m[2]);
+
+   m3x3f rot;
+   v3_divs( m[0], s[0], rot[0] );
+   v3_divs( m[1], s[1], rot[1] );
+   v3_divs( m[2], s[2], rot[2] );
+
+   m3x3_q( rot, q );
+}
+
+static void m4x3_expand_aabb_point( m4x3f m, boxf box, v3f point )
+{
+   v3f v;
+   m4x3_mulv( m, point, v );
+
+   v3_minv( box[0], v, box[0] );
+   v3_maxv( box[1], v, box[1] );
+}
+
+static void m4x3_transform_aabb( m4x3f m, boxf box )
+{
+   v3f a; v3f b;
+   
+   v3_copy( box[0], a );
+   v3_copy( box[1], b );
+   v3_fill( box[0],  INFINITY );
+   v3_fill( box[1], -INFINITY );
+
+   m4x3_expand_aabb_point( m, box, (v3f){ a[0], a[1], a[2] } );
+   m4x3_expand_aabb_point( m, box, (v3f){ a[0], b[1], a[2] } );
+   m4x3_expand_aabb_point( m, box, (v3f){ b[0], b[1], a[2] } );
+   m4x3_expand_aabb_point( m, box, (v3f){ b[0], a[1], a[2] } );
+
+   m4x3_expand_aabb_point( m, box, (v3f){ a[0], a[1], b[2] } );
+   m4x3_expand_aabb_point( m, box, (v3f){ a[0], b[1], b[2] } );
+   m4x3_expand_aabb_point( m, box, (v3f){ b[0], b[1], b[2] } );
+   m4x3_expand_aabb_point( m, box, (v3f){ b[0], a[1], b[2] } );
+}
+
 /*
  * -----------------------------------------------------------------------------
  *                        Closest point functions