From c068f22329d5cc722622a2183bbb22eef2093df7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 24 Aug 2026 11:16:07 +0200 Subject: initial import --- src/parsec/part_api.cpp | 1462 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1462 insertions(+) create mode 100644 src/parsec/part_api.cpp (limited to 'src/parsec/part_api.cpp') diff --git a/src/parsec/part_api.cpp b/src/parsec/part_api.cpp new file mode 100644 index 0000000..b218973 --- /dev/null +++ b/src/parsec/part_api.cpp @@ -0,0 +1,1462 @@ +/* + * PARSEC - API Functions + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:24 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger 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 +#include +#include +#include + +// 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 "aud_defs.h" +#include "net_defs.h" +#include "vid_defs.h" + +// mathematics header +#include "utl_math.h" + +// particle types +#include "parttype.h" + +// local module header +#include "part_api.h" + +// proprietary module headers +#include "con_aux.h" +#include "e_color.h" +#include "e_record.h" +#include "e_supp.h" +#include "obj_ctrl.h" +#include "part_ani.h" +#include "part_sys.h" + + + +// size of generic particle cluster +#define DEFAULT_CLUSTER_SIZE 256 + +// size of customdraw particle cluster +#define DEFAULT_CUSTOMDRAW_CLUSTER_SIZE 32 + +// size of genobject particle cluster +#define DEFAULT_GENOBJECT_CLUSTER_SIZE 128 + +// particle sphere rotation velocities +#define SPHERE_ROT_PITCH 0x0015 //0x01a0 +#define SPHERE_ROT_YAW -0x0022 //-0x02a0 +#define SPHERE_ROT_ROLL 0x000d //0x0100 + +// properties of pulsating sphere +#define SPHERE_PULSE_AMPLITUDE FLOAT_TO_GEOMV( 12.0f ) +#define SPHERE_PULSE_FREQUENCY 90 + +// properties of stochastic motion sphere +#define SPHERE_STOCHASTIC_MOTION_SPEED 100 + +// maximum number of registered particle definitions +#define MAX_PARTICLE_DEFS 256 + + +// array of registered particle definitions ----------------------------------- +// +PUBLIC int NumParticleDefinitions = 0; +PUBLIC pdefref_s ParticleDefinitions[ MAX_PARTICLE_DEFS ]; + + +// reference z values for particles ------------------------------------------- +// +float sphere_ref_z = 1.0f; + + +// init reference z values for particles according to resolution -------------- +// +void PRT_InitParticleSizes( float resoscale ) +{ + sphere_ref_z = resoscale * SPHERE_REF_Z; +} + + +// allocate new particle cluster and insert into list ------------------------- +// +pcluster_s * PRT_NewCluster( + dword type, // cluster type (CT_xx) + int numelements, // number of cluster elements (particles) + size_t auxstorage // size of user-defined storage in bytes +) +{ + ASSERT( numelements > 0 ); + + // calc size of cluster header according to type + int headsiz; + switch ( type & CT_TYPEMASK ) { + + case CT_CONSTANT_VELOCITY: + headsiz = sizeof( linear_pcluster_s ); + break; + + case CT_LIGHTNING: + headsiz = sizeof( lightning_pcluster_s ); + break; + + case CT_OBJECTCENTERED_SPHERE: + headsiz = sizeof( basesphere_pcluster_s ); + break; + + case CT_PHOTON_SPHERE: + headsiz = sizeof( photon_sphere_pcluster_s ); + break; + + case CT_PARTICLE_SPHERE: + headsiz = sizeof( sphereobj_pcluster_s ); + break; + + case CT_CALLBACK_TRAJECTORY: + headsiz = sizeof( callback_pcluster_s ); + break; + + case CT_CUSTOMDRAW: + headsiz = sizeof( customdraw_pcluster_s ); + break; + + case CT_GENOBJECT_PARTICLES: + headsiz = sizeof( genobject_pcluster_s ); + break; + + default: + PANIC( 0 ); + } + + pcluster_s *temp = (pcluster_s *) ALLOCMEM( headsiz ); + if ( temp == NULL ) + OUTOFMEM( 0 ); + + // clear cluster header (including inherited fields!) + memset( temp, 0, headsiz ); + + // insert cluster at head of cluster list + temp->next = Particles->next; + temp->prec = Particles; + temp->next->prec = temp; + temp->prec->next = temp; + + // size of basic particle structures + size_t storesiz = sizeof( particle_s ) * numelements; + + // size of extinfo if desired + if ( type & CT_EXTINFO_STORAGE ) + storesiz += sizeof( pextinfo_s ) * numelements; + + // size of auxiliary info + size_t auxofs = storesiz; + if ( auxstorage > 0 ) + storesiz += auxstorage + sizeof( pusrinfo_s ); + + // allocate storage for elements of cluster + temp->rep = (particle_s *) ALLOCMEM( storesiz ); + temp->type = type; + temp->numel = 0; + temp->maxnumel = numelements; + + ASSERT( temp->rep != NULL ); + if ( temp->rep == NULL ) + OUTOFMEM( 0 ); + + // init particle storage of cluster to zero + memset( temp->rep, 0, storesiz ); + + // init auxstorage header if allocated + if ( auxstorage > 0 ) { + pusrinfo_s *uinfo = (pusrinfo_s *) ( (char*)temp->rep + auxofs ); + + uinfo->infovalid = FALSE; + uinfo->blocksize = auxstorage; + + temp->userinfo = uinfo; + } + + // add to global maximum number of elements + Particles->maxnumel += numelements; + + // caller has to cast this pointer to its actual type! + return temp; +} + + +// delete entire particle cluster --------------------------------------------- +// +void PRT_DeleteCluster( pcluster_s* cluster ) +{ + ASSERT( cluster != NULL ); + + // maintain global cluster pointers + if ( CurLinearCluster == cluster ) + CurLinearCluster = NULL; + if ( CustomDrawCluster == cluster ) + CustomDrawCluster = NULL; + + // if cluster is object-relative remove from list if contained + if ( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ) + PRT_RemoveClusterFromAttachedList( (objectbase_pcluster_s *) cluster ); + + // sub from global current/maximum number of elements + Particles->numel -= cluster->numel; + Particles->maxnumel -= cluster->maxnumel; + + // unlink cluster from list + cluster->prec->next = cluster->next; + cluster->next->prec = cluster->prec; + + // free storage (cluster header and particle storage) + FREEMEM( cluster->rep ); + FREEMEM( cluster ); +} + + +// delete entire particle cluster (don't check any attachment lists) ---------- +// +void PRT_DeleteCluster_NoListRemoval( pcluster_s* cluster ) +{ + ASSERT( cluster != NULL ); + + // maintain global cluster pointers + if ( CurLinearCluster == cluster ) + CurLinearCluster = NULL; + if ( CustomDrawCluster == cluster ) + CustomDrawCluster = NULL; + + // sub from global current/maximum number of elements + Particles->numel -= cluster->numel; + Particles->maxnumel -= cluster->maxnumel; + + // unlink cluster from list + cluster->prec->next = cluster->next; + cluster->next->prec = cluster->prec; + + // free storage (cluster header and particle storage) + FREEMEM( cluster->rep ); + FREEMEM( cluster ); +} + + +// remove cluster from its attachment list if contained in any ---------------- +// +void PRT_RemoveClusterFromAttachedList( objectbase_pcluster_s* cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + ASSERT( cluster->baseobject != NULL ); + + objectbase_pcluster_s *scan = cluster->baseobject->AttachedPClusters; + objectbase_pcluster_s *precnode = NULL; + + for ( ; scan; scan = scan->attachlist ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + ASSERT( scan->baseobject == cluster->baseobject ); + + if ( scan == cluster ) { + + if ( precnode ) + precnode->attachlist = scan->attachlist; + else + cluster->baseobject->AttachedPClusters = scan->attachlist; + + return; + } + + precnode = scan; + } +} + + +// check if genobject has attached particle clusters -------------------------- +// +int PRT_ObjectHasAttachedClusters( GenObject* genobjpo ) +{ + ASSERT( genobjpo != NULL ); + + return ( genobjpo->AttachedPClusters != NULL ); +} + + +// check if genobject has attached particle clusters of a certain type -------- +// +objectbase_pcluster_s * PRT_ObjectHasAttachedClustersOfType( GenObject* genobjpo, int animtype ) +{ + ASSERT( genobjpo != NULL ); + + objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; + for ( ; scan; scan = scan->attachlist ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + if ( scan->animtype == animtype ) + return scan; + } + + return NULL; +} + + +// delete all clusters of certain type attached to a genobject ---------------- +// +int PRT_DeleteAttachedClustersOfType( const GenObject * genobjpo, int animtype ) +{ + ASSERT( genobjpo != NULL ); + + // count number of removed clusters + int numremoved = 0; + + objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; + objectbase_pcluster_s *precnode = NULL; + + while ( scan != NULL ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + if ( scan->animtype == animtype ) { + + if ( precnode ) + precnode->attachlist = scan->attachlist; + else + ((GenObject*)genobjpo)->AttachedPClusters = scan->attachlist; + + pcluster_s *temp = scan; + scan = scan->attachlist; + + PRT_DeleteCluster_NoListRemoval( temp ); + + numremoved++; + continue; + } + + precnode = scan; + scan = scan->attachlist; + } + + // return number of removed clusters + return numremoved; +} + + +// attach cluster to genobject (insert at head of attachment list) ------------ +// +void PRT_AttachClusterToObject( GenObject* genobjpo, objectbase_pcluster_s* cluster ) +{ + ASSERT( genobjpo != NULL ); + ASSERT( cluster != NULL ); + ASSERT( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + // set reference to genobject + cluster->baseobject = genobjpo; + + // insert at head of list + cluster->attachlist = genobjpo->AttachedPClusters; + genobjpo->AttachedPClusters = cluster; +} + + +// free cluster list attached to genobject ------------------------------------ +// +void PRT_FreeAttachedClusterList( GenObject* genobjpo ) +{ + ASSERT( genobjpo != NULL ); + + objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; + + while ( scan != NULL ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + objectbase_pcluster_s *temp = scan->attachlist; + PRT_DeleteCluster_NoListRemoval( scan ); + scan = temp; + } + + genobjpo->AttachedPClusters = NULL; +} + + +// allocate particle definition (fields have to be set afterwards!) ----------- +// +pdef_s * PRT_AllocParticleDefinition( + int numtexframes, // number of texture animation frames + int numxfoframes // number of texture transformation frames +) +{ + //NOTE: + // this function allocates a new pdef_s containing enough + // space for the supplied number of animation frames. + // NULL will be returned if there is not enough memory for + // the pdef_s (including its animation tables). + + //NOTE: + // the table entries themselves are not filled by this function. + // this has to be done afterwards. (mandatory!!) + + ASSERT( numtexframes >= 1 ); + ASSERT( numxfoframes >= 0 ); + + // alloc mem for particle definition struct + tables + size_t texsiz = sizeof( texfrm_s ) * numtexframes; + size_t xfosiz = sizeof( xfofrm_s ) * numxfoframes; + size_t memsiz = sizeof( pdef_s ) + texsiz + xfosiz; + + pdef_s *pdef = (pdef_s *) ALLOCMEM( memsiz ); + if ( pdef == NULL ) + return NULL; + + // clear struct mem + memset( pdef, 0, memsiz ); + + // set table addresses + pdef->tex_table = (texfrm_s *) ( (char*)pdef + sizeof( pdef_s ) ); + pdef->xfo_table = ( numxfoframes > 0 ) ? + (xfofrm_s *) ( (char*)pdef + sizeof( pdef_s ) + texsiz ) : NULL; + + // set end indexes to table length (start and repeat are zero anyway) + pdef->tex_end = numtexframes - 1; + pdef->xfo_end = ( numxfoframes > 0 ) ? numxfoframes - 1 : 0; + + //NOTE: + // this is just the default for start, repeat, and end index. the user + // has to set them to their correct values, if different from the default. + + return pdef; +} + + +// register particle definition (create struct and associate name with it) ---- +// +pdef_s * PRT_RegisterParticleDefinition( + const char* pdefname, // unique name for particle definition + pdefreg_s* pdefreg, // definition info + int overwrite // overwrite old pdef if name already exists +) +{ + //NOTE: + // this function takes a pdefreg_s, creates the actual pdef_s + // and registers it using the supplied name. + // NULL will be returned if it could not be registered for + // some reason (too many pdefs, no mem, ...). + + ASSERT( pdefname != NULL ); + ASSERT( pdefreg != NULL ); + + ptexreg_s *texdef = pdefreg->texinfo; + pxforeg_s *xfodef = pdefreg->xfoinfo; + + int texframes = pdefreg->textabsize; + int xfoframes = pdefreg->xfotabsize; + + ASSERT( texdef != NULL ); + ASSERT( texframes >= 1 ); + ASSERT( xfoframes >= 0 ); + ASSERT( ( xfoframes == 0 ) || ( xfodef != NULL ) ); + + // check if slot for pdef available + if ( NumParticleDefinitions >= MAX_PARTICLE_DEFS ) { + ASSERT( NumParticleDefinitions == MAX_PARTICLE_DEFS ); + return NULL; + } + + //NOTE: + // even if an already existing pdef is going to be overwritten this + // check is performed. thus at least one slot must always be available. + + // create header and alloc mem + pdef_s *pdef = PRT_AllocParticleDefinition( texframes, xfoframes ); + if ( pdef == NULL ) { + ASSERT( 0 ); + return NULL; + } + + // fill in texture map info + int curframe = 0; + for ( curframe = 0; curframe < texframes; curframe++ ) { + + // look up texture via name + TextureMap *texmap = FetchTextureMap( texdef[ curframe ].texname ); + if ( texmap == NULL ) { + FREEMEM( pdef ); + return NULL; + } + + // write frame info + pdef->tex_table[ curframe ].deltatime = texdef[ curframe ].deltatime; + pdef->tex_table[ curframe ].texmap = texmap; + } + + // set anim points + pdef->tex_start = pdefreg->texstart; + pdef->tex_rep = pdefreg->texrep; + pdef->tex_end = pdefreg->texend; + + // fill in trafo info + if ( xfoframes > 0 ) { + + // write frame info + for ( curframe = 0; curframe < xfoframes; curframe++ ) { + pdef->xfo_table[ curframe ].deltatime = xfodef[ curframe ].deltatime; + pdef->xfo_table[ curframe ].imgtrafo = xfodef[ curframe ].imgtrafo; + } + + // set anim points + pdef->xfo_start = pdefreg->xfostart; + pdef->xfo_rep = pdefreg->xforep; + pdef->xfo_end = pdefreg->xfoend; + } + + // scan all registered particle definitions + int curdef = 0; + for ( curdef = 0; curdef < NumParticleDefinitions; curdef++ ) { + if ( strcmp( ParticleDefinitions[ curdef ].defname, pdefname ) == 0 ) { + break; + } + } + + // found already registered pdef of same name? + if ( curdef < NumParticleDefinitions ) { + + // already existing pdefs (names) may be overwritten + if ( overwrite ) { + + ParticleDefinitions[ curdef ].def = pdef; + + //NOTE: + // the old pdef is kept around as zombie. it cannot be deleted + // until the last particle using it has died. for this reason + // the current approach is to never delete it. (note that functions + // like PART_DEF::PDEF_explode1() also keep around the old pointer, + // at least until they detect that the pdef has been changed.) + + } else { + + // delete new pdef and return old + FREEMEM( pdef ); + pdef = ParticleDefinitions[ curdef ].def; + } + + } else { + + // append new pdef + curdef = NumParticleDefinitions++; + + ASSERT( ParticleDefinitions[ curdef ].defname == NULL ); + ASSERT( ParticleDefinitions[ curdef ].def == NULL ); + + // copy name to not depend on caller string semantics + char *name = (char *) ALLOCMEM( strlen( pdefname ) + 1 ); + if ( name == NULL ) { + ASSERT( 0 ); + return NULL; + } + strcpy( name, pdefname ); + + ParticleDefinitions[ curdef ].defname = name; + ParticleDefinitions[ curdef ].def = pdef; + } + + ASSERT( pdef != NULL ); + return pdef; +} + + +// register particle definition with looping texture-only animation ----------- +// +pdef_s * PRT_RegisterLoopTexParticle( + const char* pdefname, // unique name for particle definition + int numframes, // number of animation frames + const char**nametable, // pointer to table of texture names + int equidelta, // deltatime that is the same for all frames + int overwrite // overwrite old pdef if name already exists +) +{ + ASSERT( pdefname != NULL ); + ASSERT( numframes >= 1 ); + ASSERT( nametable != NULL ); + ASSERT( *nametable != NULL ); + ASSERT( equidelta > 0 ); + + // alloc temporary table mem + ptexreg_s *texinfo = (ptexreg_s *) ALLOCMEM( sizeof( ptexreg_s ) * numframes ); + if ( texinfo == NULL ) + return NULL; + + // init temporary table mem + for ( int fid = 0; fid < numframes; fid++ ) { + texinfo[ fid ].deltatime = equidelta; + texinfo[ fid ].texname = (char *) nametable[ fid ]; + } + + // init registration info + pdefreg_s pdefreg; + pdefreg.texinfo = texinfo; + pdefreg.textabsize = numframes; + pdefreg.texstart = 0; + pdefreg.texrep = 0; + pdefreg.texend = numframes - 1; + pdefreg.xfoinfo = NULL; + pdefreg.xfotabsize = 0; + + // do registration + pdef_s *pdef = PRT_RegisterParticleDefinition( pdefname, &pdefreg, overwrite ); + + // free temporary table mem + FREEMEM( texinfo ); + + // return acquired particle definition (may be NULL) + return pdef; +} + + +// register particle definition with one-shot texture-only animation ---------- +// +pdef_s * PRT_RegisterOneShotTexParticle( + const char* pdefname, // unique name for particle definition + int numframes, // number of animation frames + const char**nametable, // pointer to table of texture names + int equidelta, // deltatime that is the same for all frames + int overwrite // overwrite old pdef if name already exists +) +{ + ASSERT( pdefname != NULL ); + ASSERT( numframes >= 1 ); + ASSERT( nametable != NULL ); + ASSERT( *nametable != NULL ); + ASSERT( equidelta > 0 ); + + // alloc temporary table mem + ptexreg_s *texinfo = (ptexreg_s *) ALLOCMEM( sizeof( ptexreg_s ) * numframes ); + if ( texinfo == NULL ) + return NULL; + + // init temporary table mem + for ( int fid = 0; fid < numframes; fid++ ) { + texinfo[ fid ].deltatime = equidelta; + texinfo[ fid ].texname = (char *) nametable[ fid ]; + } + + // init registration info + pdefreg_s pdefreg; + pdefreg.texinfo = texinfo; + pdefreg.textabsize = numframes; + pdefreg.texstart = 0; + pdefreg.texrep = numframes - 1; + pdefreg.texend = numframes - 1; + pdefreg.xfoinfo = NULL; + pdefreg.xfotabsize = 0; + + // do registration + pdef_s *pdef = PRT_RegisterParticleDefinition( pdefname, &pdefreg, overwrite ); + + // free temporary table mem + FREEMEM( texinfo ); + + // return acquired particle definition (may be NULL) + return pdef; +} + + +// acquire already registered particle definition via its unique name --------- +// +pdef_s * PRT_AcquireParticleDefinition( + const char* pdefname, // unique name for particle definition + int* retpdefid // id of returned particle definition +) +{ + ASSERT( pdefname != NULL ); + + // scan all registered particle definitions + for ( int curdef = 0; curdef < NumParticleDefinitions; curdef++ ) { + if ( strcmp( ParticleDefinitions[ curdef ].defname, pdefname ) == 0 ) { + + // return id if desired + if ( retpdefid != NULL ) + *retpdefid = curdef; + + // return pointer to pdef + return ParticleDefinitions[ curdef ].def; + } + } + + return NULL; +} + + +// acquire already registered particle definition via its id ------------------ +// +pdef_s * PRT_AcquireParticleDefinitionById( + int pdefid // id of particle definition +) +{ + // ensure id is valid + if ( ( pdefid < 0 ) || ( pdefid >= NumParticleDefinitions ) ) + return NULL; + + // fetch from table + pdef_s *pdef = ParticleDefinitions[ pdefid ].def; + ASSERT( pdef != NULL ); + + return pdef; +} + + +// init extended particle info using particle definitions --------------------- +// +void PRT_InitParticleExtInfo( + pextinfo_s* extinfo, // pextinfo_s to initialize + pdef_s* partdef, // base particle definition (obligatory) + pdef_s* partdef_dest, // destruction particle definition (optional) + particle_s* particle // particle_s::extinfo will be set (optional) +) +{ + ASSERT( extinfo != NULL ); + ASSERT( partdef != NULL ); + ASSERT( partdef->tex_table != NULL ); + ASSERT( ( partdef_dest == NULL ) || ( partdef_dest->tex_table != NULL ) ); + + //NOTE: + // if the particle parameter is not NULL, a pointer to the + // initialized extinfo will be stored into the particle + // structure (field particle_s::extinfo). + + // clear struct mem + memset( extinfo, 0, sizeof( pextinfo_s ) ); + + // store pointer in particle structure + if ( particle != NULL ) + particle->extinfo = extinfo; + + // set particle definitions + extinfo->partdef = partdef; + extinfo->partdef_dest = partdef_dest; + + // set first frame + extinfo->tex_pos = partdef->tex_start; + extinfo->xfo_pos = partdef->xfo_start; + + // set first deltatime for texture animation frame + texfrm_s *firsttexframe = &partdef->tex_table[ extinfo->tex_pos ]; + extinfo->tex_time = firsttexframe->deltatime; + + // set first deltatime for texture transformation frame + if ( partdef->xfo_table != NULL ) { + xfofrm_s *firstxfoframe = &partdef->xfo_table[ extinfo->xfo_pos ]; + extinfo->xfo_time = firstxfoframe->deltatime; + } +} + + +// create new particle with linear animation (insert into next free slot) ----- +// +particle_s * PRT_CreateLinearParticle( + particle_s& particle // already initialized particle +) +{ + // try to reuse already allocated cluster + if ( CurLinearCluster != NULL ) { + + ASSERT( ( CurLinearCluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ); + + if ( CurLinearCluster->numel < CurLinearCluster->maxnumel ) { + + // copy particle struct into available slot in free cluster + particle_s *pmem = CurLinearCluster->rep + CurLinearCluster->numel; + *pmem = particle; + + // check whether extinfo attached + if ( particle.extinfo != NULL ) { + + if ( ( CurLinearCluster->type & CT_EXTINFO_STORAGE ) == 0 ) { + ASSERT( 0 ); + goto allocnew; + } + + // copy over extinfo + pextinfo_s *curextinfo = + (pextinfo_s *)( CurLinearCluster->rep + CurLinearCluster->maxnumel ); + curextinfo += CurLinearCluster->numel; + memcpy( curextinfo, particle.extinfo, sizeof( pextinfo_s ) ); + + // set new extinfo pointer + pmem->extinfo = curextinfo; + } + + // increase number of cluster elements + CurLinearCluster->numel++; + + // return new particle location + return pmem; + } + } + +allocnew: + + // always allocate clusters with storage for extinfo! + dword clustertype = CT_CONSTANT_VELOCITY | CT_EXTINFO_STORAGE; + int numelements = DEFAULT_CLUSTER_SIZE; + + // create new cluster for linear particles + CurLinearCluster = PRT_NewCluster( clustertype, numelements, 0 ); + + // set callback to default + linear_pcluster_s *lincluster = (linear_pcluster_s *) CurLinearCluster; + ASSERT( lincluster->callback == NULL ); + lincluster->callback = LinearParticleCollision; + + //NOTE: + // field bdsphere is zero. + + // insert recursively (tail rec) + return PRT_CreateLinearParticle( particle ); +} + + +// create new particle with customdraw property ------------------------------- +// +particle_s * PRT_CreateCustomDrawParticle( + particle_s& particle, // already initialized particle + const GenObject* baseobject // object customdraw particles belong to +) +{ + ASSERT( baseobject != NULL ); + + // try to reuse already allocated cluster + customdraw_pcluster_s *cluster = + (customdraw_pcluster_s *) CustomDrawCluster; + + if ( cluster != NULL ) { + + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CUSTOMDRAW ); + + if ( ( cluster->baseobject == baseobject ) && + ( cluster->numel < cluster->maxnumel ) ) { + + // copy particle struct into available slot in free cluster + particle_s *pmem = cluster->rep + cluster->numel; + *pmem = particle; + + // check whether extinfo attached + if ( particle.extinfo != NULL ) { + + if ( ( cluster->type & CT_EXTINFO_STORAGE ) == 0 ) { + ASSERT( 0 ); + goto allocnew; + } + + // copy over extinfo + pextinfo_s *curextinfo = + (pextinfo_s *)( cluster->rep + cluster->maxnumel ); + curextinfo += cluster->numel; + memcpy( curextinfo, particle.extinfo, sizeof( pextinfo_s ) ); + + // set new extinfo pointer + pmem->extinfo = curextinfo; + } + + // increase number of cluster elements + cluster->numel++; + + // return new particle location + return pmem; + } + } + +allocnew: + + // always allocate clusters with storage for extinfo! + dword clustertype = CT_CUSTOMDRAW | CT_EXTINFO_STORAGE; + int numelements = DEFAULT_CUSTOMDRAW_CLUSTER_SIZE; + + // create new customdraw cluster + cluster = (customdraw_pcluster_s *) + PRT_NewCluster( clustertype, numelements, 0 ); + + ASSERT( cluster != NULL ); + cluster->baseobject = baseobject; + cluster->callback = NULL; + + //NOTE: + // field bdsphere is zero. + + // insert recursively (tail rec) + CustomDrawCluster = cluster; + return PRT_CreateCustomDrawParticle( particle, baseobject ); +} + + +// translate customdraw particles belonging to specified object --------------- +// +int PRT_TranslateCustomParticles( + Vector3& tvector, // translation vector that should be applied + const GenObject* baseobject // object customdraw particles belong to +) +{ + ASSERT( baseobject != NULL ); + + int cfound = FALSE; + + //TODO: + // use clusters attached to baseobject to + // avoid scanning all clusters. + + // walk list of clusters + pcluster_s *scan = Particles->next; + for ( ; scan->next; scan = scan->next ) { + if ( ( scan->type & CT_TYPEMASK ) == CT_CUSTOMDRAW ) { + + customdraw_pcluster_s *cluster = (customdraw_pcluster_s *) scan; + + if ( cluster->baseobject != baseobject ) + continue; + + // to return if at least one found + cfound = TRUE; + + // translate particles in cluster + for ( int curp = 0; curp < cluster->numel; curp++ ) { + + particle_s *particle = &cluster->rep[ curp ]; + + particle->position.X += tvector.X; + particle->position.Y += tvector.Y; + particle->position.Z += tvector.Z; + } + } + } + + return cfound; +} + + +// draw customdraw particles belonging to specified object -------------------- +// +int PRT_DrawCustomParticles( + const GenObject* baseobject // object whose customdraw particles to draw +) +{ + ASSERT( baseobject != NULL ); + + //NOTE: + // DrawCustomParticles() is exported by PART_SYS.C + + return DrawCustomParticles( baseobject ); +} + + +// create an empty genobject particle cluster --------------------------------- +// +genobject_pcluster_s * PRT_CreateGenObjectParticleCluster( + GenObject* baseobject, // object particle should be attached to + int numelements, // number of particles to allocate + genobject_pcluster_fpt callback, // callback for cluster animation + int use_bdsphere // indicate whether the bounding-sphere of the parent should be used +) +{ + ASSERT( baseobject != NULL ); + ASSERT( numelements > 0 ); + + // always allocate clusters with storage for extinfo! + dword clustertype = CT_GENOBJECT_PARTICLES | CT_EXTINFO_STORAGE; + + // create new genobject cluster + genobject_pcluster_s *cluster = (genobject_pcluster_s *) + PRT_NewCluster( clustertype, numelements, 0 ); + + // init custom fields + ASSERT( cluster != NULL ); + cluster->animtype = SAT_GENOBJECT; + cluster->callback = callback; + + // set bounding sphere to the same as the parent object + cluster->bdsphere = use_bdsphere ? baseobject->BoundingSphere : 0; + + //TODO: + // use flag to indicate whether the bounding sphere should + // be checked/updated for each particle that is added. + + // attach cluster to referenced object + PRT_AttachClusterToObject( baseobject, cluster ); + + return cluster; +} + + +// create a genobject geometry particle and attach it to specified object ----- +// +genobject_pcluster_s * PRT_CreateGenObjectParticle( + particle_s& particle, // already initialized particle + GenObject* baseobject, // object particle should be attached to + genobject_pcluster_s* trycluster // try this cluster before scanning +) +{ + //NOTE: + // this function is meant for attaching single particles. + // if no cluster is supplied all clusters attached to the + // baseobject are walked to try and find a free slot. + // only if no slot can be found a new cluster will be allocated, + // using a default size (DEFAULT_GENOBJECT_CLUSTER_SIZE). + // such single particles most likely have a fixed position, + // therefore the callback will be NULL by default. it can be set + // afterwards, though, using the returned cluster pointer. + + //NOTE: + // typical use: + // ------------ + // genobject_pcluster_s *cluster = NULL; + // cluster = PRT_CreateGenObjectParticle( ., ., cluster ); + // cluster = PRT_CreateGenObjectParticle( ., ., cluster ); + // ... + + ASSERT( baseobject != NULL ); + + // try to reuse already allocated cluster + if ( trycluster != NULL ) { + + ASSERT( ( trycluster->type & CT_TYPEMASK ) == CT_GENOBJECT_PARTICLES ); + ASSERT( ( trycluster->baseobject == baseobject ) ); + + if ( trycluster->numel < trycluster->maxnumel ) { + + // copy particle struct into available slot in free cluster + particle_s *pmem = trycluster->rep + trycluster->numel; + *pmem = particle; + + // check whether extinfo attached + if ( particle.extinfo != NULL ) { + + if ( ( trycluster->type & CT_EXTINFO_STORAGE ) == 0 ) { + ASSERT( 0 ); + goto allocnew; + } + + // copy over extinfo + pextinfo_s *curextinfo = + (pextinfo_s *)( trycluster->rep + trycluster->maxnumel ); + curextinfo += trycluster->numel; + memcpy( curextinfo, particle.extinfo, sizeof( pextinfo_s ) ); + + // set new extinfo pointer + pmem->extinfo = curextinfo; + } + + // increase number of cluster elements + trycluster->numel++; + + // stick with cluster + return trycluster; + } + + } else { + + // try to find attached genobject cluster with enough free space + objectbase_pcluster_s *scan = baseobject->AttachedPClusters; + for ( ; scan; scan = scan->attachlist ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + ASSERT( scan->baseobject == baseobject ); + + if ( ( scan->type & CT_TYPEMASK ) == CT_GENOBJECT_PARTICLES ) { + if ( scan->numel < scan->maxnumel ) { + // insert recursively (tail rec) + return PRT_CreateGenObjectParticle( + particle, baseobject, (genobject_pcluster_s *) scan ); + } + } + } + } + +allocnew: + + //NOTE: + // we assume all particles of genobject clusters are + // contained within their parent object's bounding sphere. + // if this is not the case culling won't work correctly. + + //TODO: + // use flag to indicate whether the bounding sphere should + // be checked/updated for each particle that is added. + + // create a new genobject particle cluster + trycluster = PRT_CreateGenObjectParticleCluster( + baseobject, DEFAULT_GENOBJECT_CLUSTER_SIZE, NULL, TRUE ); + + // insert recursively (tail rec) + return PRT_CreateGenObjectParticle( particle, baseobject, trycluster ); +} + + +// create a genobject geometry particle and attach it to specified object ----- +// +genobject_pcluster_s * PRT_AddGenObjectParticle( + particle_s& particle, // already initialized particle + GenObject* baseobject, // object particle should be attached to + genobject_pcluster_s* usecluster, // try this cluster before scanning + int numelements, // number of particles to allocate + genobject_pcluster_fpt callback // callback for cluster animation +) +{ + //NOTE: + // this function is meant for filling a single attached + // cluster with multiple particles belonging together. + // it either uses the supplied cluster or creates a new + // one with numelements if numelements > 0. + // if numelements == 0 no new cluster will be created + // in case there is no slot available. the particle + // will simply be dropped then. + + //NOTE: + // typical uses: + // ------------ + // genobject_pcluster_s *cluster = NULL; + // for ( int pid = 0; pid < csiz; pid++ ) + // cluster = PRT_AddGenObjectParticle( ., ., cluster, csiz, . ); + // ------------ + // genobject_pcluster_s *cluster = + // PRT_CreateGenObjectParticleCluster( ., csiz, ., . ); + // for ( int pid = 0; pid < csiz; pid++ ) + // cluster = PRT_AddGenObjectParticle( ., ., cluster, 0, . ); + // ------------ + + ASSERT( baseobject != NULL ); + + // try to reuse already allocated cluster + if ( usecluster != NULL ) { + + ASSERT( ( usecluster->type & CT_TYPEMASK ) == CT_GENOBJECT_PARTICLES ); + ASSERT( ( usecluster->baseobject == baseobject ) ); + + if ( usecluster->numel < usecluster->maxnumel ) { + + // copy particle struct into available slot in free cluster + particle_s *pmem = usecluster->rep + usecluster->numel; + *pmem = particle; + + // check whether extinfo attached + if ( particle.extinfo != NULL ) { + + if ( ( usecluster->type & CT_EXTINFO_STORAGE ) == 0 ) { + ASSERT( 0 ); + goto allocnew; + } + + // copy over extinfo + pextinfo_s *curextinfo = + (pextinfo_s *)( usecluster->rep + usecluster->maxnumel ); + curextinfo += usecluster->numel; + memcpy( curextinfo, particle.extinfo, sizeof( pextinfo_s ) ); + + // set new extinfo pointer + pmem->extinfo = curextinfo; + } + + // increase number of cluster elements + usecluster->numel++; + + // stick with cluster + return usecluster; + } + } + +allocnew: + + // do not create particle (fail quietly) if no slot + // available and no new cluster should be allocated + if ( numelements <= 0 ) { + return NULL; + } + + //NOTE: + // we assume all particles of genobject clusters are + // contained within their parent object's bounding sphere. + // if this is not the case culling won't work correctly. + + //TODO: + // use flag to indicate whether the bounding sphere should + // be checked/updated for each particle that is added. + + // create a new genobject particle cluster + usecluster = PRT_CreateGenObjectParticleCluster( + baseobject, numelements, callback, TRUE ); + + // insert recursively (tail rec) + return PRT_AddGenObjectParticle( particle, baseobject, usecluster, numelements, callback ); +} + + +// create a particle sphere centered around specific object origin ------------ +// +basesphere_pcluster_s * PRT_CreateObjectCenteredSphere( + GenObject* objectpo, // object this sphere should be attached to + geomv_t radius, // radius of sphere (also used for bounding) + int animtype, // animation type (SAT_xx) + int clustersiz, // cluster size (number of particles in sphere) + int lifetime, // lifetime of sphere (each sphere particle) + pdrwinfo_s* pdinfo, // particle appearance (drawing) info + int owner // owner id (remote player id) +) +{ + ASSERT( objectpo != NULL ); + + // check if animation type is allowed for object-centered spheres + if ( ( animtype & SAT_VALID_FOR_OBJECTCENTERED_SPHERE ) == 0 ) { + ASSERT( 0 ); + return NULL; + } + + // fetch pdefinfo if supplied + int bitmapindx = pdinfo ? pdinfo->bmindx : SPHERE_BM_INDX; + int pcolor = pdinfo ? pdinfo->pcolor : SPHERE_PARTICLE_COLOR; + float refz = pdinfo ? pdinfo->ref_z : sphere_ref_z; + int sizebound = pdinfo ? pdinfo->sizebnd : partbitmap_size_bound; + + // determine sphere's shape + int spheretype = animtype & SAT_SPHERE_TYPE_MASK; + + // determine number of cluster elements + int allocsiz = ( animtype & SAT_NEEDS_REFCOORDS_MASK ) ? + clustersiz * 2 : clustersiz; + + // fetch extinfo if supplied + pextinfo_s *extinfo = pdinfo ? pdinfo->extinfo : NULL; + + // determine cluster hints + dword hints = CT_HINT_PARTICLES_IDENTICAL | CT_CLUSTER_GLOBAL_EXTINFO; + + // determine cluster type + dword clustertype = CT_OBJECTCENTERED_SPHERE | hints; + if ( extinfo != NULL ) { + clustertype |= CT_EXTINFO_STORAGE | CT_HINT_PARTICLES_HAVE_EXTINFO; + } + + // create new cluster + basesphere_pcluster_s *cluster = (basesphere_pcluster_s *) + PRT_NewCluster( clustertype, allocsiz, 0 ); + + // fill in basic fields + cluster->bdsphere = radius; + cluster->animtype = animtype; + cluster->lifetime = lifetime; + cluster->max_life = lifetime; + + // fill in additional fields + switch ( animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_ROTATING: + cluster->rot.pitch = SPHERE_ROT_PITCH; + cluster->rot.yaw = SPHERE_ROT_YAW; + cluster->rot.roll = SPHERE_ROT_ROLL; + break; + + case SAT_STOCHASTIC_MOTION: + cluster->rand.radius = radius; + cluster->rand.speed = SPHERE_STOCHASTIC_MOTION_SPEED; + cluster->rand.fcount = cluster->rand.speed; + break; + } + + // set particle properties + int curp = 0; + for ( curp = 0; curp < clustersiz; curp++ ) { + + Vertex3 particlepos; + CalcSphereParticlePosition( particlepos, radius, spheretype ); + + // copy extinfo into cluster + pextinfo_s *curextinfo = NULL; + if ( extinfo != NULL ) { + curextinfo = (pextinfo_s *)( cluster->rep + allocsiz ) + curp; + memcpy( curextinfo, extinfo, sizeof( pextinfo_s ) ); + } + + // init particle in cluster + PRT_InitClusterParticle( cluster, curp, bitmapindx, pcolor, + sizebound, refz, + &particlepos, NULL, + INFINITE_LIFETIME, owner, + curextinfo ); + } + + // make second copy of particles if required by animation type + if ( animtype & SAT_NEEDS_REFCOORDS_MASK ) { + for ( int refp = 0; curp < clustersiz * 2; curp++, refp++ ) { + cluster->rep[ curp ] = cluster->rep[ refp ]; + } + } + + //NOTE: + // the optional second copy of all particles is not drawn + // because the number of cluster elements is not increased + // for them. (the particles are simply copied into the cluster!) + // therefore, this block of particles is inactive and invisible + // and can safely be used for reference purposes. + // note also that the duplicate extinfo pointers pose no problem + // for the exact same reason. + + // attach sphere's particle cluster to object + PRT_AttachClusterToObject( objectpo, cluster ); + + return cluster; +} + + +// create particle object comprising a sphere --------------------------------- +// +sphereobj_pcluster_s* PRT_CreateParticleSphereObject ( + Vertex3& origin, // origin of particle object + geomv_t radius, // radius of sphere (also used for bounding) + int animtype, // animation type (SAT_xx) + int clustersiz, // cluster size (number of particles in sphere) + int lifetime, // lifetime of sphere (each sphere particle) + pdrwinfo_s* pdinfo, // particle appearance (drawing) info + int owner // owner id (remote player id) +) +{ + // check if animation type is allowed for particle sphere objects + if ( ( animtype & SAT_VALID_FOR_PSPHERE_OBJECT ) == 0 ) { + ASSERT( 0 ); + return NULL; + } + + // fetch pdefinfo if supplied + int bitmapindx = pdinfo ? pdinfo->bmindx : SPHERE_BM_INDX; + int pcolor = pdinfo ? pdinfo->pcolor : SPHERE_PARTICLE_COLOR; + float refz = pdinfo ? pdinfo->ref_z : sphere_ref_z; + int sizebound = pdinfo ? pdinfo->sizebnd : partbitmap_size_bound; + + // determine sphere's shape + int spheretype = animtype & SAT_SPHERE_TYPE_MASK; + + // determine number of cluster elements + int allocsiz = ( animtype & SAT_NEEDS_REFCOORDS_MASK ) ? + clustersiz * 2 : clustersiz; + + // fetch extinfo if supplied + pextinfo_s *extinfo = pdinfo ? pdinfo->extinfo : NULL; + + // determine cluster hints + dword hints = CT_HINT_PARTICLES_IDENTICAL | CT_CLUSTER_GLOBAL_EXTINFO; + + // determine cluster type + dword clustertype = CT_PARTICLE_SPHERE | hints; + if ( extinfo != NULL ) { + clustertype |= CT_EXTINFO_STORAGE | CT_HINT_PARTICLES_HAVE_EXTINFO; + } + + // create new cluster + sphereobj_pcluster_s *cluster = (sphereobj_pcluster_s *) + PRT_NewCluster( clustertype, allocsiz, 0 ); + + // fill in basic fields + cluster->bdsphere = radius; + cluster->origin = origin; + cluster->animtype = animtype; + cluster->lifetime = lifetime; + cluster->max_life = lifetime; + + // fill in additional fields + switch ( animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_ROTATING: + cluster->rot.pitch = SPHERE_ROT_PITCH; + cluster->rot.yaw = SPHERE_ROT_YAW; + cluster->rot.roll = SPHERE_ROT_ROLL; + break; + + case SAT_EXPLODING: + cluster->expl.speed = SPHERE_EXPLOSION_SPEED; + break; + + case SAT_PULSATING: + cluster->puls.amplitude = SPHERE_PULSE_AMPLITUDE; + cluster->puls.midradius = radius; + cluster->puls.frequency = SPHERE_PULSE_FREQUENCY; + cluster->puls.current_t = BAMS_DEG0; + cluster->puls.pitch = SPHERE_ROT_PITCH; + cluster->puls.yaw = SPHERE_ROT_YAW; + cluster->puls.roll = SPHERE_ROT_ROLL; + break; + + case SAT_CONTRACTING: + cluster->cont.speed = SPHERE_CONTRACT_SPEED; + cluster->cont.expandtime = CONTRACTING_SPHERE_EXPANSION_TIME; + cluster->cont.pitch = SPHERE_ROT_PITCH; + cluster->cont.yaw = SPHERE_ROT_YAW; + cluster->cont.roll = SPHERE_ROT_ROLL; + break; + } + + // set particle properties + int curp = 0; + for ( curp = 0; curp < clustersiz; curp++ ) { + Vertex3 particlepos; + CalcSphereParticlePosition( particlepos, radius, spheretype ); + + // copy extinfo into cluster + pextinfo_s *curextinfo = NULL; + if ( extinfo != NULL ) { + curextinfo = (pextinfo_s *)( cluster->rep + allocsiz ) + curp; + memcpy( curextinfo, extinfo, sizeof( pextinfo_s ) ); + } + + // init particle in cluster + PRT_InitClusterParticle( cluster, curp, bitmapindx, pcolor, + sizebound, refz, + &particlepos, NULL, + INFINITE_LIFETIME, owner, + curextinfo ); + } + + // make second copy of particles if required by animation type + if ( animtype & SAT_NEEDS_REFCOORDS_MASK ) { + for ( int refp = 0; curp < clustersiz * 2; curp++, refp++ ) { + cluster->rep[ curp ] = cluster->rep[ refp ]; + } + } + + //NOTE: + // the optional second copy of all particles is not drawn + // because the number of cluster elements is not increased + // for them. (the particles are simply copied into the cluster!) + // therefore, this block of particles is inactive and invisible + // and can safely be used for reference purposes. + // note also that the duplicate extinfo pointers pose no problem + // for the exact same reason. + + return cluster; +} + + + -- cgit v1.2.3