X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=blobdiff_plain;f=vg_m.h;h=368f2889aff1f23807325e92f11228f22c654305;hp=833f1f01598a628a4c09df3c873c1a7cef7a3644;hb=HEAD;hpb=c8399b3248b4965990ec2573d3ac2e27b1b8dbcc diff --git a/vg_m.h b/vg_m.h index 833f1f0..4af60c8 100644 --- a/vg_m.h +++ b/vg_m.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved +/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved * * 0. Misc * 1. Scalar operations @@ -18,12 +18,13 @@ * 5.c Closest points * 5.d Raycast & Spherecasts * 5.e Curves + * 5.f Volumes + * 5.g Inertia tensors * 6. Statistics * 6.a Random numbers - **/ + */ -#ifndef VG_M_H -#define VG_M_H +#pragma once #include "vg_platform.h" #include @@ -31,6 +32,7 @@ #define VG_PIf 3.14159265358979323846264338327950288f #define VG_TAUf 6.28318530717958647692528676655900576f + /* * ----------------------------------------------------------------------------- * Section 0. Misc Operations @@ -62,6 +64,12 @@ static int vg_validf( f32 a ) return ((vg_ftu32(a)) & 0x7F800000U) != 0x7F800000U; } +static int v3_valid( v3f a ){ + for( u32 i=0; i<3; i++ ) + if( !vg_validf(a[i]) ) return 0; + return 1; +} + /* * ----------------------------------------------------------------------------- * Section 1. Scalar Operations @@ -89,6 +97,10 @@ static inline f32 vg_fractf( f32 a ) return a - floorf( a ); } +static inline f64 vg_fractf64( f64 a ){ + return a - floor( a ); +} + static f32 vg_cfrictf( f32 velocity, f32 F ) { return -vg_signf(velocity) * vg_minf( F, fabsf(velocity) ); @@ -99,6 +111,41 @@ static inline f32 vg_rad( f32 deg ) return deg * VG_PIf / 180.0f; } +/* angle to reach b from a */ +static f32 vg_angle_diff( f32 a, f32 b ){ + f32 d = fmod(b,VG_TAUf)-fmodf(a,VG_TAUf); + if( fabsf(d) > VG_PIf ) + d = -vg_signf(d) * (VG_TAUf - fabsf(d)); + + return d; +} + +/* + * quantize float to bit count + */ +static u32 vg_quantf( f32 a, u32 bits, f32 min, f32 max ){ + u32 mask = (0x1 << bits) - 1; + return vg_clampf((a - min) * ((f32)mask/(max-min)), 0.0f, mask ); +} + +/* + * un-quantize discreet to float + */ +static f32 vg_dequantf( u32 q, u32 bits, f32 min, f32 max ){ + u32 mask = (0x1 << bits) - 1; + return min + (f32)q * ((max-min) / (f32)mask); +} + +/* https://iquilezles.org/articles/functions/ + * + * Use k to control the stretching of the function. Its maximum, which is 1, + * happens at exactly x = 1/k. + */ +static f32 vg_exp_impulse( f32 x, f32 k ){ + f32 h = k*x; + return h*expf(1.0f-h); +} + /* * ----------------------------------------------------------------------------- * Section 2.a 2D Vectors @@ -392,8 +439,7 @@ static inline void v3_normalize( v3f a ) v3_muls( a, 1.f / v3_length( a ), a ); } -static inline f32 vg_lerpf( f32 a, f32 b, f32 t ) -{ +static inline f32 vg_lerpf( f32 a, f32 b, f32 t ){ return a + t*(b-a); } @@ -402,6 +448,17 @@ static inline f64 vg_lerp( f64 a, f64 b, f64 t ) return a + t*(b-a); } +static inline void vg_slewf( f32 *a, f32 b, f32 speed ){ + f32 d = vg_signf( b-*a ), + c = *a + d*speed; + *a = vg_minf( b*d, c*d ) * d; +} + +static inline f32 vg_smoothstepf( f32 x ){ + return x*x*(3.0f - 2.0f*x); +} + + /* correctly lerp around circular period -pi -> pi */ static f32 vg_alerpf( f32 a, f32 b, f32 t ) { @@ -480,6 +537,52 @@ static inline void v3_rotate( v3f v, f32 angle, v3f axis, v3f d ) v3_add( v1, v2, d ); } +static void v3_tangent_basis( v3f n, v3f tx, v3f ty ){ + /* Compute tangent basis (box2d) */ + if( fabsf( n[0] ) >= 0.57735027f ){ + tx[0] = n[1]; + tx[1] = -n[0]; + tx[2] = 0.0f; + } + else{ + tx[0] = 0.0f; + tx[1] = n[2]; + tx[2] = -n[1]; + } + + v3_normalize( tx ); + v3_cross( n, tx, ty ); +} + +/* + * Compute yaw and pitch based of a normalized vector representing forward + * forward: -z + * result -> (YAW,PITCH,0.0) + */ +static void v3_angles( v3f v, v3f out_angles ){ + float yaw = atan2f( v[0], -v[2] ), + pitch = atan2f( + -v[1], + sqrtf( + v[0]*v[0] + v[2]*v[2] + ) + ); + + out_angles[0] = yaw; + out_angles[1] = pitch; + out_angles[2] = 0.0f; +} + +/* + * Compute the forward vector from (YAW,PITCH,ROLL) + * forward: -z + */ +static void v3_angles_vector( v3f angles, v3f out_v ){ + out_v[0] = sinf( angles[0] ) * cosf( angles[1] ); + out_v[1] = -sinf( angles[1] ); + out_v[2] = -cosf( angles[0] ) * cosf( angles[1] ); +} + /* * ----------------------------------------------------------------------------- * Section 2.c 4D Vectors @@ -593,11 +696,11 @@ static inline void q_inv( v4f q, v4f d ) d[3] = q[3]*s; } -static inline void q_nlerp( v4f a, v4f b, f32 t, v4f d ) -{ +static inline void q_nlerp( v4f a, v4f b, f32 t, v4f d ){ if( v4_dot(a,b) < 0.0f ){ - v4_muls( b, -1.0f, d ); - v4_lerp( a, d, t, d ); + v4f c; + v4_muls( b, -1.0f, c ); + v4_lerp( a, c, t, d ); } else v4_lerp( a, b, t, d ); @@ -638,6 +741,10 @@ static void q_mulv( v4f q, v3f v, v3f d ) v3_add( v1, v2, d ); } +static f32 q_dist( v4f q0, v4f q1 ){ + return acosf( 2.0f * v4_dot(q0,q1) -1.0f ); +} + /* * ----------------------------------------------------------------------------- * Section 4.a 2x2 matrices @@ -675,6 +782,16 @@ static inline void m2x2_create_rotation( m2x2f a, f32 theta ) a[1][1] = c; } +static inline void m2x2_mulv( m2x2f m, v2f v, v2f d ) +{ + v2f res; + + res[0] = m[0][0]*v[0] + m[1][0]*v[1]; + res[1] = m[0][1]*v[0] + m[1][1]*v[1]; + + v2_copy( res, d ); +} + /* * ----------------------------------------------------------------------------- * Section 4.b 3x3 matrices @@ -767,6 +884,20 @@ static void m3x3_skew_symetric( m3x3f a, v3f v ) a[2][2] = 0.0f; } +/* aka kronecker product */ +static void m3x3_outer_product( m3x3f out_m, v3f a, v3f b ) +{ + out_m[0][0] = a[0]*b[0]; + out_m[0][1] = a[0]*b[1]; + out_m[0][2] = a[0]*b[2]; + out_m[1][0] = a[1]*b[0]; + out_m[1][1] = a[1]*b[1]; + out_m[1][2] = a[1]*b[2]; + out_m[2][0] = a[2]*b[0]; + out_m[2][1] = a[2]*b[1]; + out_m[2][2] = a[2]*b[2]; +} + static void m3x3_add( m3x3f a, m3x3f b, m3x3f d ) { v3_add( a[0], b[0], d[0] ); @@ -774,6 +905,13 @@ static void m3x3_add( m3x3f a, m3x3f b, m3x3f d ) v3_add( a[2], b[2], d[2] ); } +static void m3x3_sub( m3x3f a, m3x3f b, m3x3f d ) +{ + v3_sub( a[0], b[0], d[0] ); + v3_sub( a[1], b[1], d[1] ); + v3_sub( a[2], b[2], d[2] ); +} + static inline void m3x3_copy( m3x3f a, m3x3f b ) { v3_copy( a[0], b[0] ); @@ -787,12 +925,12 @@ static inline void m3x3_identity( m3x3f a ) m3x3_copy( id, a ); } -static void m3x3_diagonal( m3x3f a, f32 v ) +static void m3x3_diagonal( m3x3f out_a, f32 v ) { - m3x3_identity( a ); - a[0][0] = v; - a[1][1] = v; - a[2][2] = v; + m3x3_identity( out_a ); + out_a[0][0] = v; + out_a[1][1] = v; + out_a[2][2] = v; } static void m3x3_setdiagonalv3( m3x3f a, v3f v ) @@ -1159,8 +1297,7 @@ static void m4x3_decompose( m4x3f m, v3f co, v4f q, v3f s ) m3x3_q( rot, q ); } -static void m4x3_expand_aabb_point( m4x3f m, boxf box, v3f point ) -{ +static void m4x3_expand_aabb_point( m4x3f m, boxf box, v3f point ){ v3f v; m4x3_mulv( m, point, v ); @@ -1168,26 +1305,19 @@ static void m4x3_expand_aabb_point( m4x3f m, boxf box, v3f point ) v3_maxv( box[1], v, box[1] ); } -static void m4x3_transform_aabb( m4x3f m, boxf box ) -{ +static void m4x3_expand_aabb_aabb( m4x3f m, boxf boxa, boxf boxb ){ 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] } ); + v3_copy( boxb[0], a ); + v3_copy( boxb[1], b ); + m4x3_expand_aabb_point( m, boxa, (v3f){ a[0], a[1], a[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ a[0], b[1], a[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ b[0], b[1], a[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ b[0], a[1], a[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ a[0], a[1], b[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ a[0], b[1], b[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ b[0], b[1], b[2] } ); + m4x3_expand_aabb_point( m, boxa, (v3f){ b[0], a[1], b[2] } ); } - static inline void m4x3_lookat( m4x3f m, v3f pos, v3f target, v3f up ) { v3f dir; @@ -1374,6 +1504,36 @@ static inline void m4x4_inv( m4x4f a, m4x4f d ) v4_muls( d[3], det, d[3] ); } +/* + * http://www.terathon.com/lengyel/Lengyel-Oblique.pdf + */ +static void m4x4_clip_projection( m4x4f mat, v4f plane ){ + v4f c = + { + (vg_signf(plane[0]) + mat[2][0]) / mat[0][0], + (vg_signf(plane[1]) + mat[2][1]) / mat[1][1], + -1.0f, + (1.0f + mat[2][2]) / mat[3][2] + }; + + v4_muls( plane, 2.0f / v4_dot(plane,c), c ); + + mat[0][2] = c[0]; + mat[1][2] = c[1]; + mat[2][2] = c[2] + 1.0f; + mat[3][2] = c[3]; +} + +/* + * Undoes the above operation + */ +static void m4x4_reset_clipping( m4x4f mat, float ffar, float fnear ){ + mat[0][2] = 0.0f; + mat[1][2] = 0.0f; + mat[2][2] = -(ffar + fnear) / (ffar - fnear); + mat[3][2] = -2.0f * ffar * fnear / (ffar - fnear); +} + /* * ----------------------------------------------------------------------------- * Section 5.a Boxes @@ -1422,8 +1582,7 @@ static int box_within( boxf greater, boxf lesser ) return 0; } -static inline void box_init_inf( boxf box ) -{ +static inline void box_init_inf( boxf box ){ v3_fill( box[0], INFINITY ); v3_fill( box[1], -INFINITY ); } @@ -1484,7 +1643,7 @@ static int plane_intersect3( v4f a, v4f b, v4f c, v3f p ) return 1; } -int plane_intersect2( v4f a, v4f b, v3f p, v3f n ) +static int plane_intersect2( v4f a, v4f b, v3f p, v3f n ) { f32 const epsilon = 1e-6f; @@ -1510,7 +1669,7 @@ int plane_intersect2( v4f a, v4f b, v3f p, v3f n ) static int plane_segment( v4f plane, v3f a, v3f b, v3f co ) { f32 d0 = v3_dot( a, plane ) - plane[3], - d1 = v3_dot( b, plane ) - plane[3]; + d1 = v3_dot( b, plane ) - plane[3]; if( d0*d1 < 0.0f ) { @@ -1532,6 +1691,17 @@ static inline f64 plane_polarity( f64 p[4], f64 a[3] ) ; } +static f32 ray_plane( v4f plane, v3f co, v3f dir ){ + f32 d = v3_dot( plane, dir ); + if( fabsf(d) > 1e-6f ){ + v3f v0; + v3_muls( plane, plane[3], v0 ); + v3_sub( v0, co, v0 ); + return v3_dot( v0, plane ) / d; + } + else return INFINITY; +} + /* * ----------------------------------------------------------------------------- * Section 5.c Closest point functions @@ -1542,7 +1712,7 @@ static inline f64 plane_polarity( f64 p[4], f64 a[3] ) * These closest point tests were learned from Real-Time Collision Detection by * Christer Ericson */ -VG_STATIC f32 closest_segment_segment( v3f p1, v3f q1, v3f p2, v3f q2, +static f32 closest_segment_segment( v3f p1, v3f q1, v3f p2, v3f q2, f32 *s, f32 *t, v3f c1, v3f c2) { v3f d1,d2,r; @@ -1619,7 +1789,7 @@ VG_STATIC f32 closest_segment_segment( v3f p1, v3f q1, v3f p2, v3f q2, return v3_length2( v0 ); } -VG_STATIC int point_inside_aabb( boxf box, v3f point ) +static int point_inside_aabb( boxf box, v3f point ) { if((point[0]<=box[1][0]) && (point[1]<=box[1][1]) && (point[2]<=box[1][2]) && (point[0]>=box[0][0]) && (point[1]>=box[0][1]) && (point[2]>=box[0][2]) ) @@ -1628,13 +1798,13 @@ VG_STATIC int point_inside_aabb( boxf box, v3f point ) return 0; } -VG_STATIC void closest_point_aabb( v3f p, boxf box, v3f dest ) +static void closest_point_aabb( v3f p, boxf box, v3f dest ) { v3_maxv( p, box[0], dest ); v3_minv( dest, box[1], dest ); } -VG_STATIC void closest_point_obb( v3f p, boxf box, +static void closest_point_obb( v3f p, boxf box, m4x3f mtx, m4x3f inv_mtx, v3f dest ) { v3f local; @@ -1643,7 +1813,7 @@ VG_STATIC void closest_point_obb( v3f p, boxf box, m4x3_mulv( mtx, local, dest ); } -VG_STATIC f32 closest_point_segment( v3f a, v3f b, v3f point, v3f dest ) +static f32 closest_point_segment( v3f a, v3f b, v3f point, v3f dest ) { v3f v0, v1; v3_sub( b, a, v0 ); @@ -1655,7 +1825,7 @@ VG_STATIC f32 closest_point_segment( v3f a, v3f b, v3f point, v3f dest ) return t; } -VG_STATIC void closest_on_triangle( v3f p, v3f tri[3], v3f dest ) +static void closest_on_triangle( v3f p, v3f tri[3], v3f dest ) { v3f ab, ac, ap; f32 d1, d2; @@ -1751,7 +1921,7 @@ enum contact_type k_contact_type_edge }; -VG_STATIC enum contact_type closest_on_triangle_1( v3f p, v3f tri[3], v3f dest ) +static enum contact_type closest_on_triangle_1( v3f p, v3f tri[3], v3f dest ) { v3f ab, ac, ap; f32 d1, d2; @@ -1879,7 +2049,7 @@ static void closest_point_elipse( v2f p, v2f e, v2f o ) * ----------------------------------------------------------------------------- */ -int ray_aabb1( boxf box, v3f co, v3f dir_inv, f32 dist ) +static int ray_aabb1( boxf box, v3f co, v3f dir_inv, f32 dist ) { v3f v0, v1; f32 tmin, tmax; @@ -1902,7 +2072,7 @@ int ray_aabb1( boxf box, v3f co, v3f dir_inv, f32 dist ) /* Time of intersection with ray vs triangle */ static int ray_tri( v3f tri[3], v3f co, - v3f dir, f32 *dist ) + v3f dir, f32 *dist, int backfaces ) { f32 const kEpsilon = 0.00001f; @@ -1918,7 +2088,7 @@ static int ray_tri( v3f tri[3], v3f co, v3_cross( dir, v1, h ); v3_cross( v0, v1, n ); - if( v3_dot( n, dir ) > 0.0f ) /* Backface culling */ + if( (v3_dot( n, dir ) > 0.0f) && !backfaces ) /* Backface culling */ return 0; /* Parralel */ @@ -2056,7 +2226,7 @@ static int spherecast_triangle( v3f tri[3], f32 t_min = INFINITY, t1; - if( ray_tri( sum, co, dir, &t1 ) ){ + if( ray_tri( sum, co, dir, &t1, 0 ) ){ t_min = vg_minf( t_min, t1 ); hit = 1; } @@ -2124,6 +2294,143 @@ static void eval_bezier3( v3f p0, v3f p1, v3f p2, f32 t, v3f p ) v3_muladds( p, p2, t*t, p ); } +/* + * ----------------------------------------------------------------------------- + * Section 5.f Volumes + * ----------------------------------------------------------------------------- + */ + +static f32 vg_sphere_volume( f32 r ){ + return (4.0f/3.0f) * VG_PIf * r*r*r; +} + +static f32 vg_box_volume( boxf box ){ + v3f e; + v3_sub( box[1], box[0], e ); + return e[0]*e[1]*e[2]; +} + +static f32 vg_cylinder_volume( f32 r, f32 h ){ + return VG_PIf * r*r * h; +} + +static f32 vg_capsule_volume( f32 r, f32 h ){ + return vg_sphere_volume( r ) + vg_cylinder_volume( r, h-r*2.0f ); +} + +static void vg_sphere_bound( f32 r, boxf out_box ){ + v3_fill( out_box[0], -r ); + v3_fill( out_box[1], r ); +} + +static void vg_capsule_bound( f32 r, f32 h, boxf out_box ){ + v3_copy( (v3f){-r,-h*0.5f,r}, out_box[0] ); + v3_copy( (v3f){-r, h*0.5f,r}, out_box[1] ); +} + + +/* + * ----------------------------------------------------------------------------- + * Section 5.g Inertia Tensors + * ----------------------------------------------------------------------------- + */ + +/* + * Translate existing inertia tensor + */ +static void vg_translate_inertia( m3x3f inout_inertia, f32 mass, v3f d ){ + /* + * I = I_0 + m*[(d.d)E_3 - d(X)d] + * + * I: updated tensor + * I_0: original tensor + * m: scalar mass + * d: translation vector + * (X): outer product + * E_3: identity matrix + */ + m3x3f t, outer, scale; + m3x3_diagonal( t, v3_dot(d,d) ); + m3x3_outer_product( outer, d, d ); + m3x3_sub( t, outer, t ); + m3x3_diagonal( scale, mass ); + m3x3_mul( scale, t, t ); + m3x3_add( inout_inertia, t, inout_inertia ); +} + +/* + * Rotate existing inertia tensor + */ +static void vg_rotate_inertia( m3x3f inout_inertia, m3x3f rotation ){ + /* + * I = R I_0 R^T + * + * I: updated tensor + * I_0: original tensor + * R: rotation matrix + * R^T: tranposed rotation matrix + */ + + m3x3f Rt; + m3x3_transpose( rotation, Rt ); + m3x3_mul( rotation, inout_inertia, inout_inertia ); + m3x3_mul( inout_inertia, Rt, inout_inertia ); +} +/* + * Create inertia tensor for box + */ +static void vg_box_inertia( boxf box, f32 mass, m3x3f out_inertia ){ + v3f e, com; + v3_sub( box[1], box[0], e ); + v3_muladds( box[0], e, 0.5f, com ); + + f32 ex2 = e[0]*e[0], + ey2 = e[1]*e[1], + ez2 = e[2]*e[2], + ix = (ey2+ez2) * mass * (1.0f/12.0f), + iy = (ex2+ez2) * mass * (1.0f/12.0f), + iz = (ex2+ey2) * mass * (1.0f/12.0f); + + m3x3_identity( out_inertia ); + m3x3_setdiagonalv3( out_inertia, (v3f){ ix, iy, iz } ); + vg_translate_inertia( out_inertia, mass, com ); +} + +/* + * Create inertia tensor for sphere + */ +static void vg_sphere_inertia( f32 r, f32 mass, m3x3f out_inertia ){ + f32 ixyz = r*r * mass * (2.0f/5.0f); + + m3x3_identity( out_inertia ); + m3x3_setdiagonalv3( out_inertia, (v3f){ ixyz, ixyz, ixyz } ); +} + +/* + * Create inertia tensor for capsule + */ +static void vg_capsule_inertia( f32 r, f32 h, f32 mass, m3x3f out_inertia ){ + f32 density = mass / vg_capsule_volume( r, h ), + ch = h-r*2.0f, /* cylinder height */ + cm = VG_PIf * ch*r*r * density, /* cylinder mass */ + hm = VG_TAUf * (1.0f/3.0f) * r*r*r * density, /* hemisphere mass */ + + iy = r*r*cm * 0.5f, + ixz = iy * 0.5f + cm*ch*ch*(1.0f/12.0f), + + aux0= (hm*2.0f*r*r)/5.0f; + + iy += aux0 * 2.0f; + + f32 aux1= ch*0.5f, + aux2= aux0 + hm*(aux1*aux1 + 3.0f*(1.0f/8.0f)*ch*r); + + ixz += aux2*2.0f; + + m3x3_identity( out_inertia ); + m3x3_setdiagonalv3( out_inertia, (v3f){ ixz, iy, ixz } ); +} + /* * ----------------------------------------------------------------------------- * Section 6.a PSRNG and some distributions @@ -2151,59 +2458,55 @@ static void eval_bezier3( v3f p0, v3f p1, v3f p2, f32 t, v3f p ) /* changes to STATE_VECTOR_LENGTH also require changes to this */ #define MT_STATE_VECTOR_M 397 -struct { +typedef struct vg_rand vg_rand; +struct vg_rand { u32 mt[MT_STATE_VECTOR_LENGTH]; i32 index; -} -static vg_rand; +}; -static void vg_rand_seed( unsigned long seed ) -{ +static void vg_rand_seed( vg_rand *rand, unsigned long seed ) { /* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator * from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer * Programming," Vol. 2 (2nd Ed.) pp.102. */ - vg_rand.mt[0] = seed & 0xffffffff; - for( vg_rand.index=1; vg_rand.indexmt[0] = seed & 0xffffffff; + for( rand->index=1; rand->indexindex++){ + rand->mt[rand->index] = (6069 * rand->mt[rand->index-1]) & 0xffffffff; } } /* * Generates a pseudo-randomly generated long. */ -static u32 vg_randu32(void) -{ +static u32 vg_randu32( vg_rand *rand ) { u32 y; /* mag[x] = x * 0x9908b0df for x = 0,1 */ static u32 mag[2] = {0x0, 0x9908b0df}; - if( vg_rand.index >= MT_STATE_VECTOR_LENGTH || vg_rand.index < 0 ){ + if( rand->index >= MT_STATE_VECTOR_LENGTH || rand->index < 0 ){ /* generate STATE_VECTOR_LENGTH words at a time */ int kk; - if( vg_rand.index >= MT_STATE_VECTOR_LENGTH+1 || vg_rand.index < 0 ){ - vg_rand_seed( 4357 ); + if( rand->index >= MT_STATE_VECTOR_LENGTH+1 || rand->index < 0 ){ + vg_rand_seed( rand, 4357 ); } for( kk=0; kk> 1) ^ mag[y & 0x1]; + y = (rand->mt[kk] & MT_UPPER_MASK) | + (rand->mt[kk+1] & MT_LOWER_MASK); + rand->mt[kk] = rand->mt[kk+MT_STATE_VECTOR_M] ^ (y>>1) ^ mag[y & 0x1]; } for( ; kkmt[kk] & MT_UPPER_MASK) | + (rand->mt[kk+1] & MT_LOWER_MASK); + rand->mt[kk] = + rand->mt[ kk+(MT_STATE_VECTOR_M-MT_STATE_VECTOR_LENGTH)] ^ (y >> 1) ^ mag[y & 0x1]; } - y = (vg_rand.mt[MT_STATE_VECTOR_LENGTH-1] & MT_UPPER_MASK) | - (vg_rand.mt[0] & MT_LOWER_MASK); - vg_rand.mt[MT_STATE_VECTOR_LENGTH-1] = - vg_rand.mt[MT_STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1]; - vg_rand.index = 0; + y = (rand->mt[MT_STATE_VECTOR_LENGTH-1] & MT_UPPER_MASK) | + (rand->mt[0] & MT_LOWER_MASK); + rand->mt[MT_STATE_VECTOR_LENGTH-1] = + rand->mt[MT_STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1]; + rand->index = 0; } - y = vg_rand.mt[vg_rand.index++]; + y = rand->mt[rand->index++]; y ^= (y >> 11); y ^= (y << 7) & MT_TEMPERING_MASK_B; y ^= (y << 15) & MT_TEMPERING_MASK_C; @@ -2214,21 +2517,21 @@ static u32 vg_randu32(void) /* * Generates a pseudo-randomly generated f64 in the range [0..1]. */ -static inline f64 vg_randf64(void) -{ - return (f64)vg_randu32()/(f64)0xffffffff; +static inline f64 vg_randf64( vg_rand *rand ){ + return (f64)vg_randu32(rand)/(f64)0xffffffff; } -static inline f64 vg_randf64_range( f64 min, f64 max ) -{ - return vg_lerp( min, max, (f64)vg_randf64() ); +static inline f64 vg_randf64_range( vg_rand *rand, f64 min, f64 max ){ + return vg_lerp( min, max, (f64)vg_randf64(rand) ); } -static inline void vg_rand_dir( v3f dir ) -{ - dir[0] = vg_randf64(); - dir[1] = vg_randf64(); - dir[2] = vg_randf64(); +static inline void vg_rand_dir( vg_rand *rand, v3f dir ){ + dir[0] = vg_randf64(rand); + dir[1] = vg_randf64(rand); + dir[2] = vg_randf64(rand); + + /* warning: *could* be 0 length. + * very unlikely.. 1 in (2^32)^3. but its mathematically wrong. */ v3_muls( dir, 2.0f, dir ); v3_sub( dir, (v3f){1.0f,1.0f,1.0f}, dir ); @@ -2236,10 +2539,73 @@ static inline void vg_rand_dir( v3f dir ) v3_normalize( dir ); } -static inline void vg_rand_sphere( v3f co ) -{ - vg_rand_dir(co); - v3_muls( co, cbrtf( vg_randf64() ), co ); +static inline void vg_rand_sphere( vg_rand *rand, v3f co ){ + vg_rand_dir(rand,co); + v3_muls( co, cbrtf( vg_randf64(rand) ), co ); } -#endif /* VG_M_H */ +static void vg_rand_disc( vg_rand *rand, v2f co ){ + f32 a = vg_randf64(rand) * VG_TAUf; + co[0] = sinf(a); + co[1] = cosf(a); + v2_muls( co, sqrtf( vg_randf64(rand) ), co ); +} + +static void vg_rand_cone( vg_rand *rand, v3f out_dir, f32 angle ){ + f32 r = sqrtf(vg_randf64(rand)) * angle * 0.5f, + a = vg_randf64(rand) * VG_TAUf; + + out_dir[0] = sinf(a) * sinf(r); + out_dir[1] = cosf(a) * sinf(r); + out_dir[2] = cosf(r); +} + +static void vg_hsv_rgb( v3f hsv, v3f rgb ){ + i32 i = floorf( hsv[0]*6.0f ); + f32 v = hsv[2], + f = hsv[0] * 6.0f - (f32)i, + p = v * (1.0f-hsv[1]), + q = v * (1.0f-f*hsv[1]), + t = v * (1.0f-(1.0f-f)*hsv[1]); + + switch( i % 6 ){ + case 0: rgb[0] = v; rgb[1] = t; rgb[2] = p; break; + case 1: rgb[0] = q; rgb[1] = v; rgb[2] = p; break; + case 2: rgb[0] = p; rgb[1] = v; rgb[2] = t; break; + case 3: rgb[0] = p; rgb[1] = q; rgb[2] = v; break; + case 4: rgb[0] = t; rgb[1] = p; rgb[2] = v; break; + case 5: rgb[0] = v; rgb[1] = p; rgb[2] = q; break; + } +} + +static void vg_rgb_hsv( v3f rgb, v3f hsv ){ + f32 min = v3_minf( rgb ), + max = v3_maxf( rgb ), + range = max-min, + k_epsilon = 0.00001f; + + hsv[2] = max; + if( range < k_epsilon ){ + hsv[0] = 0.0f; + hsv[1] = 0.0f; + return; + } + + if( max > k_epsilon ){ + hsv[1] = range/max; + } + else { + hsv[0] = 0.0f; + hsv[1] = 0.0f; + return; + } + + if( rgb[0] >= max ) + hsv[0] = (rgb[1]-rgb[2])/range; + else if( max == rgb[1] ) + hsv[0] = 2.0f+(rgb[2]-rgb[0])/range; + else + hsv[0] = 4.0f+(rgb[0]-rgb[1])/range; + + hsv[0] = vg_fractf( hsv[0] * (60.0f/360.0f) ); +}