private static void ComputeFieldOfViewInDodecantZero( int radius, int observerHeight, Func<ICoordsCanon,bool> isOnBoard, Func<ICoordsCanon, int> targetHeight, Func<ICoordsCanon, int> terrainHeight, Action<ICoordsCanon> setFieldOfView) { #if TraceFOV radius = 16; DebugTracing.EnabledFags |= TraceFlag.FieldOfView; #endif var currentCoords = HexCoords.NewCanonCoords(0,1); if ( ! isOnBoard(currentCoords) ) return; if (radius > 0) setFieldOfView(currentCoords); var queue = new FovQueue(); var current = new FovCone( 2, new IntVector2D(1,2), new IntVector2D(0,1), new RiseRun(terrainHeight(currentCoords) - observerHeight, 1) ); while (current.Range <= radius) { current = ComputeFoVForRange( observerHeight, current, isOnBoard, targetHeight, terrainHeight, setFieldOfView, queue); } }
public void Enqueue(FovCone cone) { if (!IsCacheOccuppied) { Cache = cone; IsCacheOccuppied = true; } else if (Cache.Range == cone.Range && Cache.RiseRun == cone.RiseRun) { Cache = new FovCone(Cache.Range, Cache.VectorTop, cone.VectorBottom, cone.RiseRun); } else { Queue.Enqueue(Cache); Cache = cone; } }
static IntVector2D LogAndEnqueue(Action <FovCone> enqueue, int range, IntVector2D top, IntVector2D bottom, RiseRun riseRun, int code ) { if (top.GT(bottom)) { var cone = new FovCone(range + 1, top, bottom, riseRun); #if TraceFOV TraceFlag.FieldOfView.Trace(false, " EQ: ({0}) code: {1}", cone, code); #endif enqueue(cone); return(bottom); } else { return(top); } }
// This method has two main purposes: (1) it marks points inside the // portion that are within the radius as in the field of view, and // (2) it computes which portions of the following column are in the // field of view, and puts them on a work queue for later processing. // // A more sophisticated algorithm would say that a cell is visible if there is // *any* straight line segment that passes through *any* portion of the origin cell // and any portion of the target cell, passing through only transparent cells // along the way. This is the "Permissive Field Of View" algorithm, and it // is much harder to implement. // // Search for transitions from opaque to transparent or // transparent to opaque and use those to determine what // portions of the *next* column are visible from the origin. private static FovCone ComputeFoVForRange( int observerHeight, FovCone cone, Func<ICoordsCanon,bool> isOnBoard, Func<ICoordsCanon, int> targetHeight, Func<ICoordsCanon, int> terrainHeight, Action<ICoordsCanon> setFieldOfView, FovQueue queue) { Action<FovCone> enqueue = queue.Enqueue; var range = cone.Range; var topVector = cone.VectorTop; var topRiseRun = cone.RiseRun; var bottomVector = cone.VectorBottom; // track the overlap-cone between adjacent hexes as we move down. var overlapVector = cone.VectorTop; var hexX = XFromVector(range, topVector, true); #if TraceFOV TraceFlag.FieldOfView.Trace(false, "DQ: ({0}) from {1}", cone, hexX); #endif do { while (overlapVector.GT(bottomVector)) { var coordsCurrent = HexCoords.NewCanonCoords(hexX,range); var hexVectorBottom = VectorHexBottom(coordsCurrent); if (isOnBoard(coordsCurrent)) { #region Set current hex parameters var hexVectorTop = VectorHexTop(coordsCurrent); var hexElevation = targetHeight(coordsCurrent); var hexHeight = terrainHeight(coordsCurrent); var hexRiseRun = new RiseRun(hexHeight-observerHeight, range); #endregion #region Check visibility of current hex var riseRun = new RiseRun(hexElevation-observerHeight, GetRange(coordsCurrent)); if ( riseRun >= cone.RiseRun && bottomVector.LE(coordsCurrent.Vector) && coordsCurrent.Vector.LE(topVector) ) { setFieldOfView(coordsCurrent); #if TraceFOV TraceFlag.FieldOfView.Trace(false," Set visible: {0} / {1}; {2} >= {3}", MapCoordsDodecant(coordsCurrent), coordsCurrent.ToString(), riseRun, cone.RiseRun); #endif } #endregion #region Check hex transition if (hexRiseRun > topRiseRun) { topVector = LogAndEnqueue(enqueue, range, topVector, hexVectorTop, topRiseRun, 0); topRiseRun = hexRiseRun; } else if (hexRiseRun > cone.RiseRun) { topVector = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 1); topRiseRun = hexRiseRun; } else if (hexRiseRun < cone.RiseRun) { topVector = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 2); topRiseRun = cone.RiseRun; } #endregion } overlapVector = VectorMax(hexVectorBottom, bottomVector); if (hexVectorBottom.GT(bottomVector)) -- hexX; } #region Dequeue next cone if (queue.Count == 0) { topVector = LogAndEnqueue(enqueue, range, topVector, bottomVector, topRiseRun, 3); cone = queue.Dequeue(); break; } else { cone = queue.Dequeue(); if(cone.Range != range) { topVector = LogAndEnqueue(enqueue, range, topVector, bottomVector, topRiseRun, 4); break; } #if TraceFOV TraceFlag.FieldOfView.Trace(false, "DQ: ({0}) from {1}", cone, hexX); #endif } #endregion #region Check cone transition if (cone.RiseRun > topRiseRun) { topVector = LogAndEnqueue(enqueue, range, topVector, cone.VectorTop, topRiseRun, 5); topRiseRun = cone.RiseRun; } else if (cone.RiseRun < topRiseRun) { topVector = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 6); topRiseRun = cone.RiseRun; // TODO Why is this commented out? } #endregion overlapVector = topVector; bottomVector = cone.VectorBottom; } while(true); // Pick-up any cone portion at bottom of range still unprocessed if (topVector.GT(bottomVector)) LogAndEnqueue(enqueue, range, topVector, bottomVector, cone.RiseRun, 7); return cone; }