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 graphic_prt.c
21 /// @brief Particle system drawing and management code.
22 /// @details
23 
24 #include "graphic_prt.h"
25 
26 #include "particle.inl"
27 #include "char.inl"
28 #include "profile.inl"
29 
30 #include "game.h"
31 #include "texture.h"
32 #include "camera.h"
33 #include "input.h"
34 #include "lighting.h"
35 
36 #include "egoboo_setup.h"
37 #include "egoboo.h"
38 
39 //--------------------------------------------------------------------------------------------
40 //--------------------------------------------------------------------------------------------
41 // dynamically calculate the particle texture coordinates
42 
43 int   ptex_w[2] = {256, 256};
44 int   ptex_h[2] = {256, 256};
45 float ptex_wscale[2] = {1.0f, 1.0f};
46 float ptex_hscale[2] = {1.0f, 1.0f};
47 
48 //--------------------------------------------------------------------------------------------
prt_get_texture_style(const TX_REF itex)49 int prt_get_texture_style( const TX_REF itex )
50 {
51     int index;
52 
53     index = -1;
54     switch ( REF_TO_INT( itex ) )
55     {
56         case TX_PARTICLE_TRANS:
57             index = 0;
58             break;
59 
60         case TX_PARTICLE_LIGHT:
61             index = 1;
62             break;
63     }
64 
65     return index;
66 }
67 
68 //--------------------------------------------------------------------------------------------
prt_set_texture_params(const TX_REF itex)69 void prt_set_texture_params( const TX_REF itex )
70 {
71     int index;
72     oglx_texture_t * ptex;
73 
74     index = prt_get_texture_style( itex );
75     if ( index < 0 ) return;
76 
77     ptex = TxTexture_get_ptr( itex );
78     if ( NULL == ptex ) return;
79 
80     ptex_w[index] = ptex->imgW;
81     ptex_h[index] = ptex->imgH;
82     ptex_wscale[index] = ( float )ptex->imgW / ( float )ptex->base.width;
83     ptex_hscale[index] = ( float )ptex->imgH / ( float )ptex->base.height;
84 }
85 
86 //--------------------------------------------------------------------------------------------
87 //--------------------------------------------------------------------------------------------
88 
89 /// The data values necessary to sort particles by their position to the camera
90 struct s_prt_registry_entity
91 {
92     PRT_REF index;
93     float   dist;
94 };
95 typedef struct s_prt_registry_entity prt_registry_entity_t;
96 
97 //--------------------------------------------------------------------------------------------
98 //--------------------------------------------------------------------------------------------
99 static gfx_rv prt_instance_update( camera_t * pcam, const PRT_REF particle, Uint8 trans, bool_t do_lighting );
100 static void calc_billboard_verts( GLvertex vlst[], prt_instance_t * pinst, float size, bool_t do_reflect );
101 static int  cmp_prt_registry_entity( const void * vlhs, const void * vrhs );
102 
103 static void draw_one_attachment_point( chr_instance_t * pinst, mad_t * pmad, int vrt_offset );
104 static void prt_draw_attached_point( prt_bundle_t * pbdl_prt );
105 
106 static void render_prt_bbox( prt_bundle_t * pbdl_prt );
107 
108 //--------------------------------------------------------------------------------------------
109 //--------------------------------------------------------------------------------------------
110 Uint32  instance_update = ( Uint32 )~0;
111 
112 //--------------------------------------------------------------------------------------------
113 //--------------------------------------------------------------------------------------------
cmp_prt_registry_entity(const void * vlhs,const void * vrhs)114 int cmp_prt_registry_entity( const void * vlhs, const void * vrhs )
115 {
116     const prt_registry_entity_t * lhs, * rhs;
117 
118     lhs = ( prt_registry_entity_t * ) vlhs;
119     rhs = ( prt_registry_entity_t * ) vrhs;
120 
121     return lhs->dist - rhs->dist;
122 }
123 
124 //--------------------------------------------------------------------------------------------
125 //--------------------------------------------------------------------------------------------
render_all_prt_begin(camera_t * pcam,prt_registry_entity_t reg[],size_t reg_count)126 size_t render_all_prt_begin( camera_t * pcam, prt_registry_entity_t reg[], size_t reg_count )
127 {
128     fvec3_t vfwd, vcam;
129     size_t  numparticle;
130 
131     update_all_prt_instance( pcam );
132 
133     mat_getCamForward( pcam->mView.v, vfwd.v );
134     vcam = pcam->pos;
135 
136     // Original points
137     numparticle = 0;
138     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
139     {
140         prt_instance_t * pinst;
141 
142         if ( numparticle >= reg_count ) break;
143 
144         pinst = &( prt_bdl.prt_ptr->inst );
145 
146         if ( !pinst->indolist ) continue;
147 
148         if ( 0 != pinst->size )
149         {
150             fvec3_t   vpos;
151             float dist;
152 
153             vpos.x = pinst->pos.x - vcam.x;
154             vpos.y = pinst->pos.y - vcam.y;
155             vpos.z = pinst->pos.z - vcam.z;
156 
157             dist = fvec3_dot_product( vfwd.v, vpos.v );
158 
159             if ( dist > 0 )
160             {
161                 reg[numparticle].index = REF_TO_INT( prt_bdl.prt_ref );
162                 reg[numparticle].dist  = dist;
163                 numparticle++;
164             }
165         }
166     }
167     PRT_END_LOOP();
168 
169     // sort the particles from close to far
170     qsort( reg, numparticle, sizeof( prt_registry_entity_t ), cmp_prt_registry_entity );
171 
172     return numparticle;
173 }
174 
175 //--------------------------------------------------------------------------------------------
render_one_prt_solid(const PRT_REF iprt)176 gfx_rv render_one_prt_solid( const PRT_REF iprt )
177 {
178     /// @details BB@> Render the solid version of the particle
179 
180     GLvertex vtlist[4];
181     int i;
182 
183     prt_t * pprt;
184     prt_instance_t * pinst;
185 
186     if ( !DISPLAY_PRT( iprt ) )
187     {
188         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, iprt, "invalid particle" );
189         return gfx_error;
190     }
191     pprt = PrtList.lst + iprt;
192 
193     // if the particle is hidden, do not continue
194     if ( pprt->is_hidden ) return gfx_fail;
195 
196     // if the particle instance data is not valid, do not continue
197     if ( !pprt->inst.valid ) return gfx_fail;
198     pinst = &( pprt->inst );
199 
200     // only render solid sprites
201     if ( SPRITE_SOLID != pprt->type ) return gfx_fail;
202 
203     // billboard for the particle
204     calc_billboard_verts( vtlist, pinst, pinst->size, bfalse );
205 
206     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT );
207     {
208         // Use the depth test to eliminate hidden portions of the particle
209         GL_DEBUG( glEnable )( GL_DEPTH_TEST );                                  // GL_ENABLE_BIT
210         GL_DEBUG( glDepthFunc )( GL_LESS );                                   // GL_DEPTH_BUFFER_BIT
211 
212         // enable the depth mask for the solid portion of the particles
213         GL_DEBUG( glDepthMask )( GL_TRUE );           // GL_ENABLE_BIT
214 
215         // draw draw front and back faces of polygons
216         GL_DEBUG( glDisable )( GL_CULL_FACE );        // GL_ENABLE_BIT
217 
218         // Since the textures are probably mipmapped or minified with some kind of
219         // interpolation, we can never really turn blending off,
220         GL_DEBUG( glEnable )( GL_BLEND );                                           // GL_ENABLE_BIT
221         GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );            // GL_ENABLE_BIT
222 
223         // only display the portion of the particle that is
224         // 100% solid
225         GL_DEBUG( glEnable )( GL_ALPHA_TEST );        // GL_ENABLE_BIT
226         GL_DEBUG( glAlphaFunc )( GL_EQUAL, 1.0f );       // GL_COLOR_BUFFER_BIT
227 
228         oglx_texture_Bind( TxTexture_get_ptr(( TX_REF )TX_PARTICLE_TRANS ) );
229 
230         GL_DEBUG( glColor4f )( pinst->fintens, pinst->fintens, pinst->fintens, 1.0f );  // GL_CURRENT_BIT
231 
232         GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
233         {
234             for ( i = 0; i < 4; i++ )
235             {
236                 GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
237                 GL_DEBUG( glVertex3fv )( vtlist[i].pos );
238             }
239         }
240         GL_DEBUG_END();
241     }
242     ATTRIB_POP( __FUNCTION__ );
243 
244     return gfx_success;
245 }
246 
247 //--------------------------------------------------------------------------------------------
render_all_prt_solid(camera_t * pcam,prt_registry_entity_t reg[],size_t numparticle)248 void render_all_prt_solid( camera_t * pcam, prt_registry_entity_t reg[], size_t numparticle )
249 {
250     /// @details BB@> do solid sprites first
251 
252     size_t cnt;
253     PRT_REF prt;
254 
255     gfx_begin_3d( pcam );
256     {
257         // apply solid particles from near to far
258         for ( cnt = 0; cnt < numparticle; cnt++ )
259         {
260             // Get the index from the color slot
261             prt = reg[cnt].index;
262 
263             render_one_prt_solid( prt );
264         }
265     }
266     gfx_end_3d();
267 }
268 
269 //--------------------------------------------------------------------------------------------
render_one_prt_trans(const PRT_REF iprt)270 gfx_rv render_one_prt_trans( const PRT_REF iprt )
271 {
272     /// @details BB@> do all kinds of transparent sprites next
273 
274     GLvertex vtlist[4];
275     int i;
276     prt_t * pprt;
277     prt_instance_t * pinst;
278 
279     if ( !DISPLAY_PRT( iprt ) )
280     {
281         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, iprt, "invalid particle" );
282         return gfx_error;
283     }
284     pprt = PrtList.lst + iprt;
285 
286     // if the particle is hidden, do not continue
287     if ( pprt->is_hidden ) return gfx_fail;
288 
289     // if the particle instance data is not valid, do not continue
290     if ( !pprt->inst.valid ) return gfx_fail;
291     pinst = &( pprt->inst );
292 
293     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT );
294     {
295         bool_t draw_particle;
296         GLXvector4f particle_color;
297 
298         // don't write into the depth buffer (disable glDepthMask for transparent objects)
299         GL_DEBUG( glDepthMask )( GL_FALSE );        // GL_DEPTH_BUFFER_BIT
300 
301         // do not draw hidden surfaces
302         GL_DEBUG( glEnable )( GL_DEPTH_TEST );      // GL_ENABLE_BIT
303         GL_DEBUG( glDepthFunc )( GL_LEQUAL );       // GL_DEPTH_BUFFER_BIT
304 
305         // draw draw front and back faces of polygons
306         GL_DEBUG( glDisable )( GL_CULL_FACE );    // ENABLE_BIT
307 
308         draw_particle = bfalse;
309         if ( SPRITE_SOLID == pprt->type )
310         {
311             // do the alpha blended edge ("anti-aliasing") of the solid particle
312 
313             // only display the alpha-edge of the particle
314             GL_DEBUG( glEnable )( GL_ALPHA_TEST );        // GL_ENABLE_BIT
315             GL_DEBUG( glAlphaFunc )( GL_LESS, 1.0f );     // GL_COLOR_BUFFER_BIT
316 
317             GL_DEBUG( glEnable )( GL_BLEND );                                 // GL_ENABLE_BIT
318             GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
319 
320             particle_color[RR] = pinst->fintens;
321             particle_color[GG] = pinst->fintens;
322             particle_color[BB] = pinst->fintens;
323             particle_color[AA] = 1.0f;
324 
325             pinst->texture_ref = TX_PARTICLE_TRANS;
326             oglx_texture_Bind( TxTexture_get_ptr( pinst->texture_ref ) );
327 
328             draw_particle = btrue;
329         }
330         else if ( SPRITE_LIGHT == pprt->type )
331         {
332             // do the light sprites
333             float intens = pinst->fintens * pinst->falpha;
334 
335             GL_DEBUG( glDisable )( GL_ALPHA_TEST );                         // GL_ENABLE_BIT
336 
337             GL_DEBUG( glEnable )( GL_BLEND );                               // GL_ENABLE_BIT
338             GL_DEBUG( glBlendFunc )( GL_ONE, GL_ONE );                      // GL_COLOR_BUFFER_BIT
339 
340             particle_color[RR] = intens;
341             particle_color[GG] = intens;
342             particle_color[BB] = intens;
343             particle_color[AA] = 1.0f;
344 
345             pinst->texture_ref = TX_PARTICLE_LIGHT;
346             oglx_texture_Bind( TxTexture_get_ptr( pinst->texture_ref ) );
347 
348             draw_particle = ( intens > 0.0f );
349         }
350         else if ( SPRITE_ALPHA == pprt->type )
351         {
352             // do the transparent sprites
353 
354             // do not display the completely transparent portion
355             GL_DEBUG( glEnable )( GL_ALPHA_TEST );                            // GL_ENABLE_BIT
356             GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );                         // GL_COLOR_BUFFER_BIT
357 
358             GL_DEBUG( glEnable )( GL_BLEND );                                 // GL_ENABLE_BIT
359             GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
360 
361             particle_color[RR] = pinst->fintens;
362             particle_color[GG] = pinst->fintens;
363             particle_color[BB] = pinst->fintens;
364             particle_color[AA] = pinst->falpha;
365 
366             pinst->texture_ref = TX_PARTICLE_TRANS;
367             oglx_texture_Bind( TxTexture_get_ptr( pinst->texture_ref ) );
368 
369             draw_particle = ( pinst->falpha > 0.0f );
370         }
371         else
372         {
373             // unknown type
374             return gfx_error;
375         }
376 
377         if ( draw_particle )
378         {
379             calc_billboard_verts( vtlist, pinst, pinst->size, bfalse );
380 
381             GL_DEBUG( glColor4fv )( particle_color );             // GL_CURRENT_BIT
382 
383             // Go on and draw it
384             GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
385             {
386                 for ( i = 0; i < 4; i++ )
387                 {
388                     GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
389                     GL_DEBUG( glVertex3fv )( vtlist[i].pos );
390                 }
391             }
392             GL_DEBUG_END();
393         }
394     }
395     ATTRIB_POP( __FUNCTION__ );
396 
397     return gfx_success;
398 }
399 
400 //--------------------------------------------------------------------------------------------
render_all_prt_trans(camera_t * pcam,prt_registry_entity_t reg[],size_t numparticle)401 void render_all_prt_trans( camera_t * pcam, prt_registry_entity_t reg[], size_t numparticle )
402 {
403     /// @details BB@> do all kinds of transparent sprites next
404 
405     int cnt;
406 
407     gfx_begin_3d( pcam );
408     {
409         // apply transparent particles from far to near
410         for ( cnt = (( int )numparticle ) - 1; cnt >= 0; cnt-- )
411         {
412             // Get the index from the color slot
413             render_one_prt_trans(( PRT_REF )reg[cnt].index );
414         }
415     }
416     gfx_end_3d();
417 }
418 
419 //--------------------------------------------------------------------------------------------
render_all_particles(camera_t * pcam)420 void render_all_particles( camera_t * pcam )
421 {
422     /// @details ZZ@> This function draws the sprites for particle systems
423 
424     prt_registry_entity_t reg[MAX_PRT];
425     size_t numparticle;
426 
427     numparticle = render_all_prt_begin( pcam, reg, MAX_PRT );
428 
429     render_all_prt_solid( pcam, reg, numparticle );
430     render_all_prt_trans( pcam, reg, numparticle );
431 }
432 
433 //--------------------------------------------------------------------------------------------
434 //--------------------------------------------------------------------------------------------
render_all_prt_ref_begin(camera_t * pcam,prt_registry_entity_t reg[],size_t reg_count)435 size_t render_all_prt_ref_begin( camera_t * pcam, prt_registry_entity_t reg[], size_t reg_count )
436 {
437     fvec3_t vfwd, vcam;
438     size_t  numparticle;
439 
440     update_all_prt_instance( pcam );
441 
442     mat_getCamForward( pcam->mView.v, vfwd.v );
443     vcam = pcam->pos;
444 
445     // Original points
446     numparticle = 0;
447     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
448     {
449         prt_instance_t * pinst;
450         fvec3_t   vpos;
451         float dist;
452 
453         if ( numparticle >= reg_count ) break;
454 
455         pinst = &( prt_bdl.prt_ptr->inst );
456 
457         if ( !pinst->indolist ) continue;
458 
459         vpos = fvec3_sub( pinst->ref_pos.v, vcam.v );
460         dist = fvec3_dot_product( vfwd.v, vpos.v );
461 
462         if ( dist > 0 )
463         {
464             reg[numparticle].index = REF_TO_INT( iprt );
465             reg[numparticle].dist  = dist;
466             numparticle++;
467         }
468 
469     }
470     PRT_END_LOOP();
471 
472     // sort the particles from close to far
473     qsort( reg, numparticle, sizeof( prt_registry_entity_t ), cmp_prt_registry_entity );
474 
475     return numparticle;
476 }
477 
478 //--------------------------------------------------------------------------------------------
render_one_prt_ref(const PRT_REF iprt)479 gfx_rv render_one_prt_ref( const PRT_REF iprt )
480 {
481     /// @details BB@> render one particle
482 
483     GLvertex vtlist[4];
484     int startalpha;
485     int i;
486     prt_t * pprt;
487     prt_instance_t * pinst;
488 
489     if ( !DISPLAY_PRT( iprt ) )
490     {
491         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, iprt, "invalid particle" );
492         return gfx_error;
493     }
494     pprt = PrtList.lst + iprt;
495 
496     // if the particle is hidden, do not continue
497     if ( pprt->is_hidden ) return gfx_fail;
498 
499     if ( !pprt->inst.valid || !pprt->inst.ref_valid ) return gfx_fail;
500     pinst = &( pprt->inst );
501 
502     // Fill in the rest of the data. (make it match the case for characters)
503     startalpha = 255;
504     startalpha -= 2.0f * ( pprt->enviro.floor_level - pinst->ref_pos.z );
505     startalpha *= 0.5f;
506     startalpha = CLIP( startalpha, 0, 255 );
507 
508     //startalpha = ( startalpha | gfx.reffadeor ) >> 1;  // Fix for Riva owners
509     //startalpha = CLIP(startalpha, 0, 255);
510 
511     if ( startalpha > 0 )
512     {
513         ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT );
514         {
515             bool_t draw_particle;
516             GLXvector4f particle_color;
517 
518             // don't write into the depth buffer (disable glDepthMask for transparent objects)
519             GL_DEBUG( glDepthMask )( GL_FALSE );      // ENABLE_BIT
520 
521             // do not draw hidden surfaces
522             GL_DEBUG( glEnable )( GL_DEPTH_TEST );    // ENABLE_BIT
523             GL_DEBUG( glDepthFunc )( GL_LEQUAL );     // GL_DEPTH_BUFFER_BIT
524 
525             // draw draw front and back faces of polygons
526             GL_DEBUG( glDisable )( GL_CULL_FACE );    // ENABLE_BIT
527 
528             draw_particle = bfalse;
529             if ( SPRITE_LIGHT == pprt->type )
530             {
531                 // do the light sprites
532                 float intens = startalpha * INV_FF * pinst->falpha * pinst->fintens;
533 
534                 GL_DEBUG( glDisable )( GL_ALPHA_TEST );         // ENABLE_BIT
535 
536                 GL_DEBUG( glEnable )( GL_BLEND );               // ENABLE_BIT
537                 GL_DEBUG( glBlendFunc )( GL_ONE, GL_ONE );  // GL_COLOR_BUFFER_BIT
538 
539                 particle_color[RR] = intens;
540                 particle_color[GG] = intens;
541                 particle_color[BB] = intens;
542                 particle_color[AA] = 1.0f;
543 
544                 pinst->texture_ref = TX_PARTICLE_LIGHT;
545                 oglx_texture_Bind( TxTexture_get_ptr( pinst->texture_ref ) );
546 
547                 draw_particle = intens > 0.0f;
548             }
549             else if ( SPRITE_SOLID == pprt->type || SPRITE_ALPHA == pprt->type )
550             {
551                 // do the transparent sprites
552 
553                 float alpha = startalpha * INV_FF;
554                 if ( SPRITE_ALPHA == pprt->type )
555                 {
556                     alpha *= pinst->falpha;
557                 }
558 
559                 // do not display the completely transparent portion
560                 GL_DEBUG( glEnable )( GL_ALPHA_TEST );         // ENABLE_BIT
561                 GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );      // GL_COLOR_BUFFER_BIT
562 
563                 GL_DEBUG( glEnable )( GL_BLEND );                                 // ENABLE_BIT
564                 GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
565 
566                 particle_color[RR] = pinst->fintens;
567                 particle_color[GG] = pinst->fintens;
568                 particle_color[BB] = pinst->fintens;
569                 particle_color[AA] = alpha;
570 
571                 pinst->texture_ref = TX_PARTICLE_TRANS;
572                 oglx_texture_Bind( TxTexture_get_ptr( pinst->texture_ref ) );
573 
574                 draw_particle = alpha > 0.0f;
575             }
576             else
577             {
578                 // unknown type
579                 return gfx_fail;
580             }
581 
582             if ( draw_particle )
583             {
584                 // Calculate the position of the four corners of the billboard
585                 // used to display the particle.
586                 calc_billboard_verts( vtlist, pinst, pinst->size, btrue );
587 
588                 GL_DEBUG( glColor4fv )( particle_color );      // GL_CURRENT_BIT
589 
590                 GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
591                 {
592                     for ( i = 0; i < 4; i++ )
593                     {
594                         GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
595                         GL_DEBUG( glVertex3fv )( vtlist[i].pos );
596                     }
597                 }
598                 GL_DEBUG_END();
599             }
600         }
601         ATTRIB_POP( __FUNCTION__ );
602 
603     }
604 
605     return gfx_success;
606 }
607 
608 //--------------------------------------------------------------------------------------------
render_all_prt_ref(camera_t * pcam,prt_registry_entity_t reg[],size_t numparticle)609 void render_all_prt_ref( camera_t * pcam, prt_registry_entity_t reg[], size_t numparticle )
610 {
611     size_t cnt;
612     PRT_REF prt;
613 
614     gfx_begin_3d( pcam );
615     {
616         // Render each particle that was on
617         for ( cnt = 0; cnt < numparticle; cnt++ )
618         {
619             // Get the index from the color slot
620             prt = reg[cnt].index;
621 
622             render_one_prt_ref(( PRT_REF )reg[cnt].index );
623         }
624     }
625     gfx_end_3d();
626 }
627 
628 //--------------------------------------------------------------------------------------------
render_prt_ref(camera_t * pcam)629 void render_prt_ref( camera_t * pcam )
630 {
631     /// @details ZZ@> This function draws sprites reflected in the floor
632 
633     prt_registry_entity_t reg[MAX_PRT];
634     size_t numparticle;
635 
636     numparticle = render_all_prt_ref_begin( pcam, reg, MAX_PRT );
637     render_all_prt_ref( pcam, reg, numparticle );
638 }
639 
640 //--------------------------------------------------------------------------------------------
641 //--------------------------------------------------------------------------------------------
calc_billboard_verts(GLvertex vlst[],prt_instance_t * pinst,float size,bool_t do_reflect)642 void calc_billboard_verts( GLvertex vlst[], prt_instance_t * pinst, float size, bool_t do_reflect )
643 {
644     // Calculate the position of the four corners of the billboard
645     // used to display the particle.
646 
647     int i, index;
648     fvec3_t prt_pos, prt_up, prt_right;
649 
650     if ( NULL == vlst || NULL == pinst ) return;
651 
652     switch ( REF_TO_INT( pinst->texture_ref ) )
653     {
654         default:
655         case TX_PARTICLE_TRANS:
656             index = 0;
657             break;
658 
659         case TX_PARTICLE_LIGHT:
660             index = 1;
661             break;
662     }
663 
664     // use the pre-computed reflection parameters
665     if ( do_reflect )
666     {
667         prt_pos   = pinst->ref_pos;
668         prt_up    = pinst->ref_up;
669         prt_right = pinst->ref_right;
670     }
671     else
672     {
673         prt_pos   = pinst->pos;
674         prt_up    = pinst->up;
675         prt_right = pinst->right;
676     }
677 
678     for ( i = 0; i < 4; i++ )
679     {
680         vlst[i].pos[XX] = prt_pos.x;
681         vlst[i].pos[YY] = prt_pos.y;
682         vlst[i].pos[ZZ] = prt_pos.z;
683     }
684 
685     vlst[0].pos[XX] += ( -prt_right.x - prt_up.x ) * size;
686     vlst[0].pos[YY] += ( -prt_right.y - prt_up.y ) * size;
687     vlst[0].pos[ZZ] += ( -prt_right.z - prt_up.z ) * size;
688 
689     vlst[1].pos[XX] += ( prt_right.x - prt_up.x ) * size;
690     vlst[1].pos[YY] += ( prt_right.y - prt_up.y ) * size;
691     vlst[1].pos[ZZ] += ( prt_right.z - prt_up.z ) * size;
692 
693     vlst[2].pos[XX] += ( prt_right.x + prt_up.x ) * size;
694     vlst[2].pos[YY] += ( prt_right.y + prt_up.y ) * size;
695     vlst[2].pos[ZZ] += ( prt_right.z + prt_up.z ) * size;
696 
697     vlst[3].pos[XX] += ( -prt_right.x + prt_up.x ) * size;
698     vlst[3].pos[YY] += ( -prt_right.y + prt_up.y ) * size;
699     vlst[3].pos[ZZ] += ( -prt_right.z + prt_up.z ) * size;
700 
701     vlst[0].tex[SS] = CALCULATE_PRT_U1( index, pinst->image_ref );
702     vlst[0].tex[TT] = CALCULATE_PRT_V1( index, pinst->image_ref );
703 
704     vlst[1].tex[SS] = CALCULATE_PRT_U0( index, pinst->image_ref );
705     vlst[1].tex[TT] = CALCULATE_PRT_V1( index, pinst->image_ref );
706 
707     vlst[2].tex[SS] = CALCULATE_PRT_U0( index, pinst->image_ref );
708     vlst[2].tex[TT] = CALCULATE_PRT_V0( index, pinst->image_ref );
709 
710     vlst[3].tex[SS] = CALCULATE_PRT_U1( index, pinst->image_ref );
711     vlst[3].tex[TT] = CALCULATE_PRT_V0( index, pinst->image_ref );
712 }
713 
714 //--------------------------------------------------------------------------------------------
715 //--------------------------------------------------------------------------------------------
render_all_prt_attachment()716 void render_all_prt_attachment()
717 {
718     GL_DEBUG( glDisable )( GL_BLEND );
719 
720     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
721     {
722         prt_draw_attached_point( &prt_bdl );
723     }
724     PRT_END_LOOP();
725 }
726 
727 //--------------------------------------------------------------------------------------------
render_all_prt_bbox()728 void render_all_prt_bbox()
729 {
730     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
731     {
732         render_prt_bbox( &prt_bdl );
733     }
734     PRT_END_LOOP();
735 }
736 
737 //--------------------------------------------------------------------------------------------
draw_one_attachment_point(chr_instance_t * pinst,mad_t * pmad,int vrt_offset)738 void draw_one_attachment_point( chr_instance_t * pinst, mad_t * pmad, int vrt_offset )
739 {
740     /// @details BB@> a function that will draw some of the vertices of the given character.
741     ///     The original idea was to use this to debug the grip for attached items.
742 
743     Uint32 vrt;
744 
745     GLint matrix_mode[1];
746     GLboolean texture_1d_enabled, texture_2d_enabled;
747 
748     if ( NULL == pinst || NULL == pmad ) return;
749 
750     vrt = ( int )pinst->vrt_count - ( int )vrt_offset;
751 
752     if ( vrt < 0 || vrt >= pinst->vrt_count ) return;
753 
754     texture_1d_enabled = GL_DEBUG( glIsEnabled )( GL_TEXTURE_1D );
755     texture_2d_enabled = GL_DEBUG( glIsEnabled )( GL_TEXTURE_2D );
756 
757     // disable the texturing so all the points will be white,
758     // not the texture color of the last vertex we drawn
759     if ( texture_1d_enabled ) GL_DEBUG( glDisable )( GL_TEXTURE_1D );
760     if ( texture_2d_enabled ) GL_DEBUG( glDisable )( GL_TEXTURE_2D );
761 
762     GL_DEBUG( glPointSize )( 5 );
763 
764     // save the matrix mode
765     GL_DEBUG( glGetIntegerv )( GL_MATRIX_MODE, matrix_mode );
766 
767     // store the GL_MODELVIEW matrix (this stack has a finite depth, minimum of 32)
768     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
769     GL_DEBUG( glPushMatrix )();
770     GL_DEBUG( glMultMatrixf )( pinst->matrix.v );
771 
772     GL_DEBUG( glBegin( GL_POINTS ) );
773     {
774         GL_DEBUG( glVertex3fv )( pinst->vrt_lst[vrt].pos );
775     }
776     GL_DEBUG_END();
777 
778     // Restore the GL_MODELVIEW matrix
779     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
780     GL_DEBUG( glPopMatrix )();
781 
782     // restore the matrix mode
783     GL_DEBUG( glMatrixMode )( matrix_mode[0] );
784 
785     if ( texture_1d_enabled ) GL_DEBUG( glEnable )( GL_TEXTURE_1D );
786     if ( texture_2d_enabled ) GL_DEBUG( glEnable )( GL_TEXTURE_2D );
787 }
788 
789 //--------------------------------------------------------------------------------------------
prt_draw_attached_point(prt_bundle_t * pbdl_prt)790 void prt_draw_attached_point( prt_bundle_t * pbdl_prt )
791 {
792     mad_t * pholder_mad;
793     cap_t * pholder_cap;
794     chr_t * pholder;
795 
796     prt_t * loc_pprt;
797 
798     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return;
799     loc_pprt = pbdl_prt->prt_ptr;
800 
801     if ( !DISPLAY_PPRT( loc_pprt ) ) return;
802 
803     if ( !INGAME_CHR( loc_pprt->attachedto_ref ) ) return;
804     pholder = ChrList.lst + loc_pprt->attachedto_ref;
805 
806     pholder_cap = pro_get_pcap( pholder->profile_ref );
807     if ( NULL == pholder_cap ) return;
808 
809     pholder_mad = chr_get_pmad( GET_REF_PCHR( pholder ) );
810     if ( NULL == pholder_mad ) return;
811 
812     draw_one_attachment_point( &( pholder->inst ), pholder_mad, loc_pprt->attachedto_vrt_off );
813 }
814 
815 //--------------------------------------------------------------------------------------------
816 //--------------------------------------------------------------------------------------------
update_all_prt_instance(camera_t * pcam)817 gfx_rv update_all_prt_instance( camera_t * pcam )
818 {
819     gfx_rv retval;
820 
821     if ( NULL == pcam ) pcam = PCamera;
822     if ( NULL == pcam )
823     {
824         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
825         return gfx_error;
826     }
827 
828     // only one update per frame
829     if ( instance_update == update_wld ) return gfx_success;
830     instance_update = update_wld;
831 
832     // assume the best
833     retval = gfx_success;
834 
835     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
836     {
837         prt_instance_t * pinst;
838 
839         pinst = &( prt_bdl.prt_ptr->inst );
840 
841         // only do frame counting for particles that are fully activated!
842         prt_bdl.prt_ptr->obj_base.frame_count++;
843         if ( prt_bdl.prt_ptr->frames_remaining > 0 ) prt_bdl.prt_ptr->frames_remaining--;
844 
845         if ( !prt_bdl.prt_ptr->inst.indolist )
846         {
847             pinst->valid     = bfalse;
848             pinst->ref_valid = bfalse;
849         }
850         else
851         {
852             // calculate the "billboard" for this particle
853             if ( gfx_error == prt_instance_update( pcam, iprt, 255, btrue ) )
854             {
855                 retval = gfx_error;
856             }
857         }
858     }
859     PRT_END_LOOP();
860 
861     return retval;
862 }
863 
864 //--------------------------------------------------------------------------------------------
prt_instance_update_vertices(camera_t * pcam,prt_instance_t * pinst,prt_t * pprt)865 gfx_rv prt_instance_update_vertices( camera_t * pcam, prt_instance_t * pinst, prt_t * pprt )
866 {
867     pip_t * ppip;
868 
869     fvec3_t   vfwd, vup, vright;
870     fvec3_t   vfwd_ref, vup_ref, vright_ref;
871 
872     if ( NULL == pinst )
873     {
874         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL particle instance" );
875         return gfx_error;
876     }
877     pinst->valid     = bfalse;
878     pinst->ref_valid = bfalse;
879 
880     if ( NULL == pcam ) pcam = PCamera;
881     if ( NULL == pcam )
882     {
883         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
884         return gfx_error;
885     }
886 
887     if ( !DISPLAY_PPRT( pprt ) )
888     {
889         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, GET_INDEX_PPRT( pprt ), "invalid particle" );
890         return gfx_error;
891     }
892 
893     if ( !LOADED_PIP( pprt->pip_ref ) )
894     {
895         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, pprt->pip_ref, "invalid pip" );
896         return gfx_error;
897     }
898     ppip = PipStack.lst + pprt->pip_ref;
899 
900     pinst->type = pprt->type;
901 
902     pinst->image_ref = FP8_TO_INT( pprt->image + pprt->image_stt );
903 
904     // set the position
905     pinst->pos         = prt_get_pos( pprt );
906     pinst->orientation = ppip->orientation;
907 
908     // calculate the billboard vectors for the reflecions
909     pinst->ref_pos      = prt_get_pos( pprt );
910     pinst->ref_pos.z    = 2 * pprt->enviro.floor_level - pinst->pos.z;
911 
912     // get the vector from the camera to the particle
913     vfwd = fvec3_sub( pinst->pos.v, pcam->pos.v );
914     vfwd = fvec3_normalize( vfwd.v );
915 
916     vfwd_ref = fvec3_sub( pinst->ref_pos.v, pcam->pos.v );
917     vfwd_ref = fvec3_normalize( vfwd_ref.v );
918 
919     // set the up and right vectors
920     if ( ppip->rotatetoface && !INGAME_CHR( pprt->attachedto_ref ) && ( ABS( pprt->vel.x ) + ABS( pprt->vel.y ) + ABS( pprt->vel.z ) > 0 ) )
921     {
922         // the particle points along its direction of travel
923 
924         vup = pprt->vel;
925         vup   = fvec3_normalize( vup.v );
926 
927         // get the correct "right" vector
928         vright = fvec3_cross_product( vfwd.v, vup.v );
929         vright = fvec3_normalize( vright.v );
930 
931         vup_ref    = vup;
932         vright_ref = fvec3_cross_product( vfwd_ref.v, vup.v );
933         vright_ref = fvec3_normalize( vright_ref.v );
934     }
935     else if ( ORIENTATION_B == pinst->orientation )
936     {
937         // use the camera up vector
938         vup = pcam->vup;
939         vup = fvec3_normalize( vup.v );
940 
941         // get the correct "right" vector
942         vright = fvec3_cross_product( vfwd.v, vup.v );
943         vright = fvec3_normalize( vright.v );
944 
945         vup_ref    = vup;
946         vright_ref = fvec3_cross_product( vfwd_ref.v, vup.v );
947         vright_ref = fvec3_normalize( vright_ref.v );
948     }
949     else if ( ORIENTATION_V == pinst->orientation )
950     {
951         // Using just the global up vector here is too harsh.
952         // Smoothly interpolate the global up vector with the camera up vector
953         // so that when the camera is looking straight down, the billboard's plane
954         // is turned by 45 degrees to the camera (instead of 90 degrees which is invisible)
955 
956         float weight;
957         fvec3_t vup_cam;
958 
959         // use the camera up vector
960         vup_cam = pcam->vup;
961 
962         // use the global up vector
963         vup.x = vup.y = 0;
964         vup.z = 1;
965 
966         // adjust the vector so that the particle doesn't disappear if
967         // you are viewing it from from the top or the bottom
968         weight = 1.0f - ABS( vup_cam.z );
969         if ( vup_cam.z < 0 ) weight *= -1;
970 
971         vup.x = vup.x + weight * vup_cam.x;
972         vup.y = vup.y + weight * vup_cam.y;
973         vup.z = vup.z + weight * vup_cam.z;
974         vup = fvec3_normalize( vup.v );
975 
976         // get the correct "right" vector
977         vright = fvec3_cross_product( vfwd.v, vup.v );
978         vright = fvec3_normalize( vright.v );
979 
980         vright_ref = fvec3_cross_product( vfwd.v, vup_ref.v );
981         vright_ref = fvec3_normalize( vright_ref.v );
982 
983         vup_ref    = vup;
984         vright_ref = fvec3_cross_product( vfwd_ref.v, vup.v );
985         vright_ref = fvec3_normalize( vright_ref.v );
986     }
987     else if ( ORIENTATION_H == pinst->orientation )
988     {
989         vup.x = vup.y = 0;
990         vup.z = 1;
991 
992         // force right to be horizontal
993         vright = fvec3_cross_product( vfwd.v, vup.v );
994 
995         // force "up" to be close to the camera forward, but horizontal
996         vup = fvec3_cross_product( vup.v, vright.v );
997         vup_ref = fvec3_cross_product( vup.v, vright_ref.v );
998 
999         // normalize them
1000         vright = fvec3_normalize( vright.v );
1001         vup    = fvec3_normalize( vup.v );
1002 
1003         vright_ref = vright;
1004         vup_ref    = vup;
1005     }
1006     else if ( INGAME_CHR( pprt->attachedto_ref ) )
1007     {
1008         chr_instance_t * cinst = chr_get_pinstance( pprt->attachedto_ref );
1009 
1010         if ( chr_matrix_valid( ChrList.lst + pprt->attachedto_ref ) )
1011         {
1012             // use the character matrix to orient the particle
1013             // assume that the particle "up" is in the z-direction in the object's
1014             // body fixed axes. should work for the gonnes & such
1015 
1016             switch ( pinst->orientation )
1017             {
1018                 case ORIENTATION_X: mat_getChrForward( cinst->matrix.v, vup.v ); break;
1019                 case ORIENTATION_Y: mat_getChrRight( cinst->matrix.v, vup.v ); break;
1020 
1021                 default:
1022                 case ORIENTATION_Z: mat_getChrUp( cinst->matrix.v, vup.v ); break;
1023             }
1024 
1025             fvec3_self_normalize( vup.v );
1026         }
1027         else
1028         {
1029             // use the camera directions?
1030             switch ( pinst->orientation )
1031             {
1032                 case ORIENTATION_X: vup = pcam->vfw; break;
1033                 case ORIENTATION_Y: vup = pcam->vrt; break;
1034 
1035                 default:
1036                 case ORIENTATION_Z: vup = pcam->vup; break;
1037             }
1038         }
1039 
1040         vup = fvec3_normalize( vup.v );
1041 
1042         // get the correct "right" vector
1043         vright = fvec3_cross_product( vfwd.v, vup.v );
1044         vright = fvec3_normalize( vright.v );
1045 
1046         vup_ref    = vup;
1047         vright_ref = fvec3_cross_product( vfwd_ref.v, vup.v );
1048         vright_ref = fvec3_normalize( vright_ref.v );
1049     }
1050     else
1051     {
1052         // use the camera up vector
1053         vup = pcam->vup;
1054         vup = fvec3_normalize( vup.v );
1055 
1056         // get the correct "right" vector
1057         vright = fvec3_cross_product( vfwd.v, vup.v );
1058         vright = fvec3_normalize( vright.v );
1059 
1060         vup_ref    = vup;
1061         vright_ref = fvec3_cross_product( vfwd_ref.v, vup.v );
1062         vright_ref = fvec3_normalize( vright_ref.v );
1063     }
1064 
1065     // calculate the actual vectors using the particle rotation
1066     if ( 0 == pprt->rotate )
1067     {
1068         pinst->up    = vup;
1069         pinst->right = vright;
1070 
1071         pinst->ref_up    = vup_ref;
1072         pinst->ref_right = vright_ref;
1073     }
1074     else
1075     {
1076         float  sinval, cosval;
1077         TURN_T turn = TO_TURN( pprt->rotate );
1078 
1079         cosval = turntocos[ turn ];
1080         sinval = turntosin[ turn ];
1081 
1082         pinst->up.x    = vup.x * cosval - vright.x * sinval;
1083         pinst->up.y    = vup.y * cosval - vright.y * sinval;
1084         pinst->up.z    = vup.z * cosval - vright.z * sinval;
1085 
1086         pinst->right.x = vup.x * sinval + vright.x * cosval;
1087         pinst->right.y = vup.y * sinval + vright.y * cosval;
1088         pinst->right.z = vup.z * sinval + vright.z * cosval;
1089 
1090         pinst->ref_up.x    = vup_ref.x * cosval - vright_ref.x * sinval;
1091         pinst->ref_up.y    = vup_ref.y * cosval - vright_ref.y * sinval;
1092         pinst->ref_up.z    = vup_ref.z * cosval - vright_ref.z * sinval;
1093 
1094         pinst->ref_right.x = vup_ref.x * sinval + vright_ref.x * cosval;
1095         pinst->ref_right.y = vup_ref.y * sinval + vright_ref.y * cosval;
1096         pinst->ref_right.z = vup_ref.z * sinval + vright_ref.z * cosval;
1097     }
1098 
1099     // calculate the billboard normal
1100     pinst->nrm = fvec3_cross_product( pinst->right.v, pinst->up.v );
1101 
1102     // flip the normal so that the front front of the quad is toward the camera
1103     if ( fvec3_dot_product( vfwd.v, pinst->nrm.v ) < 0 )
1104     {
1105         pinst->nrm.x *= -1;
1106         pinst->nrm.y *= -1;
1107         pinst->nrm.z *= -1;
1108     }
1109 
1110     // Now we have to calculate the mirror-like reflection of the particles
1111     // this was a bit hard to figure. What happens is that the components of the
1112     // up and right vectors that are in the plane of the quad and closest to the world up are reversed.
1113     //
1114     // This is easy to think about in a couple of examples:
1115     // 1) If the quad is like a picture frame then whatever component (up or right)
1116     //    that actually points in the wodld up direction is reversed.
1117     //    This corresponds to the case where zdot == +/- 1 in the code below
1118     //
1119     // 2) If the particle is like a rug, then basically nothing happens since
1120     //    neither the up or right vectors point in the world up direction.
1121     //    This corresponds to 0 == ndot in the code below.
1122     //
1123     // This process does not affect the normal the length of the vector, or the
1124     // direction of the normal to the quad.
1125 
1126     {
1127         float zdot;  // the dot product between the up or the right vector and the world up
1128         float ndot;  // the dot product between either the up or the right vector
1129         float factor;
1130         fvec3_t world_up;
1131 
1132         // the normal sense of "up"
1133         world_up.x = world_up.y = 0;
1134         world_up.z = 1.0f;
1135 
1136         // the following statement could be optimized
1137         // since we know the only non-zero component of world_up is z
1138         ndot = fvec3_dot_product( pinst->nrm.v, world_up.v );
1139 
1140         // do nothing if the quad is basically horizontal
1141         if ( ndot < 1.0f - 1e-6 )
1142         {
1143             //---- do the right vector first
1144             {
1145                 // the following statement could be optimized
1146                 // since we know the only non-zero component of world_up is z
1147                 zdot = fvec3_dot_product( pinst->ref_right.v, world_up.v );
1148 
1149                 if ( ABS( zdot ) > 1e-6 )
1150                 {
1151                     factor = zdot / ( 1.0f - ndot * ndot );
1152 
1153                     pinst->ref_right.x += 2.0f * factor * ( ndot * pinst->nrm.x - world_up.x );
1154                     pinst->ref_right.y += 2.0f * factor * ( ndot * pinst->nrm.y - world_up.y );
1155                     pinst->ref_right.z += 2.0f * factor * ( ndot * pinst->nrm.z - world_up.z );
1156                 }
1157             }
1158 
1159             //---- do the up vector second
1160             {
1161                 // the following statement could be optimized
1162                 // since we know the only non-zero component of world_up is z
1163                 zdot = fvec3_dot_product( pinst->ref_up.v, world_up.v );
1164 
1165                 if ( ABS( zdot ) > 1e-6 )
1166                 {
1167                     factor = zdot / ( 1.0f - ndot * ndot );
1168 
1169                     pinst->ref_up.x += 2.0f * factor * ( ndot * pinst->nrm.x - world_up.x );
1170                     pinst->ref_up.y += 2.0f * factor * ( ndot * pinst->nrm.y - world_up.y );
1171                     pinst->ref_up.z += 2.0f * factor * ( ndot * pinst->nrm.z - world_up.z );
1172                 }
1173             }
1174         }
1175     }
1176 
1177     // set some particle dependent properties
1178     pinst->scale = prt_get_scale( pprt );
1179     pinst->size  = FP8_TO_FLOAT( pprt->size ) * pinst->scale;
1180 
1181     // this instance is now completely valid
1182     pinst->valid     = btrue;
1183     pinst->ref_valid = btrue;
1184 
1185     return gfx_success;
1186 }
1187 
1188 //--------------------------------------------------------------------------------------------
prt_instance_make_matrix(prt_instance_t * pinst)1189 fmat_4x4_t prt_instance_make_matrix( prt_instance_t * pinst )
1190 {
1191     fmat_4x4_t mat = IdentityMatrix();
1192 
1193     mat.CNV( 0, 1 ) = -pinst->up.x;
1194     mat.CNV( 1, 1 ) = -pinst->up.y;
1195     mat.CNV( 2, 1 ) = -pinst->up.z;
1196 
1197     mat.CNV( 0, 0 ) = pinst->right.x;
1198     mat.CNV( 1, 0 ) = pinst->right.y;
1199     mat.CNV( 2, 0 ) = pinst->right.z;
1200 
1201     mat.CNV( 0, 2 ) = pinst->nrm.x;
1202     mat.CNV( 1, 2 ) = pinst->nrm.y;
1203     mat.CNV( 2, 2 ) = pinst->nrm.z;
1204 
1205     return mat;
1206 }
1207 
1208 //--------------------------------------------------------------------------------------------
prt_instance_update_lighting(prt_instance_t * pinst,prt_t * pprt,Uint8 trans,bool_t do_lighting)1209 gfx_rv prt_instance_update_lighting( prt_instance_t * pinst, prt_t * pprt, Uint8 trans, bool_t do_lighting )
1210 {
1211     Uint32 alpha;
1212     Sint16  self_light;
1213     lighting_cache_t global_light, loc_light;
1214     float amb, dir;
1215     fmat_4x4_t mat;
1216 
1217     if ( NULL == pinst )
1218     {
1219         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL instance" );
1220         return gfx_error;
1221     }
1222     if ( NULL == pprt )
1223     {
1224         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL particle" );
1225         return gfx_error;
1226     }
1227 
1228     // To make life easier
1229     alpha = trans;
1230 
1231     // interpolate the lighting for the origin of the object
1232     grid_lighting_interpolate( PMesh, &global_light, pinst->pos.x, pinst->pos.y );
1233 
1234     // rotate the lighting data to body_centered coordinates
1235     mat = prt_instance_make_matrix( pinst );
1236     lighting_project_cache( &loc_light, &global_light, mat );
1237 
1238     // determine the normal dependent amount of light
1239     lighting_evaluate_cache( &loc_light, pinst->nrm.v, pinst->pos.z, PMesh->tmem.bbox, &amb, &dir );
1240 
1241     // LIGHT-blended sprites automatically glow. ALPHA-blended and SOLID
1242     // sprites need to convert the light channel into additional alpha
1243     // lighting to make them "glow"
1244     self_light = 0;
1245     if ( SPRITE_LIGHT != pinst->type )
1246     {
1247         self_light  = ( 255 == pinst->light ) ? 0 : pinst->light;
1248     }
1249 
1250     // determine the ambient lighting
1251     pinst->famb = 0.9f * pinst->famb + 0.1f * ( self_light + amb );
1252     pinst->fdir = 0.9f * pinst->fdir + 0.1f * dir;
1253 
1254     // determine the overall lighting
1255     pinst->fintens = pinst->fdir * INV_FF;
1256     if ( do_lighting )
1257     {
1258         pinst->fintens += pinst->famb * INV_FF;
1259     }
1260     pinst->fintens = CLIP( pinst->fintens, 0.0f, 1.0f );
1261 
1262     // determine the alpha component
1263     pinst->falpha = ( alpha * INV_FF ) * ( pinst->alpha * INV_FF );
1264     pinst->falpha = CLIP( pinst->falpha, 0.0f, 1.0f );
1265 
1266     return gfx_success;
1267 }
1268 
1269 //--------------------------------------------------------------------------------------------
prt_instance_update(camera_t * pcam,const PRT_REF particle,Uint8 trans,bool_t do_lighting)1270 gfx_rv prt_instance_update( camera_t * pcam, const PRT_REF particle, Uint8 trans, bool_t do_lighting )
1271 {
1272     prt_t          * pprt;
1273     prt_instance_t * pinst;
1274     gfx_rv        retval;
1275 
1276     if ( !DISPLAY_PRT( particle ) )
1277     {
1278         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, particle, "invalid particle" );
1279         return gfx_error;
1280     }
1281     pprt = PrtList.lst + particle;
1282     pinst = &( pprt->inst );
1283 
1284     // assume the best
1285     retval = gfx_success;
1286 
1287     // make sure that the vertices are interpolated
1288     if ( gfx_error == prt_instance_update_vertices( pcam, pinst, pprt ) )
1289     {
1290         retval = gfx_error;
1291     }
1292 
1293     // do the lighting
1294     if ( gfx_error == prt_instance_update_lighting( pinst, pprt, trans, do_lighting ) )
1295     {
1296         retval = gfx_error;
1297     }
1298 
1299     return retval;
1300 }
1301 
1302 //--------------------------------------------------------------------------------------------
render_prt_bbox(prt_bundle_t * pbdl_prt)1303 void render_prt_bbox( prt_bundle_t * pbdl_prt )
1304 {
1305     prt_t * loc_pprt;
1306     pip_t * loc_ppip;
1307 
1308     if ( NULL == pbdl_prt || NULL == pbdl_prt->prt_ptr ) return;
1309     loc_pprt = pbdl_prt->prt_ptr;
1310     loc_ppip = pbdl_prt->pip_ptr;
1311 
1312     // only draw bullets
1313     //if ( 50 != loc_ppip->vel_hrz_pair.base ) return;
1314 
1315     if ( !DISPLAY_PPRT( loc_pprt ) ) return;
1316 
1317     // draw the object bounding box as a part of the graphics debug mode F7
1318     if (( cfg.dev_mode && SDLKEYDOWN( SDLK_F7 ) ) || single_frame_mode )
1319     {
1320         oct_bb_t loc_bb, tmp_bb, exp_bb;
1321 
1322         // copy the bounding volume
1323         oct_bb_copy( &tmp_bb, &( loc_pprt->prt_max_cv ) );
1324 
1325         // make sure that it has some minimum extent
1326         //for(cnt = 0; cnt < OCT_COUNT; cnt++ )
1327         //{
1328         //    tmp_bb.mins[cnt] = MIN( tmp_bb.mins[cnt], -1 );
1329         //    tmp_bb.maxs[cnt] = MAX( tmp_bb.mins[cnt],  1 );
1330         //}
1331 
1332         // determine the expanded collision volumes for both objects
1333         phys_expand_oct_bb( &tmp_bb, loc_pprt->vel.v, 0, 1, &exp_bb );
1334 
1335         // shift the source bounding boxes to be centered on the given positions
1336         oct_bb_add_fvec3( &exp_bb, loc_pprt->pos.v, &loc_bb );
1337 
1338         GL_DEBUG( glDisable )( GL_TEXTURE_2D );
1339         {
1340             GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
1341             render_oct_bb( &loc_bb, btrue, btrue );
1342         }
1343         GL_DEBUG( glEnable )( GL_TEXTURE_2D );
1344     }
1345 }