1 //********************************************************************************************
2 //*
3 //* This file is part of Egoboo.
4 //*
5 //* Egoboo is free software: you can redistribute it and/or modify it
6 //* under the terms of the GNU General Public License as published by
7 //* the Free Software Foundation, either version 3 of the License, or
8 //* (at your option) any later version.
9 //*
10 //* Egoboo is distributed in the hope that it will be useful, but
11 //* WITHOUT ANY WARRANTY; without even the implied warranty of
12 //* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 //* General Public License for more details.
14 //*
15 //* You should have received a copy of the GNU General Public License
16 //* along with Egoboo. If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19
20 /// @file mesh.c
21 /// @brief Functions for creating, reading, and writing Egoboo's .mpd mesh file
22 /// @details
23
24 #include "mesh.inl"
25
26 #include "log.h"
27 #include "graphic.h"
28
29 #include "egoboo_math.inl"
30 #include "egoboo_endian.h"
31 #include "egoboo_fileutil.h"
32 #include "egoboo_strutil.h"
33 #include "egoboo_math.h"
34 #include "egoboo.h"
35 #include "egoboo_mem.h"
36
37 //--------------------------------------------------------------------------------------------
38 //--------------------------------------------------------------------------------------------
39 // mesh initialization - not accessible by scripts
40 static void mesh_init_tile_offset( ego_mpd_t * pmesh );
41 static void mesh_make_vrtstart( ego_mpd_t * pmesh );
42
43 static grid_mem_t * grid_mem_ctor( grid_mem_t * pmem );
44 static grid_mem_t * grid_mem_dtor( grid_mem_t * pmem );
45 static bool_t grid_mem_alloc( grid_mem_t * pmem, ego_mpd_info_t * pinfo );
46 static bool_t grid_mem_free( grid_mem_t * pmem );
47 static void grid_make_fanstart( grid_mem_t * pmesh, ego_mpd_info_t * pinfo );
48
49 static tile_mem_t * tile_mem_ctor( tile_mem_t * pmem );
50 static tile_mem_t * tile_mem_dtor( tile_mem_t * pmem );
51 static bool_t tile_mem_free( tile_mem_t * pmem );
52 static bool_t tile_mem_alloc( tile_mem_t * pmem, ego_mpd_info_t * pinfo );
53
54 static ego_mpd_info_t * mesh_info_ctor( ego_mpd_info_t * pinfo );
55 static ego_mpd_info_t * mesh_info_dtor( ego_mpd_info_t * pinfo );
56 static void mesh_info_init( ego_mpd_info_t * pinfo, int numvert, size_t tiles_x, size_t tiles_y );
57
58 // some twist/normal functions
59 static bool_t mesh_make_normals( ego_mpd_t * pmesh );
60
61 static bool_t mesh_convert( ego_mpd_t * pmesh_dst, mpd_t * pmesh_src );
62 static bool_t mesh_make_bbox( ego_mpd_t * pmesh );
63
64 static float grid_get_mix( float u0, float u, float v0, float v );
65
66 //--------------------------------------------------------------------------------------------
67 //--------------------------------------------------------------------------------------------
68 ego_mpd_t mesh;
69
70 int mesh_mpdfx_tests = 0;
71 int mesh_bound_tests = 0;
72 int mesh_pressure_tests = 0;
73
74 //--------------------------------------------------------------------------------------------
75 //--------------------------------------------------------------------------------------------
mesh_info_ctor(ego_mpd_info_t * pinfo)76 ego_mpd_info_t * mesh_info_ctor( ego_mpd_info_t * pinfo )
77 {
78 if ( NULL == pinfo ) return pinfo;
79
80 memset( pinfo, 0, sizeof( *pinfo ) );
81
82 return pinfo;
83 }
84
85 //--------------------------------------------------------------------------------------------
mesh_info_init(ego_mpd_info_t * pinfo,int numvert,size_t tiles_x,size_t tiles_y)86 void mesh_info_init( ego_mpd_info_t * pinfo, int numvert, size_t tiles_x, size_t tiles_y )
87 {
88 // set the desired number of tiles
89 pinfo->tiles_x = tiles_x;
90 pinfo->tiles_y = tiles_y;
91 pinfo->tiles_count = pinfo->tiles_x * pinfo->tiles_y;
92
93 // set the desired number of fertices
94 if ( numvert < 0 )
95 {
96 numvert = MAXMESHVERTICES * pinfo->tiles_count;
97 };
98 pinfo->vertcount = numvert;
99 }
100
101 //--------------------------------------------------------------------------------------------
mesh_info_dtor(ego_mpd_info_t * pinfo)102 ego_mpd_info_t * mesh_info_dtor( ego_mpd_info_t * pinfo )
103 {
104 if ( NULL == pinfo ) return NULL;
105
106 memset( pinfo, 0, sizeof( *pinfo ) );
107
108 return pinfo;
109 }
110
111 //--------------------------------------------------------------------------------------------
112 //--------------------------------------------------------------------------------------------
tile_mem_ctor(tile_mem_t * pmem)113 tile_mem_t * tile_mem_ctor( tile_mem_t * pmem )
114 {
115 if ( NULL == pmem ) return pmem;
116
117 memset( pmem, 0, sizeof( *pmem ) );
118
119 return pmem;
120 }
121
122 //--------------------------------------------------------------------------------------------
tile_mem_dtor(tile_mem_t * pmem)123 tile_mem_t * tile_mem_dtor( tile_mem_t * pmem )
124 {
125 if ( NULL == pmem ) return NULL;
126
127 tile_mem_free( pmem );
128
129 memset( pmem, 0, sizeof( *pmem ) );
130
131 return pmem;
132 }
133
134 //--------------------------------------------------------------------------------------------
135 //--------------------------------------------------------------------------------------------
mesh_ctor(ego_mpd_t * pmesh)136 ego_mpd_t * mesh_ctor( ego_mpd_t * pmesh )
137 {
138 /// @details BB@> initialize the ego_mpd_t structure
139
140 if ( NULL != pmesh )
141 {
142 memset( pmesh, 0, sizeof( *pmesh ) );
143
144 mesh_init_tile_offset( pmesh );
145
146 tile_mem_ctor( &( pmesh->tmem ) );
147 grid_mem_ctor( &( pmesh->gmem ) );
148 mesh_info_ctor( &( pmesh->info ) );
149 }
150
151 // global initialization
152 mesh_make_twist();
153
154 return pmesh;
155 }
156
157 //--------------------------------------------------------------------------------------------
mesh_dtor(ego_mpd_t * pmesh)158 ego_mpd_t * mesh_dtor( ego_mpd_t * pmesh )
159 {
160 if ( NULL == pmesh ) return NULL;
161
162 if ( NULL == tile_mem_dtor( &( pmesh->tmem ) ) ) return NULL;
163 if ( NULL == grid_mem_dtor( &( pmesh->gmem ) ) ) return NULL;
164 if ( NULL == mesh_info_dtor( &( pmesh->info ) ) ) return NULL;
165
166 return pmesh;
167 }
168
169 //--------------------------------------------------------------------------------------------
mesh_renew(ego_mpd_t * pmesh)170 ego_mpd_t * mesh_renew( ego_mpd_t * pmesh )
171 {
172 pmesh = mesh_dtor( pmesh );
173
174 return mesh_ctor( pmesh );
175 }
176
177 //--------------------------------------------------------------------------------------------
mesh_ctor_1(ego_mpd_t * pmesh,int tiles_x,int tiles_y)178 ego_mpd_t * mesh_ctor_1( ego_mpd_t * pmesh, int tiles_x, int tiles_y )
179 {
180
181 if ( NULL == pmesh ) return pmesh;
182
183 memset( pmesh, 0, sizeof( *pmesh ) );
184
185 // intitalize the mesh info using the max number of vertices for each tile
186 mesh_info_init( &( pmesh->info ), -1, tiles_x, tiles_y );
187
188 // allocate the mesh memory
189 tile_mem_alloc( &( pmesh->tmem ), &( pmesh->info ) );
190 grid_mem_alloc( &( pmesh->gmem ), &( pmesh->info ) );
191
192 return pmesh;
193 }
194
195 //--------------------------------------------------------------------------------------------
mesh_create(ego_mpd_t * pmesh,int tiles_x,int tiles_y)196 ego_mpd_t * mesh_create( ego_mpd_t * pmesh, int tiles_x, int tiles_y )
197 {
198 if ( NULL == pmesh )
199 {
200 pmesh = EGOBOO_NEW( ego_mpd_t );
201 pmesh = mesh_ctor( pmesh );
202 }
203
204 return mesh_ctor_1( pmesh, tiles_x, tiles_y );
205 }
206
207 //--------------------------------------------------------------------------------------------
mesh_destroy(ego_mpd_t ** ppmesh)208 bool_t mesh_destroy( ego_mpd_t ** ppmesh )
209 {
210 if ( NULL == ppmesh || NULL == *ppmesh ) return bfalse;
211
212 mesh_dtor( *ppmesh );
213
214 *ppmesh = NULL;
215
216 return btrue;
217 }
218
219 //--------------------------------------------------------------------------------------------
mesh_init_tile_offset(ego_mpd_t * pmesh)220 void mesh_init_tile_offset( ego_mpd_t * pmesh )
221 {
222 int cnt;
223
224 // Fix the tile offsets for the mesh textures
225 for ( cnt = 0; cnt < MAXTILETYPE; cnt++ )
226 {
227 pmesh->tileoff[cnt].x = (( cnt >> 0 ) & 7 ) / 8.0f;
228 pmesh->tileoff[cnt].y = (( cnt >> 3 ) & 7 ) / 8.0f;
229 }
230 }
231
232 //--------------------------------------------------------------------------------------------
mesh_remove_ambient(ego_mpd_t * pmesh)233 bool_t mesh_remove_ambient( ego_mpd_t * pmesh )
234 {
235 /// @details BB@> remove extra ambient light in the lightmap
236
237 Uint32 cnt;
238 UFP8_T min_vrt_a = 255;
239
240 if ( NULL == pmesh ) return bfalse;
241
242 for ( cnt = 0; cnt < pmesh->info.tiles_count; cnt++ )
243 {
244 min_vrt_a = MIN( min_vrt_a, pmesh->gmem.grid_list[cnt].a );
245 }
246
247 for ( cnt = 0; cnt < pmesh->info.tiles_count; cnt++ )
248 {
249 pmesh->gmem.grid_list[cnt].a -= min_vrt_a;
250 }
251
252 return btrue;
253 }
254
255 //--------------------------------------------------------------------------------------------
mesh_recalc_twist(ego_mpd_t * pmesh)256 bool_t mesh_recalc_twist( ego_mpd_t * pmesh )
257 {
258 Uint32 fan;
259 ego_mpd_info_t * pinfo;
260 tile_mem_t * ptmem;
261 grid_mem_t * pgmem;
262
263 if ( NULL == pmesh ) return bfalse;
264 pinfo = &( pmesh->info );
265 ptmem = &( pmesh->tmem );
266 pgmem = &( pmesh->gmem );
267
268 // recalculate the twist
269 for ( fan = 0; fan < pinfo->tiles_count; fan++ )
270 {
271 Uint8 twist = cartman_get_fan_twist( pmesh, fan );
272 pgmem->grid_list[fan].twist = twist;
273 }
274
275 return btrue;
276 }
277
278 //--------------------------------------------------------------------------------------------
mesh_set_texture(ego_mpd_t * pmesh,Uint16 tile,Uint16 image)279 bool_t mesh_set_texture( ego_mpd_t * pmesh, Uint16 tile, Uint16 image )
280 {
281 ego_tile_info_t * ptile;
282 Uint16 tile_value, tile_upper, tile_lower;
283
284 if ( !mesh_grid_is_valid( pmesh, tile ) ) return bfalse;
285 ptile = pmesh->tmem.tile_list + tile;
286
287 // get the upper and lower bits for this tile image
288 tile_value = pmesh->tmem.tile_list[tile].img;
289 tile_lower = image & TILE_LOWER_MASK;
290 tile_upper = tile_value & TILE_UPPER_MASK;
291
292 // set the actual image
293 pmesh->tmem.tile_list[tile].img = tile_upper | tile_lower;
294
295 // update the pre-computed texture info
296 return mesh_update_texture( pmesh, tile );
297 }
298
299 //--------------------------------------------------------------------------------------------
mesh_update_texture(ego_mpd_t * pmesh,Uint32 tile)300 bool_t mesh_update_texture( ego_mpd_t * pmesh, Uint32 tile )
301 {
302 size_t mesh_vrt;
303 int tile_vrt;
304 Uint16 vertices;
305 float offu, offv;
306 Uint16 image;
307 Uint8 type;
308
309 tile_mem_t * ptmem;
310 grid_mem_t * pgmem;
311 ego_mpd_info_t * pinfo;
312 ego_tile_info_t * ptile;
313
314 ptmem = &( pmesh->tmem );
315 pgmem = &( pmesh->gmem );
316 pinfo = &( pmesh->info );
317
318 if ( !mesh_grid_is_valid( pmesh, tile ) ) return bfalse;
319 ptile = ptmem->tile_list + tile;
320
321 image = TILE_GET_LOWER_BITS( ptile->img );
322 type = ptile->type & 0x3F;
323
324 offu = pmesh->tileoff[image].x; // Texture offsets
325 offv = pmesh->tileoff[image].y;
326
327 mesh_vrt = ptmem->tile_list[tile].vrtstart; // Number of vertices
328 vertices = tile_dict[type].numvertices; // Number of vertices
329 for ( tile_vrt = 0; tile_vrt < vertices; tile_vrt++, mesh_vrt++ )
330 {
331 ptmem->tlst[mesh_vrt][SS] = tile_dict[type].u[tile_vrt] + offu;
332 ptmem->tlst[mesh_vrt][TT] = tile_dict[type].v[tile_vrt] + offv;
333 }
334
335 return btrue;
336 }
337
338 //--------------------------------------------------------------------------------------------
mesh_make_texture(ego_mpd_t * pmesh)339 bool_t mesh_make_texture( ego_mpd_t * pmesh )
340 {
341 Uint32 cnt;
342 ego_mpd_info_t * pinfo;
343
344 if ( NULL == pmesh ) return bfalse;
345 pinfo = &( pmesh->info );
346
347 // set the texture coordinate for every vertex
348 for ( cnt = 0; cnt < pinfo->tiles_count; cnt++ )
349 {
350 mesh_update_texture( pmesh, cnt );
351 }
352
353 return btrue;
354 }
355
356 //--------------------------------------------------------------------------------------------
mesh_finalize(ego_mpd_t * pmesh)357 ego_mpd_t * mesh_finalize( ego_mpd_t * pmesh )
358 {
359 if ( NULL == pmesh ) return NULL;
360
361 mesh_make_vrtstart( pmesh );
362 mesh_remove_ambient( pmesh );
363 mesh_recalc_twist( pmesh );
364 mesh_make_normals( pmesh );
365 mesh_make_bbox( pmesh );
366 mesh_make_texture( pmesh );
367
368 return pmesh;
369 }
370
371 //--------------------------------------------------------------------------------------------
mesh_convert(ego_mpd_t * pmesh_dst,mpd_t * pmesh_src)372 bool_t mesh_convert( ego_mpd_t * pmesh_dst, mpd_t * pmesh_src )
373 {
374 Uint32 cnt;
375
376 mpd_mem_t * pmem_src;
377 mpd_info_t * pinfo_src;
378
379 tile_mem_t * ptmem_dst;
380 grid_mem_t * pgmem_dst;
381 ego_mpd_info_t * pinfo_dst;
382 bool_t allocated_dst;
383
384 if ( NULL == pmesh_src ) return bfalse;
385 pmem_src = &( pmesh_src->mem );
386 pinfo_src = &( pmesh_src->info );
387
388 // clear out all data in the destination mesh
389 if ( NULL == mesh_renew( pmesh_dst ) ) return bfalse;
390 ptmem_dst = &( pmesh_dst->tmem );
391 pgmem_dst = &( pmesh_dst->gmem );
392 pinfo_dst = &( pmesh_dst->info );
393
394 // set up the destination mesh from the source mesh
395 mesh_info_init( pinfo_dst, pinfo_src->vertcount, pinfo_src->tiles_x, pinfo_src->tiles_y );
396
397 allocated_dst = tile_mem_alloc( ptmem_dst, pinfo_dst );
398 if ( !allocated_dst ) return bfalse;
399
400 allocated_dst = grid_mem_alloc( pgmem_dst, pinfo_dst );
401 if ( !allocated_dst ) return bfalse;
402
403 // copy all the per-tile info
404 for ( cnt = 0; cnt < pinfo_dst->tiles_count; cnt++ )
405 {
406 tile_info_t * ptile_src = pmem_src->tile_list + cnt;
407 ego_tile_info_t * ptile_dst = ptmem_dst->tile_list + cnt;
408 ego_grid_info_t * pgrid_dst = pgmem_dst->grid_list + cnt;
409
410 memset( ptile_dst, 0, sizeof( *ptile_dst ) );
411 ptile_dst->type = ptile_src->type;
412 ptile_dst->img = ptile_src->img;
413
414 memset( pgrid_dst, 0, sizeof( *pgrid_dst ) );
415 pgrid_dst->fx = ptile_src->fx;
416 pgrid_dst->twist = ptile_src->twist;
417
418 // lcache is set in a hepler function
419 // nlst is set in a hepler function
420 }
421
422 // copy all the per-vertex info
423 for ( cnt = 0; cnt < pinfo_src->vertcount; cnt++ )
424 {
425 // copy all info from mpd_mem_t
426 ptmem_dst->plst[cnt][XX] = pmem_src->vlst[cnt].pos.x;
427 ptmem_dst->plst[cnt][YY] = pmem_src->vlst[cnt].pos.y;
428 ptmem_dst->plst[cnt][ZZ] = pmem_src->vlst[cnt].pos.z;
429
430 // default color
431 ptmem_dst->clst[cnt][XX] = ptmem_dst->clst[cnt][GG] = ptmem_dst->clst[cnt][BB] = 0.0f;
432
433 // tlist is set below
434 }
435
436 // copy some of the pre-calculated grid lighting
437 for ( cnt = 0; cnt < pinfo_dst->tiles_count; cnt++ )
438 {
439 size_t vertex = ptmem_dst->tile_list[cnt].vrtstart;
440 pgmem_dst->grid_list[cnt].a = pmem_src->vlst[vertex].a;
441 pgmem_dst->grid_list[cnt].l = 0.0f;
442 }
443
444 return btrue;
445 }
446
447 //--------------------------------------------------------------------------------------------
mesh_load(const char * modname,ego_mpd_t * pmesh)448 ego_mpd_t * mesh_load( const char *modname, ego_mpd_t * pmesh )
449 {
450 // trap bad module names
451 if ( !VALID_CSTR( modname ) ) return pmesh;
452
453 // initialize the mesh
454 {
455 // create a new mesh if we are passed a NULL pointer
456 if ( NULL == pmesh )
457 {
458 pmesh = mesh_ctor( pmesh );
459 }
460
461 if ( NULL == pmesh ) return pmesh;
462
463 // free any memory that has been allocated
464 pmesh = mesh_renew( pmesh );
465 }
466
467 // actually do the loading
468 {
469 mpd_t local_mpd, * pmpd;
470
471 // load a raw mpd
472 mpd_ctor( &local_mpd );
473 tile_dictionary_load_vfs( "mp_data/fans.txt" , tile_dict, MAXMESHTYPE );
474 pmpd = mpd_load( vfs_resolveReadFilename( "mp_data/level.mpd" ), &local_mpd );
475
476 // convert it into a convenient version for Egoboo
477 if ( !mesh_convert( pmesh, pmpd ) )
478 {
479 pmesh = NULL;
480 }
481
482 // delete the now useless mpd data
483 mpd_dtor( &local_mpd );
484 }
485
486 // do some calculation to set up the mpd as a game mesh
487 pmesh = mesh_finalize( pmesh );
488
489 return pmesh;
490 }
491
492 //--------------------------------------------------------------------------------------------
493 //--------------------------------------------------------------------------------------------
grid_mem_ctor(grid_mem_t * pmem)494 grid_mem_t * grid_mem_ctor( grid_mem_t * pmem )
495 {
496 if ( NULL == pmem ) return pmem;
497
498 memset( pmem, 0, sizeof( *pmem ) );
499
500 return pmem;
501 }
502
503 //--------------------------------------------------------------------------------------------
grid_mem_dtor(grid_mem_t * pmem)504 grid_mem_t * grid_mem_dtor( grid_mem_t * pmem )
505 {
506 if ( NULL == pmem ) return NULL;
507
508 grid_mem_free( pmem );
509
510 memset( pmem, 0, sizeof( *pmem ) );
511
512 return pmem;
513 }
514
515 //--------------------------------------------------------------------------------------------
grid_mem_alloc(grid_mem_t * pgmem,ego_mpd_info_t * pinfo)516 bool_t grid_mem_alloc( grid_mem_t * pgmem, ego_mpd_info_t * pinfo )
517 {
518
519 if ( NULL == pgmem || NULL == pinfo || 0 == pinfo->vertcount ) return bfalse;
520
521 // free any memory already allocated
522 if ( !grid_mem_free( pgmem ) ) return bfalse;
523
524 if ( pinfo->vertcount > MESH_MAXTOTALVERTRICES )
525 {
526 log_warning( "Mesh requires too much memory ( %d requested, but max is %d ). \n", pinfo->vertcount, MESH_MAXTOTALVERTRICES );
527 return bfalse;
528 }
529
530 // set the desired blocknumber of grids
531 pgmem->grids_x = pinfo->tiles_x;
532 pgmem->grids_y = pinfo->tiles_y;
533 pgmem->grid_count = pgmem->grids_x * pgmem->grids_y;
534
535 // set the mesh edge info
536 pgmem->edge_x = ( pgmem->grids_x + 1 ) << GRID_BITS;
537 pgmem->edge_y = ( pgmem->grids_y + 1 ) << GRID_BITS;
538
539 // set the desired blocknumber of blocks
540 pgmem->blocks_x = ( pinfo->tiles_x >> 2 );
541 if ( HAS_SOME_BITS( pinfo->tiles_x, 0x03 ) ) pgmem->blocks_x++;
542
543 pgmem->blocks_y = ( pinfo->tiles_y >> 2 );
544 if ( HAS_SOME_BITS( pinfo->tiles_y, 0x03 ) ) pgmem->blocks_y++;
545
546 pgmem->blocks_count = pgmem->blocks_x * pgmem->blocks_y;
547
548 // allocate per-grid memory
549 pgmem->grid_list = EGOBOO_NEW_ARY( ego_grid_info_t, pgmem->grid_count );
550 if ( NULL == pgmem->grid_list ) goto grid_mem_alloc_fail;
551
552 // helper info
553 pgmem->blockstart = EGOBOO_NEW_ARY( Uint32, pgmem->blocks_y );
554 if ( NULL == pgmem->blockstart ) goto grid_mem_alloc_fail;
555
556 pgmem->tilestart = EGOBOO_NEW_ARY( Uint32, pinfo->tiles_y );
557 if ( NULL == pgmem->tilestart ) goto grid_mem_alloc_fail;
558
559 // initialize the fanstart/blockstart data
560 grid_make_fanstart( pgmem, pinfo );
561
562 return btrue;
563
564 grid_mem_alloc_fail:
565
566 grid_mem_free( pgmem );
567 log_error( "grid_mem_alloc() - reduce the maximum number of vertices! (Check MESH_MAXTOTALVERTRICES)\n" );
568 return bfalse;
569 }
570
571 //--------------------------------------------------------------------------------------------
grid_mem_free(grid_mem_t * pmem)572 bool_t grid_mem_free( grid_mem_t * pmem )
573 {
574 if ( NULL == pmem ) return bfalse;
575
576 // free the memory
577 EGOBOO_DELETE_ARY( pmem->grid_list );
578 EGOBOO_DELETE_ARY( pmem->blockstart );
579 EGOBOO_DELETE_ARY( pmem->tilestart );
580
581 // reset some values to safe values
582 memset( pmem, 0, sizeof( *pmem ) );
583
584 return btrue;
585 }
586
587 //--------------------------------------------------------------------------------------------
tile_mem_alloc(tile_mem_t * pmem,ego_mpd_info_t * pinfo)588 bool_t tile_mem_alloc( tile_mem_t * pmem, ego_mpd_info_t * pinfo )
589 {
590
591 if ( NULL == pmem || NULL == pinfo || 0 == pinfo->vertcount ) return bfalse;
592
593 // free any memory already allocated
594 if ( !tile_mem_free( pmem ) ) return bfalse;
595
596 if ( pinfo->vertcount > MESH_MAXTOTALVERTRICES )
597 {
598 log_warning( "Mesh requires too much memory ( %d requested, but max is %d ). \n", pinfo->vertcount, MESH_MAXTOTALVERTRICES );
599 return bfalse;
600 }
601
602 // allocate per-vertex memory
603 pmem->plst = EGOBOO_NEW_ARY( GLXvector3f, pinfo->vertcount );
604 if ( NULL == pmem->plst ) goto mesh_mem_alloc_fail;
605
606 pmem->tlst = EGOBOO_NEW_ARY( GLXvector2f, pinfo->vertcount );
607 if ( NULL == pmem->tlst ) goto mesh_mem_alloc_fail;
608
609 pmem->clst = EGOBOO_NEW_ARY( GLXvector3f, pinfo->vertcount );
610 if ( NULL == pmem->clst ) goto mesh_mem_alloc_fail;
611
612 pmem->nlst = EGOBOO_NEW_ARY( GLXvector3f, pinfo->vertcount );
613 if ( NULL == pmem->nlst ) goto mesh_mem_alloc_fail;
614
615 // allocate per-tile memory
616 pmem->tile_list = ego_tile_info_alloc_ary( pinfo->tiles_count );
617 if ( NULL == pmem->tile_list ) goto mesh_mem_alloc_fail;
618
619 pmem->vert_count = pinfo->vertcount;
620 pmem->tile_count = pinfo->tiles_count;
621
622 return btrue;
623
624 mesh_mem_alloc_fail:
625
626 tile_mem_free( pmem );
627 log_error( "tile_mem_alloc() - reduce the maximum number of vertices! (Check MESH_MAXTOTALVERTRICES)\n" );
628 return bfalse;
629 }
630
631 //--------------------------------------------------------------------------------------------
tile_mem_free(tile_mem_t * pmem)632 bool_t tile_mem_free( tile_mem_t * pmem )
633 {
634 if ( NULL == pmem ) return bfalse;
635
636 // free the memory
637 EGOBOO_DELETE_ARY( pmem->plst );
638 EGOBOO_DELETE_ARY( pmem->nlst );
639 EGOBOO_DELETE_ARY( pmem->clst );
640 EGOBOO_DELETE_ARY( pmem->tlst );
641
642 // per-tile values
643 EGOBOO_DELETE_ARY( pmem->tile_list );
644
645 // reset some values to safe values
646 pmem->vert_count = 0;
647 pmem->tile_count = 0;
648
649 return btrue;
650 }
651
652 //--------------------------------------------------------------------------------------------
grid_make_fanstart(grid_mem_t * pgmem,ego_mpd_info_t * pinfo)653 void grid_make_fanstart( grid_mem_t * pgmem, ego_mpd_info_t * pinfo )
654 {
655 /// @details ZZ@> This function builds a look up table to ease calculating the
656 /// fan number given an x,y pair
657
658 int cnt;
659
660 if ( NULL == pgmem || NULL == pinfo ) return;
661
662 // do the tilestart
663 for ( cnt = 0; cnt < pinfo->tiles_y; cnt++ )
664 {
665 pgmem->tilestart[cnt] = pinfo->tiles_x * cnt;
666 }
667
668 // calculate some of the block info
669 if ( pgmem->blocks_x >= MAXMESHBLOCKY )
670 {
671 log_warning( "Number of mesh blocks in the x direction too large (%d out of %d).\n", pgmem->blocks_x, MAXMESHBLOCKY );
672 }
673
674 if ( pgmem->blocks_y >= MAXMESHBLOCKY )
675 {
676 log_warning( "Number of mesh blocks in the y direction too large (%d out of %d).\n", pgmem->blocks_y, MAXMESHBLOCKY );
677 }
678
679 // do the blockstart
680 for ( cnt = 0; cnt < pgmem->blocks_y; cnt++ )
681 {
682 pgmem->blockstart[cnt] = pgmem->blocks_x * cnt;
683 }
684 }
685
686 //--------------------------------------------------------------------------------------------
mesh_make_vrtstart(ego_mpd_t * pmesh)687 void mesh_make_vrtstart( ego_mpd_t * pmesh )
688 {
689 size_t vert;
690 Uint32 tile;
691
692 ego_mpd_info_t * pinfo;
693 tile_mem_t * ptmem;
694
695 if ( NULL == pmesh ) return;
696
697 pinfo = &( pmesh->info );
698 ptmem = &( pmesh->tmem );
699
700 vert = 0;
701 for ( tile = 0; tile < pinfo->tiles_count; tile++ )
702 {
703 Uint8 ttype;
704
705 ptmem->tile_list[tile].vrtstart = vert;
706
707 ttype = ptmem->tile_list[tile].type;
708
709 // throw away any remaining upper bits
710 ttype &= 0x3F;
711
712 vert += tile_dict[ttype].numvertices;
713 }
714
715 if ( vert != pinfo->vertcount )
716 {
717 log_warning( "mesh_make_vrtstart() - unexpected number of vertices %d of %d\n", vert, pinfo->vertcount );
718 }
719 }
720
721 //--------------------------------------------------------------------------------------------
mesh_make_twist()722 void mesh_make_twist()
723 {
724 /// @details ZZ@> This function precomputes surface normals and steep hill acceleration for
725 /// the mesh
726
727 Uint16 cnt;
728
729 float gdot;
730 fvec3_t grav = VECT3( 0, 0, gravity );
731
732 for ( cnt = 0; cnt < 256; cnt++ )
733 {
734 fvec3_t gperp; // gravity perpendicular to the mesh
735 fvec3_t gpara; // gravity parallel to the mesh (what pushes you)
736 fvec3_t nrm;
737
738 twist_to_normal( cnt, nrm.v, 1.0f );
739
740 map_twist_nrm[cnt] = nrm;
741
742 map_twist_x[cnt] = ( Uint16 )( - vec_to_facing( nrm.z, nrm.y ) );
743 map_twist_y[cnt] = vec_to_facing( nrm.z, nrm.x );
744
745 // this is about 5 degrees off of vertical
746 map_twist_flat[cnt] = bfalse;
747 if ( nrm.z > 0.9945f )
748 {
749 map_twist_flat[cnt] = btrue;
750 }
751
752 // projection of the gravity parallel to the surface
753 gdot = grav.z * nrm.z;
754
755 gperp.x = gdot * nrm.x;
756 gperp.y = gdot * nrm.y;
757 gperp.z = gdot * nrm.z;
758
759 gpara.x = grav.x - gperp.x;
760 gpara.y = grav.y - gperp.y;
761 gpara.z = grav.z - gperp.z;
762
763 map_twistvel_x[cnt] = gpara.x;
764 map_twistvel_y[cnt] = gpara.y;
765 map_twistvel_z[cnt] = gpara.z;
766 }
767 }
768
769 //--------------------------------------------------------------------------------------------
cartman_get_fan_twist(const ego_mpd_t * pmesh,Uint32 tile)770 Uint8 cartman_get_fan_twist( const ego_mpd_t * pmesh, Uint32 tile )
771 {
772 size_t vrtstart;
773 float z0, z1, z2, z3;
774 float zx, zy;
775
776 // check for a valid tile
777 if ( INVALID_TILE == tile || tile > pmesh->info.tiles_count ) return TWIST_FLAT;
778
779 // if the tile is actually labelled as FANOFF, ignore it completely
780 if ( FANOFF == pmesh->tmem.tile_list[tile].img ) return TWIST_FLAT;
781
782 vrtstart = pmesh->tmem.tile_list[tile].vrtstart;
783
784 z0 = pmesh->tmem.plst[vrtstart + 0][ZZ];
785 z1 = pmesh->tmem.plst[vrtstart + 1][ZZ];
786 z2 = pmesh->tmem.plst[vrtstart + 2][ZZ];
787 z3 = pmesh->tmem.plst[vrtstart + 3][ZZ];
788
789 zx = CARTMAN_FIXNUM * ( z0 + z3 - z1 - z2 ) / CARTMAN_SLOPE;
790 zy = CARTMAN_FIXNUM * ( z2 + z3 - z0 - z1 ) / CARTMAN_SLOPE;
791
792 return cartman_get_twist( zx, zy );
793 }
794
795 //--------------------------------------------------------------------------------------------
mesh_make_bbox(ego_mpd_t * pmesh)796 bool_t mesh_make_bbox( ego_mpd_t * pmesh )
797 {
798 /// @details BB@> set the bounding box for each tile, and for the entire mesh
799
800 size_t mesh_vrt;
801 int tile_vrt;
802 Uint32 cnt;
803
804 tile_mem_t * ptmem;
805 grid_mem_t * pgmem;
806 ego_mpd_info_t * pinfo;
807
808 if ( NULL == pmesh ) return bfalse;
809 ptmem = &( pmesh->tmem );
810 pgmem = &( pmesh->gmem );
811 pinfo = &( pmesh->info );
812
813 ptmem->bbox.mins[XX] = ptmem->bbox.maxs[XX] = ptmem->plst[0][XX];
814 ptmem->bbox.mins[YY] = ptmem->bbox.maxs[YY] = ptmem->plst[0][YY];
815 ptmem->bbox.mins[ZZ] = ptmem->bbox.maxs[ZZ] = ptmem->plst[0][ZZ];
816
817 for ( cnt = 0; cnt < pmesh->info.tiles_count; cnt++ )
818 {
819 oct_bb_t * poct;
820 ego_tile_info_t * ptile;
821 Uint16 vertices;
822 Uint8 type;
823 oct_vec_t ovec;
824
825 ptile = ptmem->tile_list + cnt;
826 poct = &( ptile->oct );
827
828 type = ptile->type;
829 type &= 0x3F;
830
831 mesh_vrt = ptmem->tile_list[cnt].vrtstart; // Number of vertices
832 vertices = tile_dict[type].numvertices; // Number of vertices
833
834 // initialize the bounding box
835 oct_vec_ctor( ovec, ptmem->plst[mesh_vrt] );
836 oct_bb_set_ovec( poct, ovec );
837 mesh_vrt++;
838
839 // add the rest of the points into the bounding box
840 for ( tile_vrt = 1; tile_vrt < vertices; tile_vrt++, mesh_vrt++ )
841 {
842 oct_vec_ctor( ovec, ptmem->plst[mesh_vrt] );
843 oct_bb_self_sum_ovec( poct, ovec );
844 }
845
846 // ensure that NO tile has zero volume.
847 // if a tile is declared to have all the same height, it will accidentally be called "empty".
848 if ( poct->empty )
849 {
850 if ( ABS( poct->maxs[OCT_X] - poct->mins[OCT_X] ) +
851 ABS( poct->maxs[OCT_Y] - poct->mins[OCT_Y] ) +
852 ABS( poct->maxs[OCT_Z] - poct->mins[OCT_Z] ) > 0.0f )
853 {
854 ovec[OCT_X] = ovec[OCT_Y] = ovec[OCT_Z] = 1e-6;
855 ovec[OCT_XY] = ovec[OCT_YX] = SQRT_TWO * ovec[OCT_X];
856 oct_bb_self_grow( poct, ovec );
857 }
858 }
859
860 // extend the mesh bounding box
861 ptmem->bbox.mins[XX] = MIN( ptmem->bbox.mins[XX], poct->mins[XX] );
862 ptmem->bbox.mins[YY] = MIN( ptmem->bbox.mins[YY], poct->mins[YY] );
863 ptmem->bbox.mins[ZZ] = MIN( ptmem->bbox.mins[ZZ], poct->mins[ZZ] );
864
865 ptmem->bbox.maxs[XX] = MAX( ptmem->bbox.maxs[XX], poct->maxs[XX] );
866 ptmem->bbox.maxs[YY] = MAX( ptmem->bbox.maxs[YY], poct->maxs[YY] );
867 ptmem->bbox.maxs[ZZ] = MAX( ptmem->bbox.maxs[ZZ], poct->maxs[ZZ] );
868 }
869
870 return btrue;
871 }
872
873 //--------------------------------------------------------------------------------------------
mesh_make_normals(ego_mpd_t * pmesh)874 bool_t mesh_make_normals( ego_mpd_t * pmesh )
875 {
876 /// @details BB@> this function calculates a set of normals for the 4 corners
877 /// of a given tile. It is supposed to generate smooth normals for
878 /// most tiles, but where there is a creas (i.e. between the floor and
879 /// a wall) the normals should not be smoothed.
880
881 int ix, iy;
882 Uint32 fan0, fan1;
883 tile_mem_t * ptmem;
884 grid_mem_t * pgmem;
885
886 int edge_is_crease[4];
887 fvec3_t nrm_lst[4], vec_sum;
888 float weight_lst[4];
889
890 // test for mesh
891 if ( NULL == pmesh ) return bfalse;
892 ptmem = &( pmesh->tmem );
893 pgmem = &( pmesh->gmem );
894
895 // set the default normal for each fan, based on the calculated twist value
896 for ( fan0 = 0; fan0 < ptmem->tile_count; fan0++ )
897 {
898 Uint8 twist = pgmem->grid_list[fan0].twist;
899
900 ptmem->nlst[fan0][XX] = map_twist_nrm[twist].x;
901 ptmem->nlst[fan0][YY] = map_twist_nrm[twist].y;
902 ptmem->nlst[fan0][ZZ] = map_twist_nrm[twist].z;
903 }
904
905 // find an "average" normal of each corner of the tile
906 for ( iy = 0; iy < pmesh->info.tiles_y; iy++ )
907 {
908 for ( ix = 0; ix < pmesh->info.tiles_x; ix++ )
909 {
910 int ix_off[4] = {0, 1, 1, 0};
911 int iy_off[4] = {0, 0, 1, 1};
912 int i, j, k;
913
914 fan0 = mesh_get_tile_int( pmesh, ix, iy );
915 if ( !mesh_grid_is_valid( pmesh, fan0 ) ) continue;
916
917 nrm_lst[0].x = ptmem->nlst[fan0][XX];
918 nrm_lst[0].y = ptmem->nlst[fan0][YY];
919 nrm_lst[0].z = ptmem->nlst[fan0][ZZ];
920
921 // for each corner of this tile
922 for ( i = 0; i < 4; i++ )
923 {
924 int dx, dy;
925 int loc_ix_off[4];
926 int loc_iy_off[4];
927
928 // the offset list needs to be shifted depending on what i is
929 j = ( 6 - i ) % 4;
930
931 if ( 1 == ix_off[4-j] ) dx = -1; else dx = 0;
932 if ( 1 == iy_off[4-j] ) dy = -1; else dy = 0;
933
934 for ( k = 0; k < 4; k++ )
935 {
936 loc_ix_off[k] = ix_off[( 4-j + k ) % 4 ] + dx;
937 loc_iy_off[k] = iy_off[( 4-j + k ) % 4 ] + dy;
938 }
939
940 // cache the normals
941 // nrm_lst[0] is already known.
942 for ( j = 1; j < 4; j++ )
943 {
944 int jx, jy;
945
946 jx = ix + loc_ix_off[j];
947 jy = iy + loc_iy_off[j];
948
949 fan1 = mesh_get_tile_int( pmesh, jx, jy );
950
951 if ( mesh_grid_is_valid( pmesh, fan1 ) )
952 {
953 nrm_lst[j].x = ptmem->nlst[fan1][XX];
954 nrm_lst[j].y = ptmem->nlst[fan1][YY];
955 nrm_lst[j].z = ptmem->nlst[fan1][ZZ];
956
957 if ( nrm_lst[j].z < 0 )
958 {
959 nrm_lst[j].x *= -1.0f;
960 nrm_lst[j].y *= -1.0f;
961 nrm_lst[j].z *= -1.0f;
962 }
963 }
964 else
965 {
966 nrm_lst[j].x = 0;
967 nrm_lst[j].y = 0;
968 nrm_lst[j].z = 1;
969 }
970 }
971
972 // find the creases
973 for ( j = 0; j < 4; j++ )
974 {
975 float vdot;
976 int k = ( j + 1 ) % 4;
977
978 vdot = fvec3_dot_product( nrm_lst[j].v, nrm_lst[k].v );
979
980 edge_is_crease[j] = ( vdot < INV_SQRT_TWO );
981
982 weight_lst[j] = fvec3_dot_product( nrm_lst[j].v, nrm_lst[0].v );
983 }
984
985 weight_lst[0] = 1.0f;
986 if ( edge_is_crease[0] )
987 {
988 // this means that there is a crease between tile 0 and 1
989 weight_lst[1] = 0.0f;
990 }
991
992 if ( edge_is_crease[3] )
993 {
994 // this means that there is a crease between tile 0 and 3
995 weight_lst[3] = 0.0f;
996 }
997
998 if ( edge_is_crease[0] && edge_is_crease[3] )
999 {
1000 // this means that there is a crease between tile 0 and 1
1001 // and a crease between tile 0 and 3, isolating tile 2
1002 weight_lst[2] = 0.0f;
1003 }
1004
1005 vec_sum = nrm_lst[0];
1006 for ( j = 1; j < 4; j++ )
1007 {
1008 if ( weight_lst[j] > 0.0f )
1009 {
1010 vec_sum.x += nrm_lst[j].x * weight_lst[j];
1011 vec_sum.y += nrm_lst[j].y * weight_lst[j];
1012 vec_sum.z += nrm_lst[j].z * weight_lst[j];
1013 }
1014 }
1015
1016 vec_sum = fvec3_normalize( vec_sum.v );
1017
1018 ptmem->tile_list[fan0].ncache[i][XX] = vec_sum.x;
1019 ptmem->tile_list[fan0].ncache[i][YY] = vec_sum.y;
1020 ptmem->tile_list[fan0].ncache[i][ZZ] = vec_sum.z;
1021 }
1022 }
1023 }
1024
1025 // dy_min = iy_off[i] - 1;
1026 // dy_max = iy_off[i];
1027
1028 // wt_cnt = 0;
1029 // vec_sum.x = vec_sum.y = vec_sum.z = 0.0f;
1030 // for (dy = dy_min; dy <= dy_max; dy++)
1031 // {
1032 // jy = iy + dy;
1033 // for (dx = dx_min; dx <= dx_max; dx++)
1034 // {
1035 // jx = ix + dx;
1036
1037 // fan1 = mesh_get_tile_int(pmesh, jx, jy);
1038 // if ( mesh_grid_is_valid(pmesh, fan1) )
1039 // {
1040 // float wt;
1041
1042 // vec1.x = ptmem->nlst[fan1][XX];
1043 // vec1.y = ptmem->nlst[fan1][YY];
1044 // vec1.z = ptmem->nlst[fan1][ZZ];
1045 // if ( vec1.z < 0 )
1046 // {
1047 // vec1.x *= -1.0f;
1048 // vec1.y *= -1.0f;
1049 // vec1.z *= -1.0f;
1050 // }
1051
1052 // wt = fvec3_dot_product( vec0.v, vec1.v );
1053 // if ( wt > 0 )
1054 // {
1055 // vec_sum.x += wt * vec1.x;
1056 // vec_sum.y += wt * vec1.y;
1057 // vec_sum.z += wt * vec1.z;
1058
1059 // wt_cnt += 1;
1060 // }
1061 // }
1062 // }
1063 // }
1064
1065 // if ( wt_cnt > 1 )
1066 // {
1067 // vec_sum = fvec3_normalize( vec_sum.v );
1068
1069 // ptmem->ncache[fan0][i][XX] = vec_sum.x;
1070 // ptmem->ncache[fan0][i][YY] = vec_sum.y;
1071 // ptmem->ncache[fan0][i][ZZ] = vec_sum.z;
1072 // }
1073 // else
1074 // {
1075 // ptmem->ncache[fan0][i][XX] = vec0.x;
1076 // ptmem->ncache[fan0][i][YY] = vec0.y;
1077 // ptmem->ncache[fan0][i][ZZ] = vec0.z;
1078 // }
1079 // }
1080 // }
1081 //}
1082
1083 return btrue;
1084 }
1085
1086 //--------------------------------------------------------------------------------------------
grid_light_one_corner(ego_mpd_t * pmesh,int fan,float height,float nrm[],float * plight)1087 bool_t grid_light_one_corner( ego_mpd_t * pmesh, int fan, float height, float nrm[], float * plight )
1088 {
1089 bool_t reflective;
1090 lighting_cache_t * lighting;
1091
1092 if ( NULL == pmesh || NULL == plight || !mesh_grid_is_valid( pmesh, fan ) ) return bfalse;
1093
1094 // get the grid lighting
1095 lighting = &( pmesh->gmem.grid_list[fan].cache );
1096
1097 reflective = ( 0 != mesh_test_fx( pmesh, fan, MPDFX_DRAWREF ) );
1098
1099 // evaluate the grid lighting at this node
1100 if ( reflective )
1101 {
1102 float light_dir, light_amb;
1103
1104 lighting_evaluate_cache( lighting, nrm, height, pmesh->tmem.bbox, &light_amb, &light_dir );
1105
1106 // make ambient light only illuminate 1/2
1107 ( *plight ) = light_amb + 0.5f * light_dir;
1108 }
1109 else
1110 {
1111 ( *plight ) = lighting_evaluate_cache( lighting, nrm, height, pmesh->tmem.bbox, NULL, NULL );
1112 }
1113
1114 // clip the light to a reasonable value
1115 ( *plight ) = CLIP(( *plight ), 0, 255 );
1116
1117 return btrue;
1118 }
1119
1120 //--------------------------------------------------------------------------------------------
mesh_test_one_corner(ego_mpd_t * pmesh,GLXvector3f pos,float * pdelta)1121 bool_t mesh_test_one_corner( ego_mpd_t * pmesh, GLXvector3f pos, float * pdelta )
1122 {
1123 float loc_delta, low_delta, hgh_delta;
1124 float hgh_wt, low_wt;
1125
1126 if ( NULL == pdelta ) pdelta = &loc_delta;
1127
1128 // interpolate the lighting for the given corner of the mesh
1129 *pdelta = grid_lighting_test( pmesh, pos, &low_delta, &hgh_delta );
1130
1131 // determine the weighting
1132 hgh_wt = ( pos[ZZ] - pmesh->tmem.bbox.mins[kZ] ) / ( pmesh->tmem.bbox.maxs[kZ] - pmesh->tmem.bbox.mins[kZ] );
1133 hgh_wt = CLIP( hgh_wt, 0.0f, 1.0f );
1134 low_wt = 1.0f - hgh_wt;
1135
1136 *pdelta = low_wt * low_delta + hgh_wt * hgh_delta;
1137
1138 return btrue;
1139 }
1140
1141 //--------------------------------------------------------------------------------------------
mesh_light_one_corner(ego_mpd_t * pmesh,int itile,GLXvector3f pos,GLXvector3f nrm,float * plight)1142 bool_t mesh_light_one_corner( ego_mpd_t * pmesh, int itile, GLXvector3f pos, GLXvector3f nrm, float * plight )
1143 {
1144 lighting_cache_t grid_light;
1145 bool_t reflective;
1146
1147 if ( !mesh_grid_is_valid( pmesh, itile ) ) return bfalse;
1148
1149 // add in the effect of this lighting cache node
1150 reflective = ( 0 != mesh_test_fx( pmesh, itile, MPDFX_DRAWREF ) );
1151
1152 // interpolate the lighting for the given corner of the mesh
1153 grid_lighting_interpolate( pmesh, &grid_light, pos[XX], pos[YY] );
1154
1155 if ( reflective )
1156 {
1157 float light_dir, light_amb;
1158
1159 lighting_evaluate_cache( &grid_light, nrm, pos[ZZ], pmesh->tmem.bbox, &light_amb, &light_dir );
1160
1161 // make ambient light only illuminate 1/2
1162 ( *plight ) = light_amb + 0.5f * light_dir;
1163 }
1164 else
1165 {
1166 ( *plight ) = lighting_evaluate_cache( &grid_light, nrm, pos[ZZ], pmesh->tmem.bbox, NULL, NULL );
1167 }
1168
1169 return btrue;
1170 }
1171
1172 //--------------------------------------------------------------------------------------------
mesh_test_corners(ego_mpd_t * pmesh,int itile,float threshold)1173 bool_t mesh_test_corners( ego_mpd_t * pmesh, int itile, float threshold )
1174 {
1175 bool_t retval;
1176 int corner;
1177
1178 tile_mem_t * ptmem;
1179 light_cache_t * lcache;
1180 light_cache_t * d1_cache;
1181
1182 if ( NULL == pmesh || !mesh_grid_is_valid( pmesh, itile ) ) return bfalse;
1183 ptmem = &( pmesh->tmem );
1184
1185 // get the normal and lighting cache for this tile
1186 lcache = &( ptmem->tile_list[itile].lcache );
1187 d1_cache = &( ptmem->tile_list[itile].d1_cache );
1188
1189 retval = bfalse;
1190 for ( corner = 0; corner < 4; corner++ )
1191 {
1192 float delta;
1193 float * pdelta;
1194 float * plight;
1195 GLXvector3f * ppos;
1196
1197 pdelta = ( *d1_cache ) + corner;
1198 plight = ( *lcache ) + corner;
1199 ppos = ptmem->plst + ptmem->tile_list[itile].vrtstart + corner;
1200
1201 mesh_test_one_corner( pmesh, *ppos, &delta );
1202
1203 if ( 0.0f == *plight )
1204 {
1205 delta = 10.0f;
1206 }
1207 else
1208 {
1209 delta /= *plight;
1210 delta = CLIP( delta, 0, 10.0f );
1211 }
1212
1213 *pdelta += delta;
1214
1215 if ( *pdelta > threshold ) retval = btrue;
1216 }
1217
1218 return retval;
1219 }
1220
1221 //--------------------------------------------------------------------------------------------
mesh_light_corners(ego_mpd_t * pmesh,int itile,float mesh_lighting_keep)1222 float mesh_light_corners( ego_mpd_t * pmesh, int itile, float mesh_lighting_keep )
1223 {
1224 int corner;
1225 float max_delta;
1226
1227 ego_mpd_info_t * pinfo;
1228 tile_mem_t * ptmem;
1229 grid_mem_t * pgmem;
1230 normal_cache_t * ncache;
1231 light_cache_t * lcache;
1232 light_cache_t * d1_cache, * d2_cache;
1233
1234 if ( NULL == pmesh || !mesh_grid_is_valid( pmesh, itile ) ) return 0.0f;
1235 pinfo = &( pmesh->info );
1236 ptmem = &( pmesh->tmem );
1237 pgmem = &( pmesh->gmem );
1238
1239 // get the normal and lighting cache for this tile
1240 ncache = &( ptmem->tile_list[itile].ncache );
1241 lcache = &( ptmem->tile_list[itile].lcache );
1242 d1_cache = &( ptmem->tile_list[itile].d1_cache );
1243 d2_cache = &( ptmem->tile_list[itile].d2_cache );
1244
1245 max_delta = 0.0f;
1246 for ( corner = 0; corner < 4; corner++ )
1247 {
1248 float light_new, light_old, delta, light_tmp;
1249
1250 GLXvector3f * pnrm;
1251 float * plight;
1252 float * pdelta1, * pdelta2;
1253 GLXvector3f * ppos;
1254
1255 pnrm = ( *ncache ) + corner;
1256 plight = ( *lcache ) + corner;
1257 pdelta1 = ( *d1_cache ) + corner;
1258 pdelta2 = ( *d2_cache ) + corner;
1259 ppos = ptmem->plst + ptmem->tile_list[itile].vrtstart + corner;
1260
1261 light_new = 0.0f;
1262 mesh_light_one_corner( pmesh, itile, *ppos, *pnrm, &light_new );
1263
1264 if ( *plight != light_new )
1265 {
1266 light_old = *plight;
1267 *plight = light_old * mesh_lighting_keep + light_new * ( 1.0f - mesh_lighting_keep );
1268
1269 // measure the actual delta
1270 delta = ABS( light_old - *plight );
1271
1272 // measure the relative change of the lighting
1273 light_tmp = 0.5f * ( ABS( *plight ) + ABS( light_old ) );
1274 if ( 0.0f == light_tmp )
1275 {
1276 delta = 10.0f;
1277 }
1278 else
1279 {
1280 delta /= light_tmp;
1281 delta = CLIP( delta, 0.0f, 10.0f );
1282 }
1283
1284 // add in the actual change this update
1285 *pdelta2 += ABS( delta );
1286
1287 // update the estimate to match the actual change
1288 *pdelta1 = *pdelta2;
1289 }
1290
1291 max_delta = MAX( max_delta, *pdelta1 );
1292 }
1293
1294 return max_delta;
1295 }
1296
1297 //--------------------------------------------------------------------------------------------
mesh_interpolate_vertex(tile_mem_t * pmem,int fan,float pos[],float * plight)1298 bool_t mesh_interpolate_vertex( tile_mem_t * pmem, int fan, float pos[], float * plight )
1299 {
1300 int cnt;
1301 int ix_off[4] = {0, 1, 1, 0}, iy_off[4] = {0, 0, 1, 1};
1302 float u, v, wt, weight_sum;
1303
1304 oct_bb_t * poct;
1305 light_cache_t * lc;
1306
1307 if ( NULL == plight ) return bfalse;
1308 ( *plight ) = 0.0f;
1309
1310 if ( NULL == pmem ) return bfalse;
1311
1312 poct = &( pmem->tile_list[fan].oct );
1313 lc = &( pmem->tile_list[fan].lcache );
1314
1315 // determine a u,v coordinate for the vertex
1316 u = ( pos[XX] - poct->mins[XX] ) / ( poct->maxs[XX] - poct->mins[XX] );
1317 v = ( pos[YY] - poct->mins[YY] ) / ( poct->maxs[YY] - poct->mins[YY] );
1318
1319 // average the cached data on the 4 corners of the mesh
1320 weight_sum = 0.0f;
1321 for ( cnt = 0; cnt < 4; cnt++ )
1322 {
1323 wt = grid_get_mix( ix_off[cnt], u, iy_off[cnt], v );
1324
1325 weight_sum += wt;
1326 ( *plight ) += wt * ( *lc )[cnt];
1327 }
1328
1329 if (( *plight ) > 0 && weight_sum > 0.0f )
1330 {
1331 ( *plight ) /= weight_sum;
1332 }
1333 else
1334 {
1335 ( *plight ) = 0;
1336 }
1337
1338 ( *plight ) = CLIP(( *plight ), 0, 255 );
1339
1340 return btrue;
1341 }
1342
1343 #define BLAH_MIX_1(DU,UU) (4.0f/9.0f*((UU)-(-1+(DU)))*((2+(DU))-(UU)))
1344 #define BLAH_MIX_2(DU,UU,DV,VV) (BLAH_MIX_1(DU,UU)*BLAH_MIX_1(DV,VV))
1345
grid_get_mix(float u0,float u,float v0,float v)1346 float grid_get_mix( float u0, float u, float v0, float v )
1347 {
1348 float wt_u, wt_v;
1349 float du = u - u0;
1350 float dv = v - v0;
1351
1352 // du *= 1.0f;
1353 if ( ABS( du ) > 1.0f ) return 0.0f;
1354 wt_u = ( 1.0f - du ) * ( 1.0f + du );
1355
1356 // dv *= 1.0f;
1357 if ( ABS( dv ) > 1.0f ) return 0.0f;
1358 wt_v = ( 1.0f - dv ) * ( 1.0f + dv );
1359
1360 return wt_u * wt_v;
1361 }
1362
1363 //--------------------------------------------------------------------------------------------
mesh_test_wall(const ego_mpd_t * pmesh,const float pos[],const float radius,const BIT_FIELD bits,mesh_wall_data_t * pdata)1364 BIT_FIELD mesh_test_wall( const ego_mpd_t * pmesh, const float pos[], const float radius, const BIT_FIELD bits, mesh_wall_data_t * pdata )
1365 {
1366 /// @details BB@> an abstraction of the functions of chr_hit_wall() and prt_hit_wall()
1367
1368 mesh_wall_data_t loc_data;
1369
1370 BIT_FIELD pass;
1371 int ix, iy;
1372 float loc_radius;
1373
1374 ego_irect_t bound;
1375
1376 // deal with the optional parameters
1377 if ( NULL == pdata ) pdata = &loc_data;
1378
1379 // if there is no interaction with the mesh, return 0
1380 if ( 0 == bits ) return 0;
1381
1382 // require a valid position
1383 if ( NULL == pos ) return 0;
1384
1385 // if the mesh is empty, return 0
1386 if ( NULL == pmesh || 0 == pmesh->info.tiles_count || 0 == pmesh->tmem.tile_count ) return 0;
1387 pdata->pinfo = ( ego_mpd_info_t* ) & ( pmesh->info );
1388 pdata->tlist = pmesh->tmem.tile_list;
1389 pdata->glist = pmesh->gmem.grid_list;
1390
1391 // make an alias for the radius
1392 loc_radius = radius;
1393
1394 // set a minimum radius
1395 if ( 0.0f == loc_radius )
1396 {
1397 loc_radius = GRID_FSIZE * 0.5f;
1398 }
1399
1400 // make sure it is positive
1401 loc_radius = ABS( loc_radius );
1402
1403 pdata->fx_min = pos[kX] - loc_radius;
1404 pdata->fx_max = pos[kX] + loc_radius;
1405
1406 pdata->fy_min = pos[kY] - loc_radius;
1407 pdata->fy_max = pos[kY] + loc_radius;
1408
1409 // make a large limit in case the pos is so large that it cannot be represented by an int
1410 pdata->fx_min = MAX( pdata->fx_min, -9.0f * pmesh->gmem.edge_x );
1411 pdata->fx_max = MIN( pdata->fx_max, 10.0f * pmesh->gmem.edge_x );
1412
1413 pdata->fy_min = MAX( pdata->fy_min, -9.0f * pmesh->gmem.edge_y );
1414 pdata->fy_max = MIN( pdata->fy_max, 10.0f * pmesh->gmem.edge_y );
1415
1416 // find an integer bound.
1417 // we need to know about out of range values below clamp these to valid values
1418 bound.xmin = FLOOR( pdata->fx_min / GRID_FSIZE );
1419 bound.xmax = FLOOR( pdata->fx_max / GRID_FSIZE );
1420 bound.ymin = FLOOR( pdata->fy_min / GRID_FSIZE );
1421 bound.ymax = FLOOR( pdata->fy_max / GRID_FSIZE );
1422
1423 // limit the test values to be in-bounds
1424 pdata->fx_min = MAX( pdata->fx_min, 0.0f );
1425 pdata->fx_max = MIN( pdata->fx_max, pmesh->gmem.edge_x );
1426 pdata->fy_min = MAX( pdata->fy_min, 0.0f );
1427 pdata->fy_max = MIN( pdata->fy_max, pmesh->gmem.edge_y );
1428
1429 pdata->ix_min = MAX( bound.xmin, 0 );
1430 pdata->ix_max = MIN( bound.xmax, pmesh->info.tiles_x - 1 );
1431 pdata->iy_min = MAX( bound.ymin, 0 );
1432 pdata->iy_max = MIN( bound.ymax, pmesh->info.tiles_y - 1 );
1433
1434 // clear the bit accumulator
1435 pass = 0;
1436
1437 // detect out of bounds in the y-direction
1438 if ( bound.ymin < 0 || bound.ymax >= pdata->pinfo->tiles_y )
1439 {
1440 pass = ( MPDFX_IMPASS | MPDFX_WALL ) & bits;
1441 mesh_bound_tests++;
1442 }
1443 if ( 0 != pass ) return pass;
1444
1445 // detect out of bounds in the x-direction
1446 if ( bound.xmin < 0 || bound.xmax >= pdata->pinfo->tiles_x )
1447 {
1448 pass = ( MPDFX_IMPASS | MPDFX_WALL ) & bits;
1449 mesh_bound_tests++;
1450 }
1451 if ( 0 != pass ) return pass;
1452
1453 for ( iy = pdata->iy_min; iy <= pdata->iy_max; iy++ )
1454 {
1455 // since we KNOW that this is in range, allow raw access to the data strucutre
1456 int irow = pmesh->gmem.tilestart[iy];
1457
1458 for ( ix = pdata->ix_min; ix <= pdata->ix_max; ix++ )
1459 {
1460 int itile = ix + irow;
1461
1462 // since we KNOW that this is in range, allow raw access to the data strucutre
1463 pass = pdata->glist[itile].fx & bits;
1464 if ( 0 != pass )
1465 {
1466 return pass;
1467 }
1468
1469 mesh_mpdfx_tests++;
1470 }
1471 }
1472
1473 return pass;
1474 }
1475
1476 //--------------------------------------------------------------------------------------------
mesh_get_pressure(const ego_mpd_t * pmesh,const float pos[],float radius,BIT_FIELD bits)1477 float mesh_get_pressure( const ego_mpd_t * pmesh, const float pos[], float radius, BIT_FIELD bits )
1478 {
1479 const float tile_area = GRID_FSIZE * GRID_FSIZE;
1480
1481 Uint32 itile;
1482 int ix_min, ix_max, iy_min, iy_max;
1483 float fx_min, fx_max, fy_min, fy_max, obj_area;
1484 int ix, iy;
1485
1486 float loc_pressure, loc_radius;
1487
1488 const ego_mpd_info_t * pinfo;
1489 const ego_tile_info_t * tlist;
1490 const ego_grid_info_t * glist;
1491
1492 // deal with the optional parameters
1493 loc_pressure = 0.0f;
1494
1495 if ( NULL == pos || 0 == bits ) return 0;
1496
1497 if ( NULL == pmesh || 0 == pmesh->info.tiles_count || 0 == pmesh->tmem.tile_count ) return 0;
1498 pinfo = &( pmesh->info );
1499 tlist = pmesh->tmem.tile_list;
1500 glist = pmesh->gmem.grid_list;
1501
1502 // make an alias for the radius
1503 loc_radius = radius;
1504
1505 // set a minimum radius
1506 if ( 0.0f == loc_radius )
1507 {
1508 loc_radius = GRID_FSIZE * 0.5f;
1509 }
1510
1511 // make sure it is positive
1512 loc_radius = ABS( loc_radius );
1513
1514 fx_min = pos[kX] - loc_radius;
1515 fx_max = pos[kX] + loc_radius;
1516
1517 fy_min = pos[kY] - loc_radius;
1518 fy_max = pos[kY] + loc_radius;
1519
1520 obj_area = ( fx_max - fx_min ) * ( fy_max - fy_min );
1521
1522 ix_min = FLOOR( fx_min / GRID_FSIZE );
1523 ix_max = FLOOR( fx_max / GRID_FSIZE );
1524
1525 iy_min = FLOOR( fy_min / GRID_FSIZE );
1526 iy_max = FLOOR( fy_max / GRID_FSIZE );
1527
1528 for ( iy = iy_min; iy <= iy_max; iy++ )
1529 {
1530 float ty_min, ty_max;
1531
1532 bool_t tile_valid = btrue;
1533
1534 ty_min = ( iy + 0 ) * GRID_FSIZE;
1535 ty_max = ( iy + 1 ) * GRID_FSIZE;
1536
1537 if ( iy < 0 || iy >= pinfo->tiles_y )
1538 {
1539 tile_valid = bfalse;
1540 }
1541
1542 for ( ix = ix_min; ix <= ix_max; ix++ )
1543 {
1544 bool_t is_blocked = bfalse;
1545 float tx_min, tx_max;
1546
1547 float area_ratio;
1548 float ovl_x_min, ovl_x_max;
1549 float ovl_y_min, ovl_y_max;
1550
1551 tx_min = ( ix + 0 ) * GRID_FSIZE;
1552 tx_max = ( ix + 1 ) * GRID_FSIZE;
1553
1554 if ( ix < 0 || ix >= pinfo->tiles_x )
1555 {
1556 tile_valid = bfalse;
1557 }
1558
1559 if ( tile_valid )
1560 {
1561 itile = mesh_get_tile_int( pmesh, ix, iy );
1562 tile_valid = mesh_grid_is_valid( pmesh, itile );
1563 if ( !tile_valid )
1564 {
1565 is_blocked = btrue;
1566 }
1567 else
1568 {
1569 is_blocked = ( 0 != mesh_has_some_mpdfx( glist[itile].fx, bits ) );
1570 }
1571 }
1572
1573 if ( !tile_valid )
1574 {
1575 is_blocked = btrue;
1576 }
1577
1578 if ( is_blocked )
1579 {
1580 // hiting the mesh
1581 float min_area;
1582
1583 // determine the area overlap of the tile with the
1584 // object's bounding box
1585 ovl_x_min = MAX( fx_min, tx_min );
1586 ovl_x_max = MIN( fx_max, tx_max );
1587
1588 ovl_y_min = MAX( fy_min, ty_min );
1589 ovl_y_max = MIN( fy_max, ty_max );
1590
1591 min_area = MIN( tile_area, obj_area );
1592
1593 area_ratio = 0.0f;
1594 if ( ovl_x_min <= ovl_x_max && ovl_y_min <= ovl_y_max )
1595 {
1596 if ( 0.0f == min_area )
1597 {
1598 area_ratio = 1.0f;
1599 }
1600 else
1601 {
1602 area_ratio = ( ovl_x_max - ovl_x_min ) * ( ovl_y_max - ovl_y_min ) / min_area;
1603 }
1604 }
1605
1606 loc_pressure += area_ratio;
1607
1608 mesh_pressure_tests++;
1609 }
1610 }
1611 }
1612
1613 return loc_pressure;
1614 }
1615
1616 //--------------------------------------------------------------------------------------------
mesh_get_diff(const ego_mpd_t * pmesh,const float pos[],float radius,float center_pressure,Uint32 bits)1617 fvec2_t mesh_get_diff( const ego_mpd_t * pmesh, const float pos[], float radius, float center_pressure, Uint32 bits )
1618 {
1619 /// @details BB@> determine the shortest "way out", but creating an array of "pressures"
1620 /// with each element representing the pressure when the object is moved in different directions
1621 /// by 1/2 a tile.
1622
1623 const float jitter_size = GRID_FSIZE * 0.5f;
1624 float pressure_ary[9];
1625 float fx, fy;
1626 fvec2_t diff = ZERO_VECT2;
1627 float sum_diff = 0.0f;
1628 float dpressure;
1629
1630 int cnt;
1631
1632 // find the pressure for the 9 points of jittering around the current position
1633 pressure_ary[4] = center_pressure;
1634 for ( cnt = 0, fy = pos[kY] - jitter_size; fy <= pos[kY] + jitter_size; fy += jitter_size )
1635 {
1636 for ( fx = pos[kX] - jitter_size; fx <= pos[kX] + jitter_size; fx += jitter_size, cnt++ )
1637 {
1638 fvec2_t jitter_pos = VECT2( fx, fy );
1639
1640 if ( 4 == cnt ) continue;
1641
1642 pressure_ary[cnt] = mesh_get_pressure( pmesh, jitter_pos.v, radius, bits );
1643 }
1644 }
1645
1646 // determine the "minimum number of tiles to move" to get into a clear area
1647 diff.x = diff.y = 0.0f;
1648 sum_diff = 0.0f;
1649 for ( cnt = 0, fy = -0.5f; fy <= 0.5f; fy += 0.5f )
1650 {
1651 for ( fx = -0.5f; fx <= 0.5f; fx += 0.5f, cnt++ )
1652 {
1653 if ( 4 == cnt ) continue;
1654
1655 dpressure = ( pressure_ary[cnt] - center_pressure );
1656
1657 // find the maximal pressure gradient == the minimal distance to move
1658 if ( 0.0f != dpressure )
1659 {
1660 float weight;
1661 float dist = pressure_ary[4] / dpressure;
1662 fvec2_t tmp = VECT2( dist * fx, dist * fy );
1663
1664 weight = 1.0f / dist;
1665
1666 diff.x += tmp.y * weight;
1667 diff.y += tmp.x * weight;
1668 sum_diff += ABS( weight );
1669 }
1670 }
1671 }
1672 // normalize the displacement by dividing by the weight...
1673 // unnecessary if the following normalization is kept in
1674 //if( sum_diff > 0.0f )
1675 //{
1676 // diff.x /= sum_diff;
1677 // diff.y /= sum_diff;
1678 //}
1679
1680 // limit the maximum displacement to less than one tile
1681 if ( ABS( diff.x ) + ABS( diff.y ) > 0.0f )
1682 {
1683 float fmax = MAX( ABS( diff.x ), ABS( diff.y ) );
1684
1685 diff.x /= fmax;
1686 diff.y /= fmax;
1687 }
1688
1689 return diff;
1690 }
1691
1692 //--------------------------------------------------------------------------------------------
mesh_hit_wall(const ego_mpd_t * pmesh,const float pos[],const float radius,const BIT_FIELD bits,float nrm[],float * pressure,mesh_wall_data_t * pdata)1693 BIT_FIELD mesh_hit_wall( const ego_mpd_t * pmesh, const float pos[], const float radius, const BIT_FIELD bits, float nrm[], float * pressure, mesh_wall_data_t * pdata )
1694 {
1695 /// @details BB@> an abstraction of the functions of chr_hit_wall() and prt_hit_wall()
1696
1697 BIT_FIELD loc_pass;
1698 Uint32 itile, pass;
1699 int ix, iy;
1700 bool_t invalid;
1701
1702 float loc_pressure;
1703 fvec3_base_t loc_nrm;
1704
1705 bool_t needs_pressure = ( NULL != pressure );
1706 bool_t needs_nrm = ( NULL != nrm );
1707
1708 mesh_wall_data_t loc_data;
1709
1710 // deal with the optional parameters
1711 if ( NULL == pressure ) pressure = &loc_pressure;
1712 *pressure = 0.0f;
1713
1714 if ( NULL == nrm ) nrm = loc_nrm;
1715 nrm[kX] = nrm[kY] = 0.0f;
1716
1717 // if pdata is not NULL, someone has already run a version of mesh_test_wall
1718 if ( NULL == pdata )
1719 {
1720 pdata = &loc_data;
1721
1722 // Do the simplest test.
1723 // Initializes the shared mesh_wall_data_t struct, so no need to do it again
1724 // Eliminates all cases of bad source data, so no need to test them again.
1725 if ( 0 == mesh_test_wall( pmesh, pos, radius, bits, pdata ) ) return 0;
1726 }
1727
1728 // mesh_test_wall() clamps pdata->ix_* and pdata->iy_* to valid values
1729
1730 pass = loc_pass = 0;
1731 nrm[kX] = nrm[kY] = 0.0f;
1732 for ( iy = pdata->iy_min; iy <= pdata->iy_max; iy++ )
1733 {
1734 float ty_min, ty_max;
1735
1736 invalid = bfalse;
1737
1738 ty_min = ( iy + 0 ) * GRID_FSIZE;
1739 ty_max = ( iy + 1 ) * GRID_FSIZE;
1740
1741 if ( iy < 0 || iy >= pdata->pinfo->tiles_y )
1742 {
1743 loc_pass |= ( MPDFX_IMPASS | MPDFX_WALL );
1744
1745 if ( needs_nrm )
1746 {
1747 nrm[kY] += pos[kY] - ( ty_max + ty_min ) * 0.5f;
1748 }
1749
1750 invalid = btrue;
1751 mesh_bound_tests++;
1752 }
1753
1754 for ( ix = pdata->ix_min; ix <= pdata->ix_max; ix++ )
1755 {
1756 float tx_min, tx_max;
1757
1758 tx_min = ( ix + 0 ) * GRID_FSIZE;
1759 tx_max = ( ix + 1 ) * GRID_FSIZE;
1760
1761 if ( ix < 0 || ix >= pdata->pinfo->tiles_x )
1762 {
1763 loc_pass |= MPDFX_IMPASS | MPDFX_WALL;
1764
1765 if ( needs_nrm )
1766 {
1767 nrm[kX] += pos[kX] - ( tx_max + tx_min ) * 0.5f;
1768 }
1769
1770 invalid = btrue;
1771 mesh_bound_tests++;
1772 }
1773
1774 if ( !invalid )
1775 {
1776 itile = mesh_get_tile_int( pmesh, ix, iy );
1777 if ( mesh_grid_is_valid( pmesh, itile ) )
1778 {
1779 BIT_FIELD mpdfx = pdata->glist[itile].fx;
1780 bool_t is_blocked = ( 0 != mesh_has_some_mpdfx( mpdfx, bits ) );
1781
1782 if ( is_blocked )
1783 {
1784 SET_BIT( loc_pass, mpdfx );
1785
1786 if ( needs_nrm )
1787 {
1788 nrm[kX] += pos[kX] - ( tx_max + tx_min ) * 0.5f;
1789 nrm[kY] += pos[kY] - ( ty_max + ty_min ) * 0.5f;
1790 }
1791 }
1792 }
1793 }
1794 }
1795 }
1796
1797 pass = loc_pass & bits;
1798
1799 if ( 0 == pass )
1800 {
1801 // if there is no impact at all, there is no normal and no pressure
1802 nrm[kX] = nrm[kY] = 0.0f;
1803 *pressure = 0.0f;
1804 }
1805 else
1806 {
1807 if ( needs_nrm )
1808 {
1809 // special cases happen a lot. try to avoid computing the square root
1810 if ( 0.0f == nrm[kX] && 0.0f == nrm[kY] )
1811 {
1812 // no normal does not mean no net pressure,
1813 // just that all the simplistic normal calculations balance
1814 }
1815 else if ( 0.0f == nrm[kX] )
1816 {
1817 nrm[kY] = SGN( nrm[kY] );
1818 }
1819 else if ( 0.0f == nrm[kY] )
1820 {
1821 nrm[kX] = SGN( nrm[kX] );
1822 }
1823 else
1824 {
1825 float dist = SQRT( nrm[kX] * nrm[kX] + nrm[kY] * nrm[kY] );
1826
1827 //*pressure = dist;
1828 nrm[kX] /= dist;
1829 nrm[kY] /= dist;
1830 }
1831 }
1832
1833 if ( needs_pressure )
1834 {
1835 *pressure = mesh_get_pressure( pmesh, pos, radius, bits );
1836 }
1837 }
1838
1839 return pass;
1840 }
1841
1842 //--------------------------------------------------------------------------------------------
1843 //--------------------------------------------------------------------------------------------
mesh_get_max_vertex_0(const ego_mpd_t * pmesh,int grid_x,int grid_y)1844 float mesh_get_max_vertex_0( const ego_mpd_t * pmesh, int grid_x, int grid_y )
1845 {
1846 Uint32 itile;
1847 int type;
1848 Uint32 cnt;
1849 float zmax;
1850 size_t vcount, vstart, ivrt;
1851
1852 if ( NULL == pmesh ) return 0.0f;
1853
1854 itile = mesh_get_tile_int( pmesh, grid_x, grid_y );
1855
1856 if ( INVALID_TILE == itile ) return 0.0f;
1857
1858 type = pmesh->tmem.tile_list[itile].type;
1859 vstart = pmesh->tmem.tile_list[itile].vrtstart;
1860 vcount = MIN( 4, pmesh->tmem.vert_count );
1861
1862 ivrt = vstart;
1863 zmax = pmesh->tmem.plst[ivrt][ZZ];
1864 for ( ivrt++, cnt = 1; cnt < vcount; ivrt++, cnt++ )
1865 {
1866 zmax = MAX( zmax, pmesh->tmem.plst[ivrt][ZZ] );
1867 }
1868
1869 return zmax;
1870 }
1871
1872 //--------------------------------------------------------------------------------------------
mesh_get_max_vertex_1(const ego_mpd_t * pmesh,int grid_x,int grid_y,float xmin,float ymin,float xmax,float ymax)1873 float mesh_get_max_vertex_1( const ego_mpd_t * pmesh, int grid_x, int grid_y, float xmin, float ymin, float xmax, float ymax )
1874 {
1875 Uint32 itile, cnt;
1876 int type;
1877 float zmax;
1878 size_t vcount, vstart, ivrt;
1879
1880 int ix_off[4] = {1, 1, 0, 0};
1881 int iy_off[4] = {0, 1, 1, 0};
1882
1883 if ( NULL == pmesh ) return 0.0f;
1884
1885 itile = mesh_get_tile_int( pmesh, grid_x, grid_y );
1886
1887 if ( INVALID_TILE == itile ) return 0.0f;
1888
1889 type = pmesh->tmem.tile_list[itile].type;
1890 vstart = pmesh->tmem.tile_list[itile].vrtstart;
1891 vcount = MIN( 4, pmesh->tmem.vert_count );
1892
1893 zmax = -1e6;
1894 for ( ivrt = vstart, cnt = 0; cnt < vcount; ivrt++, cnt++ )
1895 {
1896 float fx, fy;
1897 GLXvector3f * pvert = pmesh->tmem.plst + ivrt;
1898
1899 // we are evaluating the height based on the grid, not the actual vertex positions
1900 fx = ( grid_x + ix_off[cnt] ) * GRID_FSIZE;
1901 fy = ( grid_y + iy_off[cnt] ) * GRID_FSIZE;
1902
1903 if ( fx >= xmin && fx <= xmax && fy >= ymin && fy <= ymax )
1904 {
1905 zmax = MAX( zmax, ( *pvert )[ZZ] );
1906 }
1907 }
1908
1909 if ( -1e6 == zmax ) zmax = 0.0f;
1910
1911 return zmax;
1912 }
1913
1914 //--------------------------------------------------------------------------------------------
1915 //--------------------------------------------------------------------------------------------
ego_tile_info_init(ego_tile_info_t * ptr)1916 ego_tile_info_t * ego_tile_info_init( ego_tile_info_t * ptr )
1917 {
1918 if ( NULL == ptr ) return ptr;
1919
1920 memset( ptr, 0, sizeof( *ptr ) );
1921
1922 // set the non-zero, non-NULL, non-bfalse values
1923 ptr->fanoff = btrue;
1924 ptr->inrenderlist_frame = -1;
1925 ptr->needs_lighting_update = btrue;
1926
1927 return ptr;
1928 }
1929
1930 //--------------------------------------------------------------------------------------------
ego_tile_info_alloc()1931 ego_tile_info_t * ego_tile_info_alloc()
1932 {
1933 ego_tile_info_t * retval = NULL;
1934
1935 retval = EGOBOO_NEW( ego_tile_info_t );
1936 if ( NULL == retval ) return NULL;
1937
1938 return ego_tile_info_init( retval );
1939 }
1940
1941 //--------------------------------------------------------------------------------------------
ego_tile_info_init_ary(ego_tile_info_t * ptr,size_t count)1942 ego_tile_info_t * ego_tile_info_init_ary( ego_tile_info_t * ptr, size_t count )
1943 {
1944 Uint32 cnt;
1945
1946 if ( NULL == ptr ) return ptr;
1947
1948 for ( cnt = 0; cnt < count; cnt++ )
1949 {
1950 ego_tile_info_init( ptr + cnt );
1951 }
1952
1953 return ptr;
1954 }
1955
1956 //--------------------------------------------------------------------------------------------
ego_tile_info_alloc_ary(size_t count)1957 ego_tile_info_t * ego_tile_info_alloc_ary( size_t count )
1958 {
1959 ego_tile_info_t * retval = NULL;
1960
1961 retval = EGOBOO_NEW_ARY( ego_tile_info_t, count );
1962 if ( NULL == retval ) return NULL;
1963
1964 return ego_tile_info_init_ary( retval, count );
1965 }
1966