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