Browse Source

Combine all map data during build

master
Forest Belton 2 years ago
parent
commit
8fc404754a
7 changed files with 171 additions and 120 deletions
  1. +2
    -1
      .gitignore
  2. +13
    -11
      Makefile
  3. +0
    -71
      scripts/generate_coll_map.py
  4. +140
    -0
      scripts/generate_map.py
  5. +7
    -31
      src/bg.s
  6. +1
    -1
      src/main.s
  7. +8
    -5
      src/util.s

+ 2
- 1
.gitignore View File

@ -2,4 +2,5 @@
*.2bpp
is.gb
is.gb.sym
png/map/*.s
png/map/*.s
png/map/*.inc

+ 13
- 11
Makefile View File

@ -1,33 +1,35 @@
# Graphics
SPRITE_PNG := $(shell find png/sprite -type f -name '*.png')
MAP_PNG := $(shell find png/map -type f -name '*.png' -not -name '*_coll.png')
MAP_COLL := $(shell find png/map -type f -name '*_coll.png')
MAP_ASM := $(MAP_PNG:%.png:%.s)
ALL_2BPP := $(SPRITE_PNG:%.png=%.2bpp) $(MAP_PNG:%.png=%.map.2bpp) $(MAP_PNG:%.png=%.tiles.2bpp)
# Graphics (generated)
MAP_S := $(MAP_PNG:%.png=%.s)
MAP_INC := $(MAP_PNG:%.png=%.inc)
SPRITE_2BPP := $(SPRITE_PNG:%.png=%.2bpp)
# Code
SFILES := $(shell find src -type f -name '*.s')
OFILES := $(SFILES:%.s=%.o) $(MAP_ASM)
OFILES := $(SFILES:%.s=%.o) $(MAP_S:%.s=%.o)
.PHONY: clean
is.gb is.gb.sym:$(OFILES) $(ALL_2BPP)
is.gb is.gb.sym: $(OFILES)
rgblink -o $@ -n $@.sym $(OFILES)
rgbfix -v $@
$(OFILES): $(ALL_2BPP)
$(OFILES): $(MAP_INC)
$(OFILES): $(SPRITE_2BPP)
%.o: %.s
rgbasm -i inc -o $@ $<
png/map/%.tiles.2bpp png/map/%.map.2bpp: png/map/%.png
rgbgfx -u -t $(<:%.png=%.map.2bpp) -o $(<:%.png=%.tiles.2bpp) $<
png/map/%.s: png/map/%_coll.png
python scripts/generate_coll_map.py -o $@ $<
png/map/%.s png/map/%.inc: png/map/%.png png/map/%_coll.png
python scripts/generate_map.py $<
png/sprite/%.2bpp: png/sprite/%.png
rgbgfx -o $@ $<
clean:
rm -f is.gb is.gb.sym $(ALL_2BPP) $(OFILES) $(MAP_ASM)
rm -f is.gb is.gb.sym $(OFILES) $(MAP_S) $(MAP_INC) $(SPRITE_2BPP)

+ 0
- 71
scripts/generate_coll_map.py View File

@ -1,71 +0,0 @@
import argparse
import pathlib
import sys
from PIL import Image
GREEN = (0, 255, 0)
RED = (255, 0, 0)
def generate_coll_map(in_path: str, out_path: str) -> None:
png = Image.open(in_path).convert("RGB")
assert png.height % 8 == 0 and png.width % 8 == 0
out_bytes = []
bits = []
for y in range(png.height // 8):
for x in range(png.width // 8):
pixel = png.getpixel((x * 8, y * 8))
bit = None
if pixel == RED:
bit = 1
elif pixel == GREEN:
bit = 0
else:
print(f"unsupported pixel in collision map {pixel}", file=sys.stderr)
return
bits.append(bit)
if len(bits) == 8:
byte = sum([bit << i for i, bit in enumerate(bits)])
out_bytes.append(byte)
bits = []
lines = []
for line_no in range(0, len(out_bytes), 16):
line = out_bytes[line_no : line_no + 16]
lines.append(" DB " + ", ".join(["$%02X" % b for b in line]))
section = pathlib.Path(in_path).name.replace("_coll.png", "")
all_lines = "\n".join(lines)
with open(out_path, "wb") as outf:
outf.write(
f"""SECTION "{section.upper()} MAP", ROM0
; Values are provided in tiles
DEF {section}_width EQU {png.width // 8}
DEF {section}_height EQU {png.height // 8}
{section}_collision_map::
{all_lines}
""".encode(
"utf-8"
)
)
def main() -> None:
parser = argparse.ArgumentParser("generate_coll_map")
parser.add_argument("-o", "--output", required=True)
parser.add_argument("png")
args = parser.parse_args()
generate_coll_map(args.png, args.output)
if __name__ == "__main__":
main()

+ 140
- 0
scripts/generate_map.py View File

@ -0,0 +1,140 @@
import argparse
import pathlib
import subprocess
import sys
import tempfile
from typing import NoReturn
from PIL import Image
GREEN = (0, 255, 0)
RED = (255, 0, 0)
def abort(msg: str) -> NoReturn:
print(msg, file=sys.stderr)
sys.exit(1)
def generate_coll_map(in_path: pathlib.Path, width: int, height: int) -> str:
png = Image.open(in_path).convert("RGB")
if png.width % 8 != 0 or png.height % 8 != 0:
abort(f"file '{in_path}' has invalid dimensions (should be multiple of 8)")
if png.width // 8 != width or png.height // 8 != height:
abort(f"file '{in_path}' has different size from map")
out_bytes = []
bits = []
for y in range(png.height // 8):
for x in range(png.width // 8):
pixel = png.getpixel((x * 8, y * 8))
bit = None
if pixel == RED:
bit = 1
elif pixel == GREEN:
bit = 0
else:
abort(f"unsupported pixel in collision map: {pixel}")
bits.append(bit)
if len(bits) == 8:
byte = sum([bit << i for i, bit in enumerate(bits)])
out_bytes.append(byte)
bits = []
png.close()
lines = []
for line_no in range(0, len(out_bytes), 16):
line = out_bytes[line_no : line_no + 16]
lines.append(" DB " + ", ".join(["$%02X" % b for b in line]))
return "\n".join(lines)
def format_bytes(data: bytes) -> str:
lines = []
for line_no in range(0, len(data), 16):
line = data[line_no : line_no + 16]
lines.append(" DB " + ", ".join(["$%02X" % b for b in line]))
return "\n".join(lines)
def generate_map(pngfile: str) -> None:
pngpath = pathlib.Path(pngfile).resolve()
incpath = pngpath.parent / pngpath.name.replace(".png", ".inc")
spath = pngpath.parent / pngpath.name.replace(".png", ".s")
png = Image.open(pngpath)
if png.width % 8 != 0 or png.height % 8 != 0:
abort(f"file '{pngfile}' has invalid dimensions (should be multiple of 8)")
width = png.width // 8
height = png.height // 8
png.close()
with tempfile.NamedTemporaryFile() as tilef, tempfile.NamedTemporaryFile() as mapf:
subprocess.run(
[
"rgbgfx",
"-u",
"-t",
tilef.name,
"-o",
mapf.name,
pngfile,
]
)
map_data = format_bytes(tilef.read())
tile_data = format_bytes(mapf.read())
section = pngpath.name.replace(".png", "")
collpath = pngpath.parent / pngpath.name.replace(".png", "_coll.png")
coll_map = generate_coll_map(collpath, width, height)
with open(incpath, "w") as outf:
outf.write(
f"""DEF {section}_WIDTH EQU {width}
DEF {section}_HEIGHT EQU {height}
DEF {section}_NUM_TILES EQUS "({section}_TILES_end - {section}_TILES)"
DEF {section}_MAP_SIZE EQUS "({section}_MAP_end - {section}_MAP)"
ASSERT {section}_MAP_SIZE == {section}_WIDTH * {section}_HEIGHT
"""
)
with open(spath, "w") as outf:
outf.write(
f"""SECTION "MAP - {section}", ROMX
{section}_MAP::
{map_data}
{section}_MAP_end::
{section}_TILES::
{tile_data}
{section}_TILES_end::
{section}_COLLISION::
{coll_map}
{section}_COLLISION_end::
"""
)
def main() -> None:
parser = argparse.ArgumentParser("generate_map")
parser.add_argument("png")
args = parser.parse_args()
generate_map(args.png)
if __name__ == "__main__":
main()

+ 7
- 31
src/bg.s View File

@ -1,43 +1,19 @@
INCLUDE "hardware.inc"
; SCENE.Z80
;
; Map Source File.
;
; Info:
; Section : ROM0
; Bank : 0
; Map size : 20 x 18
; Tile set : C:\Users\case\Downloads\woods.gbr
; Plane count : 1 plane (8 bits)
; Plane order : Tiles are continues
; Tile offset : 0
; Split data : No
;
; This file was generated by GBMB v1.8
sceneWidth EQU 20
sceneHeight EQU 18
INCLUDE "png/map/intro.inc"
SECTION "BG0", ROM0
bg:
INCBIN "png/map/intro.map.2bpp"
bg_tiles:
INCBIN "png/map/intro.tiles.2bpp"
bg_tiles_end:
BG_Init::
; copy map
ld e, 18
ld e, intro_HEIGHT
ld bc, _SCRN0
ld hl, bg
ld hl, intro_MAP
.copy_map_row:
ld d, 20
ld d, intro_WIDTH
call memcpy
dec e
jr z, .done
ld d, 32 - 20
ld d, 32 - intro_WIDTH
.skip:
; skip over trailing part in vram
inc c
@ -50,9 +26,9 @@ BG_Init::
.done:
; copy tiles
ld hl, bg_tiles
ld hl, intro_TILES
ld bc, _VRAM
ld d, bg_tiles_end - bg_tiles
ld d, intro_NUM_TILES
call memcpy
ret

+ 1
- 1
src/main.s View File

@ -46,7 +46,7 @@ start:
call Keys_Update
call Player_Update
call waitForVblank
call wait_for_vblank
ld a, HIGH(_OAM)
call DMA_Start

+ 8
- 5
src/util.s View File

@ -1,17 +1,18 @@
SECTION "Utilities", ROM0
; Busy-wait until vertical blank occurs
waitForVblank::
wait_for_vblank::
ld a, [$ff41]
and 3
cp 1
jr nz, waitForVblank
jr nz, wait_for_vblank
ret
; Copies data between two regions
; Copy data between two regions
; @param bc Pointer to the destination region
; @param hl Pointer to the source region
; @param d Size (in bytes) to copy. Must be >0
; @destroy a, b, c, d, h, l
memcpy::
ld a, [hli]
ld [bc], a
@ -20,19 +21,21 @@ memcpy::
jr nz, memcpy
ret
; Fills a memory region with a value
; Fill a memory region with a value
; @param hl Pointer to the destination region
; @param a Byte to fill with
; @param c Number of bytes to fill. Must be >0
; @destroy a, c, hl
memset::
ld [hli], a
dec c
jr nz, memset
ret
; Divides a value by 10 (http://homepage.divms.uiowa.edu/~jones/bcd/decimal.html#division)
; Divide a value by 10 (http://homepage.divms.uiowa.edu/~jones/bcd/decimal.html#division)
; @param b Dividend
; @return a Quotient
; @destroy b
div10::
ld a, b
srl a

Loading…
Cancel
Save