Skip to content

Commit b4308f6

Browse files
committed
Refactored raycaster levels/slices into private funcs/vars
1 parent 23dfd9b commit b4308f6

File tree

4 files changed

+113
-83
lines changed

4 files changed

+113
-83
lines changed

camera.go

+93-55
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,10 @@ type Camera struct {
6767
sky *ebiten.Image
6868

6969
//--texture width--//
70-
texWidth int
71-
72-
//--slices--//
73-
s []*image.Rectangle
70+
texSize int
7471

7572
//--structs that contain rects and tints for each level render--//
76-
levels []*Level
73+
levels []*level
7774

7875
// zbuffer for sprite casting
7976
zBuffer []float64
@@ -83,18 +80,17 @@ type Camera struct {
8380
spriteOrder []int
8481
spriteDistance []float64
8582

86-
spriteLvls []*Level
83+
spriteLvls []*level
8784
tex *TextureHandler
8885

89-
floorLvl *HorLevel
86+
floorLvl *horLevel
9087

9188
// used for concurrency
9289
semaphore chan struct{}
9390
}
9491

9592
// NewCamera initalizes a Camera object
96-
func NewCamera(width int, height int, texWid int, mapObj Map, slices []*image.Rectangle,
97-
levels []*Level, horizontalLevel *HorLevel, spriteLvls []*Level, tex *TextureHandler) *Camera {
93+
func NewCamera(width int, height int, texSize int, mapObj Map, tex *TextureHandler) *Camera {
9894

9995
fmt.Printf("Initializing Camera\n")
10096

@@ -121,12 +117,11 @@ func NewCamera(width int, height int, texWid int, mapObj Map, slices []*image.Re
121117
c.w = width
122118
c.h = height
123119
c.pitch = 0
124-
c.texWidth = texWid
125-
c.s = slices
126-
c.levels = levels
120+
c.texSize = texSize
121+
c.tex = tex
127122

128-
c.floorLvl = horizontalLevel
129-
c.spriteLvls = spriteLvls
123+
c.levels = c.createLevels(mapObj.NumLevels())
124+
c.floorLvl = c.createFloorLevel()
130125

131126
// set zbuffer based on screen width
132127
c.zBuffer = make([]float64, width)
@@ -138,7 +133,7 @@ func NewCamera(width int, height int, texWid int, mapObj Map, slices []*image.Re
138133
c.mapHeight = len(firstLevel)
139134

140135
c.sprites = []Sprite{}
141-
c.tex = tex
136+
c.updateSpriteLevels(16)
142137

143138
// initialize a pool of channels to limit concurrent floor and sprite casting
144139
// from https://pocketgophers.com/limit-concurrent-use/
@@ -150,10 +145,25 @@ func NewCamera(width int, height int, texWid int, mapObj Map, slices []*image.Re
150145
return c
151146
}
152147

148+
func (c *Camera) SetFloorTexture(floor *ebiten.Image) {
149+
c.floor = floor
150+
}
151+
152+
func (c *Camera) SetSkyTexture(sky *ebiten.Image) {
153+
c.sky = sky
154+
}
155+
153156
// Update - updates the camera view
154157
func (c *Camera) Update(sprites []Sprite) {
155158
// clear horizontal buffer by making a new one
156-
c.floorLvl.Clear(c.w, c.h)
159+
c.floorLvl.clear(c.w, c.h)
160+
161+
if len(sprites) != len(c.sprites) {
162+
// sprite buffer may need to be increased in size
163+
c.updateSpriteLevels(len(sprites))
164+
} else {
165+
c.clearAllSpriteLevel()
166+
}
157167

158168
//--do raycast--//
159169
c.sprites = sprites
@@ -162,7 +172,7 @@ func (c *Camera) Update(sprites []Sprite) {
162172

163173
func (c *Camera) raycast() {
164174
// cast level
165-
numLevels := cap(c.levels)
175+
numLevels := c.mapObj.NumLevels()
166176
var wg sync.WaitGroup
167177
for i := 0; i < numLevels; i++ {
168178
wg.Add(1)
@@ -195,14 +205,7 @@ func (c *Camera) raycast() {
195205
func (c *Camera) asyncCastLevel(levelNum int, wg *sync.WaitGroup) {
196206
defer wg.Done()
197207

198-
var rMap [][]int
199-
200-
numLevels := c.mapObj.NumLevels()
201-
if levelNum < numLevels {
202-
rMap = c.mapObj.Level(levelNum)
203-
} else {
204-
rMap = c.mapObj.Level(numLevels - 1) // if above highest level just keep extending last one up
205-
}
208+
rMap := c.mapObj.Level(levelNum)
206209

207210
for x := 0; x < c.w; x++ {
208211
c.castLevel(x, rMap, c.levels[levelNum], levelNum, wg)
@@ -222,7 +225,7 @@ func (c *Camera) asyncCastSprite(spriteNum int, wg *sync.WaitGroup) {
222225

223226
// credit : Raycast loop and setting up of vectors for matrix calculations
224227
// courtesy - http://lodev.org/cgtutor/raycasting.html
225-
func (c *Camera) castLevel(x int, grid [][]int, lvl *Level, levelNum int, wg *sync.WaitGroup) {
228+
func (c *Camera) castLevel(x int, grid [][]int, lvl *level, levelNum int, wg *sync.WaitGroup) {
226229
var _cts, _sv []*image.Rectangle
227230
var _st []*color.RGBA
228231

@@ -356,17 +359,17 @@ func (c *Camera) castLevel(x int, grid [][]int, lvl *Level, levelNum int, wg *sy
356359
wallX -= math.Floor(wallX)
357360

358361
//x coordinate on the texture
359-
texX := int(wallX * float64(c.texWidth))
362+
texX := int(wallX * float64(c.texSize))
360363
if side == 0 && rayDirX > 0 {
361-
texX = c.texWidth - texX - 1
364+
texX = c.texSize - texX - 1
362365
}
363366

364367
if side == 1 && rayDirY < 0 {
365-
texX = c.texWidth - texX - 1
368+
texX = c.texSize - texX - 1
366369
}
367370

368371
//--set current texture slice to be slice x--//
369-
_cts[x] = c.s[texX]
372+
_cts[x] = c.tex.slices[texX]
370373

371374
//--set height of slice--//
372375
_sv[x].Min.Y = drawStart
@@ -445,14 +448,14 @@ func (c *Camera) castLevel(x int, grid [][]int, lvl *Level, levelNum int, wg *sy
445448
currentFloorY := weight*floorYWall + (1.0-weight)*rayPosY
446449

447450
var floorTexX, floorTexY int
448-
floorTexX = int(currentFloorX*float64(c.texWidth)) % c.texWidth
449-
floorTexY = int(currentFloorY*float64(c.texWidth)) % c.texWidth
451+
floorTexX = int(currentFloorX*float64(c.texSize)) % c.texSize
452+
floorTexY = int(currentFloorY*float64(c.texSize)) % c.texSize
450453

451454
//floor
452455
// buffer[y][x] = (texture[3][texWidth * floorTexY + floorTexX] >> 1) & 8355711;
453456
// the same vertical slice method cannot be used for floor rendering
454457
floorTexNum := 0
455-
floorTex := c.floorLvl.TexRGBA[floorTexNum]
458+
floorTex := c.floorLvl.texRGBA[floorTexNum]
456459

457460
//pixel := floorTex.RGBAAt(floorTexX, floorTexY)
458461
pxOffset := floorTex.PixOffset(floorTexX, floorTexY)
@@ -472,11 +475,11 @@ func (c *Camera) castLevel(x int, grid [][]int, lvl *Level, levelNum int, wg *sy
472475
pixel.B = uint8(float64(pixel.B) * float64(pixelSt.B) / 256)
473476

474477
//c.horLvl.HorBuffer.SetRGBA(x, y, pixel)
475-
pxOffset = c.floorLvl.HorBuffer.PixOffset(x, y)
476-
c.floorLvl.HorBuffer.Pix[pxOffset] = pixel.R
477-
c.floorLvl.HorBuffer.Pix[pxOffset+1] = pixel.G
478-
c.floorLvl.HorBuffer.Pix[pxOffset+2] = pixel.B
479-
c.floorLvl.HorBuffer.Pix[pxOffset+3] = pixel.A
478+
pxOffset = c.floorLvl.horBuffer.PixOffset(x, y)
479+
c.floorLvl.horBuffer.Pix[pxOffset] = pixel.R
480+
c.floorLvl.horBuffer.Pix[pxOffset+1] = pixel.G
481+
c.floorLvl.horBuffer.Pix[pxOffset+2] = pixel.B
482+
c.floorLvl.horBuffer.Pix[pxOffset+3] = pixel.A
480483
}
481484
}()
482485
}
@@ -513,7 +516,7 @@ func (c *Camera) castSprite(spriteOrdIndex int) {
513516
var uDiv float64 = 1 / sprite.Scale()
514517
var vDiv float64 = 1 / sprite.Scale()
515518

516-
var vMove float64 = -(sprite.PosZ()-0.5)*float64(c.texWidth)*2 + sprite.VerticalOffset()
519+
var vMove float64 = -(sprite.PosZ()-0.5)*float64(c.texSize)*2 + sprite.VerticalOffset()
517520

518521
vMoveScreen := int(vMove/transformY) + c.pitch + int(c.posZ/transformY)
519522

@@ -558,11 +561,11 @@ func (c *Camera) castSprite(spriteOrdIndex int) {
558561
//3) it's on the screen (right)
559562
//4) ZBuffer, with perpendicular distance
560563
if transformY > 0 && stripe > 0 && stripe < c.w && transformY < c.zBuffer[stripe] {
561-
var spriteLvl *Level
564+
var spriteLvl *level
562565
if !renderSprite {
563566
renderSprite = true
564567
spriteLvl = c.makeSpriteLevel(spriteOrdIndex)
565-
spriteSlices = MakeSlices(spriteTexWidth, spriteTexHeight, spriteTexRect.Min.X, spriteTexRect.Min.Y)
568+
spriteSlices = makeSlices(spriteTexWidth, spriteTexHeight, spriteTexRect.Min.X, spriteTexRect.Min.Y)
566569
} else {
567570
spriteLvl = c.spriteLvls[spriteOrdIndex]
568571
}
@@ -611,14 +614,51 @@ func (c *Camera) castSprite(spriteOrdIndex int) {
611614
}
612615
}
613616

614-
func (c *Camera) UpdateSpriteLevels(spriteLvls []*Level) {
615-
// TODO: this should be refactored rather than bouncing it around between game/camera
616-
c.spriteLvls = spriteLvls
617+
// creates level slices for raycasting each level
618+
func (c *Camera) createLevels(numLevels int) []*level {
619+
levelArr := make([]*level, numLevels)
620+
621+
for i := 0; i < numLevels; i++ {
622+
levelArr[i] = new(level)
623+
levelArr[i].Sv = sliceView(c.w, c.h)
624+
levelArr[i].Cts = make([]*image.Rectangle, c.w)
625+
levelArr[i].St = make([]*color.RGBA, c.w)
626+
levelArr[i].CurrTex = make([]*ebiten.Image, c.w)
627+
}
628+
629+
return levelArr
630+
}
631+
632+
// creates floor slices for raycasting floor
633+
func (c *Camera) createFloorLevel() *horLevel {
634+
horizontalLevel := new(horLevel)
635+
horizontalLevel.clear(c.w, c.h)
636+
horizontalLevel.texRGBA = []*image.RGBA{c.tex.FloorTex}
637+
return horizontalLevel
617638
}
618639

619-
func (c *Camera) makeSpriteLevel(spriteOrdIndex int) *Level {
620-
spriteLvl := new(Level)
621-
spriteLvl.Sv = SliceView(c.w, c.h)
640+
// updates sprite slice array as a level
641+
func (c *Camera) updateSpriteLevels(spriteCapacity int) {
642+
if c.spriteLvls != nil {
643+
capacity := len(c.spriteLvls)
644+
if spriteCapacity <= capacity {
645+
// no need to grow, just need to clear it out
646+
c.clearAllSpriteLevel()
647+
return
648+
}
649+
650+
for capacity <= spriteCapacity {
651+
capacity *= 2
652+
}
653+
654+
spriteCapacity = capacity
655+
}
656+
c.spriteLvls = make([]*level, spriteCapacity)
657+
}
658+
659+
func (c *Camera) makeSpriteLevel(spriteOrdIndex int) *level {
660+
spriteLvl := new(level)
661+
spriteLvl.Sv = sliceView(c.w, c.h)
622662
spriteLvl.Cts = make([]*image.Rectangle, c.w)
623663
spriteLvl.St = make([]*color.RGBA, c.w)
624664
spriteLvl.CurrTex = make([]*ebiten.Image, c.w)
@@ -628,6 +668,12 @@ func (c *Camera) makeSpriteLevel(spriteOrdIndex int) *Level {
628668
return spriteLvl
629669
}
630670

671+
func (c *Camera) clearAllSpriteLevel() {
672+
for i := 0; i < len(c.spriteLvls); i++ {
673+
c.clearSpriteLevel(i)
674+
}
675+
}
676+
631677
func (c *Camera) clearSpriteLevel(spriteOrdIndex int) {
632678
c.spriteLvls[spriteOrdIndex] = nil
633679
}
@@ -701,14 +747,6 @@ func (c *Camera) getValidCameraMove(moveX, moveY float64, checkAlternate bool) (
701747
return posX, posY
702748
}
703749

704-
func (c *Camera) SetFloorTexture(floor *ebiten.Image) {
705-
c.floor = floor
706-
}
707-
708-
func (c *Camera) SetSkyTexture(sky *ebiten.Image) {
709-
c.sky = sky
710-
}
711-
712750
// Set camera position vector
713751
func (c *Camera) SetPosition(pos *geom.Vector2) {
714752
c.pos = pos

level.go

+12-16
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"github.com/hajimehoshi/ebiten/v2"
88
)
99

10-
// Level --struct to represent rects and tints of vertical level slices --//
11-
type Level struct {
10+
// level --struct to represent rects and tints of vertical level slices --//
11+
type level struct {
1212
// Sv --texture draw location
1313
Sv []*image.Rectangle
1414

@@ -22,8 +22,8 @@ type Level struct {
2222
CurrTex []*ebiten.Image
2323
}
2424

25-
// SliceView Creates rectangle slices for each x in width.
26-
func SliceView(width, height int) []*image.Rectangle {
25+
// sliceView Creates rectangle slices for each x in width.
26+
func sliceView(width, height int) []*image.Rectangle {
2727
arr := make([]*image.Rectangle, width)
2828

2929
for x := 0; x < width; x++ {
@@ -34,19 +34,15 @@ func SliceView(width, height int) []*image.Rectangle {
3434
return arr
3535
}
3636

37-
// HorLevel is for handling horizontal renders that cannot use vertical slices (e.g. floor, ceiling)
38-
type HorLevel struct {
39-
// HorBuffer is the image representing the pixels to render during the update
40-
HorBuffer *image.RGBA
37+
// horLevel is for handling horizontal renders that cannot use vertical slices (e.g. floor, ceiling)
38+
type horLevel struct {
39+
// horBuffer is the image representing the pixels to render during the update
40+
horBuffer *image.RGBA
4141

42-
// TexRGBA contains image.RGBA textures used as sources for the HorBuffer
43-
TexRGBA []*image.RGBA
42+
// texRGBA contains image.RGBA textures used as sources for the HorBuffer
43+
texRGBA []*image.RGBA
4444
}
4545

46-
func (h *HorLevel) Clear(width, height int) {
47-
h.HorBuffer = image.NewRGBA(image.Rect(0, 0, width, height))
48-
}
49-
50-
func (h *HorLevel) Set(x, y int, c color.Color) {
51-
h.HorBuffer.Set(x, y, c)
46+
func (h *horLevel) clear(width, height int) {
47+
h.horBuffer = image.NewRGBA(image.Rect(0, 0, width, height))
5248
}

render.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func (c *Camera) Draw(screen *ebiten.Image) {
1515
screen.Clear()
1616

1717
//--draw basic sky and floor--//
18-
texRect := image.Rect(0, 0, c.texWidth, c.texWidth)
18+
texRect := image.Rect(0, 0, c.texSize, c.texSize)
1919
whiteRGBA := &color.RGBA{255, 255, 255, 255}
2020

2121
floorRect := image.Rect(0, int(float64(c.h)*0.5)+c.pitch,
@@ -33,7 +33,7 @@ func (c *Camera) Draw(screen *ebiten.Image) {
3333
}
3434

3535
// draw textured floor
36-
floorImg := ebiten.NewImageFromImage(c.floorLvl.HorBuffer)
36+
floorImg := ebiten.NewImageFromImage(c.floorLvl.horBuffer)
3737
if floorImg == nil {
3838
log.Fatal("floorImg is nil")
3939
} else {

texture.go

+6-10
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@ import (
99
type TextureHandler struct {
1010
slices []*image.Rectangle
1111
Textures []*ebiten.Image
12+
13+
// TODO: an interface would better suit texture handling
14+
FloorTex *image.RGBA
1215
}
1316

14-
func NewTextureHandler(texWidth int) *TextureHandler {
17+
func NewTextureHandler(texSize int) *TextureHandler {
1518
t := &TextureHandler{}
1619

17-
//--for clarity in slice loop--//
18-
texHeight := texWidth
19-
2020
//--init array--//
21-
t.slices = MakeSlices(texWidth, texHeight, 0, 0)
21+
t.slices = makeSlices(texSize, texSize, 0, 0)
2222

2323
return t
2424
}
2525

26-
func MakeSlices(width, height, xOffset, yOffset int) []*image.Rectangle {
26+
func makeSlices(width, height, xOffset, yOffset int) []*image.Rectangle {
2727
newSlices := make([]*image.Rectangle, width)
2828

2929
//--loop through creating a "slice" for each texture x--//
@@ -35,7 +35,3 @@ func MakeSlices(width, height, xOffset, yOffset int) []*image.Rectangle {
3535

3636
return newSlices
3737
}
38-
39-
func (t *TextureHandler) GetSlices() []*image.Rectangle {
40-
return t.slices
41-
}

0 commit comments

Comments
 (0)