way better cloud data
[carveJwlIkooP6JGAAIwe30JlM.git] / ent_skateshop.c
1 #ifndef ENT_SKATESHOP_C
2 #define ENT_SKATESHOP_C
3
4 #define VG_GAME
5 #include "vg/vg.h"
6 #include "vg/vg_steam_ugc.h"
7 #include "ent_skateshop.h"
8 #include "world.h"
9 #include "player.h"
10 #include "gui.h"
11 #include "menu.h"
12 #include "pointcloud.h"
13
14 /*
15 * Checks string equality but does a hash check first
16 */
17 static inline int const_str_eq( u32 hash, const char *str, const char *cmp )
18 {
19 if( hash == vg_strdjb2(cmp) )
20 if( !strcmp( str, cmp ) )
21 return 1;
22 return 0;
23 }
24
25 /*
26 * Get an existing cache instance, allocate a new one to be loaded, or NULL if
27 * there is no space
28 */
29 VG_STATIC struct cache_board *skateshop_cache_fetch( u32 registry_index )
30 {
31 struct registry_board *reg = NULL;
32
33 if( registry_index < global_skateshop.registry_count ){
34 reg = &global_skateshop.registry[ registry_index ];
35
36 if( reg->cache_ptr ){
37 return reg->cache_ptr;
38 }
39 }
40
41 /* lru eviction. should be a linked list maybe... */
42 double min_time = 1e300;
43 struct cache_board *min_board = NULL;
44
45 SDL_AtomicLock( &global_skateshop.sl_cache_access );
46 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
47 struct cache_board *cache_ptr = &global_skateshop.cache[i];
48
49 if( cache_ptr->state == k_cache_board_state_load_request ) continue;
50 if( cache_ptr->ref_count ) continue;
51
52 if( cache_ptr->last_use_time < min_time ){
53 min_time = cache_ptr->last_use_time;
54 min_board = cache_ptr;
55 }
56 }
57
58 if( min_board ){
59 if( min_board->state == k_cache_board_state_loaded ){
60 struct registry_board *other =
61 &global_skateshop.registry[ min_board->registry_id ];
62
63 vg_info( "Deallocating board: '%s'\n", min_board, other->filename );
64
65 player_board_unload( &min_board->board );
66 other->cache_ptr = NULL;
67 }
68
69 if( reg ){
70 vg_info( "Allocating board (reg:%u) '%s'\n",
71 registry_index, reg->filename );
72 }
73 else{
74 vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index );
75 }
76
77 min_board->registry_id = registry_index;
78 min_board->last_use_time = vg.time;
79 min_board->ref_count = 0;
80 min_board->state = k_cache_board_state_load_request;
81 }
82 else{
83 vg_error( "No free boards to load registry!\n" );
84 }
85
86 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
87 return min_board;
88 }
89
90 VG_STATIC void skateshop_update_viewpage(void)
91 {
92 u32 page = global_skateshop.selected_registry_id/SKATESHOP_VIEW_SLOT_MAX;
93
94 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
95 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
96 u32 request_id = page*SKATESHOP_VIEW_SLOT_MAX + i;
97
98 if( slot->cache_ptr ) unwatch_cache_board( slot->cache_ptr );
99
100 slot->cache_ptr = skateshop_cache_fetch( request_id );
101 if( slot->cache_ptr ) watch_cache_board( slot->cache_ptr );
102 }
103 }
104
105 /* generic reciever */
106 VG_STATIC void workshop_async_any_complete( void *data, u32 size )
107 {
108 skaterift_end_op();
109 }
110
111 /*
112 * op/subroutine: k_workshop_op_item_load
113 * -----------------------------------------------------------------------------
114 */
115
116 /*
117 * Reciever for board completion; only promotes the status in the main thread
118 */
119 VG_STATIC void skateshop_async_board_loaded( void *payload, u32 size )
120 {
121 SDL_AtomicLock( &global_skateshop.sl_cache_access );
122 struct cache_board *cache_ptr = payload;
123 cache_ptr->last_use_time = vg.time;
124 cache_ptr->state = k_cache_board_state_loaded;
125
126 struct registry_board *reg =
127 &global_skateshop.registry[ cache_ptr->registry_id ];
128 reg->cache_ptr = cache_ptr;
129 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
130
131 vg_success( "Async board loaded (%s)\n", reg->filename );
132 }
133
134 /*
135 * Thread(or subroutine of thread), for checking view slots that weve installed.
136 * Load the model if a view slot wants it
137 */
138 VG_STATIC void workshop_visibile_load_loop_thread( void *_args )
139 {
140 char path[1024];
141 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
142 struct cache_board *cache_ptr = &global_skateshop.cache[i];
143
144 SDL_AtomicLock( &global_skateshop.sl_cache_access );
145 if( cache_ptr->state == k_cache_board_state_load_request ){
146 if( cache_ptr->registry_id >= global_skateshop.registry_count ){
147 /* should maybe have a different value for this case */
148 cache_ptr->state = k_cache_board_state_none;
149 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
150 continue;
151 }
152
153 /* continue with the request */
154 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
155
156 struct registry_board *reg =
157 &global_skateshop.registry[ cache_ptr->registry_id ];
158
159 if( reg->workshop_id ){
160 vg_async_item *call =
161 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
162
163 struct async_workshop_filepath_info *info = call->payload;
164 info->buf = path;
165 info->id = reg->workshop_id;
166 info->len = vg_list_size(path) - strlen("/board.mdl")-1;
167 vg_async_dispatch( call, async_workshop_get_filepath );
168 vg_async_stall(); /* too bad! */
169
170 if( path[0] == '\0' ){
171 SDL_AtomicLock( &global_skateshop.sl_cache_access );
172 cache_ptr->state = k_cache_board_state_none;
173 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
174
175 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n",
176 reg->workshop_id );
177 continue;
178 }
179 else{
180 strcat( path, "/board.mdl" );
181 }
182 }
183 else{
184 snprintf( path, 256, "models/boards/%s", reg->filename );
185 }
186
187 player_board_load( &cache_ptr->board, path );
188 vg_async_call( skateshop_async_board_loaded, cache_ptr, 0 );
189 }
190 else
191 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
192 }
193 vg_async_call( workshop_async_any_complete, NULL, 0 );
194 }
195
196 /*
197 * op: k_workshop_op_item_scan
198 * -----------------------------------------------------------------------------
199 */
200
201 /*
202 * Reciever for scan completion. copies the registry_count back into t0
203 */
204 VG_STATIC void workshop_async_reg_update( void *data, u32 size )
205 {
206 vg_info( "Registry update notify\n" );
207 global_skateshop.registry_count = global_skateshop.t1_registry_count;
208 }
209
210 VG_STATIC void workshop_steam_scan(void)
211 {
212 /*
213 * Steam workshop scan
214 */
215 vg_info( "Scanning steam workshop for boards\n" );
216 PublishedFileId_t workshop_ids[ SKATESHOP_REGISTRY_MAX ];
217 u32 workshop_count = SKATESHOP_REGISTRY_MAX;
218
219 vg_async_item *call = vg_async_alloc(
220 sizeof(struct async_workshop_installed_files_info));
221 struct async_workshop_installed_files_info *info = call->payload;
222 info->buffer = workshop_ids;
223 info->len = &workshop_count;
224 vg_async_dispatch( call, async_workshop_get_installed_files );
225 vg_async_stall();
226
227 for( u32 j=0; j<workshop_count; j++ ){
228 PublishedFileId_t id = workshop_ids[j];
229
230 for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
231 struct registry_board *reg = &global_skateshop.registry[i];
232
233 if( reg->workshop_id == id ){
234 reg->state = k_registry_board_state_indexed;
235 goto next_file_workshop;
236 }
237 }
238
239 if( global_skateshop.t1_registry_count == SKATESHOP_REGISTRY_MAX ){
240 vg_error( "You have too many boards installed!\n" );
241 break;
242 }
243
244 vg_info( "new listing from the steam workshop!: "PRINTF_U64"\n", id );
245
246 struct registry_board *reg = &global_skateshop.registry[
247 global_skateshop.t1_registry_count ++ ];
248
249 reg->cache_ptr = NULL;
250 snprintf( reg->filename, 64, PRINTF_U64, id );
251 reg->filename_hash = vg_strdjb2( reg->filename );
252 reg->workshop_id = id;
253 reg->state = k_registry_board_state_indexed;
254
255 workshop_file_info_clear( &reg->workshop );
256 strcpy( reg->workshop.title, "Workshop file" );
257
258 /* load the metadata off the disk */
259 vg_async_item *call =
260 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
261
262 const char *meta_file = "/board.mdl.inf";
263 char path[ 1024 ];
264 struct async_workshop_filepath_info *info = call->payload;
265 info->buf = path;
266 info->id = reg->workshop_id;
267 info->len = vg_list_size(path) - strlen(meta_file)-1;
268 vg_async_dispatch( call, async_workshop_get_filepath );
269 vg_async_stall(); /* too bad! */
270
271 strcat( path, meta_file );
272 workshop_load_metadata( path, &reg->workshop );
273
274 next_file_workshop:;
275 }
276 }
277
278 /*
279 * Async thread which scans local files for boards, as well as scheduling
280 * synchronous calls to the workshop
281 */
282 VG_STATIC void workshop_scan_thread( void *_args )
283 {
284 vg_linear_clear( vg_mem.scratch );
285
286 for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
287 struct registry_board *reg = &global_skateshop.registry[i];
288 reg->state = k_registry_board_state_indexed_absent;
289 }
290
291 /*
292 * Local disk scan
293 */
294 vg_info( "Scanning models/boards/*.mdl\n" );
295 tinydir_dir dir;
296 tinydir_open( &dir, "models/boards" );
297
298 while( dir.has_next ){
299 tinydir_file file;
300 tinydir_readfile( &dir, &file );
301
302 if( file.is_reg ){
303 u32 hash = vg_strdjb2( file.name );
304
305 for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
306 struct registry_board *reg = &global_skateshop.registry[i];
307
308 if( const_str_eq( hash, file.name, reg->filename ) ){
309 reg->state = k_registry_board_state_indexed;
310 goto next_file;
311 }
312 }
313
314 if( global_skateshop.t1_registry_count == SKATESHOP_REGISTRY_MAX ){
315 vg_error( "You have too many boards installed!\n" );
316 break;
317 }
318
319 vg_info( "new listing!: %s\n", file.name );
320
321 struct registry_board *reg =
322 &global_skateshop.registry[global_skateshop.t1_registry_count ++];
323
324 reg->cache_ptr = NULL;
325 vg_strncpy( file.name, reg->filename, 64, k_strncpy_always_add_null );
326 vg_strncpy( file.name, reg->workshop.title,
327 64, k_strncpy_always_add_null );
328 reg->filename_hash = hash;
329 reg->workshop_id = 0;
330 reg->state = k_registry_board_state_indexed;
331 reg->workshop.author = 0;
332 strcpy( reg->workshop.author_name, "custom" );
333 }
334
335 next_file: tinydir_next( &dir );
336 }
337
338 tinydir_close(&dir);
339
340 if( steam_ready ) workshop_steam_scan();
341
342 vg_async_call( workshop_async_reg_update, NULL, 0 );
343 vg_async_stall();
344 workshop_visibile_load_loop_thread(NULL);
345 }
346
347 /*
348 * Asynchronous scan of local disk for items and add them to the registry
349 */
350 VG_STATIC void workshop_op_item_scan(void)
351 {
352 skaterift_begin_op( k_workshop_op_item_scan );
353 vg_loader_start( workshop_scan_thread, NULL );
354 }
355
356 /*
357 * Regular stuff
358 * -----------------------------------------------------------------------------
359 */
360
361 /* we can only keep using a viewslot pointer for multiple frames if we watch it
362 * using this function */
363 VG_STATIC void watch_cache_board( struct cache_board *ptr )
364 {
365 if( ptr->ref_count >= 32 ){
366 vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
367 }
368
369 ptr->last_use_time = vg.time;
370 ptr->ref_count ++;
371 }
372
373 /* after this is called, the calling code only has access to the pointer for the
374 * duration of the rest of the frame */
375 VG_STATIC void unwatch_cache_board( struct cache_board *ptr )
376 {
377 if( ptr->ref_count == 0 ){
378 vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
379 }
380
381 ptr->ref_count --;
382 }
383
384 /*
385 * Callback handler for persona state changes,
386 * it sets the author names on the registries
387 */
388 VG_STATIC void callback_persona_statechange( CallbackMsg_t *msg )
389 {
390 PersonaStateChange_t *info = (PersonaStateChange_t *)msg->m_pubParam;
391 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
392
393 if( info->m_nChangeFlags & k_EPersonaChangeName ){
394 for( u32 i=0; i<global_skateshop.registry_count; i++ ){
395 struct registry_board *reg = &global_skateshop.registry[i];
396 if( reg->workshop.author == info->m_ulSteamID ){
397 const char *name = SteamAPI_ISteamFriends_GetFriendPersonaName(
398 hSteamFriends, info->m_ulSteamID );
399 str_utf8_collapse( name, reg->workshop.author_name, 32 );
400 }
401 }
402 }
403 }
404
405 /*
406 * VG event init
407 */
408 VG_STATIC void skateshop_init(void)
409 {
410 u32 reg_size = sizeof(struct registry_board)*SKATESHOP_REGISTRY_MAX,
411 cache_size = sizeof(struct cache_board)*SKATESHOP_BOARD_CACHE_MAX;
412
413 global_skateshop.registry = vg_linear_alloc( vg_mem.rtmemory, reg_size );
414 global_skateshop.registry_count = 0;
415 global_skateshop.cache = vg_linear_alloc( vg_mem.rtmemory, cache_size );
416
417 memset( global_skateshop.cache, 0, cache_size );
418
419 for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
420 struct cache_board *board = &global_skateshop.cache[i];
421 board->state = k_cache_board_state_none;
422 board->registry_id = 0xffffffff;
423 board->last_use_time = -99999.9;
424 board->ref_count = 0;
425 }
426
427 if( steam_ready ){
428 steam_register_callback( k_iPersonaStateChange,
429 callback_persona_statechange );
430 }
431 }
432
433 VG_STATIC struct cache_board *skateshop_selected_cache_if_loaded(void)
434 {
435 if( global_skateshop.registry_count > 0 ){
436 u32 reg_id = global_skateshop.selected_registry_id;
437 struct registry_board *reg = &global_skateshop.registry[ reg_id ];
438
439 SDL_AtomicLock( &global_skateshop.sl_cache_access );
440 if( reg->cache_ptr &&
441 (reg->cache_ptr->state == k_cache_board_state_loaded ) )
442 {
443 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
444 return reg->cache_ptr;
445 }
446 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
447 }
448
449 return NULL;
450 }
451
452 /*
453 * VG event preupdate
454 */
455 void temp_update_playermodel(void);
456 VG_STATIC void global_skateshop_preupdate(void)
457 {
458 float rate = vg_minf( 1.0f, vg.time_frame_delta * 2.0f );
459 global_skateshop.factive = vg_lerpf( global_skateshop.factive,
460 global_skateshop.active, rate );
461
462 if( !global_skateshop.active ) return;
463
464 world_instance *world = get_active_world();
465
466 ent_skateshop *shop = global_skateshop.ptr_ent;
467
468 /* camera positioning */
469 ent_camera *ref = mdl_arritm( &world->ent_camera,
470 mdl_entity_id_id(shop->id_camera) );
471
472 v3f dir = {0.0f,-1.0f,0.0f};
473 mdl_transform_vector( &ref->transform, dir, dir );
474 m3x3_mulv( localplayer.invbasis, dir, dir );
475 player_vector_angles( localplayer.cam_override_angles, dir, 1.0f, 0.0f );
476
477 v3f lookat;
478 if( shop->type == k_skateshop_type_boardshop ||
479 shop->type == k_skateshop_type_worldshop ){
480 ent_marker *display = mdl_arritm( &world->ent_marker,
481 mdl_entity_id_id(shop->boards.id_display) );
482
483 v3_sub( display->transform.co, localplayer.rb.co, lookat );
484
485 }
486 else if( shop->type == k_skateshop_type_charshop ){
487 v3_sub( ref->transform.co, localplayer.rb.co, lookat );
488 }
489 else{
490 vg_fatal_error( "Unknown store (%u)\n", shop->type );
491 }
492
493 q_axis_angle( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f},
494 atan2f(lookat[0],lookat[2]) );
495
496 v3_copy( ref->transform.co, localplayer.cam_override_pos );
497 localplayer.cam_override_fov = ref->fov;
498 localplayer.cam_override_strength = global_skateshop.factive;
499
500 /* input */
501 if( shop->type == k_skateshop_type_boardshop ){
502 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
503 gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
504
505 struct cache_board *selected_cache = skateshop_selected_cache_if_loaded();
506
507 if( selected_cache ){
508 gui_helper_action( button_display_string( k_srbind_maccept ), "pick" );
509 }
510
511 /*
512 * Controls
513 * ----------------------
514 */
515
516 if( button_down( k_srbind_mleft ) ){
517 if( global_skateshop.selected_registry_id > 0 ){
518 global_skateshop.selected_registry_id --;
519 }
520 }
521
522 if( button_down( k_srbind_mright ) ){
523 if( global_skateshop.selected_registry_id+1 <
524 global_skateshop.registry_count )
525 {
526 global_skateshop.selected_registry_id ++;
527 }
528 }
529
530 if( selected_cache && button_down( k_srbind_maccept ) ){
531 vg_info( "chose board from skateshop (%u)\n",
532 global_skateshop.selected_registry_id );
533
534 if( localplayer.board_view_slot ){
535 unwatch_cache_board( localplayer.board_view_slot );
536 }
537
538 localplayer.board_view_slot = selected_cache;
539 watch_cache_board( localplayer.board_view_slot );
540
541 global_skateshop_exit();
542 return;
543 }
544 }
545 else if( shop->type == k_skateshop_type_charshop ){
546 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
547 gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
548 gui_helper_action( button_display_string( k_srbind_maccept ), "pick" );
549
550 if( button_down( k_srbind_mleft ) ){
551 if( cl_playermdl_id > 0 ){
552 cl_playermdl_id --;
553 }
554 else{
555 cl_playermdl_id = 2; /* HACK */
556 }
557 temp_update_playermodel(); /* HACK */
558 }
559
560 if( button_down( k_srbind_mright ) ){
561 if( cl_playermdl_id+1 < 3 ){
562 cl_playermdl_id ++;
563 }
564 else{
565 cl_playermdl_id = 0; /* HACK */
566 }
567 temp_update_playermodel(); /* HACK */
568 /*lol*/
569 }
570
571 if( button_down( k_srbind_maccept ) ){
572 global_skateshop_exit();
573 }
574 }
575 else if( shop->type == k_skateshop_type_worldshop ){
576 gui_helper_action( axis_display_string( k_sraxis_mbrowse_h ), "browse" );
577
578 v2f input;
579 joystick_state( k_srjoystick_steer, input );
580 pointcloud.control[0] += input[0] * vg.time_delta;
581 pointcloud.control[2] += input[1] * vg.time_delta;
582
583 pointcloud.control[0] = vg_clampf( pointcloud.control[0], -10.0f, 10.0f );
584 pointcloud.control[2] = vg_clampf( pointcloud.control[2], -10.0f, 10.0f );
585
586 if( button_press( k_srbind_trick1 ) ){
587 pointcloud.control[3] += vg.time_delta*0.2f;
588 }
589 if( button_press( k_srbind_trick0 ) ){
590 pointcloud.control[3] -= vg.time_delta*0.2f;
591 }
592 pointcloud.control[3] = vg_clampf( pointcloud.control[3], 0.001f, 10.0f );
593 }
594 else{
595 vg_fatal_error( "Unknown store (%u)\n", shop->type );
596 }
597
598 if( button_down( k_srbind_mback ) ){
599 global_skateshop_exit();
600 return;
601 }
602 }
603
604 VG_STATIC void skateshop_render_boardshop(void)
605 {
606 world_instance *world = get_active_world();
607 ent_skateshop *shop = global_skateshop.ptr_ent;
608
609 u32 slot_count = vg_list_size(global_skateshop.shop_view_slots);
610
611 ent_marker *mark_rack = mdl_arritm( &world->ent_marker,
612 mdl_entity_id_id(shop->boards.id_rack)),
613 *mark_display = mdl_arritm( &world->ent_marker,
614 mdl_entity_id_id(shop->boards.id_display));
615
616 int visibility[ SKATESHOP_VIEW_SLOT_MAX ];
617 SDL_AtomicLock( &global_skateshop.sl_cache_access );
618 for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
619 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
620
621 visibility[i] = 1;
622
623 if( slot->cache_ptr == NULL ) visibility[i] = 0;
624 else if( slot->cache_ptr->state != k_cache_board_state_loaded )
625 visibility[i] = 0;
626 }
627 SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
628
629 /* Render loaded boards in the view slots */
630 for( u32 i=0; i<slot_count; i++ ){
631 struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
632 float selected = 0.0f;
633
634 if( !visibility[i] ) goto fade_out;
635
636 mdl_transform xform;
637 transform_identity( &xform );
638
639 xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
640 mdl_transform_mul( &mark_rack->transform, &xform, &xform );
641
642 if( slot->cache_ptr->registry_id ==
643 global_skateshop.selected_registry_id ){
644 selected = 1.0f;
645 }
646
647 float t = slot->view_blend;
648 v3_lerp( xform.co, mark_display->transform.co, t, xform.co );
649 q_nlerp( xform.q, mark_display->transform.q, t, xform.q );
650 v3_lerp( xform.s, mark_display->transform.s, t, xform.s );
651
652 m4x3f mmdl;
653 mdl_transform_m4x3( &xform, mmdl );
654 render_board( &main_camera, world, &slot->cache_ptr->board, mmdl,
655 k_board_shader_entity );
656
657 fade_out:;
658 float rate = 5.0f*vg.time_delta;
659 slot->view_blend = vg_lerpf( slot->view_blend, selected, rate );
660 }
661
662 ent_marker *mark_info = mdl_arritm( &world->ent_marker,
663 mdl_entity_id_id(shop->boards.id_info));
664 m4x3f mtext, mrack;
665 mdl_transform_m4x3( &mark_info->transform, mtext );
666 mdl_transform_m4x3( &mark_rack->transform, mrack );
667
668 #if 0
669 const char *text_title = "Fish - Title";
670 const char *text_author = "by Shaniqua";
671 #endif
672
673 m4x3f mlocal, mmdl;
674 m4x3_identity( mlocal );
675
676 float scale = 0.2f,
677 thickness = 0.03f;
678
679 font3d_bind( &world_global.font, &main_camera );
680 shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
681
682 /* Selection counter
683 * ------------------------------------------------------------------ */
684 m3x3_zero( mlocal );
685 v3_zero( mlocal[3] );
686 mlocal[0][0] = -scale*2.0f;
687 mlocal[1][2] = -scale*2.0f;
688 mlocal[2][1] = -thickness;
689 mlocal[3][2] = -0.7f;
690 m4x3_mul( mrack, mlocal, mmdl );
691
692 if( global_skateshop.registry_count == 0 ){
693 font3d_simple_draw( &world_global.font, 0,
694 "Nothing installed", &main_camera, mmdl );
695 }
696 else{
697 char buf[16];
698 int i=0;
699 i+=highscore_intl( buf+i, global_skateshop.selected_registry_id+1, 3 );
700 buf[i++] = '/';
701 i+=highscore_intl( buf+i, global_skateshop.registry_count, 3 );
702 buf[i++] = '\0';
703
704 font3d_simple_draw( &world_global.font, 0, buf, &main_camera, mmdl );
705 }
706
707 struct cache_board *cache_ptr = skateshop_selected_cache_if_loaded();
708 if( !cache_ptr ) return;
709
710 struct registry_board *reg =
711 &global_skateshop.registry[cache_ptr->registry_id];
712 struct workshop_file_info *info = &reg->workshop;
713
714 /* Skin title
715 * ----------------------------------------------------------------- */
716 m3x3_zero( mlocal );
717 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
718 mlocal[3][0] = -font3d_string_width( &world_global.font, 0, info->title );
719 mlocal[3][0] *= scale*0.5f;
720 mlocal[3][1] = 0.1f;
721 m4x3_mul( mtext, mlocal, mmdl );
722 font3d_simple_draw( &world_global.font, 0, info->title, &main_camera, mmdl );
723
724 /* Author name
725 * ----------------------------------------------------------------- */
726 scale *= 0.4f;
727 m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
728 mlocal[3][0] = -font3d_string_width( &world_global.font, 0,
729 info->author_name );
730 mlocal[3][0] *= scale*0.5f;
731 mlocal[3][1] = 0.0f;
732 m4x3_mul( mtext, mlocal, mmdl );
733 font3d_simple_draw( &world_global.font, 0,
734 info->author_name, &main_camera, mmdl );
735 }
736
737 VG_STATIC void skateshop_render_charshop(void)
738 {
739 }
740
741 VG_STATIC void skateshop_render_worldshop(void)
742 {
743 world_instance *world = get_active_world();
744
745 m4x3f mmdl;
746 ent_skateshop *shop = global_skateshop.ptr_ent;
747 ent_marker *mark_display = mdl_arritm( &world->ent_marker,
748 mdl_entity_id_id(shop->worlds.id_display));
749 mdl_transform_m4x3( &mark_display->transform, mmdl );
750
751 /* TODO? ... */
752 #if 0
753 v3f vol;
754 v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], vol );
755
756 v2f rect = { 1.0f, 1.0f },
757 map = { vol[0], vol[2] },
758 result;
759
760 f32 rp = rect[0] * map[1],
761 rc = rect[1] * map[0];
762
763 u32 axis, other;
764 if( rc > rp ) axis = 0;
765 else axis = 1;
766 other = axis ^ 0x1;
767
768 result[axis] = rect[axis];
769 result[other] = (rect[axis] * map[other]) / map[axis];
770
771 m4x3f mlocal, mx;
772 m4x3_identity( mlocal );
773
774 mlocal[0][0] = result[0];
775 mlocal[2][2] = result[1];
776 mlocal[1][1] = (vol[1]/vol[0]) * mlocal[0][0];
777 mlocal[3][0] = (rect[0]-result[0])*0.5f - rect[0]*0.5f; /* sea level? */
778 mlocal[3][2] = (rect[1]-result[1])*0.5f - rect[1]*0.5f;
779 m4x3_mul( mmdl, mlocal, mx );
780 #endif
781
782 m4x3_rotate_y( mmdl, vg.time * 0.2 );
783
784 #if 1
785 glEnable(GL_BLEND);
786 glBlendFunc(GL_ONE, GL_ONE);
787 glDisable(GL_DEPTH_TEST);
788 #endif
789 pointcloud_render( world, &main_camera, mmdl );
790 glDisable(GL_BLEND);
791 glEnable(GL_DEPTH_TEST);
792 }
793
794 /*
795 * World: render event
796 */
797 VG_STATIC void skateshop_render(void)
798 {
799 if( !global_skateshop.active ) return;
800
801 ent_skateshop *shop = global_skateshop.ptr_ent;
802
803 if( shop->type == k_skateshop_type_boardshop ){
804 skateshop_render_boardshop();
805 }
806 else if( shop->type == k_skateshop_type_charshop ){
807 skateshop_render_charshop();
808 }
809 else if( shop->type == k_skateshop_type_worldshop ){
810 skateshop_render_worldshop();
811 }
812 else{
813 vg_fatal_error( "Unknown store (%u)\n", shop->type );
814 }
815 }
816
817 /*
818 * Entity logic: entrance event
819 */
820 VG_STATIC void ent_skateshop_call( world_instance *world, ent_call *call )
821 {
822 u32 index = mdl_entity_id_id( call->id );
823 ent_skateshop *shop = mdl_arritm( &world->ent_skateshop, index );
824 vg_info( "skateshop_call\n" );
825
826 if( menu.active ) return;
827 if( skaterift.async_op != k_async_op_none ) return;
828
829 if( call->function == k_ent_function_trigger ){
830 if( localplayer.subsystem != k_player_subsystem_walk ){
831 return;
832 }
833
834 vg_info( "Entering skateshop\n" );
835
836 localplayer.immobile = 1;
837 menu.disable_open = 1;
838 global_skateshop.active = 1;
839
840 v3_zero( localplayer.rb.v );
841 v3_zero( localplayer.rb.w );
842 localplayer._walk.move_speed = 0.0f;
843 global_skateshop.ptr_ent = shop;
844
845 if( shop->type == k_skateshop_type_boardshop ){
846 skateshop_update_viewpage();
847 workshop_op_item_scan();
848 }
849 }
850 }
851
852 /*
853 * Entity logic: exit event
854 */
855 VG_STATIC void global_skateshop_exit(void)
856 {
857 vg_info( "exit skateshop\n" );
858 localplayer.immobile = 0;
859 global_skateshop.active = 0;
860 menu.disable_open = 0;
861 srinput.ignore_input_frames = 2;
862 }
863
864 #endif /* ENT_SKATESHOP_C */