diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
| commit | c068f22329d5cc722622a2183bbb22eef2093df7 (patch) | |
| tree | 12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec/part_ani.cpp | |
| download | openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.tar.xz openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.zip | |
Diffstat (limited to 'src/parsec/part_ani.cpp')
| -rw-r--r-- | src/parsec/part_ani.cpp | 1065 |
1 files changed, 1065 insertions, 0 deletions
diff --git a/src/parsec/part_ani.cpp b/src/parsec/part_ani.cpp new file mode 100644 index 0000000..26aa396 --- /dev/null +++ b/src/parsec/part_ani.cpp @@ -0,0 +1,1065 @@ +/* + * PARSEC - Behavior and Appearance Animation + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:36 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1997-2000 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "net_defs.h" + +// mathematics header +#include "utl_math.h" + +// particle types +#include "parttype.h" + +// local module header +#include "part_ani.h" + +// proprietary module headers +#include "obj_ctrl.h" +#include "obj_expl.h" +#include "obj_game.h" +#include "part_api.h" +#include "g_sfx.h" +#include "part_sys.h" +#include "g_wfx.h" + + +// flags +//#define ENERGYFIELD_DESTRUCTION_MESSAGE +#define DO_BRUTEFORCE_CDETECTION // do collision detection for lightning +#define PROTECT_ANIM_CLUSTER_WALK + + + +// lightning segment deviation/variation +#define MAX_LIGHTNING_SEG_DEVIATION 1120 +#define LIGHTNING_SEG_DEVIATION_FAC 0x10 + +// reference z of lightning impact particles +#define IMPACT_PARTICLES_REF_Z 3.6f + + + +// reference z values for particles ------------------------------------------- +// +float impact_particles_ref_z = 1.0f; + + +// init reference z values for particles according to resolution -------------- +// +void PAN_InitParticleSizes( float resoscale ) +{ + impact_particles_ref_z = resoscale * IMPACT_PARTICLES_REF_Z; +} + + +// disable a single particle in a specified cluster --------------------------- +// +PRIVATE +void DisableParticle( pcluster_s *cluster, int pid ) +{ + //NOTE: + // individual particle lifetime is currently only + // used by linear and customdraw particles. all other + // clusters maintain only wholesale cluster lifetime. + + ASSERT( cluster != NULL ); + ASSERT( ( ( cluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ) || + ( ( cluster->type & CT_TYPEMASK ) == CT_CUSTOMDRAW ) ); + + cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; +} + + +// brute force collision detection of linear particle with all ships ---------- +// +void LinearParticleCollision( linear_pcluster_s *cluster, int pid ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( pid >= 0 ) && ( pid < cluster->numel ) ); + ASSERT( cluster->rep[ pid ].flags & PARTICLE_COLLISION ); + + //NOTE: + // this is set as callback by PART_WFX::MaintainHelix() + // to detect collisions of helix particles. + + int owner = cluster->rep[ pid ].owner; + + // check local ship + if ( ( owner != LocalPlayerId ) && NetJoined && + PRT_ParticleInBoundingSphere( MyShip, cluster->rep[ pid ].position ) ) { + + // disable particle + cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; + + //TODO: + // detect hull impact + + OBJ_EventShipImpact( MyShip, TRUE ); + + switch ( cluster->rep[ pid ].flags & PARTICLE_IS_MASK ) { + + case PARTICLE_IS_HELIX : + OBJ_ShipHelixDamage( MyShip, owner ); + break; + case PARTICLE_IS_PHOTON : + OBJ_ShipPhotonDamage( MyShip, owner ); + break; + default: + break; + } + return; + } + + // check shiplist + ShipObject *walkships = FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( NetConnected && ( GetObjectOwner( walkships ) == (dword)owner ) ) + continue; + + if ( !PRT_ParticleInBoundingSphere( walkships, cluster->rep[ pid ].position ) ) + continue; + + // disable particle + cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; + + //TODO: + // detect hull impact + + OBJ_EventShipImpact( walkships, TRUE ); + + switch ( cluster->rep[ pid ].flags & PARTICLE_IS_MASK ) { + + case PARTICLE_IS_HELIX : + OBJ_ShipHelixDamage( walkships, owner ); + break; + case PARTICLE_IS_PHOTON : + OBJ_ShipPhotonDamage( walkships, owner ); + break; + default: + break; + } + } +} + + +// calculate position advance of constant velocity particles ------------------ +// +PRIVATE +void CalcConstantVelocityAnimation( linear_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ); + + // advance all particles in cluster along their + // local velocity vectors and maintain lifetime + int numactive = 0; + for ( int curp = 0; curp < cluster->numel; curp++ ) { + + // skip already inactive particles + if ( ( cluster->rep[ curp ].flags & PARTICLE_ACTIVE ) == 0 ) { + continue; + } else { + numactive++; + } + + // maintain lifetime + if ( ( cluster->rep[ curp ].lifetime -= CurScreenRefFrames ) < 0 ) + DisableParticle( cluster, curp ); + + // move along vector + Vector3 advvec; + advvec.X = cluster->rep[ curp ].velocity.X * CurScreenRefFrames; + advvec.Y = cluster->rep[ curp ].velocity.Y * CurScreenRefFrames; + advvec.Z = cluster->rep[ curp ].velocity.Z * CurScreenRefFrames; + + cluster->rep[ curp ].position.X += advvec.X; + cluster->rep[ curp ].position.Y += advvec.Y; + cluster->rep[ curp ].position.Z += advvec.Z; + + // invoke callback for cluster/particle + if ( cluster->rep[ curp ].flags & PARTICLE_COLLISION ) { + if ( cluster->callback != NULL ) { + (*cluster->callback)( cluster, curp ); + } + } + } + + // remove cluster if no active particles contained anymore + if ( numactive == 0 ) { + PRT_DeleteCluster( cluster ); + } +} + + +// calc init position for particle stochastically placed on spherical surface - +// +void CalcSphereParticlePosition( Vertex3& position, geomv_t radius, int spheretype ) +{ + Vertex3 sphereloc; + sphereloc.X = 0; + sphereloc.Y = 0; + sphereloc.Z = radius; + + Xmatrx rotmatrx; + MakeIdMatrx( rotmatrx ); + + if ( spheretype == SAT_SPHERETYPE_NORMAL ) { + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + bams_t yaw = RAND() * 2 - BAMS_DEG180; + + CamRotX( rotmatrx, pitch ); + CamRotY( rotmatrx, yaw ); + + } else if ( spheretype == SAT_SPHERETYPE_DISC ) { + + int r = ( RAND() >> 10 ) & 1; + + if ( r == 0 ) { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x0c0000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } else { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x150000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } + + } else if ( spheretype == SAT_SPHERETYPE_DISCWITHCORE ) { + + int r = RAND() & 3; + + if ( r == 0 ) { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x0c0000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } else if ( r == 1 ) { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x150000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } else { + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + bams_t yaw = RAND() * 2 - BAMS_DEG180; + + CamRotX( rotmatrx, pitch ); + CamRotY( rotmatrx, yaw ); + } + + } + + MtxVctMUL( rotmatrx, &sphereloc, &position ); +} + + +// calc rotation of sphere particle ------------------------------------------- +// +void CalcSphereParticleRotation( Vertex3& position, bams_t pitch, bams_t yaw, bams_t roll ) +{ + Xmatrx rotmatrx; + MakeIdMatrx( rotmatrx ); + CamRotX( rotmatrx, pitch ); + CamRotY( rotmatrx, yaw ); + CamRotZ( rotmatrx, roll ); + + Vertex3 rotpos; + MtxVctMUL( rotmatrx, &position, &rotpos ); + position = rotpos; +} + + +// calc explosion trajectory of sphere particle ------------------------------- +// +PRIVATE +void CalcSphereParticleExplosion( Vertex3& position, geomv_t speed ) +{ + Vector3 vec = position; + NormVctX( &vec ); + + position.X += GEOMV_MUL( vec.X, speed ); + position.Y += GEOMV_MUL( vec.Y, speed ); + position.Z += GEOMV_MUL( vec.Z, speed ); +} + + +// calc contraction of sphere ------------------------------------------------- +// +int CalcSphereContraction( Vertex3& position, geomv_t speed ) +{ + // check for null vector + geomv_t vx = position.X; + geomv_t vy = position.Y; + geomv_t vz = position.Z; + + ABS_GEOMV( vx ); + ABS_GEOMV( vy ); + ABS_GEOMV( vz ); + + if ( ( vx <= GEOMV_VANISHING ) && + ( vy <= GEOMV_VANISHING ) && + ( vz <= GEOMV_VANISHING ) ) { + return TRUE; + } + + // create direction unit-vector + Vector3 vec = position; + NormVctX( &vec ); + + int sign_x = ( position.X < 0 ) ? -1 : 1; + int sign_y = ( position.Y < 0 ) ? -1 : 1; + int sign_z = ( position.Z < 0 ) ? -1 : 1; + + position.X -= GEOMV_MUL( vec.X, speed ); + position.Y -= GEOMV_MUL( vec.Y, speed ); + position.Z -= GEOMV_MUL( vec.Z, speed ); + + int asign_x = ( position.X < 0 ) ? -1 : 1; + int asign_y = ( position.Y < 0 ) ? -1 : 1; + int asign_z = ( position.Z < 0 ) ? -1 : 1; + + int collapsed = FALSE; + + if ( sign_x != asign_x ) collapsed = TRUE; + if ( sign_y != asign_y ) collapsed = TRUE; + if ( sign_z != asign_z ) collapsed = TRUE; + + return collapsed; +} + + +// calculate pulsating sphere ------------------------------------------------- +// +PRIVATE +void CalcSpherePulse( Vertex3& position, Vertex3& pulsebase, geomv_t pulseval ) +{ + // create direction unit-vector + Vector3 vec = pulsebase; + NormVctX( &vec ); + + // pulseval is a sine or cosine function and determines + // the deviation from the base position (radius) + position.X = pulsebase.X + GEOMV_MUL( vec.X, pulseval ); + position.Y = pulsebase.Y + GEOMV_MUL( vec.Y, pulseval ); + position.Z = pulsebase.Z + GEOMV_MUL( vec.Z, pulseval ); +} + + +// calc animation of ship centered particle sphere ---------------------------- +// +PRIVATE +void CalcObjectCenteredSphereAnimation( basesphere_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_OBJECTCENTERED_SPHERE ); + + if ( cluster->lifetime > 0 ) { + + cluster->lifetime -= CurScreenRefFrames; + + // handle optional particle auto-depletion + if ( cluster->animtype & SAT_AUTO_DEPLETE_PARTICLES ) { + + ASSERT( cluster->lifetime <= cluster->max_life ); + float strength = (float)cluster->lifetime / cluster->max_life; + + int maxnumel = cluster->maxnumel; + if ( cluster->animtype & SAT_NEEDS_REFCOORDS_MASK ) + maxnumel /= 2; + int numel = (int)(maxnumel * strength); + if ( numel < cluster->maxnumel ) { + cluster->numel = numel; + } + } + + // perform basic animation + switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_ROTATING: + { + bams_t pitch = cluster->rot.pitch * CurScreenRefFrames; + bams_t yaw = cluster->rot.yaw * CurScreenRefFrames; + bams_t roll = cluster->rot.roll * CurScreenRefFrames; + + for ( int pid = 0; pid < cluster->numel; pid++ ) + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + break; + + case SAT_STOCHASTIC_MOTION: + if ( ( cluster->rand.fcount -= CurScreenRefFrames ) < 0 ) { + cluster->rand.fcount = cluster->rand.speed; + + geomv_t radius = cluster->rand.radius; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticlePosition( cluster->rep[ pid ].position, + radius, SAT_SPHERETYPE_NORMAL ); + } + } + break; + } + + } else { + + switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { + + default: + + if ( cluster->animtype & SAT_RESET_MEGASHIELD_FLAG ) { + ASSERT( cluster->baseobject != NULL ); + if ( OBJECT_TYPE_SHIP( cluster->baseobject ) ) { + ShipObject *baseship = (ShipObject *) cluster->baseobject; + baseship->Specials &= ~SPMASK_INVULNERABILITY; + baseship->MegaShieldAbsorption = 0; + } + } + + PRT_DeleteCluster( cluster ); + } + } +} + + +// calc animation of autonomous particle sphere ------------------------------- +// +PRIVATE +void CalcSphereObjectAnimation( sphereobj_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_PARTICLE_SPHERE ); + + if ( cluster->lifetime > 0 ) { + + refframe_t refframes = CurScreenRefFrames; + if ( ( cluster->lifetime -= refframes ) < 0 ) { + refframes += cluster->lifetime; + } + + // handle optional particle auto-depletion + if ( cluster->animtype & SAT_AUTO_DEPLETE_PARTICLES ) { + + ASSERT( cluster->lifetime <= cluster->max_life ); + float strength = (float)cluster->lifetime / cluster->max_life; + + int maxnumel = cluster->maxnumel; + if ( cluster->animtype & SAT_NEEDS_REFCOORDS_MASK ) + maxnumel /= 2; + int numel = (int)(maxnumel * strength); + if ( numel < cluster->maxnumel ) { + cluster->numel = numel; + } + } + + // perform basic animation + switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_ROTATING: + { + bams_t pitch = cluster->rot.pitch * CurScreenRefFrames; + bams_t yaw = cluster->rot.yaw * CurScreenRefFrames; + bams_t roll = cluster->rot.roll * CurScreenRefFrames; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + } + break; + + case SAT_EXPLODING: + { + geomv_t speed = cluster->expl.speed * refframes; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleExplosion( cluster->rep[ pid ].position, + speed ); + } + // update cluster radius + cluster->bdsphere += speed; + } + break; + + case SAT_PULSATING: + { + sincosval_s sincosv; + GetSinCos( cluster->puls.current_t, &sincosv ); + + geomv_t pulseval = GEOMV_MUL( sincosv.sinval, cluster->puls.amplitude ); + + bams_t pitch = cluster->puls.pitch * CurScreenRefFrames; + bams_t yaw = cluster->puls.yaw * CurScreenRefFrames; + bams_t roll = cluster->puls.roll * CurScreenRefFrames; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid + cluster->numel ].position, + pitch, yaw, roll ); + CalcSpherePulse( cluster->rep[ pid ].position, + cluster->rep[ pid + cluster->numel ].position, + pulseval ); + } + // update cluster radius + cluster->bdsphere = cluster->puls.midradius + pulseval; + + cluster->puls.current_t += cluster->puls.frequency * CurScreenRefFrames; + } + break; + + case SAT_CONTRACTING: + { + bams_t pitch = cluster->cont.pitch * CurScreenRefFrames; + bams_t yaw = cluster->cont.yaw * CurScreenRefFrames; + bams_t roll = cluster->cont.roll * CurScreenRefFrames; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + + if ( cluster->cont.expandtime > 0 ) { + cluster->cont.expandtime -= CurScreenRefFrames; + + geomv_t contraction = cluster->cont.speed * CurScreenRefFrames; + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereContraction( cluster->rep[ pid ].position, -contraction ); + } + // update cluster radius + cluster->bdsphere += contraction; + } + + } + break; + } + + } else { + + // animation type-dependent behavior after lifetime is spent + switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_CONTRACTING: + { + bams_t pitch = cluster->cont.pitch * CurScreenRefFrames; + bams_t yaw = cluster->cont.yaw * CurScreenRefFrames; + bams_t roll = cluster->cont.roll * CurScreenRefFrames; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + int delit = 0; + + { + geomv_t contraction = cluster->cont.speed * CurScreenRefFrames; + for ( int pid = 0; pid < cluster->numel; pid++ ) { + delit += CalcSphereContraction( cluster->rep[ pid ].position, contraction ); + } + // update cluster radius + cluster->bdsphere -= contraction; + } + + if ( delit ) { + + if ( cluster->animtype & SAT_DECREMENT_EXTRA_COUNTER ) { + CurrentNumPrtExtras--; + +#ifdef ENERGYFIELD_DESTRUCTION_MESSAGE + ShowMessage( "extracount decreased" ); +#endif + } + + PRT_DeleteCluster( cluster ); + } + + } + break; + + default: + PRT_DeleteCluster( cluster ); + } + } +} + + +// visualize impact of lightning beams onto protective hull ------------------- +// +INLINE +void VisualizeLightningImpact( ShipObject *shippo, Vertex3& impactpoint ) +{ + ASSERT( shippo != NULL ); + + pdrwinfo_s drawinfo; + drawinfo.bmindx = SPHERE_BM_INDX; + drawinfo.pcolor = SPHERE_PARTICLE_COLOR; + drawinfo.ref_z = impact_particles_ref_z; + drawinfo.extinfo = NULL; + drawinfo.sizebnd = partbitmap_size_bound; + + sphereobj_pcluster_s *cluster = + PRT_CreateParticleSphereObject( impactpoint, + FLOAT_TO_GEOMV( 2.5 ), + SAT_STOCHASTIC_MOTION, + 30, 20, + &drawinfo, + GetObjectOwner( shippo ) ); +} + + +// brute force collision detection of lightning particle with all ships ------- +// +PRIVATE +int CheckLightningParticleShipCollision( Vertex3& particlepos, int owner ) +{ + // check local ship + if ( ( owner != LocalPlayerId ) && NetJoined && + PRT_ParticleInBoundingSphere( MyShip, particlepos ) ) { + + if ( MyShip->MegaShieldAbsorption == 0 ) + SetScreenWhite = 20 * 16; //FIXME: nicht fix einstellen!! + + //TODO: + // detect hull impact + + OBJ_EventShipImpact( MyShip, TRUE ); + VisualizeLightningImpact( MyShip, particlepos ); + OBJ_ShipLightningDamage( MyShip, owner ); + + return TRUE; + } + + // check shiplist + ShipObject *walkships = FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( NetConnected && ( GetObjectOwner( walkships ) == (dword)owner ) ) + continue; + + if ( !PRT_ParticleInBoundingSphere( walkships, particlepos ) ) + continue; + + //TODO: + // detect hull impact + + OBJ_EventShipImpact( walkships, TRUE ); + VisualizeLightningImpact( walkships, particlepos ); + OBJ_ShipLightningDamage( walkships, owner ); + + return TRUE; + } + + return FALSE; +} + + +// reevaluate positions of all particles comprising a single lightning beam --- +// +PRIVATE +void SetLightningParticlePosition( lightning_pcluster_s *cluster, particle_s particles[], Vertex3& current, Xmatrx tmatrx ) +{ + ASSERT( cluster != NULL ); + + // set position of initial particle + particles[ 0 ].position.X = current.X; + particles[ 0 ].position.Y = current.Y; + particles[ 0 ].position.Z = current.Z; + + // iteratively calculate position of subsequent particles + for ( int pid = 1; pid < LIGHTNING_LENGTH; pid++ ) { + + Vertex3 deviation; + deviation.X = FIXED_TO_GEOMV( + ( (long)( RAND() % MAX_LIGHTNING_SEG_DEVIATION ) - + MAX_LIGHTNING_SEG_DEVIATION / 2 ) * + LIGHTNING_SEG_DEVIATION_FAC ); + deviation.Y = FIXED_TO_GEOMV( + ( (long)( RAND() % MAX_LIGHTNING_SEG_DEVIATION ) - + MAX_LIGHTNING_SEG_DEVIATION / 2 ) * + LIGHTNING_SEG_DEVIATION_FAC ); + deviation.Z = FLOAT_TO_GEOMV( 1.0 ); + + Vertex3 segvec; + MtxVctMUL( tmatrx, &deviation, &segvec ); + + current.X += segvec.X * 2; + current.Y += segvec.Y * 2; + current.Z += segvec.Z * 2; + + particles[ pid ].position.X = current.X; + particles[ pid ].position.Y = current.Y; + particles[ pid ].position.Z = current.Z; + +#ifdef DO_BRUTEFORCE_CDETECTION + + ASSERT( cluster->baseobject != NULL ); + + Vertex3 cpos; + MtxVctMUL( cluster->baseobject->ObjPosition, &particles[ pid ].position, &cpos ); + + if ( CheckLightningParticleShipCollision( cpos, particles[ pid ].owner ) ) { + while ( pid < LIGHTNING_LENGTH ) { + particles[ pid ].flags &= ~PARTICLE_ACTIVE; + pid++; + } + return; + } + + particles[ pid ].flags |= PARTICLE_ACTIVE; +#endif + + } +} + + +// animate lightning particles ------------------------------------------------ +// +PRIVATE +void CalcLightningAnimation( lightning_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_LIGHTNING ); + + if ( ( cluster->framecount -= CurScreenRefFrames ) < 0 ) { + cluster->framecount = cluster->sizzlespeed; + + GenObject *shippo = cluster->baseobject; + ASSERT( shippo != NULL ); + ASSERT( OBJECT_TYPE_SHIP( shippo ) ); + + Xmatrx tmatrx; +#if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) + MakeIdMatrx( tmatrx ); +#else + MakeNonTranslationMatrx( &shippo->ObjPosition, &tmatrx ); +#endif + + Vertex3 startpos; + +#if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) + startpos = cluster->beamstart1; +#else + MtxVctMUL( shippo->ObjPosition, &cluster->beamstart1, &startpos ); +#endif + SetLightningParticlePosition( cluster, cluster->rep, startpos, tmatrx ); + +#if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) + startpos = cluster->beamstart2; +#else + MtxVctMUL( shippo->ObjPosition, &cluster->beamstart2, &startpos ); +#endif + SetLightningParticlePosition( cluster, cluster->rep + LIGHTNING_LENGTH, startpos, tmatrx ); + } +} + + +// invoke callback function for animation of generic callback clusters -------- +// +INLINE +void CalcCallbackTrajectoryAnimation( callback_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CALLBACK_TRAJECTORY ); + ASSERT( cluster->callback != NULL ); + + // invoke callback function + if ( cluster->callback != NULL ) { + cluster->callback( cluster ); + } + + //NOTE: + // the callback function is invoked for the + // entire cluster, not single particles. +} + + +// animate customdraw particles ----------------------------------------------- +// +INLINE +void CalcCustomDrawAnimation( customdraw_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CUSTOMDRAW ); + + // invoke callback function + if ( cluster->callback != NULL ) + cluster->callback( cluster ); + + //NOTE: + // the callback function is invoked for the + // entire cluster, not single particles. + + // check if cluster should be removed + int numactive = 0; + for ( int curp = 0; curp < cluster->numel; curp++ ) { + + if ( ( cluster->rep[ curp ].flags & PARTICLE_ACTIVE ) == 0 ) + continue; + else + numactive++; + + // maintain lifetime + if ( ( cluster->rep[ curp ].lifetime -= CurScreenRefFrames ) < 0 ) + DisableParticle( cluster, curp ); + } + + // remove cluster if no active particles contained anymore + if ( numactive == 0 ) { + if ( CustomDrawCluster == cluster ) + CustomDrawCluster = NULL; + PRT_DeleteCluster( cluster ); + } +} + + +// animate genobject geometry particles --------------------------------------- +// +INLINE +void CalcGenObjectAnimation( genobject_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_GENOBJECT_PARTICLES ); + + // invoke callback function + if ( cluster->callback != NULL ) { + cluster->callback( cluster ); + } + + //NOTE: + // the callback function is invoked for the + // entire cluster, not single particles. + + //NOTE: + // there is no lifetime checking whatsoever. +} + + +// particle behavior animation: call trajectory animation function ------------ +// +INLINE +void AnimateClusterBehavior( pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + + // call trajectory animation function according to type identifier + switch ( cluster->type & CT_TYPEENUMERATIONMASK ) { + + case ( CT_CONSTANT_VELOCITY & CT_TYPEENUMERATIONMASK ): + CalcConstantVelocityAnimation( (linear_pcluster_s *) cluster ); + break; + + case ( CT_LIGHTNING & CT_TYPEENUMERATIONMASK ): + CalcLightningAnimation( (lightning_pcluster_s *) cluster ); + break; + + case ( CT_OBJECTCENTERED_SPHERE & CT_TYPEENUMERATIONMASK ): + CalcObjectCenteredSphereAnimation( (basesphere_pcluster_s *) cluster ); + break; + + case ( CT_PHOTON_SPHERE & CT_TYPEENUMERATIONMASK ): + // already called in G_SUPP::MaintainDurationWeapons() + // WFX_CalcPhotonSphereAnimation( (photon_sphere_pcluster_s *) cluster ); + break; + + case ( CT_PARTICLE_SPHERE & CT_TYPEENUMERATIONMASK ): + CalcSphereObjectAnimation( (sphereobj_pcluster_s *) cluster ); + break; + + case ( CT_CALLBACK_TRAJECTORY & CT_TYPEENUMERATIONMASK ): + CalcCallbackTrajectoryAnimation( (callback_pcluster_s *) cluster ); + break; + + case ( CT_CUSTOMDRAW & CT_TYPEENUMERATIONMASK ): + CalcCustomDrawAnimation( (customdraw_pcluster_s *) cluster ); + break; + + case ( CT_GENOBJECT_PARTICLES & CT_TYPEENUMERATIONMASK ): + CalcGenObjectAnimation( (genobject_pcluster_s *) cluster ); + break; + } +} + + +// particle appearance animation: perform texture animation ------------------- +// +INLINE +void AnimateClusterAppearance( pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + + //NOTE: + // if no particle in this cluster has an extinfo attached, + // CT_HINT_NO_APPEARANCE_ANIMATION should be specified to + // avoid the unnecessary overhead of checking all particles. + + // avoid unnecessary computations if homogeneous extinfo + int numanim = cluster->numel; + if ( numanim == 0 ) + return; + if ( cluster->type & CT_CLUSTER_GLOBAL_EXTINFO ) + numanim = 1; + + // process all particles + for ( int curp = 0; curp < numanim; curp++ ) { + + particle_s *particle = &cluster->rep[ curp ]; + if ( particle->extinfo != NULL ) { + + pextinfo_s *extinfo = particle->extinfo; + + pdef_s *pdef = extinfo->partdef; //TODO: partdef_dest + ASSERT( pdef != NULL ); + + // check texture-map frame + if ( ( extinfo->tex_time -= CurScreenRefFrames ) < 0 ) { + + if ( extinfo->tex_pos == pdef->tex_end ) { + // restart at repeat position + extinfo->tex_pos = pdef->tex_rep; + } else { + // advance to next table entry + extinfo->tex_pos++; + } + + // set deltatime for next frame + ASSERT( pdef->tex_table != NULL ); + texfrm_s *curtexframe = &pdef->tex_table[ extinfo->tex_pos ]; + extinfo->tex_time = curtexframe->deltatime; + } + + // trafo is optional + if ( pdef->xfo_table != NULL ) { + + // check texture-trafo frame + if ( ( extinfo->xfo_time -= CurScreenRefFrames ) < 0 ) { + + if ( extinfo->xfo_pos == pdef->xfo_end ) { + // restart at repeat position + extinfo->xfo_pos = pdef->xfo_rep; + } else { + // advance to next table entry + extinfo->xfo_pos++; + } + + // set deltatime for next frame + xfofrm_s *curxfoframe = &pdef->xfo_table[ extinfo->xfo_pos ]; + extinfo->xfo_time = curxfoframe->deltatime; + } + } + } + } +} + + +// do particle animation stuff ------------------------------------------------ +// +void PAN_AnimateParticles() +{ + //NOTE: + // even invisible (culled) clusters will be animated by + // this function. to alleviate the potential overhead + // involved several hints will be used to cut down on + // unnecessary computations. e.g., these hints indicate + // whether animation of invisible clusters is necessary + // for consistency of animation or if animation can be + // stalled during invisibility (e.g., rotating spheres). + // if an entire cluster consists of identical particles + // it is also possible to just animate the appearance + // of the first particle and reuse it for all others. + + // walk list of clusters + for ( pcluster_s *cluster = Particles->next; cluster->next; ) { + + // save next pointer to allow animation functions deletion of clusters + pcluster_s *nextcluster = cluster->next; + + //TODO: + // - use hints whether to stall animation if cluster invisible. + // CT_HINT_CULL_APPEARANCE_ANIMATION + // CT_HINT_CULL_POSITIONAL_ANIMATION + + // appearance animation: + // perform particle texture animation + if ( ( cluster->type & CT_HINT_NO_APPEARANCE_ANIMATION ) == 0 ) { + AnimateClusterAppearance( cluster ); + } + + // behavior animation: + // call trajectory animation function according to type identifier + if ( ( cluster->type & CT_HINT_NO_POSITIONAL_ANIMATION ) == 0 ) { + AnimateClusterBehavior( cluster ); + } + + // advance to next cluster (previously remembered) + cluster = nextcluster; + +#if defined( DEBUG ) && defined( PROTECT_ANIM_CLUSTER_WALK ) + + //NOTE: + // if an animation function has killed a cluster that was not + // the cluster the function has been called for, the pointer + // to the next cluster in the list may already be invalid. + + // assert that next cluster is still part of the list + pcluster_s *tcl; + for ( tcl = Particles->next; tcl; tcl = tcl->next ) + if ( tcl == cluster ) + break; + ASSERT( tcl != NULL ); + +#endif + + } +} + + + |
