/
layer_sprite.go
155 lines (134 loc) · 3.68 KB
/
layer_sprite.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package software
import (
"github.com/akatsuki105/dawngb/util"
. "github.com/akatsuki105/dawngb/util/datasize"
)
type spriteLayer struct {
active bool
r *Software
height int // 8 or 16
palette [4 * 8]rgb555
obpi uint8
z int // スプライト全体に履かせる下駄となるz-index(CGBのLCDC.0で決定)
}
type sprite struct {
x, y int
tileID int
xflip, yflip bool
palID int
bank uint
z int
}
func newSpriteLayer(r *Software) *spriteLayer {
l := &spriteLayer{
r: r,
height: 8,
}
return l
}
func (l *spriteLayer) drawScanline(y int, scanline []pixel) {
if l.active {
// 1行に描画されるスプライトの数は最大10個
sprites := [10]int{}
amount := 0
for i := 0; i < 40; i++ {
spriteIdx := i
spriteY := int(l.r.oam[spriteIdx*4+0]) - 16
if (spriteY <= y) && (y < spriteY+l.height) {
if amount < 10 {
sprites[amount] = spriteIdx
amount++
}
}
}
for i := amount - 1; i >= 0; i-- {
spriteIdx := sprites[i]
switch l.height {
case 8:
l.drawObjScanline8(spriteIdx, scanline, y)
case 16:
l.drawObjScanline16(spriteIdx, scanline, y)
}
}
}
}
func (l *spriteLayer) drawObjScanline8(spriteIdx int, scanline []pixel, y int) {
s := l.getSprite(spriteIdx)
z := (s.z + l.z)
tiledata := l.r.vram[(s.bank * (8 * KB)) : (s.bank*(8*KB))+0x1000]
tile := tiledata[s.tileID*16 : (s.tileID+1)*16] // 2bpp = 16byte
row := util.Flip(8, s.yflip, y-s.y) // (スプライトの一番上を0行目として)上から何行目か
planes := [2]uint8{tile[(row&0b111)*2], tile[(row&0b111)*2+1]}
palette := l.palette[s.palID*4 : (s.palID+1)*4]
for i := 0; i < 8; i++ {
lo := (planes[0] >> (7 - uint(i))) & 0b1
hi := (planes[1] >> (7 - uint(i))) & 0b1
colorID := int((hi << 1) | lo)
if colorID != 0 {
idx := s.x + util.Flip(8, s.xflip, i)
if (0 <= idx) && (idx < 160) {
if z >= scanline[idx].z || (scanline[idx].colorID == 0) {
scanline[idx].rgba = palette[colorID]
scanline[idx].z = z
scanline[idx].colorID = colorID
}
}
}
}
}
func (l *spriteLayer) drawObjScanline16(spriteIdx int, scanline []pixel, y int) {
s := l.getSprite(spriteIdx)
z := (s.z + l.z)
tiledata := l.r.vram[(s.bank * (8 * KB)) : (s.bank*(8*KB))+0x1000]
tileID := s.tileID & 0xFE
tile := tiledata[tileID*16 : (tileID+2)*16] // 2bpp
row := util.Flip(16, s.yflip, y-s.y) // (スプライトの一番上を0行目として)上から何行目か
var planes [2]uint8
if row < 8 {
planes = [2]uint8{tile[(row&0b111)*2], tile[(row&0b111)*2+1]}
} else {
planes = [2]uint8{tile[(row&0b111)*2+16], tile[(row&0b111)*2+17]}
}
palette := l.palette[s.palID*4 : (s.palID+1)*4]
for i := 0; i < 8; i++ {
lo := (planes[0] >> (7 - uint(i))) & 0b1
hi := (planes[1] >> (7 - uint(i))) & 0b1
colorID := int((hi << 1) | lo)
if colorID != 0 {
idx := s.x + util.Flip(8, s.xflip, i)
if (0 <= idx) && (idx < 160) {
if z >= scanline[idx].z || (scanline[idx].colorID == 0) {
scanline[idx].rgba = palette[colorID]
scanline[idx].z = z
scanline[idx].colorID = colorID
}
}
}
}
}
func (l *spriteLayer) getSprite(spriteIdx int) *sprite {
byte0 := l.r.oam[spriteIdx*4+0]
byte1 := l.r.oam[spriteIdx*4+1]
byte2 := l.r.oam[spriteIdx*4+2]
byte3 := l.r.oam[spriteIdx*4+3]
z := Z_SPR
if util.Bit(byte3, 7) {
z = Z_BD
}
bank := uint(0)
palID := (int(byte3>>4) & 0b1)
if l.r.model == 1 {
bank = uint(util.Btou8(util.Bit(byte3, 3)))
palID = int(byte3) & 0b111
}
return &sprite{
y: int(byte0) - 16,
x: int(byte1) - 8,
tileID: int(byte2),
xflip: util.Bit(byte3, 5),
yflip: util.Bit(byte3, 6),
palID: palID,
bank: bank,
z: z,
}
}