Lazily load sprite image data
This cuts memory use significantly, since many sprites in an object are never used. We can get savings over time by evicting sprites when they go out of scope, but that's, well, out of scope. To achieve this, I introduce an assetstore package that is in charge of loading things from the filesystem. This also allows some lingering case-sensitivity issues to be handled cleanly. I'd hoped that creating fewer ebiten.Image instances would help CPU usage, but that doesn't seem to be the case.
This commit is contained in:
@@ -2,32 +2,25 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"image"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
|
||||
"code.ur.gs/lupine/ordoor/internal/conv"
|
||||
"code.ur.gs/lupine/ordoor/internal/data"
|
||||
"code.ur.gs/lupine/ordoor/internal/maps"
|
||||
"code.ur.gs/lupine/ordoor/internal/sets"
|
||||
"code.ur.gs/lupine/ordoor/internal/assetstore"
|
||||
"code.ur.gs/lupine/ordoor/internal/ui"
|
||||
)
|
||||
|
||||
var (
|
||||
gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
|
||||
mapFile = flag.String("map", "", "Prefix path to a .map file, e.g. ./orig/Maps/Chapter01.MAP")
|
||||
txtFile = flag.String("txt", "", "Prefix path to a .txt file, e.g. ./orig/Maps/Chapter01.txt")
|
||||
gameMap = flag.String("map", "", "Name of a map, e.g., Chapter01")
|
||||
)
|
||||
|
||||
type env struct {
|
||||
gameMap *maps.GameMap
|
||||
set *sets.MapSet
|
||||
objects map[string]*conv.Object
|
||||
assets *assetstore.AssetStore
|
||||
area *assetstore.Map
|
||||
|
||||
step int
|
||||
state state
|
||||
@@ -43,39 +36,19 @@ type state struct {
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *gamePath == "" || *mapFile == "" || *txtFile == "" {
|
||||
if *gamePath == "" || *gameMap == "" {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
gameMap, err := maps.LoadGameMapByFiles(*mapFile, *txtFile)
|
||||
assets, err := assetstore.New(*gamePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't load map file: %v", err)
|
||||
log.Fatalf("Failed to scan root directory %v: %v", *gamePath, err)
|
||||
}
|
||||
|
||||
setFile := filepath.Join(*gamePath, "Sets", gameMap.MapSetFilename())
|
||||
log.Println(setFile)
|
||||
mapSet, err := sets.LoadSet(setFile)
|
||||
area, err := assets.Map(*gameMap)
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't load set file %s: %v", setFile, err)
|
||||
}
|
||||
|
||||
objects := make([]*conv.Object, 0, len(mapSet.Palette))
|
||||
for _, name := range mapSet.Palette {
|
||||
objFile := filepath.Join(*gamePath, "Obj", name+".obj")
|
||||
rawObj, err := data.LoadObject(objFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load %s: %v", name, err)
|
||||
}
|
||||
|
||||
rawObj.Name = name
|
||||
|
||||
obj, err := conv.ConvertObject(rawObj, name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
objects = append(objects, obj)
|
||||
log.Fatalf("Failed to load map %v: %v", *gameMap, err)
|
||||
}
|
||||
|
||||
state := state{
|
||||
@@ -83,14 +56,13 @@ func main() {
|
||||
origin: image.Point{0, 3000}, // FIXME: haxxx
|
||||
}
|
||||
env := &env{
|
||||
gameMap: gameMap,
|
||||
set: mapSet,
|
||||
objects: conv.MapByName(objects),
|
||||
area: area,
|
||||
assets: assets,
|
||||
state: state,
|
||||
lastState: state,
|
||||
}
|
||||
|
||||
win, err := ui.NewWindow("View Map " + *mapFile)
|
||||
win, err := ui.NewWindow("View Map " + *gameMap)
|
||||
if err != nil {
|
||||
log.Fatal("Couldn't create window: %v", err)
|
||||
}
|
||||
@@ -123,30 +95,6 @@ func (e *env) Update() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *env) getSprite(palette []string, ref maps.ObjRef) (*conv.Sprite, error) {
|
||||
// There seems to be an active bit that hides many sins
|
||||
if !ref.IsActive() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if ref.Index() >= len(palette) {
|
||||
return nil, fmt.Errorf("Palette too small: %v requested", ref.Index())
|
||||
}
|
||||
|
||||
name := palette[ref.Index()]
|
||||
|
||||
obj := e.objects[name]
|
||||
if obj == nil {
|
||||
return nil, fmt.Errorf("Failed to find surface sprite %#v -> %q", ref, name)
|
||||
}
|
||||
|
||||
if ref.Sprite() >= len(obj.Sprites) {
|
||||
return nil, fmt.Errorf("Out-of-index sprite %v requested for %v", ref.Sprite(), name)
|
||||
}
|
||||
|
||||
return obj.Sprites[ref.Sprite()], nil
|
||||
}
|
||||
|
||||
func (e *env) Draw(screen *ebiten.Image) error {
|
||||
// Bounds clipping
|
||||
// http://www.java-gaming.org/index.php?topic=24922.0
|
||||
@@ -177,8 +125,7 @@ func (e *env) Draw(screen *ebiten.Image) error {
|
||||
x := (a + b) / 2
|
||||
y := (a - b) / 2
|
||||
|
||||
if x < int(e.gameMap.MinWidth) || x >= int(e.gameMap.MaxWidth) ||
|
||||
y < int(e.gameMap.MinLength) || y >= int(e.gameMap.MaxLength) {
|
||||
if !image.Pt(x, y).In(e.area.Rect) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -192,31 +139,9 @@ func (e *env) Draw(screen *ebiten.Image) error {
|
||||
}
|
||||
|
||||
func (e *env) renderCell(x, y, z int, screen *ebiten.Image) error {
|
||||
var sprites []*conv.Sprite
|
||||
cell := e.gameMap.Cells.At(x, y, z)
|
||||
|
||||
if spr, err := e.getSprite(e.set.Palette, cell.Surface); err != nil {
|
||||
log.Printf("%v %v %v surface: %v", x, y, z, err)
|
||||
} else if spr != nil {
|
||||
sprites = append(sprites, spr)
|
||||
}
|
||||
|
||||
if spr, err := e.getSprite(e.set.Palette, cell.Center); err != nil {
|
||||
log.Printf("%v %v %v center: %v", x, y, z, err)
|
||||
} else if spr != nil {
|
||||
sprites = append(sprites, spr)
|
||||
}
|
||||
|
||||
if spr, err := e.getSprite(e.set.Palette, cell.Left); err != nil {
|
||||
log.Printf("%v %v %v left: %v", x, y, z, err)
|
||||
} else if spr != nil {
|
||||
sprites = append(sprites, spr)
|
||||
}
|
||||
|
||||
if spr, err := e.getSprite(e.set.Palette, cell.Right); err != nil {
|
||||
log.Printf("%v %v %v right: %v", x, y, z, err)
|
||||
} else if spr != nil {
|
||||
sprites = append(sprites, spr)
|
||||
images, err := e.area.ImagesForCell(x, y, z)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iso := ebiten.GeoM{}
|
||||
@@ -231,8 +156,8 @@ func (e *env) renderCell(x, y, z int, screen *ebiten.Image) error {
|
||||
|
||||
// TODO: iso.Scale(e.state.zoom, e.state.zoom) // apply current zoom factor
|
||||
|
||||
for _, sprite := range sprites {
|
||||
if err := screen.DrawImage(sprite.Image, &ebiten.DrawImageOptions{GeoM: iso}); err != nil {
|
||||
for _, img := range images {
|
||||
if err := screen.DrawImage(img, &ebiten.DrawImageOptions{GeoM: iso}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user