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