Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Refactor|World|Map: Revised API for generator list traversal
Make use of C++11 lambdas for traversing the generator lists to
improve SoC and better locality.
  • Loading branch information
danij-deng committed May 3, 2015
1 parent e4737b7 commit 94904e4
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 180 deletions.
23 changes: 7 additions & 16 deletions doomsday/client/include/world/map.h
Expand Up @@ -450,29 +450,20 @@ class Map
Generator *newGenerator();

/**
* Iterate over all generators in the map making a callback for each. Iteration
* ends when all generators have been processed or a callback returns non-zero.
* Iterate over all generators in the map making a callback for each.
*
* @param callback Callback to make for each iteration.
* @param context User data to be passed to the callback.
*
* @return @c 0 iff iteration completed wholly.
* @param func Callback to make for each Generator.
*/
dint generatorIterator(dint (*callback) (Generator *, void *), void *context = nullptr);
LoopResult forAllGenerators(std::function<LoopResult (Generator &)> func) const;

/**
* Iterate over all generators in the map which are present in the identified
* list making a callback for each. Iteration ends when all targeted generators
* have been processed or a callback returns non-zero.
*
* @param listIndex Index of the list to traverse.
* @param callback Callback to make for each iteration.
* @param context User data to be passed to the callback.
* sector making a callback for each.
*
* @return @c 0 iff iteration completed wholly.
* @param sector Sector containing the generators to process.
* @param func Callback to make for each Generator.
*/
dint generatorListIterator(duint listIndex, dint (*callback) (Generator *, void *),
void *context = nullptr);
LoopResult forAllGeneratorsInSector(Sector const &sector, std::function<LoopResult (Generator &)> func) const;

void unlink(Generator &generator);

Expand Down
2 changes: 2 additions & 0 deletions doomsday/client/include/world/mapelement.h
Expand Up @@ -127,6 +127,8 @@ class MapElement
*/
Map &map() const;

inline Map *mapPtr() const { return hasMap()? &map() : nullptr; }

/**
* Change the map attributed to the map element. Note that if the map
* element has a @em parent that attempting to change the map property of
Expand Down
36 changes: 17 additions & 19 deletions doomsday/client/src/render/rend_main.cpp
Expand Up @@ -3520,12 +3520,6 @@ static void projectSubspaceSprites()
curSubspace->setLastSpriteProjectFrame(R_FrameCount());
}

static dint generatorMarkVisibleWorker(Generator *generator, void * /*context*/)
{
R_ViewerGeneratorMarkVisible(*generator);
return 0; // Continue iteration.
}

/**
* @pre Assumes the subspace is at least partially visible.
*/
Expand Down Expand Up @@ -3557,7 +3551,11 @@ static void drawCurrentSubspace()
// Mark generators in the sector visible.
if(useParticles)
{
sector.map().generatorListIterator(sector.indexInMap(), generatorMarkVisibleWorker);
sector.map().forAllGeneratorsInSector(sector, [] (Generator &gen)
{
R_ViewerGeneratorMarkVisible(gen);
return LoopContinue;
});
}

// Sprites for this subspace have to be drawn.
Expand Down Expand Up @@ -5451,19 +5449,18 @@ void Rend_DrawVectorLight(VectorLightData const &vlight, dfloat alpha)
glEnd();
}

static String labelForGenerator(Generator const *gen)
static String labelForGenerator(Generator const &gen)
{
DENG2_ASSERT(gen);
return String("%1").arg(gen->id());
return String("%1").arg(gen.id());
}

static dint drawGenerator(Generator *gen, void * /*context*/)
static void drawGenerator(Generator const &gen)
{
#define MAX_GENERATOR_DIST 2048
static dint const MAX_GENERATOR_DIST = 2048;

if(gen->source || gen->isUntriggered())
if(gen.source || gen.isUntriggered())
{
Vector3d const origin = gen->origin();
Vector3d const origin = gen.origin();
ddouble const distToEye = (eyeOrigin - origin).length();
if(distToEye < MAX_GENERATOR_DIST)
{
Expand All @@ -5472,10 +5469,6 @@ static dint drawGenerator(Generator *gen, void * /*context*/)
1 - distToEye / MAX_GENERATOR_DIST);
}
}

return false; // Continue iteration.

#undef MAX_GENERATOR_DIST
}

/**
Expand All @@ -5484,7 +5477,12 @@ static dint drawGenerator(Generator *gen, void * /*context*/)
static void drawGenerators(Map &map)
{
if(!devDrawGenerators) return;
map.generatorIterator(drawGenerator);

map.forAllGenerators([] (Generator &gen)
{
drawGenerator(gen);
return LoopContinue;
});
}

static void drawPoint(Vector3d const &origin, dfloat opacity)
Expand Down
187 changes: 94 additions & 93 deletions doomsday/client/src/render/rend_particle.cpp
Expand Up @@ -58,12 +58,12 @@ using namespace de;

static DGLuint pointTex, ptctexname[MAX_PTC_TEXTURES];

static bool hasPoints, hasLines, hasModels, hasNoBlend, hasBlend;
static bool hasPoints, hasLines, hasModels, hasNoBlend, hasAdditive;
static bool hasPointTexs[NUM_TEX_NAMES];

struct OrderedParticle
{
Generator *generator;
Generator const *generator;
dint particleId;
dfloat distance;
};
Expand Down Expand Up @@ -256,116 +256,117 @@ static void expandOrderBuffer(size_t max)
}
}

static dint countActiveGeneratorParticlesWorker(Generator *gen, void *context)
{
if(R_ViewerGeneratorIsVisible(*gen))
{
*static_cast<size_t *>(context) += gen->activeParticleCount();
}
return false; // Continue iteration.
}

static dint populateSortBuffer(Generator *gen, void *context)
/**
* Determines whether the given particle is potentially visible for the current viewer.
*/
static bool particlePVisible(ParticleInfo const &pinfo)
{
auto *sortIndex = (size_t *) context;

if(!R_ViewerGeneratorIsVisible(*gen))
return false; // Continue iteration.
// Never if it has already expired.
if(pinfo.stage < 0) return false;

ded_ptcgen_t const *def = gen->def;
ParticleInfo const *pinfo = gen->particleInfo();
for(dint p = 0; p < gen->count; ++p, pinfo++)
{
if(pinfo->stage < 0) continue;

ConvexSubspace *subspace = pinfo->bspLeaf? pinfo->bspLeaf->subspacePtr() : 0;
if(!subspace) continue;

// Is the BSP leaf at the particle's origin visible?
if(!R_ViewerSubspaceIsVisible(*subspace))
continue; // No; this particle can't be seen.

// Don't allow zero distance.
dfloat dist = de::max(pointDist(pinfo->origin), 1.f);
if(def->maxDist != 0 && dist > def->maxDist)
continue; // Too far.
if(dist < dfloat( particleNearLimit ))
continue; // Too near.

// This particle is visible. Add it to the sort buffer.
OrderedParticle *slot = &order[(*sortIndex)++];

slot->generator = gen;
slot->particleId = p;
slot->distance = dist;

// Determine what type of particle this is, as this will affect how
// we go order our render passes and manipulate the render state.
dint stagetype = gen->stages[pinfo->stage].type;
if(stagetype == PTC_POINT)
{
hasPoints = true;
}
else if(stagetype == PTC_LINE)
{
hasLines = true;
}
else if(stagetype >= PTC_TEXTURE && stagetype < PTC_TEXTURE + MAX_PTC_TEXTURES)
{
if(ptctexname[stagetype - PTC_TEXTURE])
hasPointTexs[stagetype - PTC_TEXTURE] = true;
else
hasPoints = true;
}
else if(stagetype >= PTC_MODEL && stagetype < PTC_MODEL + MAX_PTC_MODELS)
{
hasModels = true;
}

if(gen->blendmode() == BM_ADD)
{
hasBlend = true;
}
else
{
hasNoBlend = true;
}
}
// Never if the origin lies outside the map.
if(!pinfo.bspLeaf || !pinfo.bspLeaf->hasSubspace())
return false;

return false; // Continue iteration.
// Potentially, if the subspace at the origin is visible.
return R_ViewerSubspaceIsVisible(pinfo.bspLeaf->subspace());
}

/**
* @return @c true if there are particles to be drawn.
*/
static dint listVisibleParticles(Map &map)
{
size_t numVisibleParticles;
::hasPoints = ::hasModels = ::hasLines = false;
::hasAdditive = ::hasNoBlend = false;
de::zap(::hasPointTexs);

hasPoints = hasModels = hasLines = hasBlend = hasNoBlend = false;
de::zap(hasPointTexs);

// First count how many particles are in the visible generators.
numParts = 0;
map.generatorIterator(countActiveGeneratorParticlesWorker, &numParts);
if(!numParts)
return false; // No visible generators.
// Count the total number of particles used by generators marked 'visible'.
::numParts = 0;
map.forAllGenerators([] (Generator &gen)
{
if(R_ViewerGeneratorIsVisible(gen))
{
::numParts += gen.activeParticleCount();
}
return LoopContinue;
});
if(!::numParts) return false;

// Allocate the particle depth sort buffer.
expandOrderBuffer(numParts);
expandOrderBuffer(::numParts);

// Populate the particle sort buffer and determine what type(s) of
// particle (model/point/line/etc...) we'll need to draw.
numVisibleParticles = 0;
map.generatorIterator(populateSortBuffer, &numVisibleParticles);
if(!numVisibleParticles)
return false; // No visible particles (all too far?).
size_t numVisibleParts = 0;
map.forAllGenerators([&numVisibleParts] (Generator &gen)
{
if(!R_ViewerGeneratorIsVisible(gen)) return LoopContinue; // Skip.

for(dint i = 0; i < gen.count; ++i)
{
ParticleInfo const &pinfo = gen.particleInfo()[i];

if(!particlePVisible(pinfo)) continue; // Skip.

// Skip particles too far from, or near to, the viewer.
dfloat const dist = de::max(pointDist(pinfo.origin), 1.f);
if(gen.def->maxDist != 0 && dist > gen.def->maxDist) continue;
if(dist < dfloat( ::particleNearLimit )) continue;

// This particle is visible. Add it to the sort buffer.
OrderedParticle *slot = &::order[numVisibleParts++];
slot->generator = &gen;
slot->particleId = i;
slot->distance = dist;

// Determine what type of particle this is, as this will affect how
// we go order our render passes and manipulate the render state.
dint const psType = gen.stages[pinfo.stage].type;
if(psType == PTC_POINT)
{
::hasPoints = true;
}
else if(psType == PTC_LINE)
{
::hasLines = true;
}
else if(psType >= PTC_TEXTURE && psType < PTC_TEXTURE + MAX_PTC_TEXTURES)
{
if(::ptctexname[psType - PTC_TEXTURE])
{
::hasPointTexs[psType - PTC_TEXTURE] = true;
}
else
{
::hasPoints = true;
}
}
else if(psType >= PTC_MODEL && psType < PTC_MODEL + MAX_PTC_MODELS)
{
::hasModels = true;
}

if(gen.blendmode() == BM_ADD)
{
::hasAdditive = true;
}
else
{
::hasNoBlend = true;
}
}
return LoopContinue;
});

// No visible particles?
if(!numVisibleParts) return false;

// This is the real number of possibly visible particles.
numParts = numVisibleParticles;
::numParts = numVisibleParts;

// Sort the order list back->front. A quicksort is fast enough.
qsort(order, numParts, sizeof(OrderedParticle), comparePOrder);
qsort(::order, ::numParts, sizeof(OrderedParticle), comparePOrder);

return true;
}
Expand Down Expand Up @@ -854,7 +855,7 @@ void Rend_RenderParticles(Map &map)
renderPass(false);
}

if(hasBlend)
if(hasAdditive)
{
// A second pass with additive blending.
// This makes the additive particles 'glow' through all other
Expand Down

0 comments on commit 94904e4

Please sign in to comment.