diff --git a/cmd/view-map/main.go b/cmd/view-map/main.go index 95e6dc6..97406ed 100644 --- a/cmd/view-map/main.go +++ b/cmd/view-map/main.go @@ -31,6 +31,7 @@ type env struct { set *sets.MapSet objects map[string]*conv.Object sprites map[string][]*pixel.Sprite + batch *pixel.Batch } type state struct { @@ -68,8 +69,7 @@ func main() { log.Fatalf("Couldn't load set file %s: %v", setFile, err) } - objects := make(map[string]*conv.Object) - + rawObjs := []*data.Object{} for _, name := range mapSet.Palette { objFile := filepath.Join(*gamePath, "Obj", name+".obj") obj, err := data.LoadObject(objFile) @@ -77,13 +77,18 @@ func main() { log.Fatalf("Failed to load %s: %v", name, err) } - objects[name] = conv.ConvertObject(obj, name) + obj.Name = name + rawObjs = append(rawObjs, obj) } + objects, spritesheet := conv.ConvertObjects(rawObjs) + batch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet) + env := &env{ gameMap: gameMap, set: mapSet, objects: objects, + batch: batch, } // The main thread now belongs to pixelgl @@ -153,21 +158,19 @@ func (e *env) getSprite(palette []string, ref maps.ObjRef) (*conv.Sprite, error) return &obj.Sprites[ref.Sprite()], nil } -// TODO: build all the sprites in the set into a single spritesheet so we can -// use pixel.Batch func (s *state) present(pWin *pixelgl.Window) { pWin.Clear(colornames.Black) + s.env.batch.Clear() + /* + center := pWin.Bounds().Center() - center := pWin.Bounds().Center() - - cam := pixel.IM - cam = cam.ScaledXY(center, pixel.Vec{1.0, -1.0}) // invert the Y axis - cam = cam.Scaled(pixel.ZV, s.zoom) // apply current zoom factor - cam = cam.Moved(center.Sub(s.camPos)) // Make it central - // cam = cam.Rotated(center.Sub(s.camPos), s.rot) // Apply isometric angle - s.cam = cam - pWin.SetMatrix(cam) - + cam := pixel.IM + cam = cam.ScaledXY(center, pixel.Vec{1.0, -1.0}) // invert the Y axis + cam = cam.Scaled(pixel.ZV, s.zoom) // apply current zoom factor + cam = cam.Moved(center.Sub(s.camPos)) // Make it central + s.cam = cam + pWin.SetMatrix(cam) + */ // TODO: we should be able to perform bounds clipping on these minX := int(s.env.gameMap.MinWidth) maxX := int(s.env.gameMap.MaxWidth) @@ -179,13 +182,16 @@ func (s *state) present(pWin *pixelgl.Window) { for z := minZ; z < maxZ; z++ { for y := minY; y < maxY; y++ { for x := minX; x < maxX; x++ { - s.renderCell(x, y, z, pWin) + s.renderCell(x, y, z, s.env.batch) } } } + + s.env.batch.Draw(pWin) + pWin.Update() } -func (s *state) renderCell(x, y, z int, pWin *pixelgl.Window) { +func (s *state) renderCell(x, y, z int, batch *pixel.Batch) { var sprites []*conv.Sprite cell := s.env.gameMap.Cells.At(x, y, z) @@ -223,7 +229,7 @@ func (s *state) renderCell(x, y, z int, pWin *pixelgl.Window) { for _, sprite := range sprites { if sprite != nil { - sprite.Spr.Draw(pWin, pixel.IM.Moved(iso)) + sprite.Spr.Draw(batch, pixel.IM.Moved(iso)) } } } diff --git a/cmd/view-set/main.go b/cmd/view-set/main.go index 0769a64..4c9acef 100644 --- a/cmd/view-set/main.go +++ b/cmd/view-set/main.go @@ -25,6 +25,7 @@ var ( type env struct { set *sets.MapSet objects map[string]*conv.Object + batch *pixel.Batch } type state struct { @@ -53,19 +54,22 @@ func main() { log.Fatalf("Couldn't load set file %s: %v", *setFile, err) } - objects := make(map[string]*conv.Object) - + rawObjs := []*data.Object{} for _, name := range mapSet.Palette { objFile := filepath.Join(*gamePath, "Obj", name+".obj") obj, err := data.LoadObject(objFile) if err != nil { log.Fatalf("Failed to load %s: %v", name, err) } + obj.Name = name - objects[name] = conv.ConvertObject(obj, name) + rawObjs = append(rawObjs, obj) } - env := &env{objects: objects, set: mapSet} + objects, spritesheet := conv.ConvertObjects(rawObjs) + batch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet) + + env := &env{objects: objects, set: mapSet, batch: batch} // The main thread now belongs to pixelgl pixelgl.Run(env.run) @@ -129,7 +133,12 @@ func (s *state) present(pWin *pixelgl.Window) { pWin.SetMatrix(s.cam) pWin.Clear(colornames.Black) - pixel.NewSprite(sprite.Pic, sprite.Pic.Bounds()).Draw(pWin, pixel.IM.Moved(center)) + s.env.batch.Clear() + + sprite.Spr.Draw(s.env.batch, pixel.IM.Moved(center)) + //pixel.NewSprite(sprite.Pic, sprite.Pic.Bounds()).Draw(pWin, pixel.IM.Moved(center)) + s.env.batch.Draw(pWin) + pWin.Update() } func (s *state) handleKeys(pWin *pixelgl.Window) { @@ -165,6 +174,6 @@ func (s *state) handleKeys(pWin *pixelgl.Window) { func (s *state) curObject() *conv.Object { name := s.env.set.Palette[s.objIdx] - log.Printf("name: %v, objects: %#v", name, s.env.objects) + //log.Printf("name: %v, objects: %#v", name, s.env.objects) return s.env.objects[name] } diff --git a/internal/conv/object.go b/internal/conv/object.go index 40f67f0..d731cb0 100644 --- a/internal/conv/object.go +++ b/internal/conv/object.go @@ -3,7 +3,9 @@ package conv import ( "fmt" "image/color" + "image/png" "log" + "os" "github.com/faiface/pixel" @@ -29,6 +31,89 @@ type Object struct { Sprites []Sprite } +func maxWidth(obj *data.Object) int { + out := 0 + + for _, spr := range obj.Sprites { + width := int(spr.Width) + if width > out { + out = width + } + } + + return out +} + +func fullHeight(obj *data.Object) int { + out := 0 + + for _, spr := range obj.Sprites { + out = out + int(spr.Height) + } + + return out +} + +func ConvertObjects(objects []*data.Object) (map[string]*Object, *pixel.PictureData) { + // This needs to be the maxWidth of all the objects, added together + maxX := 0 + for _, obj := range objects { + maxX = maxX + maxWidth(obj) + } + + // This needs to be the largest fullHeight of all the objects + maxY := 0 + for _, obj := range objects { + height := fullHeight(obj) + if height > maxY { + maxY = height + } + } + + xOffset := 0 + spritesheet := pixel.MakePictureData(pixel.R(0.0, 0.0, float64(maxX), float64(maxY))) + + out := make(map[string]*Object) + for _, rawObj := range objects { + log.Println("xOffset:", xOffset) + cObj := ConvertObjectWithSpritesheet(rawObj, rawObj.Name, spritesheet, xOffset) + xOffset = xOffset + maxWidth(rawObj) + out[cObj.Name] = cObj + } + + f, _ := os.Create("spritesheet.png") + img := spritesheet.Image() + png.Encode(f, img) + f.Close() + + return out, spritesheet +} + +func ConvertObjectWithSpritesheet(rawObj *data.Object, name string, pic *pixel.PictureData, xOffset int) *Object { + out := &Object{ + Name: name, + Sprites: make([]Sprite, len(rawObj.Sprites)), + } + + // We store the sprites vertically in the provided pic + yOffset := 0 + log.Printf("Converting %v: xOffset = %v", name, xOffset) + + for i, rawSpr := range rawObj.Sprites { + spr := spriteToPic(name, i, rawSpr, pic, xOffset, yOffset) + log.Printf(" %#v", spr.Frame()) + out.Sprites[i] = Sprite{ + Width: int(rawSpr.Width), + Height: int(rawSpr.Height), + Pic: pic, + Spr: spr, + } + yOffset = yOffset + int(rawSpr.Height) + } + + return out +} + func ConvertObject(rawObj *data.Object, name string) *Object { out := &Object{ Name: name, @@ -36,37 +121,56 @@ func ConvertObject(rawObj *data.Object, name string) *Object { } for i, rawSpr := range rawObj.Sprites { - pic := spriteToPic(name, i, rawSpr) + pic := pixel.MakePictureData( + pixel.R( + float64(0), + float64(0), + float64(rawSpr.Width), + float64(rawSpr.Height), + ), + ) + spr := spriteToPic(name, i, rawSpr, pic, 0, 0) + out.Sprites[i] = Sprite{ Width: int(rawSpr.Width), Height: int(rawSpr.Height), Pic: pic, - Spr: pixel.NewSprite(pic, pic.Bounds()), + Spr: spr, } } return out } -func spriteToPic(name string, idx int, sprite *data.Sprite) *pixel.PictureData { - pic := pixel.MakePictureData(pixel.R(float64(0), float64(0), float64(sprite.Width), float64(sprite.Height))) +func spriteToPic(name string, idx int, sprite *data.Sprite, pic *pixel.PictureData, xOffset, yOffset int) *pixel.Sprite { width := int(sprite.Width) height := int(sprite.Height) - log.Printf("%v %v: width=%v height=%v", name, idx, width, height) + //log.Printf("%v %v: width=%v height=%v", name, idx, width, height) for y := 0; y < height; y++ { for x := 0; x < width; x++ { b := sprite.Data[y*width+x] // Update the picture - if err := setPaletteColor(pic, x, y, b); err != nil { - log.Printf("%s %d: %d,%d: %v", name, idx, x, y, err) + if err := setPaletteColor(pic, x+xOffset, y+yOffset, b); err != nil { + //log.Printf("%s %d: %d,%d: %v", name, idx, x, y, err) } } } - return pic + // FIXME: I really don't get this fetish for starting at the bottom-left + bounds := pixel.R( + float64(xOffset), + float64(yOffset), + float64(xOffset+width), + float64(yOffset+height), + ) + + //bounds.Min = bounds.Min.ScaledXY(pixel.Vec{1.0, -1.0}) + //bounds.Max = bounds.Max.ScaledXY(pixel.Vec{1.0, -1.0}) + + return pixel.NewSprite(pic, bounds) } func setPaletteColor(pic *pixel.PictureData, x int, y int, colorIdx byte) error { diff --git a/internal/data/object.go b/internal/data/object.go index 4c7ba40..11285d3 100644 --- a/internal/data/object.go +++ b/internal/data/object.go @@ -80,6 +80,7 @@ type Object struct { ObjectHeader Filename string + Name string // left blank for use by you Sprites []*Sprite }