rigidbody math corrections & ragdoll tweaks for stability
[carveJwlIkooP6JGAAIwe30JlM.git] / world_sfd.c
1 #ifndef SFD_C
2 #define SFD_C
3
4 #include "world_sfd.h"
5 #include "shaders/scene_scoretext.h"
6 #include "shaders/scene_vertex_blend.h"
7 #include "network.h"
8 #include "entity.h"
9 #include "network_common.h"
10 #include "world_routes.h"
11
12 static f32 sfd_encode_glyph( char c ){
13 int value = 0;
14 if( c >= 'a' && c <= 'z' )
15 value = c-'a'+11;
16 else if( c >= '0' && c <= '9' )
17 value = c-'0'+1;
18 else if( c >= 'A' && c <= 'Z' )
19 value = c-'A'+11;
20 else if( c >= '\x01' && c <= '\x01'+10 )
21 value = 63-c;
22 else{
23 int base = 11+26;
24
25 switch( c ){
26 case '!': value=base+0; break;
27 case '?': value=base+1; break;
28 case ',': value=base+2; break;
29 case '.': value=base+3; break;
30 case '#': value=base+4; break;
31 case '$': value=base+5; break;
32 case '%': value=base+6; break;
33 case '*': value=base+7; break;
34 case '+': value=base+8; break;
35 case '-': value=base+9; break;
36 case '/': value=base+10; break;
37 case ':': value=base+11; break;
38 default: value=0; break;
39 }
40 }
41
42 return (float)value;
43 }
44
45 static void sfd_clear( u32 row ){
46 u32 row_h = world_sfd.h -1 -row;
47 for( int i=0; i<world_sfd.w; i++ ){
48 u32 idx = (world_sfd.w*row_h + i) * 2;
49 world_sfd.buffer[idx] = 0.0f;
50 }
51 }
52
53 static void sfd_encode( v2i co, const char *str, enum world_sfd_align align ){
54 i32 row_h = world_sfd.h -1 -co[1];
55 i32 offset_x = 0;
56
57 i32 w = VG_MIN( strlen(str), world_sfd.w );
58 if( align == k_world_sfd_center )
59 offset_x = (world_sfd.w - w) / 2;
60 else if( align == k_world_sfd_right )
61 offset_x = world_sfd.w - w;
62 else
63 offset_x = co[0];
64
65 for( i32 i=0; i<world_sfd.w; i++ ){
66 i32 u = i + offset_x,
67 idx = (world_sfd.w*row_h + u) * 2;
68
69 if( (u >= world_sfd.w) || (u < 0) )
70 continue;
71
72 if( !str[i] )
73 return;
74
75 world_sfd.buffer[idx] = sfd_encode_glyph( str[i] );
76 }
77 }
78
79 static void world_sfd_compile_scores( struct leaderboard_cache *board,
80 const char *title ){
81 for( u32 i=0; i<13; i++ )
82 sfd_clear(i);
83
84 sfd_encode( (v2i){0,0}, title, k_world_sfd_left );
85
86 if( !board ){
87 sfd_encode( (v2i){-1,4}, "Error out of range", k_world_sfd_center );
88 return;
89 }
90
91 if( !network_connected() ){
92 sfd_encode( (v2i){-1,0}, "Offline", k_world_sfd_right );
93 return;
94 }
95
96 if( board->status == k_request_status_not_found ){
97 sfd_encode( (v2i){-1,4}, "No records", k_world_sfd_center );
98 return;
99 }
100
101 if( board->status != k_request_status_ok ){
102 char buf[32];
103 vg_str s;
104 vg_strnull( &s, buf, 32 );
105 vg_strcat( &s, "Error: " );
106 vg_strcati32( &s, board->status );
107 sfd_encode( (v2i){-1,4}, buf, k_world_sfd_center );
108 return;
109 }
110
111 vg_msg body;
112 vg_msg_init( &body, board->data, board->data_len );
113
114 const char *alias = "rows";
115
116 if( world_sfd.view_weekly ){
117 alias = "rows_weekly";
118 sfd_encode( (v2i){-1,0}, "Weekly", k_world_sfd_right );
119 }
120 else {
121 sfd_encode( (v2i){-1,0}, "All-Time", k_world_sfd_right );
122 }
123
124 u32 l = 1;
125 if( vg_msg_seekframe( &body, alias ) ){
126 while( vg_msg_seekframe( &body, NULL ) ){
127 /* name */
128 const char *username = vg_msg_getkvstr( &body, "username" );
129
130 char buf[100];
131 vg_str str;
132 vg_strnull( &str, buf, 100 );
133 vg_strcati32( &str, l );
134 vg_strcat( &str, " " );
135
136 if( username )
137 vg_strcat( &str, username );
138 else
139 vg_strcat( &str, "??????" );
140
141 sfd_encode( (v2i){0,l}, str.buffer, k_world_sfd_left );
142
143 /* time */
144 vg_strnull( &str, buf, 100 );
145 i32 centiseconds = vg_msg_getkvi32( &body, "time", 0 ),
146 seconds = centiseconds / 100,
147 minutes = seconds / 60;
148
149 centiseconds %= 100;
150 seconds %= 60;
151 minutes %= 60;
152 if( minutes > 9 ) vg_strcat( &str, "?" );
153 else vg_strcati32( &str, minutes );
154 vg_strcat( &str, ":" );
155 vg_strcati32r( &str, seconds, 2, '0' );
156 vg_strcat( &str, "." );
157 vg_strcati32r( &str, centiseconds, 2, '0' );
158 sfd_encode( (v2i){-1,l}, str.buffer, k_world_sfd_right );
159 l ++;
160
161 vg_msg_skip_frame( &body );
162 }
163 }
164 else {
165 sfd_encode( (v2i){-1,4}, "No records", k_world_sfd_center );
166 }
167 }
168
169 static void world_sfd_compile_active_scores(void){
170 world_instance *world = world_current_instance();
171
172 struct leaderboard_cache *board = NULL;
173 const char *name = "Out of range";
174
175 if( world_sfd.active_route_board < mdl_arrcount( &world->ent_route ) ){
176 board = &world->leaderboard_cache[ world_sfd.active_route_board ];
177 ent_route *route = mdl_arritm( &world->ent_route,
178 world_sfd.active_route_board );
179 name = mdl_pstr( &world->meta, route->pstr_name );
180 }
181
182 world_sfd_compile_scores( board, name );
183 }
184
185 static void world_sfd_update( world_instance *world, v3f pos ){
186 if( mdl_arrcount( &world->ent_route ) ){
187 u32 closest = 0;
188 float min_dist = INFINITY;
189
190 for( u32 i=0; i<mdl_arrcount( &world->ent_route ); i++ ){
191 ent_route *route = mdl_arritm( &world->ent_route, i );
192 float dist = v3_dist2( route->board_transform[3], pos );
193
194 if( dist < min_dist ){
195 min_dist = dist;
196 closest = i;
197 }
198 }
199
200 struct leaderboard_cache *board = &world->leaderboard_cache[ closest ];
201
202 /* request new board if cache expires */
203 if( network_connected() ){
204 f64 delta = vg.time_real - board->cache_time;
205 if( (delta > 45.0) || (board->cache_time == 0.0) ){
206 board->cache_time = vg.time_real;
207 ent_route *route = mdl_arritm( &world->ent_route, closest );
208 addon_reg *world_reg =
209 world_static.instance_addons[ world - world_static.instances ];
210
211 char mod_uid[ ADDON_UID_MAX ];
212 addon_alias_uid( &world_reg->alias, mod_uid );
213
214 network_request_scoreboard(
215 mod_uid,
216 mdl_pstr( &world->meta, route->pstr_name ),
217 NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK, closest );
218 }
219 }
220
221 /* compile board text if we changed. */
222 if( world_sfd.active_route_board != closest ){
223 world_sfd_compile_active_scores();
224 }
225
226 world_sfd.active_route_board = closest;
227 }
228
229 for( int i=0; i<world_sfd.w*world_sfd.h; i++ ){
230 float *target = &world_sfd.buffer[i*2+0],
231 *cur = &world_sfd.buffer[i*2+1];
232
233 float const rate = vg.time_delta * 25.2313131414f;
234 float d1 = *target-*cur;
235
236 if( fabsf(d1) > rate ){
237 *cur += rate;
238 if( *cur > 49.0f )
239 *cur -= 49.0f;
240 }
241 else
242 *cur = *target;
243 }
244 }
245
246 static void bind_terrain_noise(void);
247 static void sfd_render( world_instance *world, camera *cam, m4x3f transform ){
248 mesh_bind( &world_sfd.mesh_display );
249 shader_scene_scoretext_use();
250 shader_scene_scoretext_uTexMain(1);
251 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, scene_scoretext );
252
253 bind_terrain_noise();
254
255 glActiveTexture( GL_TEXTURE1 );
256 glBindTexture( GL_TEXTURE_2D, world_sfd.tex_scoretex );
257
258 m4x4f pvm_prev;
259 m4x3_expand( transform, pvm_prev );
260 m4x4_mul( cam->mtx_prev.pv, pvm_prev, pvm_prev );
261
262 shader_scene_scoretext_uPv( cam->mtx.pv );
263 shader_scene_scoretext_uPvmPrev( pvm_prev );
264 shader_scene_scoretext_uMdl( transform );
265 shader_scene_scoretext_uCamera( cam->transform[3] );
266
267 for( int y=0;y<world_sfd.h; y++ ){
268 for( int x=0; x<world_sfd.w; x++ ){
269 float value = world_sfd.buffer[(y*world_sfd.w+x)*2+1];
270 shader_scene_scoretext_uInfo( (v3f){ x,y, value } );
271 mesh_draw( &world_sfd.mesh_display );
272 }
273 }
274
275 shader_scene_vertex_blend_use();
276 shader_scene_vertex_blend_uTexGarbage(0);
277 shader_scene_vertex_blend_uTexGradients(1);
278 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, scene_vertex_blend );
279
280 bind_terrain_noise();
281 glActiveTexture( GL_TEXTURE1 );
282 glBindTexture( GL_TEXTURE_2D, world_sfd.tex_scoretex );
283
284 shader_scene_vertex_blend_uPv( cam->mtx.pv );
285 shader_scene_vertex_blend_uPvmPrev( pvm_prev );
286 shader_scene_vertex_blend_uMdl( transform );
287 shader_scene_vertex_blend_uCamera( cam->transform[3] );
288
289 mesh_bind( &world_sfd.mesh_base );
290 mdl_draw_submesh( &world_sfd.sm_base );
291 }
292
293 static void world_sfd_init(void){
294 vg_info( "world_sfd_init\n" );
295 shader_scene_scoretext_register();
296 vg_linear_clear( vg_mem.scratch );
297
298 mdl_context mscoreboard;
299 mdl_open( &mscoreboard, "models/rs_scoretext.mdl", vg_mem.scratch );
300 mdl_load_metadata_block( &mscoreboard, vg_mem.scratch );
301 mdl_async_load_glmesh( &mscoreboard, &world_sfd.mesh_base, NULL );
302
303 mdl_load_mesh_block( &mscoreboard, vg_mem.scratch );
304
305 scene_context *scene = &world_sfd.scene;
306 vg_async_item *call = scene_alloc_async( scene, &world_sfd.mesh_display,
307 3000, 8000 );
308
309
310 mdl_mesh *m_backer = mdl_find_mesh( &mscoreboard, "backer" ),
311 *m_card = mdl_find_mesh( &mscoreboard, "score_card" );
312
313 mdl_submesh
314 *sm_backer = mdl_arritm( &mscoreboard.submeshs, m_backer->submesh_start ),
315 *sm_card = mdl_arritm( &mscoreboard.submeshs, m_card->submesh_start );
316 world_sfd.sm_base = *sm_backer;
317
318 m4x3f identity;
319 m4x3_identity( identity );
320
321 for( int i=0;i<4;i++ ){
322 u32 vert_start = scene->vertex_count;
323 scene_add_mdl_submesh( scene, &mscoreboard, sm_card, identity );
324
325 for( int j=0; j<sm_card->vertex_count; j++ ){
326 scene_vert *vert = &scene->arrvertices[ vert_start+j ];
327
328 float const k_glyph_uvw = 1.0f/64.0f;
329 vert->uv[0] -= k_glyph_uvw * (float)(i-1);
330 vert->norm[3] = i*42;
331 }
332 }
333
334 vg_async_dispatch( call, async_scene_upload );
335 vg_tex2d_load_qoi_async_file( "textures/scoretext.qoi",
336 VG_TEX2D_CLAMP|VG_TEX2D_NEAREST,
337 &world_sfd.tex_scoretex );
338
339 mdl_close( &mscoreboard );
340
341 int w = 27,
342 h = 13;
343
344 world_sfd.w = w;
345 world_sfd.h = h;
346 world_sfd.buffer = vg_linear_alloc( vg_mem.rtmemory, 2*w*h*sizeof(float) );
347
348 for( int i=0; i<w*h*2; i++ )
349 world_sfd.buffer[i] = 0.0f;
350 }
351
352 #endif /* WORLD_SFD_C */