precomputed shade wall lighting
This commit is contained in:
@ -10,35 +10,47 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
//Реализация алгоритма от chilly_durango
|
||||
//https://www.reddit.com/r/roguelikedev/comments/5n1tx3/fov_algorithm_sharencompare/
|
||||
//Пока не побеждена засветка стен (или это у меня руки кривые) - сложность почти O(N^2)
|
||||
|
||||
//Не только у меня:
|
||||
//chilly_durango:
|
||||
//I turned down the Radio 4 in my car to think about this on the way home - that's how much this question has been
|
||||
// bugging me. The two best from all the awful compromises I could consider were:
|
||||
// - Only lighting walls with light from the player (cheap trick, dutch)
|
||||
// - Match light value for walls with the highest light value in adjacent floor cell visible to player (seems costly)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Why am I here? Well, I just don't know what to call it - I'm sure it's an established method, and I'm aware there are probably optimisations to be had. I thought if I roughed out the algorithm here, the r/roguelikedev community would surely be able to help! I haven't included optimisations here, but if anyone wants them I got 'em :)
|
||||
Why am I here? Well, I just don't know what to call it - I'm sure it's an established method, and I'm aware there are
|
||||
probably optimisations to be had. I thought if I roughed out the algorithm here, the r/roguelikedev community would
|
||||
surely be able to help! I haven't included optimisations here, but if anyone wants them I got 'em :)
|
||||
|
||||
Method
|
||||
|
||||
Beforehand
|
||||
|
||||
List the cells in your largest-possible FOV, storing X and Y values relative to the center.
|
||||
|
||||
Store the distance from the center for each cell, and sort the list by this in ascending order.
|
||||
|
||||
Store the range of angles occludedAngles by each cell in this list, in clockwise order as absolute integers only.
|
||||
|
||||
Create a 360-char string of 0s called EmptyShade, and a 360-char string of 1s called FullShade
|
||||
- List the cells in your largest-possible FOV, storing X and Y values relative to the center.
|
||||
- Store the distance from the center for each cell, and sort the list by this in ascending order.
|
||||
- Store the range of angles occludedAngles by each cell in this list, in clockwise order as absolute integers only.
|
||||
- Create a 360-char string of 0s called EmptyShade, and a 360-char string of 1s called FullShade
|
||||
|
||||
Runtime
|
||||
|
||||
Store two strings – CurrentShade and NextShade
|
||||
- Store two strings – CurrentShade and NextShade
|
||||
- Set CurrentShade to EmptyShade to start.
|
||||
- While CurrentShade =/= FullShade: step through the Cell List:
|
||||
|
||||
Set CurrentShade to EmptyShade to start.
|
||||
- If the distance to the current cell is not equal to the previous distance checked then replace the contents
|
||||
of the CurrentShade variable with the contents of the NextShade variable.
|
||||
|
||||
While CurrentShade =/= FullShade: step through the Cell List:
|
||||
- If the tested cell is opaque – for each angle in the range occludedAngles by the cell, place a 1 at the position
|
||||
determined by angle%360 in the NextShade string.
|
||||
|
||||
If the distance to the current cell is not equal to the previous distance checked then replace the contents of the
|
||||
CurrentShade variable with the contents of the NextShade variable.
|
||||
|
||||
If the tested cell is opaque – for each angle in the range occludedAngles by the cell, place a 1 at the position
|
||||
determined by angle%360 in the NextShade string.
|
||||
|
||||
For each angle in the range occludedAngles by the cell, add 1 to the lit value for that cell for each 0 encountered
|
||||
at the position determined by angle%360 in the CurrentShade string.
|
||||
- For each angle in the range occludedAngles by the cell, add 1 to the lit value for that cell for each 0
|
||||
encountered at the position determined by angle%360 in the CurrentShade string.
|
||||
|
||||
Notes
|
||||
Benefits
|
||||
@ -74,9 +86,8 @@ var errOutOfBounds = errors.New("Cell out of bounds")
|
||||
type Cell struct {
|
||||
types.Coords
|
||||
distance float64
|
||||
occludedAngles []int //indexes of cells in CellList
|
||||
lit int //lit value
|
||||
wasOccluded bool
|
||||
occludedAngles []int //angles occluded by this cell
|
||||
lit int //light "amount"
|
||||
}
|
||||
|
||||
type CellList []*Cell
|
||||
@ -88,10 +99,9 @@ func (a DistanceSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a DistanceSorter) Less(i, j int) bool { return a[i].distance < a[j].distance }
|
||||
|
||||
type precomputedShade struct {
|
||||
initCoords types.Coords
|
||||
originCoords types.Coords
|
||||
MaxTorchRadius int
|
||||
CellList CellList
|
||||
FovMap [][]int
|
||||
LightWalls bool
|
||||
}
|
||||
|
||||
@ -142,7 +152,7 @@ func (ps *precomputedShade) PrecomputeFovMap() {
|
||||
iterCoords := types.Coords{x, y}
|
||||
distance := zeroCoords.DistanceTo(iterCoords)
|
||||
if distance <= float64(max) {
|
||||
ps.CellList = append(ps.CellList, &Cell{iterCoords, distance, nil, 0, false})
|
||||
ps.CellList = append(ps.CellList, &Cell{iterCoords, distance, nil, 0})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,7 +196,7 @@ func (ps *precomputedShade) PrecomputeFovMap() {
|
||||
|
||||
func (ps *precomputedShade) recalc(level *gamemap.Level, initCoords types.Coords, radius int) {
|
||||
|
||||
ps.initCoords = initCoords
|
||||
ps.originCoords = initCoords
|
||||
|
||||
if radius > ps.MaxTorchRadius {
|
||||
radius = ps.MaxTorchRadius //fixme
|
||||
@ -235,11 +245,6 @@ func (ps *precomputedShade) recalc(level *gamemap.Level, initCoords types.Coords
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if level.GetTile(lc).BlocksSight {
|
||||
level.GetTile(lc).Visible = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,13 +254,29 @@ func (ps *precomputedShade) ComputeFov(level *gamemap.Level, initCoords types.Co
|
||||
|
||||
for _, cell := range ps.CellList {
|
||||
//fmt.Printf("\n coords: %v, distance: %f, lit: %d", cell.Coords, cell.distance, cell.lit)
|
||||
cs, err := ps.toLevelCoords(level, initCoords, cell.Coords)
|
||||
if cell.lit > 0 {
|
||||
cs, err := ps.toLevelCoords(level, initCoords, cell.Coords)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
level.GetTile(cs).Visible = true
|
||||
}
|
||||
|
||||
//light walls, crutch
|
||||
if level.GetTile(cs).BlocksSight && ps.LightWalls {
|
||||
if cell.IsAdjacentTo(&types.Coords{0,0}) {
|
||||
level.GetTile(cs).Visible = true
|
||||
} else {
|
||||
for _, maybeNb := range ps.CellList {
|
||||
if //int(maybeNb.distance) == int(cell.distance-1) &&
|
||||
maybeNb.IsAdjacentTo(&cell.Coords) &&
|
||||
(maybeNb.X == cell.X || maybeNb.Y == cell.Y) &&
|
||||
maybeNb.lit > 0 { //magic constant!
|
||||
level.GetTile(cs).Visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,7 +289,7 @@ func (ps *precomputedShade) toLevelCoords(level *gamemap.Level, initCoords, rela
|
||||
}
|
||||
|
||||
func (ps *precomputedShade) fromLevelCoords(lc types.Coords) types.Coords {
|
||||
relativeCoords := types.Coords{lc.X - ps.initCoords.X, lc.Y - ps.initCoords.Y}
|
||||
relativeCoords := types.Coords{lc.X - ps.originCoords.X, lc.Y - ps.originCoords.Y}
|
||||
return relativeCoords
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user