From 4577ad0cb605ea46ccf54b8cc04af75dccad1974 Mon Sep 17 00:00:00 2001 From: danij Date: Fri, 9 Mar 2012 01:07:11 +0000 Subject: [PATCH] Refactor: Moved particle generators to a new class named Generators Generators comprises the underlying ptcgen_t collection and all support structures and logics for bookkeeping purposes. Each GameMap instance owns a Generators collection. --- doomsday/engine/engine.pro | 2 + doomsday/engine/portable/include/def_data.h | 2 +- doomsday/engine/portable/include/gamemap.h | 61 +- doomsday/engine/portable/include/generators.h | 142 +++++ doomsday/engine/portable/include/p_particle.h | 45 +- .../engine/portable/include/rend_particle.h | 64 +- doomsday/engine/portable/src/dd_main.c | 4 +- doomsday/engine/portable/src/gamemap.c | 108 ++++ doomsday/engine/portable/src/generators.c | 218 +++++++ doomsday/engine/portable/src/p_particle.c | 568 +++++++----------- doomsday/engine/portable/src/r_main.c | 1 - doomsday/engine/portable/src/rend_particle.c | 126 ++-- doomsday/plugins/common/src/p_mapsetup.c | 2 - 13 files changed, 832 insertions(+), 511 deletions(-) create mode 100644 doomsday/engine/portable/include/generators.h create mode 100644 doomsday/engine/portable/src/generators.c diff --git a/doomsday/engine/engine.pro b/doomsday/engine/engine.pro index be77099194..180036334c 100644 --- a/doomsday/engine/engine.pro +++ b/doomsday/engine/engine.pro @@ -186,6 +186,7 @@ DENG_HEADERS = \ portable/include/fs_util.h \ portable/include/game.h \ portable/include/gamemap.h \ + portable/include/generators.h \ portable/include/gl_defer.h \ portable/include/gl_deferredapi.h \ portable/include/gl_draw.h \ @@ -436,6 +437,7 @@ SOURCES += \ portable/src/fs_util.c \ portable/src/game.c \ portable/src/gamemap.c \ + portable/src/generators.c \ portable/src/gl_defer.c \ portable/src/gl_deferredapi.c \ portable/src/gl_draw.c \ diff --git a/doomsday/engine/portable/include/def_data.h b/doomsday/engine/portable/include/def_data.h index 9f488a8833..a9c0404e40 100644 --- a/doomsday/engine/portable/include/def_data.h +++ b/doomsday/engine/portable/include/def_data.h @@ -347,7 +347,7 @@ typedef struct ded_embsound_s { float volume; } ded_embsound_t; -typedef struct { +typedef struct ded_ptcstage_s { ded_flags_t type; int tics; float variance; // Stage variance (time). diff --git a/doomsday/engine/portable/include/gamemap.h b/doomsday/engine/portable/include/gamemap.h index 91d7dac729..85edb452b1 100644 --- a/doomsday/engine/portable/include/gamemap.h +++ b/doomsday/engine/portable/include/gamemap.h @@ -23,8 +23,11 @@ #ifndef LIBDENG_GAMEMAP_H #define LIBDENG_GAMEMAP_H +#include "p_particle.h" + struct thinkerlist_s; struct clmoinfo_s; +struct generators_s; /// Size of Blockmap blocks in map units. Must be an integer power of two. #define MAPBLOCKUNITS (128) @@ -66,6 +69,8 @@ typedef struct gamemap_s { boolean inited; } thinkers; + struct generators_s* generators; + // Client only data: cmhash_t clMobjHash[CLIENT_MOBJ_HASH_SIZE]; @@ -514,6 +519,16 @@ boolean GameMap_ClMobjIterator(GameMap* map, boolean (*callback) (struct mobj_s* struct clplane_s* GameMap_NewClPlane(GameMap* map, uint sectornum, clplanetype_t type, float dest, float speed); +/** + * Retrieve a pointer to the Generators collection for this map. + * If no collection has yet been constructed a new empty collection will be + * initialized as a result of this call. + * + * @param map GameMap instance. + * @return Generators collection for this map. + */ +struct generators_s* GameMap_Generators(GameMap* map); + /** * Initialize all Polyobjs in the map. To be called after map load. * @@ -540,9 +555,9 @@ void GameMap_LinkMobjInBlockmap(GameMap* map, struct mobj_s* mo); boolean GameMap_UnlinkMobjInBlockmap(GameMap* map, struct mobj_s* mo); int GameMap_IterateCellMobjs(GameMap* map, const uint coords[2], - int (*callback) (struct mobj_s*, void*), void* paramaters); + int (*callback) (struct mobj_s*, void*), void* parameters); int GameMap_IterateCellBlockMobjs(GameMap* map, const struct gridmapblock_s* blockCoords, - int (*callback) (struct mobj_s*, void*), void* paramaters); + int (*callback) (struct mobj_s*, void*), void* parameters); int GameMap_MobjsBoxIterator(GameMap* map, const AABoxf* box, int (*callback) (struct mobj_s*, void*), void* parameters); @@ -558,17 +573,19 @@ void GameMap_InitLineDefBlockmap(GameMap* map, const_pvec2_t min, const_pvec2_t void GameMap_LinkLineDefInBlockmap(GameMap* map, linedef_t* lineDef); int GameMap_IterateCellLineDefs(GameMap* map, const uint coords[2], - int (*callback) (linedef_t*, void*), void* paramaters); + int (*callback) (linedef_t*, void*), void* parameters); int GameMap_IterateCellBlockLineDefs(GameMap* map, const struct gridmapblock_s* blockCoords, - int (*callback) (linedef_t*, void*), void* paramaters); + int (*callback) (linedef_t*, void*), void* parameters); + +int GameMap_LineDefIterator(GameMap* map, int (*callback) (linedef_t*, void*), void* parameters); int GameMap_IterateCellPolyobjLineDefs(GameMap* map, const uint coords[2], - int (*callback) (linedef_t*, void*), void* paramaters); + int (*callback) (linedef_t*, void*), void* parameters); int GameMap_IterateCellBlockPolyobjLineDefs(GameMap* map, const struct gridmapblock_s* blockCoords, - int (*callback) (linedef_t*, void*), void* paramaters); + int (*callback) (linedef_t*, void*), void* parameters); int GameMap_LineDefsBoxIterator(GameMap* map, const AABoxf* box, - int (*callback) (linedef_t*, void*), void* paramaters); + int (*callback) (linedef_t*, void*), void* parameters); int GameMap_PolyobjLinesBoxIterator(GameMap* map, const AABoxf* box, int (*callback) (linedef_t*, void*), void* parameters); @@ -581,7 +598,7 @@ int GameMap_PolyobjLinesBoxIterator(GameMap* map, const AABoxf* box, * to GameMap_IterateCellLineDefs(), then make one or more calls to it. */ int GameMap_AllLineDefsBoxIterator(GameMap* map, const AABoxf* box, - int (*callback) (linedef_t*, void*), void* paramaters); + int (*callback) (linedef_t*, void*), void* parameters); /** * Construct an initial (empty) Subsector Blockmap for this map. @@ -595,14 +612,16 @@ void GameMap_LinkSubsectorInBlockmap(GameMap* map, subsector_t* subsector); int GameMap_IterateCellSubsectors(GameMap* map, const uint coords[2], sector_t* sector, const AABoxf* box, int localValidCount, - int (*callback) (subsector_t*, void*), void* paramaters); + int (*callback) (subsector_t*, void*), void* parameters); int GameMap_IterateCellBlockSubsectors(GameMap* map, const struct gridmapblock_s* blockCoords, sector_t* sector, const AABoxf* box, int localValidCount, - int (*callback) (subsector_t*, void*), void* paramaters); + int (*callback) (subsector_t*, void*), void* parameters); int GameMap_SubsectorsBoxIterator(GameMap* map, const AABoxf* box, sector_t* sector, int (*callback) (subsector_t*, void*), void* parameters); +int GameMap_SubsectorIterator(GameMap* map, int (*callback) (subsector_t*, void*), void* parameters); + /** * Construct an initial (empty) Polyobj Blockmap for this map. * @@ -615,9 +634,9 @@ void GameMap_LinkPolyobjInBlockmap(GameMap* map, polyobj_t* po); void GameMap_UnlinkPolyobjInBlockmap(GameMap* map, polyobj_t* po); int GameMap_IterateCellPolyobjs(GameMap* map, const uint coords[2], - int (*callback) (polyobj_t*, void*), void* paramaters); + int (*callback) (polyobj_t*, void*), void* parameters); int GameMap_IterateCellBlockPolyobjs(GameMap* map, const struct gridmapblock_s* blockCoords, - int (*callback) (polyobj_t*, void*), void* paramaters); + int (*callback) (polyobj_t*, void*), void* parameters); /** * The validCount flags are used to avoid checking polys that are marked in @@ -627,18 +646,30 @@ int GameMap_IterateCellBlockPolyobjs(GameMap* map, const struct gridmapblock_s* int GameMap_PolyobjsBoxIterator(GameMap* map, const AABoxf* box, int (*callback) (struct polyobj_s*, void*), void* parameters); +int GameMap_PolyobjIterator(GameMap* map, int (*callback) (polyobj_t*, void*), void* parameters); + +int GameMap_VertexIterator(GameMap* map, int (*callback) (vertex_t*, void*), void* parameters); + +int GameMap_SideDefIterator(GameMap* map, int (*callback) (sidedef_t*, void*), void* parameters); + +int GameMap_SectorIterator(GameMap* map, int (*callback) (sector_t*, void*), void* parameters); + +int GameMap_HEdgeIterator(GameMap* map, int (*callback) (HEdge*, void*), void* parameters); + +int GameMap_NodeIterator(GameMap* map, int (*callback) (node_t*, void*), void* parameters); + /** * Traces a line between @a from and @a to, making a callback for each * interceptable object linked within Blockmap cells which cover the path this * defines. */ int GameMap_PathTraverse2(GameMap* map, float const from[2], float const to[2], - int flags, traverser_t callback, void* paramaters); + int flags, traverser_t callback, void* parameters); int GameMap_PathTraverse(GameMap* map, float const from[2], float const to[2], - int flags, traverser_t callback/* void* paramaters=NULL*/); + int flags, traverser_t callback/* void* parameters=NULL*/); int GameMap_PathXYTraverse2(GameMap* map, float fromX, float fromY, float toX, float toY, - int flags, traverser_t callback, void* paramaters); + int flags, traverser_t callback, void* parameters); int GameMap_PathXYTraverse(GameMap* map, float fromX, float fromY, float toX, float toY, int flags, traverser_t callback); diff --git a/doomsday/engine/portable/include/generators.h b/doomsday/engine/portable/include/generators.h new file mode 100644 index 0000000000..2213477a35 --- /dev/null +++ b/doomsday/engine/portable/include/generators.h @@ -0,0 +1,142 @@ +/** + * @file generators.h + * Generator collection. @ingroup map + * + * A collection of ptcgen_t instances and implements all bookkeeping logic + * pertinent to the management of said instances. + * + * @authors Copyright © 2003-2012 Jaakko Keränen + * @authors Copyright © 2006-2012 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef LIBDENG_MAP_GENERATORS +#define LIBDENG_MAP_GENERATORS + +#include "p_particle.h" + +/// Unique identifier associated with each generator in the collection. +typedef short ptcgenid_t; + +/// Maximum number of ptcgen_ts supported by a Generators instance. +#define GENERATORS_MAX (256) + +/** + * Generators instance. Created with Generators_New(). + */ +typedef struct generators_s Generators; + +/** + * Constructs a new generators collection. Must be deleted with Generators_Delete(). + * + * @param sectorCount Number of sectors the collection must support. + */ +Generators* Generators_New(uint sectorCount); + +/** + * Destructs the generators collection @a generators. + * @param generators Generators instance. + */ +void Generators_Delete(Generators* generators); + +/** + * Clear all references to any ptcgen_t instances currently owned by this + * collection. + * + * @warning Does nothing about any memory allocated for said instances. + * It is therefore the caller's responsibilty to + */ +void Generators_Clear(Generators* generators); + +/** + * Retrieve the generator associated with the unique @a generatorId + * + * @param generators Generators instance. + * @param generatorId Unique id of the generator to lookup. + * @return Pointer to ptcgen iff found, else @c NULL. + */ +ptcgen_t* Generators_Generator(Generators* generators, ptcgenid_t generatorId); + +/** + * Lookup the unique id of @a generator in this collection. + * + * @param generators Generators instance. + * @param generator Generator to lookup an id for. + * @return The unique id if found else @c -1 iff if @a generator is not linked. + */ +ptcgenid_t Generators_GeneratorId(Generators* generators, const ptcgen_t* generator); + +/** + * Retrieve the next available generator id. + * + * @param generators Generators instance. + * @return The next available id else @c -1 iff there are no unused ids. + */ +ptcgenid_t Generators_NextAvailableId(Generators* generators); + +/** + * Unlink a generator from this collection. Ownership is unaffected. + * + * @param generators Generators instance. + * @param generator Generator to be unlinked. + * @return Same as @a generator for caller convenience. + */ +ptcgen_t* Generators_Unlink(Generators* generators, ptcgen_t* generator); + +/** + * Link a generator into this collection. Ownership does NOT transfer to + * the collection. + * + * @param generators Generators instance. + * @param slot Logical slot into which the generator will be linked. + * @return Same as @a generator for caller convenience. + */ +ptcgen_t* Generators_Link(Generators* generators, ptcgenid_t slot, ptcgen_t* generator); + +/** + * Clear all sector --> generator links. + * + * @param generators Generators instance. + */ +void Generators_ClearSectorLinks(Generators* generators); + +/** + * Link the a sector with a generator. + * + * @param generators Generators instance. + * @param generator Generator to link with the identified sector. + * @param sectorIndex Index of the sector to link the generator with. + * + * @return Same as @a generator for caller convenience. + */ +ptcgen_t* Generators_LinkToSector(Generators* generators, ptcgen_t* generator, uint sectorIndex); + +/** + * Walk the entire list of generators. + * + * @param generators Generators instance. + */ +int Generators_Iterate(Generators* generators, int (*callback) (ptcgen_t*, void*), void* parameters); + +/** + * Walk the list of sector-linked generators. + * + * @param generators Generators instance. + */ +int Generators_IterateSectorLinked(Generators* generators, uint sectorIndex, + int (*callback) (ptcgen_t*, void*), void* parameters); + +#endif /// LIBDENG_MAP_GENERATORS diff --git a/doomsday/engine/portable/include/p_particle.h b/doomsday/engine/portable/include/p_particle.h index a3ac1bd1e7..b8146bec57 100644 --- a/doomsday/engine/portable/include/p_particle.h +++ b/doomsday/engine/portable/include/p_particle.h @@ -25,9 +25,6 @@ #include "def_data.h" -// Maximum number of active generators. -#define MAX_ACTIVE_PTCGENS 256 - // Maximum number of particle textures (not instances). #define MAX_PTC_TEXTURES 32 @@ -113,8 +110,8 @@ typedef struct { typedef struct ptcgen_s { thinker_t thinker; // Func = P_PtcGenThinker plane_t* plane; // Flat-triggered. - const ded_ptcgen_t* def; // The definition of this generator. - mobj_t* source; // If mobj-triggered. + const struct ded_ptcgen_s* def; // The definition of this generator. + struct mobj_s* source; // If mobj-triggered. int srcid; // Source mobj ID. int type; // Type-triggered; mobj type number (-1=none). int type2; // Type-triggered; alternate type. @@ -130,10 +127,6 @@ typedef struct ptcgen_s { ptcstage_t* stages; } ptcgen_t; -typedef short ptcgenid_t; - -void P_PtcInit(void); -void P_PtcShutdown(void); void P_PtcInitForMap(void); /** @@ -150,35 +143,11 @@ void P_MapSpawnPlaneParticleGens(void); */ void P_CreatePtcGenLinks(void); -/** - * Convert a particle generator id to pointer. - * - * @return Pointer to ptcgen iff found, ELSE @c NULL. - */ -const ptcgen_t* P_IndexToPtcGen(ptcgenid_t ptcGenID); - -/** - * Convert a particle generator point to id. - * - * @return @c -1 iff NOT found, ELSE id of the specifed ptcgen. - */ -ptcgenid_t P_PtcGenToIndex(const ptcgen_t* gen); - -/** - * Walk the entire list of particle generators. - */ -int P_IteratePtcGens(int (*callback) (ptcgen_t*, void*), void* parameters); - -/** - * Walk the list of sector-linked particle generators. - */ -int P_IterateSectorLinkedPtcGens(sector_t* sector, int (*callback) (ptcgen_t*, void*), void* parameters); - /** * Creates a new mobj-triggered particle generator based on the given * definition. The generator is added to the list of active ptcgens. */ -void P_SpawnMobjParticleGen(const ded_ptcgen_t* def, mobj_t* source); +void P_SpawnMobjParticleGen(const struct ded_ptcgen_s* def, struct mobj_s* source); /** * Spawns all type-triggered particle generators, regardless of whether @@ -187,18 +156,18 @@ void P_SpawnMobjParticleGen(const ded_ptcgen_t* def, mobj_t* source); */ void P_SpawnTypeParticleGens(void); -void P_SpawnMapParticleGens(const Uri* mapUri); +void P_SpawnMapParticleGens(void); /** * A public function (games can call this directly). */ -void P_SpawnDamageParticleGen(mobj_t* mo, mobj_t* inflictor, int amount); +void P_SpawnDamageParticleGen(struct mobj_s* mo, struct mobj_s* inflictor, int amount); /** * Creates a new flat-triggered particle generator based on the given * definition. The generator is added to the list of active ptcgens. */ -void P_SpawnPlaneParticleGen(const ded_ptcgen_t* def, plane_t* plane); +void P_SpawnPlaneParticleGen(const struct ded_ptcgen_s* def, plane_t* plane); /** * Called after a reset once the definitions have been re-read. @@ -210,7 +179,7 @@ void P_UpdateParticleGens(void); * Currently only used visually, collisions use the constant radius. * The variance can be negative (results will be larger). */ -float P_GetParticleRadius(const ded_ptcstage_t* stageDef, int ptcIndex); +float P_GetParticleRadius(const struct ded_ptcstage_s* stageDef, int ptcIndex); /** * A particle may be attached to the floor or ceiling of the sector. diff --git a/doomsday/engine/portable/include/rend_particle.h b/doomsday/engine/portable/include/rend_particle.h index 178b70f86c..1accba38fd 100644 --- a/doomsday/engine/portable/include/rend_particle.h +++ b/doomsday/engine/portable/include/rend_particle.h @@ -1,34 +1,30 @@ -/**\file - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html - * - *\author Copyright © 2003-2012 Jaakko Keränen - *\author Copyright © 2006-2012 Daniel Swanson +/** + * @file rend_particle.h + * Particle Effect Rendering. @ingroup render * - * 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. + * @authors Copyright © 2003-2012 Jaakko Keränen + * @authors Copyright © 2006-2012 Daniel Swanson * - * 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. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -/** - * rend_particle.h: Particle effect renderer. + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ #ifndef LIBDENG_REND_PARTICLE_H #define LIBDENG_REND_PARTICLE_H +#include "p_particle.h" + extern byte useParticles; extern int maxParticles; extern float particleSpawnRate; @@ -50,10 +46,28 @@ void Rend_ParticleReleaseSystemTextures(void); void Rend_ParticleLoadExtraTextures(void); void Rend_ParticleReleaseExtraTextures(void); +/** + * Prepare for rendering a new view of the world. + */ void Rend_ParticleInitForNewFrame(void); + +/** + * The given sector is visible. All PGs in it should be rendered. + * Scans PG links. + */ void Rend_ParticleMarkInSectorVisible(sector_t* sector); +/** + * Render all the visible particle generators. + * We must render all particles ordered back->front, or otherwise + * particles from one generator will obscure particles from another. + * This would be especially bad with smoke trails. + */ void Rend_RenderParticles(void); -// Debugging aid: + +/** + * Debugging aid; Draw all active generators. + */ void Rend_RenderGenerators(void); -#endif /* LIBDENG_REND_PARTICLE_H */ + +#endif /// LIBDENG_REND_PARTICLE_H diff --git a/doomsday/engine/portable/src/dd_main.c b/doomsday/engine/portable/src/dd_main.c index 1cb14977b0..4a03828001 100644 --- a/doomsday/engine/portable/src/dd_main.c +++ b/doomsday/engine/portable/src/dd_main.c @@ -1115,7 +1115,6 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload) R_DestroyObjlinkBlockmap(); R_ClearAnimGroups(); - P_PtcShutdown(); P_ControlShutdown(); Con_Execute(CMDS_DDAY, "clearbindings", true, false); @@ -1144,8 +1143,8 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload) // Clear player data, too, since we just lost all clmobjs. Cl_InitPlayers(); - Z_FreeTags(PU_GAMESTATIC, PU_PURGELEVEL - 1); P_SetCurrentMap(0); + Z_FreeTags(PU_GAMESTATIC, PU_PURGELEVEL - 1); P_ShutdownGameMapObjDefs(); @@ -1221,7 +1220,6 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload) Materials_Init(); FI_Init(); - P_PtcInit(); /// @todo not needed in this mode. } // This is now the current game. diff --git a/doomsday/engine/portable/src/gamemap.c b/doomsday/engine/portable/src/gamemap.c index ede8698955..4d3f3e6ad1 100644 --- a/doomsday/engine/portable/src/gamemap.c +++ b/doomsday/engine/portable/src/gamemap.c @@ -29,6 +29,7 @@ #include "de_refresh.h" #include "blockmap.h" +#include "generators.h" #include "gamemap.h" const Uri* GameMap_Uri(GameMap* map) @@ -365,6 +366,17 @@ static void initPolyobj(polyobj_t* po) P_PolyobjLink(po); } +Generators* GameMap_Generators(GameMap* map) +{ + assert(map); + // Time to initialize a new collection? + if(!map->generators) + { + map->generators = Generators_New(map->numSectors); + } + return map->generators; +} + void GameMap_InitPolyobjs(GameMap* map) { uint i; @@ -691,6 +703,18 @@ int GameMap_IterateCellBlockLineDefs(GameMap* map, const GridmapBlock* blockCoor blockmapCellLinesIterator, (void*) &args); } +int GameMap_LineDefIterator(GameMap* map, int (*callback) (linedef_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numLineDefs; ++i) + { + int result = callback(map->lineDefs + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + void GameMap_LinkSubsectorInBlockmap(GameMap* map, subsector_t* ssec) { Blockmap* blockmap; @@ -813,6 +837,18 @@ int GameMap_SubsectorsBoxIterator(GameMap* map, const AABoxf* box, sector_t* sec localValidCount, callback, parameters); } +int GameMap_SubsectorIterator(GameMap* map, int (*callback) (subsector_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numSubsectors; ++i) + { + int result = callback(map->subsectors + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + void GameMap_LinkPolyobjInBlockmap(GameMap* map, polyobj_t* po) { Blockmap* blockmap; @@ -911,6 +947,18 @@ int GameMap_PolyobjsBoxIterator(GameMap* map, const AABoxf* box, return GameMap_IterateCellBlockPolyobjs(map, &blockCoords, callback, parameters); } +int GameMap_PolyobjIterator(GameMap* map, int (*callback) (polyobj_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numPolyObjs; ++i) + { + int result = callback(map->polyObjs[i], parameters); + if(result) return result; + } + return false; // Continue iteration. +} + typedef struct poiterparams_s { int (*func) (linedef_t*, void*); void* param; @@ -997,6 +1045,66 @@ int GameMap_AllLineDefsBoxIterator(GameMap* map, const AABoxf* box, return P_LinesBoxIterator(box, callback, parameters); } +int GameMap_VertexIterator(GameMap* map, int (*callback) (vertex_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numVertexes; ++i) + { + int result = callback(map->vertexes + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + +int GameMap_SideDefIterator(GameMap* map, int (*callback) (sidedef_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numSideDefs; ++i) + { + int result = callback(map->sideDefs + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + +int GameMap_SectorIterator(GameMap* map, int (*callback) (sector_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numSectors; ++i) + { + int result = callback(map->sectors + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + +int GameMap_HEdgeIterator(GameMap* map, int (*callback) (HEdge*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numHEdges; ++i) + { + int result = callback(map->hedges + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + +int GameMap_NodeIterator(GameMap* map, int (*callback) (node_t*, void*), void* parameters) +{ + uint i; + assert(map); + for(i = 0; i < map->numNodes; ++i) + { + int result = callback(map->nodes + i, parameters); + if(result) return result; + } + return false; // Continue iteration. +} + static int traverseCellPath2(Blockmap* bmap, uint const fromBlock[2], uint const toBlock[2], float const from[2], float const to[2], int (*callback) (uint const block[2], void* parameters), void* parameters) diff --git a/doomsday/engine/portable/src/generators.c b/doomsday/engine/portable/src/generators.c new file mode 100644 index 0000000000..f0d5265c7b --- /dev/null +++ b/doomsday/engine/portable/src/generators.c @@ -0,0 +1,218 @@ +/** + * @file generators.c + * Generators. @ingroup map + * + * @authors Copyright © 2003-2012 Jaakko Keränen + * @authors Copyright © 2006-2012 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "de_base.h" +#include "de_console.h" +#include "dd_zone.h" + +#include "generators.h" + +typedef struct listnode_s { + struct listnode_s* next; + ptcgen_t* gen; +} listnode_t; + +struct generators_s { + ptcgen_t* activeGens[GENERATORS_MAX]; + + listnode_t* linkStore; + uint linkStoreCursor; + uint linkStoreSize; + + // Array of list heads containing links from linkStore to generators in activeGens. + listnode_t** lists; + uint listsSize; +}; + +Generators* Generators_New(uint sectorCount) +{ + Generators* gens = Z_Malloc(sizeof(*gens), PU_MAP, 0); + if(!gens) Con_Error("Generators_New: Failed on allocation of %lu bytes for new Generators instance.", (unsigned long) sizeof(*gens)); + + memset(gens->activeGens, 0, sizeof(gens->activeGens)); + + gens->listsSize = sectorCount; + gens->lists = Z_Calloc(sizeof(listnode_t*) * sectorCount, PU_MAP, 0); + + // We can link 64 generators each into four sectors each before running out of links. + gens->linkStoreSize = 4 * GENERATORS_MAX; + gens->linkStore = Z_Malloc(sizeof(listnode_t) * gens->linkStoreSize, PU_MAP, 0); + gens->linkStoreCursor = 0; + + return gens; +} + +void Generators_Delete(Generators* gens) +{ + assert(gens); + Z_Free(gens->lists); + Z_Free(gens->linkStore); + Z_Free(gens); +} + +void Generators_Clear(Generators* gens) +{ + assert(gens); + Generators_ClearSectorLinks(gens); + memset(gens->activeGens, 0, sizeof(gens->activeGens)); +} + +ptcgen_t* Generators_Generator(Generators* gens, ptcgenid_t id) +{ + assert(gens); + if(id >= 0 && id < GENERATORS_MAX) + return gens->activeGens[id]; + return NULL; // Not found. +} + +ptcgenid_t Generators_GeneratorId(Generators* gens, const ptcgen_t* gen) +{ + assert(gens); + if(gen) + { + ptcgenid_t i; + for(i = 0; i < GENERATORS_MAX; ++i) + { + if(gens->activeGens[i] == gen) + return i; + } + } + return -1; // Not found. +} + +ptcgenid_t Generators_NextAvailableId(Generators* gens) +{ + ptcgenid_t i; + assert(gens); + /// @optimize Cache this result. + for(i = 0; i < GENERATORS_MAX; ++i) + { + if(!gens->activeGens[i]) + return i; + } + return -1; // None available. +} + +/** + * Returns an unused link from the linkStore. + */ +static listnode_t* Generators_NewLink(Generators* gens) +{ + assert(gens); + if(gens->linkStoreCursor < gens->linkStoreSize) + return &gens->linkStore[gens->linkStoreCursor++]; + + VERBOSE( Con_Message("Generators_NewLink: Exhausted store.\n") ); + return NULL; +} + +ptcgen_t* Generators_Unlink(Generators* gens, ptcgen_t* gen) +{ + ptcgenid_t i; + assert(gens); + + for(i = 0; i < GENERATORS_MAX; ++i) + { + if(gens->activeGens[i] == gen) + { + gens->activeGens[i] = 0; + break; + } + } + return gen; +} + +ptcgen_t* Generators_Link(Generators* gens, ptcgenid_t slot, ptcgen_t* gen) +{ + assert(gens); + assert(slot < GENERATORS_MAX); + // Sanity check - generator is not already linked. + assert(Generators_GeneratorId(gens, gen) < 0); + + gens->activeGens[slot] = gen; + return gen; +} + +ptcgen_t* Generators_LinkToSector(Generators* gens, ptcgen_t* gen, uint sectorIndex) +{ + listnode_t* link, *it; + assert(gens); + // Sanity check - generator is one from this collection. + assert(Generators_GeneratorId(gens, gen) >= 0); + + // Must check that it isn't already there... + for(it = gens->lists[sectorIndex]; it; it = it->next) + { + if(it->gen == gen) return gen; // No, no... + /*Con_Error("Generators_LinkToSector: Attempted repeat link of generator %p to sector %u.", (void*)gen, sectorIndex); + exit(1); // Unreachable.*/ + } + + // We need a new link. + link = Generators_NewLink(gens); + if(link) + { + link->gen = gen; + link->next = gens->lists[sectorIndex]; + gens->lists[sectorIndex] = link; + } + return gen; +} + +void Generators_ClearSectorLinks(Generators* gens) +{ + assert(gens); + if(!gens->lists) return; + + memset(gens->lists, 0, sizeof(*gens->lists) * gens->listsSize); + gens->linkStoreCursor = 0; +} + +int Generators_Iterate(Generators* gens, int (*callback) (ptcgen_t*, void*), void* parameters) +{ + ptcgenid_t i; + assert(gens); + for(i = 0; i < GENERATORS_MAX; ++i) + { + int result; + + // Only consider active generators. + if(!gens->activeGens[i]) continue; + + result = callback(gens->activeGens[i], parameters); + if(result) return result; + } + return false; // Continue iteration. +} + +int Generators_IterateSectorLinked(Generators* gens, uint sectorIndex, + int (*callback) (ptcgen_t*, void*), void* parameters) +{ + listnode_t* it; + assert(gens); + for(it = it = gens->lists[sectorIndex]; it; it = it->next) + { + int result = callback(it->gen, parameters); + if(result) return result; + } + return false; // Continue iteration. +} diff --git a/doomsday/engine/portable/src/p_particle.c b/doomsday/engine/portable/src/p_particle.c index d357d1b4a8..f978470932 100644 --- a/doomsday/engine/portable/src/p_particle.c +++ b/doomsday/engine/portable/src/p_particle.c @@ -31,6 +31,8 @@ #include "de_audio.h" #include "de_misc.h" +#include "generators.h" + #define ORDER(x,y,a,b) ( (x)<(y)? ((a)=(x),(b)=(y)) : ((b)=(x),(a)=(y)) ) #define DOT2F(a,b) ( FIX2FLT(a[VX])*FIX2FLT(b[VX]) + FIX2FLT(a[VY])*FIX2FLT(b[VY]) ) #define VECMUL(a,scalar) ( a[VX]=FixedMul(a[VX],scalar), a[VY]=FixedMul(a[VY],scalar) ) @@ -43,11 +45,6 @@ BEGIN_PROF_TIMERS() PROF_PTCGEN_LINK END_PROF_TIMERS() -typedef struct pglink_s { - struct pglink_s* next; - ptcgen_t* gen; -} pglink_t; - void P_PtcGenThinker(ptcgen_t* gen); static void P_Uncertain(fixed_t* pos, fixed_t low, fixed_t high); @@ -56,19 +53,12 @@ byte useParticles = true; int maxParticles = 0; // Unlimited. float particleSpawnRate = 1; // Unmodified. -static boolean inited = false; -static ptcgen_t* activePtcGens[MAX_ACTIVE_PTCGENS]; - -static pglink_t** pgLinks = NULL; // Array of pointers to pgLinks in pgStore. -static pglink_t* pgStore; -static unsigned int pgCursor = 0, pgMax; - static AABoxf mbox; static fixed_t tmpz, tmprad, tmpx1, tmpx2, tmpy1, tmpy2; static boolean tmcross; static linedef_t* ptcHitLine; -static boolean destroyPtcGenParticles(ptcgen_t* gen, void* paramaters) +static int releaseGeneratorParticles(ptcgen_t* gen, void* paramaters) { assert(gen); if(gen->ptcs) @@ -76,177 +66,113 @@ static boolean destroyPtcGenParticles(ptcgen_t* gen, void* paramaters) Z_Free(gen->ptcs); gen->ptcs = 0; } - return true; // Can be used as an iterator, so continue. -} - -static void unlinkPtcGen(ptcgen_t* gen) -{ - ptcgenid_t i; - for(i = 0; i < MAX_ACTIVE_PTCGENS; ++i) - { - if(activePtcGens[i] == gen) - { - activePtcGens[i] = 0; - break; - } - } + return false; // Can be used as an iterator, so continue. } -static int destroyPtcGen(ptcgen_t* gen, void* parameters) +void PtcGen_Delete(ptcgen_t* gen) { assert(gen); - GameMap_ThinkerRemove(theMap, &gen->thinker); - unlinkPtcGen(gen); - destroyPtcGenParticles(gen, 0); - return false; // Can be used as an iterator, so continue. + releaseGeneratorParticles(gen, NULL/*no parameters*/); + // The generator itself is free'd when it's next turn for thinking comes. } -static void linkPtcGen(ptcgenid_t slot, ptcgen_t* gen) +static int destroyGenerator(ptcgen_t* gen, void* parameters) { - assert(slot < MAX_ACTIVE_PTCGENS); + GameMap* map = theMap; /// @fixme Do not assume generator is from the CURRENT map. + + Generators_Unlink(GameMap_Generators(map), gen); + GameMap_ThinkerRemove(map, &gen->thinker); - activePtcGens[slot] = gen; + PtcGen_Delete(gen); + return false; // Can be used as an iterator, so continue. } -static int iterateSectorLinkedPtcGens(sector_t* sector, - int (*callback) (ptcgen_t*, void*), void* parameters) +static int findOldestGenerator(ptcgen_t* gen, void* parameters) { - if(sector) + ptcgen_t** oldest = (ptcgen_t**)parameters; + assert(oldest); + if(!(gen->flags & PGF_STATIC) && (!(*oldest) || gen->age > (*oldest)->age)) { - pglink_t* it; - for(it = it = pgLinks[GET_SECTOR_IDX(sector)]; it; it = it->next) - { - int result = callback(it->gen, parameters); - if(result) return result; - } + *oldest = gen; } return false; // Continue iteration. } -static uint findSlotForNewGen(void) +static ptcgenid_t GameMap_FindIdForNewGenerator(GameMap* map) { - ptcgenid_t i, slot = 0; - int maxage = 0; - boolean isEmpty = false; + Generators* gens; + ptcgen_t* oldest; + ptcgenid_t id; + assert(map); - // Find a suitable spot in the active ptcgens list. - i = 0; - do - { - if(!activePtcGens[i]) - { // An empty slot, put it here. - slot = i + 1; - isEmpty = true; - } - else if(!(activePtcGens[i]->flags & PGF_STATIC) && - (!slot || activePtcGens[i]->age > maxage)) - { - slot = i + 1; - maxage = activePtcGens[i]->age; - } - } while(!isEmpty && ++i < MAX_ACTIVE_PTCGENS); - - return slot; -} - -/// @return @c true iff there is an active ptcgen for the given plane. -static boolean P_HasActivePtcGen(const plane_t* plane) -{ - ptcgenid_t i; - for(i = 0; i < MAX_ACTIVE_PTCGENS; ++i) - if(NULL != activePtcGens[i] && activePtcGens[i]->plane == plane) - return true; - return false; -} + gens = GameMap_Generators(map); + if(!gens) return 0; // None found. -static ptcgen_t* P_PtcGenCreate(void) -{ - ptcgen_t* gen = Z_Calloc(sizeof(ptcgen_t), PU_MAP, 0); + // Prefer allocating a new generator if we've a spare id. + id = Generators_NextAvailableId(gens); + if(id >= 0) return id+1; - // Link the thinker to the list of (private) thinkers. - gen->thinker.function = P_PtcGenThinker; - GameMap_ThinkerAdd(theMap, &gen->thinker, false); + // See if there is an existing generator we can supplant. + /// @optimize Generators could maintain an age-sorted list. + oldest = NULL; + Generators_Iterate(gens, findOldestGenerator, (void*)&oldest); + if(oldest) return Generators_GeneratorId(gens, oldest)+1; - return gen; + return 0; // None found. } /** * Allocates a new active ptcgen and adds it to the list of active ptcgens. - * - * @fixme Linear allocation when in-game is not good... */ -static ptcgen_t* P_NewPtcGen(void) +static ptcgen_t* GameMap_NewGenerator(GameMap* map) { - ptcgenid_t slot = findSlotForNewGen(); - - // Find a suitable slot in the active ptcgens list. - if(slot) + ptcgenid_t id = GameMap_FindIdForNewGenerator(map); + if(id) { + Generators* gens = GameMap_Generators(map); ptcgen_t* gen; - // If there is already a generator here, destroy it. - if(activePtcGens[slot-1]) - destroyPtcGen(activePtcGens[slot-1], 0); + // If there is already a generator with that id - remove it. + gen = Generators_Generator(gens, id-1); + if(gen) + { + destroyGenerator(gen, NULL/*no parameters*/); + } - // Allocate a new generator. - gen = P_PtcGenCreate(); - linkPtcGen(slot-1, gen); + /// @todo Linear allocation when in-game is not good... + gen = Z_Calloc(sizeof(ptcgen_t), PU_MAP, 0); - return gen; - } + // Link the thinker to the list of (private) thinkers. + gen->thinker.function = P_PtcGenThinker; + GameMap_ThinkerAdd(map, &gen->thinker, false); - return 0; // Creation failed. -} + // Link the generator into this collection. + Generators_Link(gens, id-1, gen); -void P_PtcInit(void) -{ - if(!inited) - { // First init. - memset(activePtcGens, 0, sizeof(activePtcGens)); - } - else // Allow re-init. - { - P_UpdateParticleGens(); + return gen; } - inited = true; -} - -void P_PtcShutdown(void) -{ - if(!inited) return; - P_IteratePtcGens(destroyPtcGen, 0); - inited = false; + return 0; // Creation failed. } void P_PtcInitForMap(void) { uint startTime = Sys_GetRealTime(); - pgLinks = Z_Malloc(sizeof(pglink_t *) * NUM_SECTORS, PU_MAP, 0); - memset(pgLinks, 0, sizeof(pglink_t *) * NUM_SECTORS); - - // We can link 64 generators each into four sectors before - // running out of pgLinks. - pgMax = 4 * MAX_ACTIVE_PTCGENS; - pgStore = Z_Malloc(sizeof(pglink_t) * pgMax, PU_MAP, 0); - pgCursor = 0; - - memset(activePtcGens, 0, sizeof(activePtcGens)); - // Spawn all type-triggered particle generators. // Let's hope there aren't too many... P_SpawnTypeParticleGens(); - P_SpawnMapParticleGens(mapUri); + P_SpawnMapParticleGens(); VERBOSE2( Con_Message("P_PtcInitForMap: Done in %.2f seconds.\n", (Sys_GetRealTime() - startTime) / 1000.0f) ) } void P_MapSpawnPlaneParticleGens(void) { - if(isDedicated || !useParticles) - return; + GameMap* map = theMap; + uint i, j; + + if(isDedicated || !useParticles || !map) return; - { uint i; for(i = 0; i < NUM_SECTORS; ++i) { sector_t* sector = SECTOR_PTR(i); @@ -255,84 +181,36 @@ void P_MapSpawnPlaneParticleGens(void) if(0 == sector->lineDefCount) continue; - { int j; for(j = 0; j < 2; ++j) { plane_t* plane = sector->SP_plane(j); const ded_ptcgen_t* def = Materials_PtcGenDef(plane->PS_material); P_SpawnPlaneParticleGen(def, plane); - }} - }} -} - -const ptcgen_t* P_IndexToPtcGen(ptcgenid_t id) -{ - if(id >= 0 && id < MAX_ACTIVE_PTCGENS) - return activePtcGens[id]; - - return NULL; // Not found!? -} - -ptcgenid_t P_PtcGenToIndex(const ptcgen_t* gen) -{ - if(gen) - { - ptcgenid_t i; - - for(i = 0; i < MAX_ACTIVE_PTCGENS; ++i) - { - if(activePtcGens[i] == gen) - return i; } } - - return -1; // Not found!? -} - -/** - * Returns an unused link from the pgStore. - */ -static pglink_t* PG_GetLink(void) -{ - if(pgCursor < pgMax) - return &pgStore[pgCursor++]; - - VERBOSE(Con_Message("PG_GetLink: Out of PGen store.\n")); - return NULL; } -static void PG_LinkPtcGen(ptcgen_t* gen, uint secIDX) +static int linkGeneratorParticles(ptcgen_t* gen, void* parameters) { - pglink_t* link, *it; - - // Must check that it isn't already there... - for(it = pgLinks[secIDX]; it; it = it->next) - if(it->gen == gen) - return; // No, no... - - // We need a new PG link. - link = PG_GetLink(); - if(!link) - return; // Out of links! - - link->gen = gen; - link->next = pgLinks[secIDX]; - pgLinks[secIDX] = link; -} + Generators* gens = (Generators*)parameters; + int i; + /// @fixme Overkill? + for(i = 0; i < gen->count; ++i) + { + if(gen->ptcs[i].stage < 0) continue; -void P_ClearPtcGenLinks(void) -{ - if(pgLinks == 0) - return; - memset(pgLinks, 0, sizeof(*pgLinks) * NUM_SECTORS); - pgCursor = 0; + /// @fixme Do not assume sector is from the CURRENT map. + Generators_LinkToSector(gens, gen, GameMap_SectorIndex(theMap, gen->ptcs[i].sector)); + } + return false; // Continue iteration. } void P_CreatePtcGenLinks(void) { + Generators* gens; + #ifdef DD_PROFILE static int p; - if(++p > 40) { p = 0; @@ -340,28 +218,16 @@ void P_CreatePtcGenLinks(void) } #endif + if(!theMap) return; + gens = GameMap_Generators(theMap); + BEGIN_PROF(PROF_PTCGEN_LINK); - P_ClearPtcGenLinks(); + Generators_ClearSectorLinks(gens); - // Link all active generators to sectors? if(useParticles) { - ptcgenid_t i; - for(i = 0; i < MAX_ACTIVE_PTCGENS; ++i) - { - ptcgen_t* gen; - if((gen = activePtcGens[i]) == 0) - continue; - - // \fixme Overkill? - { int k; - for(k = 0; k < gen->count; ++k) - { - if(gen->ptcs[k].stage >= 0) - PG_LinkPtcGen(gen, GET_SECTOR_IDX(gen->ptcs[k].sector)); - }} - } + Generators_Iterate(gens, linkGeneratorParticles, gens); } END_PROF(PROF_PTCGEN_LINK); @@ -432,16 +298,17 @@ static void P_PresimParticleGen(ptcgen_t* gen, int tics) void P_SpawnMobjParticleGen(const ded_ptcgen_t* def, mobj_t* source) { - ptcgen_t* gen; + ptcgen_t* gen; - if(isDedicated || !useParticles || !(gen = P_NewPtcGen())) - return; + if(isDedicated || !useParticles)return; + + /// @fixme Do not assume the source mobj is from the CURRENT map. + gen = GameMap_NewGenerator(theMap); + if(!gen) return; -/*#if _DEBUG -Con_Message("SpawnPtcGen: %s/%i (src:%s typ:%s mo:%p)\n", - def->state, def - defs.ptcgens, defs.states[source->state-states].id, - defs.mobjs[source->type].id, source); -#endif*/ + /*DEBUG_Message(("SpawnPtcGen: %s/%i (src:%s typ:%s mo:%p)\n", + def->state, def - defs.ptcgens, defs.states[source->state-states].id, + defs.mobjs[source->type].id, source));*/ // Initialize the particle generator. gen->count = def->particles; @@ -463,13 +330,28 @@ Con_Message("SpawnPtcGen: %s/%i (src:%s typ:%s mo:%p)\n", P_PresimParticleGen(gen, def->preSim); } +static int findGeneratorForPlane(ptcgen_t* gen, void* parameters) +{ + plane_t* plane = (plane_t*)parameters; + if(gen->plane == plane) return true; // Stop iteration. + return false; // Continue iteration. +} + +/// @return @c true iff there is an active ptcgen for the given plane. +static boolean GameMap_HasGeneratorForPlane(GameMap* map, plane_t* plane) +{ + assert(map); + return 0 != Generators_Iterate(GameMap_Generators(map), findGeneratorForPlane, (void*)plane); +} + void P_SpawnPlaneParticleGen(const ded_ptcgen_t* def, plane_t* plane) { + GameMap* map = theMap; /// @fixme Do not assume plane is from the CURRENT map. planetype_t relPlane; ptcgen_t* gen; if(isDedicated || !useParticles) return; - if(NULL == def || NULL == plane) return; + if(!def || !plane) return; // Only planes in sectors with volume on the world X/Y axis can support generators. if(0 == plane->sector->lineDefCount) return; @@ -482,11 +364,11 @@ void P_SpawnPlaneParticleGen(const ded_ptcgen_t* def, plane_t* plane) plane = plane->sector->SP_plane(relPlane); // Only one generator per plane. - if(P_HasActivePtcGen(plane)) return; + if(GameMap_HasGeneratorForPlane(map, plane)) return; // Are we out of generators? - gen = P_NewPtcGen(); - if(NULL == gen) return; + gen = GameMap_NewGenerator(map); + if(!gen) return; gen->count = def->particles; // Size of source sector might determine count. @@ -985,12 +867,13 @@ float P_GetParticleZ(const particle_t* pt) static void P_SpinParticle(ptcgen_t* gen, particle_t* pt) { - static const int yawSigns[4] = { 1, 1, -1, -1 }; - static const int pitchSigns[4] = { 1, -1, 1, -1 }; + static const int yawSigns[4] = { 1, 1, -1, -1 }; + static const int pitchSigns[4] = { 1, -1, 1, -1 }; - ded_ptcstage_t* stDef = &gen->def->stages[pt->stage]; - uint index = pt - &gen->ptcs[P_PtcGenToIndex(gen) / 8]; - int yawSign, pitchSign; + Generators* gens = GameMap_Generators(theMap); /// @fixme Do not assume generator is from the CURRENT map. + ded_ptcstage_t* stDef = &gen->def->stages[pt->stage]; + uint index = pt - &gen->ptcs[Generators_GeneratorId(gens, gen) / 8]; + int yawSign, pitchSign; yawSign = yawSigns[index % 4]; pitchSign = pitchSigns[index % 4]; @@ -1315,7 +1198,7 @@ void P_PtcGenThinker(ptcgen_t* gen) // Time to die? if(++gen->age > def->maxAge && def->maxAge >= 0) { - destroyPtcGen(gen, 0); + destroyGenerator(gen, 0); return; } @@ -1390,19 +1273,19 @@ void P_PtcGenThinker(ptcgen_t* gen) void P_SpawnTypeParticleGens(void) { - int i; - ded_ptcgen_t* def; - ptcgen_t* gen; + GameMap* map = theMap; + ded_ptcgen_t* def; + ptcgen_t* gen; + int i; - if(isDedicated || !useParticles) - return; + if(isDedicated || !useParticles || !map) return; for(i = 0, def = defs.ptcGens; i < defs.count.ptcGens.num; ++i, def++) { - if(def->typeNum < 0) - continue; - if(!(gen = P_NewPtcGen())) - return; // No more generators. + if(def->typeNum < 0) continue; + + gen = GameMap_NewGenerator(map); + if(!gen) return; // No more generators. // Initialize the particle generator. gen->count = def->particles; @@ -1417,23 +1300,23 @@ void P_SpawnTypeParticleGens(void) } } -void P_SpawnMapParticleGens(const Uri* uri) +void P_SpawnMapParticleGens(void) { + GameMap* map = theMap; ded_ptcgen_t* def; ptcgen_t* gen; int i; - if(isDedicated || !useParticles) return; - if(!uri) return; + if(isDedicated || !useParticles || !map) return; for(i = 0, def = defs.ptcGens; i < defs.count.ptcGens.num; ++i, def++) { - if(!def->map || !Uri_Equality(def->map, uri)) continue; + if(!def->map || !Uri_Equality(def->map, GameMap_Uri(map))) continue; // Are we still spawning using this generator? if(def->spawnAge > 0 && ddMapTime > def->spawnAge) continue; - gen = P_NewPtcGen(); + gen = GameMap_NewGenerator(map); if(!gen) return; // No more generators. // Initialize the particle generator. @@ -1453,20 +1336,18 @@ void P_SpawnDamageParticleGen(mobj_t* mo, mobj_t* inflictor, int amount) const ded_ptcgen_t* def; // Are particles allowed? - if(isDedicated || !useParticles) - return; + if(isDedicated || !useParticles) return; - if(!mo || !inflictor || amount <= 0) - return; + if(!mo || !inflictor || amount <= 0) return; - if((def = Def_GetDamageGenerator(mo->type))) + def = Def_GetDamageGenerator(mo->type); + if(def) { - ptcgen_t* gen; - vec3_t vector, vecDelta; + GameMap* map = theMap; /// @fixme Do not assume mobj is from the CURRENT map. + ptcgen_t* gen = GameMap_NewGenerator(map); + vec3_t vector, vecDelta; - // Create it. - if(!(gen = P_NewPtcGen())) - return; // No more generators. + if(!gen) return; // No more generators. gen->count = def->particles; P_InitParticleGen(gen, def); @@ -1498,132 +1379,121 @@ void P_SpawnDamageParticleGen(mobj_t* mo, mobj_t* inflictor, int amount) } } -void P_UpdateParticleGens(void) +static int findDefForGenerator(ptcgen_t* gen, void* parameters) { - ptcgenid_t i; + ded_ptcgen_t* def; + int i; - for(i = 0; i < MAX_ACTIVE_PTCGENS; ++i) + // Search for a suitable definition. + def = defs.ptcGens; + for(i = 0; i < defs.count.ptcGens.num; ++i, def++) { - int j; - ded_ptcgen_t* def; - boolean found; - ptcgen_t* gen; - - if(!activePtcGens[i]) - continue; + // A type generator? + if(def->typeNum >= 0 && + (gen->type == def->typeNum || gen->type2 == def->type2Num)) + { + return i+1; // Stop iteration. + } - gen = activePtcGens[i]; + // A damage generator? + if(gen->source && gen->source->type == def->damageNum) + { + return i+1; + } - // Map generators cannot be updated (we have no means to reliably - // identify them), so destroy them. - if(gen->flags & PGF_UNTRIGGERED) + // A state generator? + if(gen->source && def->state[0] && + gen->source->state - states == Def_GetStateNum(def->state)) { - destroyPtcGen(gen, 0); - continue; + return i+1; } - // Search for a suitable definition. - j = 0; - def = defs.ptcGens; - found = false; - while(j < defs.count.ptcGens.num && !found) + // A flat generator? + if(gen->plane && def->material) { - // A type generator? - if(def->typeNum >= 0 && - (gen->type == def->typeNum || gen->type2 == def->type2Num)) - { - found = true; - } - // A damage generator? - else if(gen->source && gen->source->type == def->damageNum) - { - found = true; - } - // A state generator? - else if(gen->source && def->state[0] && - gen->source->state - states == Def_GetStateNum(def->state)) + material_t* mat = gen->plane->PS_material; + material_t* defMat = Materials_ToMaterial(Materials_ResolveUri2(def->material, true/*quiet please*/)); + + if(def->flags & PGF_FLOOR_SPAWN) + mat = gen->plane->sector->SP_plane(PLN_FLOOR)->PS_material; + if(def->flags & PGF_CEILING_SPAWN) + mat = gen->plane->sector->SP_plane(PLN_CEILING)->PS_material; + + // Is this suitable? + if(mat == defMat) { - found = true; + return i+1; } - // A flat generator? - else if(gen->plane && def->material) - { - material_t* mat = gen->plane->PS_material; - material_t* defMat = Materials_ToMaterial(Materials_ResolveUri2(def->material, true/*quiet please*/)); - if(def->flags & PGF_FLOOR_SPAWN) - mat = gen->plane->sector->SP_plane(PLN_FLOOR)->PS_material; - if(def->flags & PGF_CEILING_SPAWN) - mat = gen->plane->sector->SP_plane(PLN_CEILING)->PS_material; - - // Is this suitable? - if(mat == defMat) + if(def->flags & PGF_GROUP) + { + // Generator triggered by all materials in the (animation) group. + /** + * A search is necessary only if we know both the used material and + * the specified material in this definition are in *a* group. + */ + if(Material_IsGroupAnimated(defMat) && Material_IsGroupAnimated(mat)) { - found = true; - } - else if(def->flags & PGF_GROUP) - { // Generator triggered by all materials in the (animation) group. - /** - * A search is necessary only if we know both the used material and - * the specified material in this definition are in *a* group. - */ - if(Material_IsGroupAnimated(defMat) && Material_IsGroupAnimated(mat)) + int g, numGroups = Materials_AnimGroupCount(); + for(g = 0; g < numGroups; ++g) { - int g, numGroups = Materials_AnimGroupCount(); - for(g = 0; g < numGroups; ++g) + if(Materials_IsPrecacheAnimGroup(g)) + continue; // Precache groups don't apply. + + if(Materials_IsMaterialInAnimGroup(defMat, g) && + Materials_IsMaterialInAnimGroup(mat, g)) { - if(Materials_IsPrecacheAnimGroup(g)) - continue; // Precache groups don't apply. - - if(Materials_IsMaterialInAnimGroup(defMat, g) && - Materials_IsMaterialInAnimGroup(mat, g)) - { - // Both are in this group! This def will do. - found = true; - break; - } + // Both are in this group! This def will do. + return i+1; } } } } - else - { - j++; - def++; - } - } - - if(found) - { // Update the generator using the new definition. - gen->def = def; - } - else - { // Nothing else we can do, destroy it. - destroyPtcGen(gen, 0); } } - // Re-spawn map generators. - P_SpawnMapParticleGens(mapUri); + return 0; // Not found. } -int P_IteratePtcGens(int (*callback) (ptcgen_t*, void*), void* parameters) +static int updateGenerator(ptcgen_t* gen, void* parameters) { - ptcgenid_t i; - for(i = 0; i < MAX_ACTIVE_PTCGENS; ++i) - { - int result; + int defIndex; - // Only consider active generators. - if(!activePtcGens[i]) continue; + // Map generators cannot be updated (we have no means to reliably + // identify them), so destroy them. + if(gen->flags & PGF_UNTRIGGERED) + { + destroyGenerator(gen, 0); + return false; // Continue iteration. + } - result = callback(activePtcGens[i], parameters); - if(result) return result; + defIndex = findDefForGenerator(gen, NULL/*no parameters*/); + if(defIndex) + { + // Update the generator using the new definition. + ded_ptcgen_t* def = defs.ptcGens + (defIndex-1); + gen->def = def; } + else + { + // Nothing else we can do, destroy it. + destroyGenerator(gen, 0); + } + return false; // Continue iteration. } -int P_IterateSectorLinkedPtcGens(sector_t* sector, int (*callback) (ptcgen_t*, void*), void* parameters) +void P_UpdateParticleGens(void) { - return iterateSectorLinkedPtcGens(sector, callback, parameters); + Generators* gens; + + if(!theMap) return; + gens = GameMap_Generators(theMap); + if(!gens) return; + + // Update existing generators. + Generators_Iterate(gens, updateGenerator, NULL/*no parameters*/); + + // Re-spawn map generators. + P_SpawnMapParticleGens(); } diff --git a/doomsday/engine/portable/src/r_main.c b/doomsday/engine/portable/src/r_main.c index 5caf412f3a..542d147792 100644 --- a/doomsday/engine/portable/src/r_main.c +++ b/doomsday/engine/portable/src/r_main.c @@ -537,7 +537,6 @@ void R_Init(void) R_SkyInit(); Rend_Init(); frameCount = 0; - P_PtcInit(); } /** diff --git a/doomsday/engine/portable/src/rend_particle.c b/doomsday/engine/portable/src/rend_particle.c index 767963a117..59de3884e9 100644 --- a/doomsday/engine/portable/src/rend_particle.c +++ b/doomsday/engine/portable/src/rend_particle.c @@ -1,33 +1,25 @@ -/**\file rend_particle.c - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html - * - *\author Copyright © 2003-2012 Jaakko Keränen - *\author Copyright © 2006-2012 Daniel Swanson +/** + * @file rend_particle.c + * Particle Effect Rendering. @ingroup render * - * 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. + * @authors Copyright © 2003-2012 Jaakko Keränen + * @authors Copyright © 2006-2012 Daniel Swanson * - * 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. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -/** - * Particle Effect Rendering. + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ -// HEADER FILES ------------------------------------------------------------ - #include #include "de_base.h" @@ -42,48 +34,34 @@ #include "image.h" #include "texturecontent.h" - -// MACROS ------------------------------------------------------------------ +#include "generators.h" // Point + custom textures. #define NUM_TEX_NAMES (MAX_PTC_TEXTURES) -// TYPES ------------------------------------------------------------------- - typedef struct { ptcgenid_t ptcGenID; // Generator id. int ptID; // Particle id. float distance; } porder_t; -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - extern float vang, vpitch; -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - DGLuint pointTex, ptctexname[MAX_PTC_TEXTURES]; int particleNearLimit = 0; float particleDiffuse = 4; byte devDrawGenerators = false; // Display active generators? -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - static size_t numParts; static boolean hasPoints, hasLines, hasModels, hasNoBlend, hasBlend; static boolean hasPointTexs[NUM_TEX_NAMES]; -static byte visiblePtcGens[MAX_ACTIVE_PTCGENS]; +static byte visiblePtcGens[GENERATORS_MAX]; static size_t orderSize = 0; static porder_t* order = NULL; -// CODE -------------------------------------------------------------------- +// Currently active Generators collection. Global for performance. +static Generators* gens; void Rend_ParticleRegister(void) { @@ -98,14 +76,14 @@ void Rend_ParticleRegister(void) static int markPtcGenVisible(ptcgen_t* gen, void* parameters) { - visiblePtcGens[P_PtcGenToIndex(gen)] = true; + visiblePtcGens[Generators_GeneratorId(gens, gen)] = true; return false; // Continue iteration. } static boolean isPtcGenVisible(const ptcgen_t* gen) { - return visiblePtcGens[P_PtcGenToIndex(gen)]; + return visiblePtcGens[Generators_GeneratorId(gens, gen)]; } static float pointDist(fixed_t c[3]) @@ -216,24 +194,22 @@ void Rend_ParticleReleaseExtraTextures(void) memset(ptctexname, 0, sizeof(ptctexname)); } -/** - * Prepare for rendering a new view of the world. - */ void Rend_ParticleInitForNewFrame(void) { if(!useParticles) return; // Clear all visibility flags. - memset(visiblePtcGens, 0, MAX_ACTIVE_PTCGENS); + memset(visiblePtcGens, 0, GENERATORS_MAX); } -/** - * The given sector is visible. All PGs in it should be rendered. - * Scans PG links. - */ void Rend_ParticleMarkInSectorVisible(sector_t* sector) { - if(!useParticles) return; - P_IterateSectorLinkedPtcGens(sector, markPtcGenVisible, NULL); + if(!useParticles || !theMap || !sector) return; + + /// @fixme Do the assume sector is from the CURRENT map. + gens = GameMap_Generators(theMap); + if(!gens) return; + + Generators_IterateSectorLinked(gens, GameMap_SectorIndex(theMap, sector), markPtcGenVisible, NULL/*no parameters*/); } /** @@ -317,7 +293,7 @@ static int populateSortBuffer(ptcgen_t* gen, void* parameters) // This particle is visible. Add it to the sort buffer. slot = &order[(*m)++]; - slot->ptcGenID = P_PtcGenToIndex(gen); + slot->ptcGenID = Generators_GeneratorId(gens, gen); slot->ptID = p; slot->distance = dist; @@ -365,7 +341,7 @@ static int listVisibleParticles(void) // First count how many particles are in the visible generators. numParts = 0; - P_IteratePtcGens(countParticles, &numParts); + Generators_Iterate(gens, countParticles, &numParts); if(!numParts) return false; // No visible generators. @@ -375,7 +351,7 @@ static int listVisibleParticles(void) // Populate the particle sort buffer and determine what type(s) of // particle (model/point/line/etc...) we'll need to draw. numVisibleParticles = 0; - P_IteratePtcGens(populateSortBuffer, &numVisibleParticles); + Generators_Iterate(gens, populateSortBuffer, &numVisibleParticles); if(!numVisibleParticles) return false; // No visible particles (all too far?). @@ -557,7 +533,7 @@ static void renderParticles(int rtype, boolean withBlend) boolean flatOnPlane = false, flatOnWall = false, nearPlane, nearWall; short stageType; - gen = P_IndexToPtcGen(slot->ptcGenID); + gen = Generators_Generator(gens, slot->ptcGenID); pt = &gen->ptcs[slot->ptID]; st = &gen->stages[pt->stage]; @@ -802,6 +778,7 @@ static void renderParticles(int rtype, boolean withBlend) static void renderPass(boolean useBlending) { + int i; assert(!Sys_GLCheckError()); // Set blending mode. @@ -817,8 +794,8 @@ static void renderPass(boolean useBlending) if(hasPoints) renderParticles(PTC_POINT, useBlending); - { int i; for(i = 0; i < NUM_TEX_NAMES; ++i) + { if(hasPointTexs[i]) renderParticles(PTC_TEXTURE + i, useBlending); } @@ -830,19 +807,15 @@ static void renderPass(boolean useBlending) assert(!Sys_GLCheckError()); } -/** - * Render all the visible particle generators. - * We must render all particles ordered back->front, or otherwise - * particles from one generator will obscure particles from another. - * This would be especially bad with smoke trails. - */ void Rend_RenderParticles(void) { - if(!useParticles) - return; + if(!useParticles || !theMap) return; - if(!listVisibleParticles()) - return; // No visible particles at all? + gens = GameMap_Generators(theMap); + if(!gens) return; + + // No visible particles at all? + if(!listVisibleParticles()) return; // Render all the visible particles. if(hasNoBlend) @@ -892,7 +865,7 @@ static int drawGeneratorOrigin(ptcgen_t* gen, void* parameters) float scale = dist / (theWindow->geometry.size.width / 2); char buf[80]; - sprintf(buf, "%i", P_PtcGenToIndex(gen)); + sprintf(buf, "%i", Generators_GeneratorId(gens, gen)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); @@ -920,15 +893,14 @@ static int drawGeneratorOrigin(ptcgen_t* gen, void* parameters) #undef MAX_GENERATOR_DIST } -/** - * Debugging aid; Draw all active generators. - */ void Rend_RenderGenerators(void) { float eye[3]; - if(!devDrawGenerators) - return; + if(!devDrawGenerators || !theMap) return; + + gens = GameMap_Generators(theMap); + if(!gens) return; eye[VX] = vx; eye[VY] = vz; @@ -936,7 +908,7 @@ void Rend_RenderGenerators(void) glDisable(GL_DEPTH_TEST); - P_IteratePtcGens(drawGeneratorOrigin, eye); + Generators_Iterate(gens, drawGeneratorOrigin, eye); // Restore previous state. glEnable(GL_DEPTH_TEST); diff --git a/doomsday/plugins/common/src/p_mapsetup.c b/doomsday/plugins/common/src/p_mapsetup.c index 9915135b5b..b6027ec454 100644 --- a/doomsday/plugins/common/src/p_mapsetup.c +++ b/doomsday/plugins/common/src/p_mapsetup.c @@ -755,8 +755,6 @@ int P_SetupMapWorker(void* paramaters) // Initialize The Logical Sound Manager. S_MapChange(); - Z_FreeTags(PU_MAP, PU_PURGELEVEL - 1); - #if __JHERETIC__ || __JHEXEN__ // The pointers in the body queue just became invalid. P_ClearBodyQueue();