Skip to content

Commit

Permalink
Refactor|World|Map: Extracted algorithm for map element path intercep…
Browse files Browse the repository at this point in the history
…tion

Map now provides all the API mechanisms to implement this algorithm
outside of the map itself. In the future this can be wrapped up into
class similar to LineOfSight.
  • Loading branch information
danij-deng committed Oct 11, 2013
1 parent ece0b61 commit 104d96c
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 174 deletions.
8 changes: 4 additions & 4 deletions doomsday/api/api_map.h
Expand Up @@ -95,15 +95,15 @@ struct intercept_s;
///@}

/**
* @defgroup lineBoxIteratorFlags Line Box Iterator Flags
* @defgroup lineIteratorFlags Line Iterator Flags
* @ingroup apiFlags world
*/
///@{
#define LBF_SECTOR 0x1 ///< Process map lines defining sectors
#define LBF_POLYOBJ 0x2 ///< Process map lines defining polyobjs
#define LIF_SECTOR 0x1 ///< Process map lines defining sectors
#define LIF_POLYOBJ 0x2 ///< Process map lines defining polyobjs

/// Process all map line types.
#define LBF_ALL LBF_SECTOR | LBF_POLYOBJ
#define LIF_ALL LIF_SECTOR | LIF_POLYOBJ
///@}

/**
Expand Down
46 changes: 24 additions & 22 deletions doomsday/client/include/world/map.h
Expand Up @@ -451,7 +451,10 @@ class Map
Polyobj **poly, Plane **plane, Surface **surface) const;

int mobjBoxIterator(AABoxd const &box,
int (*callback) (struct mobj_s *, void *), void *context = 0) const;
int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const;

int mobjPathIterator(Vector2d const &from, Vector2d const &to,
int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const;

/**
* Lines and Polyobj lines (note polyobj lines are iterated first).
Expand All @@ -461,24 +464,30 @@ class Map
* equal to this will be skipped over (can be used to avoid processing
* a line multiple times during complex / non-linear traversals.
*
* @param flags @ref lineBoxIteratorFlags
* @param flags @ref lineIteratorFlags
*/
int lineBoxIterator(AABoxd const &box, int flags,
int (*callback) (Line *, void *), void *context = 0) const;
int (*callback) (Line *line, void *context), void *context = 0) const;

/// @copydoc lineBoxIterator()
inline int lineBoxIterator(AABoxd const &box,
int (*callback) (Line *, void *), void *context = 0) const
int (*callback) (Line *line, void *context), void *context = 0) const
{
return lineBoxIterator(box, LBF_ALL, callback, context);
return lineBoxIterator(box, LIF_ALL, callback, context);
}

/**
* @param flags @ref lineIteratorFlags
*/
int linePathIterator(Vector2d const &from, Vector2d const &to, int flags,
int (*callback) (Line *line, void *context), void *context = 0) const;

int bspLeafBoxIterator(AABoxd const &box, Sector *sector,
int (*callback) (BspLeaf *, void *), void *context = 0) const;
int (*callback) (BspLeaf *bspLeaf, void *context), void *context = 0) const;

/// @copydoc bspLeafsBoxIterator()
inline int bspLeafBoxIterator(AABoxd const &box,
int (*callback) (BspLeaf *, void *), void *context = 0) const
int (*callback) (BspLeaf *bspLeaf, void *context), void *context = 0) const
{
return bspLeafBoxIterator(box, 0, callback, context);
}
Expand All @@ -490,7 +499,8 @@ class Map
* multiple times during complex / non-linear traversals.
*/
int polyobjBoxIterator(AABoxd const &box,
int (*callback) (struct polyobj_s *, void *), void *context = 0) const;
int (*callback) (struct polyobj_s *polyobj, void *context),
void *context = 0) const;

/**
* The callback function will be called once for each line that crosses
Expand All @@ -506,10 +516,10 @@ class Map
* above or under the sector.
*/
int mobjTouchedSectorIterator(struct mobj_s *mo,
int (*callback) (Sector *, void *), void *context = 0) const;
int (*callback) (Sector *sector, void *context), void *context = 0) const;

int lineTouchingMobjIterator(Line *line,
int (*callback) (struct mobj_s *, void *), void *context = 0) const;
int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const;

/**
* Increment validCount before using this. 'func' is called for each mobj
Expand All @@ -520,29 +530,21 @@ class Map
* a bunch of LineMobjs iterations.)
*/
int sectorTouchingMobjIterator(Sector *sector,
int (*callback) (struct mobj_s *, void *), void *context = 0) const;

/**
* Trace 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 pathTraverse(de::Vector2d const &from, de::Vector2d const &to, int flags,
traverser_t callback, void *context = 0);
int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const;

/**
* Retrieve an immutable copy of the LOS trace line state.
*
* @todo Map should not own this data.
*/
divline_t const &traceLine() const;
divline_t &traceLine() const;

/**
* Retrieve an immutable copy of the LOS TraceOpening state.
*
* @todo Map should not own this data.
*/
TraceOpening const &traceOpening() const;
TraceOpening &traceOpening() const;

/**
* Update the TraceOpening state for according to the opening defined by the
Expand Down Expand Up @@ -675,7 +677,7 @@ class Map
*
* @note This result is not cached. May return @c 0 if no bias sources exist.
*/
BiasSource *biasSourceNear(de::Vector3d const &point) const;
BiasSource *biasSourceNear(Vector3d const &point) const;

/**
* Lookup the unique index for the given bias @a source.
Expand Down
131 changes: 125 additions & 6 deletions doomsday/client/src/world/api_map.cpp
Expand Up @@ -45,6 +45,7 @@

#ifdef __CLIENT__
# include "render/lightgrid.h"
# include "render/r_main.h" // validCount
#endif

using namespace de;
Expand Down Expand Up @@ -1624,38 +1625,156 @@ DENG_EXTERN_C int BspLeaf_BoxIterator(AABoxd const *box, Sector *sector,
return App_World().map().bspLeafBoxIterator(*box, sector, callback, context);
}

static void collectLineIntercept(Line &line, divline_t const &traceLos)
{
fixed_t lineFromX[2] = { DBL2FIX(line.fromOrigin().x), DBL2FIX(line.fromOrigin().y) };
fixed_t lineToX[2] = { DBL2FIX( line.toOrigin().x), DBL2FIX( line.toOrigin().y) };

// Is this line crossed?
// Avoid precision problems with two routines.
int s1, s2;
if(traceLos.direction[VX] > FRACUNIT * 16 || traceLos.direction[VY] > FRACUNIT * 16 ||
traceLos.direction[VX] < -FRACUNIT * 16 || traceLos.direction[VY] < -FRACUNIT * 16)
{
s1 = V2x_PointOnLineSide(lineFromX, traceLos.origin, traceLos.direction);
s2 = V2x_PointOnLineSide(lineToX, traceLos.origin, traceLos.direction);
}
else
{
s1 = line.pointOnSide(Vector2d(FIX2FLT(traceLos.origin[VX]),
FIX2FLT(traceLos.origin[VY]))) < 0;
s2 = line.pointOnSide(Vector2d(FIX2FLT(traceLos.origin[VX] + traceLos.direction[VX]),
FIX2FLT(traceLos.origin[VY] + traceLos.direction[VY]))) < 0;
}
if(s1 == s2) return;

fixed_t lineDirectionX[2] = { DBL2FIX(line.direction().x), DBL2FIX(line.direction().y) };

// On the correct side of the trace origin?
float distance = FIX2FLT(V2x_Intersection(lineFromX, lineDirectionX,
traceLos.origin, traceLos.direction));
if(distance >= 0)
{
P_AddIntercept(ICPT_LINE, distance, &line);
}
}

static int collectCellLineInterceptsWorker(Line *line, void *context)
{
collectLineIntercept(*line, *static_cast<divline_t *>(context));
return false; // Continue iteration.
}

static void collectMobjIntercept(mobj_t &mobj, divline_t const &traceLos)
{
// Ignore cameras.
if(mobj.dPlayer && (mobj.dPlayer->flags & DDPF_CAMERA))
return;

// Check a corner to corner crossection for hit.
AABoxd const aaBox = Mobj_AABox(mobj);

vec2d_t from, to;
if((traceLos.direction[VX] ^ traceLos.direction[VY]) > 0)
{
// \ Slope
V2d_Set(from, aaBox.minX, aaBox.maxY);
V2d_Set(to, aaBox.maxX, aaBox.minY);
}
else
{
// / Slope
V2d_Set(from, aaBox.minX, aaBox.minY);
V2d_Set(to, aaBox.maxX, aaBox.maxY);
}

// Is this line crossed?
if(Divline_PointOnSide(&traceLos, from) == Divline_PointOnSide(&traceLos, to))
return;

// Calculate interception point.
divline_t dl;
dl.origin[VX] = DBL2FIX(from[VX]);
dl.origin[VY] = DBL2FIX(from[VY]);
dl.direction[VX] = DBL2FIX(to[VX] - from[VX]);
dl.direction[VY] = DBL2FIX(to[VY] - from[VY]);
coord_t distance = FIX2FLT(Divline_Intersection(&dl, &traceLos));

// On the correct side of the trace origin?
if(distance >= 0)
{
P_AddIntercept(ICPT_MOBJ, distance, &mobj);
}
}

static int collectCellMobjInterceptsWorker(mobj_t *mobj, void *context)
{
collectMobjIntercept(*mobj, *static_cast<divline_t *>(context));
return false; // Continue iteration.
}

static int traverseMapPath(Map &map, Vector2d const &from, Vector2d const &to,
int flags, traverser_t callback, void *context = 0)
{
// A new trace begins.
divline_t &traceLine = map.traceLine();

traceLine.origin[VX] = FLT2FIX(from.x);
traceLine.origin[VY] = FLT2FIX(from.y);
traceLine.direction[VX] = FLT2FIX(to.x - from.x);
traceLine.direction[VY] = FLT2FIX(to.y - from.y);

P_ClearIntercepts();
validCount++;

// Step #1: Collect intercepts.
if(flags & PT_ADDLINES)
{
map.linePathIterator(from, to, LIF_ALL, collectCellLineInterceptsWorker, &traceLine);
}
if(flags & PT_ADDMOBJS)
{
map.mobjPathIterator(from, to, collectCellMobjInterceptsWorker, &traceLine);
}

// Step #2: Process sorted intercepts.
return P_TraverseIntercepts(callback, context);
}

#undef P_PathTraverse2
DENG_EXTERN_C int P_PathTraverse2(const_pvec2d_t from, const_pvec2d_t to,
int flags, traverser_t callback, void *context)
{
if(!App_World().hasMap()) return false; // Continue iteration.
return App_World().map().pathTraverse(from, to, flags, callback, context);
return traverseMapPath(App_World().map(), from, to, flags, callback, context);
}

#undef P_PathTraverse
DENG_EXTERN_C int P_PathTraverse(const_pvec2d_t from, const_pvec2d_t to,
int flags, traverser_t callback)
{
if(!App_World().hasMap()) return false; // Continue iteration.
return App_World().map().pathTraverse(from, to, flags, callback);
return traverseMapPath(App_World().map(), from, to, flags, callback);
}

#undef P_PathXYTraverse2
DENG_EXTERN_C int P_PathXYTraverse2(coord_t fromX, coord_t fromY,
coord_t toX, coord_t toY, int flags, traverser_t callback, void *context)
{
if(!App_World().hasMap()) return false; // Continue iteration.
return App_World().map().pathTraverse(Vector2d(fromX, fromY), Vector2d(toX, toY),
flags, callback, context);
return traverseMapPath(App_World().map(),
Vector2d(fromX, fromY), Vector2d(toX, toY),
flags, callback, context);
}

#undef P_PathXYTraverse
DENG_EXTERN_C int P_PathXYTraverse(coord_t fromX, coord_t fromY, coord_t toX, coord_t toY, int flags,
traverser_t callback)
{
if(!App_World().hasMap()) return false; // Continue iteration.
return App_World().map().pathTraverse(Vector2d(fromX, fromY), Vector2d(toX, toY),
flags, callback);
return traverseMapPath(App_World().map(),
Vector2d(fromX, fromY), Vector2d(toX, toY),
flags, callback);
}

#undef P_CheckLineSight
Expand Down

0 comments on commit 104d96c

Please sign in to comment.