package conv import ( "fmt" "image/color" "log" "github.com/faiface/pixel" "ur.gs/ordoor/internal/data" ) const ( // Textures can be this size at most. So putting every sprite onto the same // sheet is a fraught business. With effective packing, it may be fine. // If not, I'll have to come up with another idea maxTextureX = 8192 maxTextureY = 8192 ) // Important conversions: // // * Width & height now stored using int // * Colour data is now 32-bit rather than using a palette type Sprite struct { Width int Height int Pic *pixel.PictureData Spr *pixel.Sprite } type Object struct { Name string Sprites []*Sprite } func ConvertObjects(objects []*data.Object) (map[string]*Object, *pixel.PictureData) { // FIXME: this is rather inefficient. It would be better to determine the // maximum size we need for the objects at hand. spritesheet := pixel.MakePictureData( pixel.R(0.0, 0.0, float64(maxTextureX), float64(maxTextureY)), ) // These are updated each time a sprite is added to the sheet xOffset := 0 yOffset := 0 rowMaxY := 0 out := make(map[string]*Object) for _, rawObj := range objects { sprites := make([]*Sprite, 0, len(rawObj.Sprites)) for i, rawSpr := range rawObj.Sprites { width := int(rawSpr.Width) height := int(rawSpr.Height) // CR+LF if needed if xOffset+width > maxTextureX { xOffset = 0 yOffset += rowMaxY } if xOffset+width > maxTextureX || yOffset+height > maxTextureY { panic("Sprite does not fit in spritesheet") } spr := spriteToPic(rawObj.Name, i, rawSpr, spritesheet, xOffset, yOffset) sprites = append(sprites, &Sprite{width, height, spritesheet, spr}) xOffset = xOffset + width if height > rowMaxY { rowMaxY = height } } out[rawObj.Name] = &Object{Name: rawObj.Name, Sprites: sprites} } return out, spritesheet } func ConvertObjectWithSpritesheet(rawObj *data.Object, name string, pic *pixel.PictureData, xOffset int) *Object { // We store the sprites vertically in the provided pic yOffset := 0 out := &Object{ Name: name, Sprites: make([]*Sprite, len(rawObj.Sprites)), } for i, rawSpr := range rawObj.Sprites { spr := spriteToPic(name, i, rawSpr, pic, xOffset, yOffset) 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, Sprites: make([]*Sprite, len(rawObj.Sprites)), } for i, rawSpr := range rawObj.Sprites { 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: spr, } } return out } 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) 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+xOffset, y+yOffset, b); err != nil { log.Printf("%s %d: %d,%d: %v", name, idx, x, y, err) } } } bounds := pixel.R( float64(xOffset), float64(yOffset), float64(xOffset+width), float64(yOffset+height), ) return pixel.NewSprite(pic, bounds) } func setPaletteColor(pic *pixel.PictureData, x int, y int, colorIdx byte) error { vec := pixel.V(float64(x), float64(y)) idx := pic.Index(vec) if idx > len(pic.Pix)-1 { return fmt.Errorf("Got index %v which exceeds bounds", idx) } r, g, b, a := data.ColorPalette[int(colorIdx)].RGBA() color := color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)} pic.Pix[idx] = color return nil }