From b05e887d8a19126dad1461a89b08d13e96efa5c6 Mon Sep 17 00:00:00 2001 From: Tim Sarbin Date: Fri, 1 Nov 2019 22:12:07 -0400 Subject: [PATCH] Lots of map rendering changes. --- Common/Math.go | 8 ++ Map/Engine.go | 42 +++++++-- Map/Region.go | 187 +++++++++++++++++++++++++--------------- Scenes/MapEngineTest.go | 22 +++-- 4 files changed, 173 insertions(+), 86 deletions(-) diff --git a/Common/Math.go b/Common/Math.go index 8c7cb704..f2c8894e 100644 --- a/Common/Math.go +++ b/Common/Math.go @@ -24,6 +24,14 @@ func MaxInt32(a, b int32) int32 { return b } +func NextPow2(x int32) int32 { + result := int32(1) + for result < x { + result *= 2 + } + return result +} + func AbsInt32(a int32) int32 { if a < 0 { return -a diff --git a/Map/Engine.go b/Map/Engine.go index ddad265c..77a89d45 100644 --- a/Map/Engine.go +++ b/Map/Engine.go @@ -51,25 +51,57 @@ func (v *Engine) Render(target *ebiten.Image) { for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { tile := v.regions[0].Region.DS1.Tiles[y][x] for i := range tile.Floors { - v.regions[0].Region.RenderTile(400+offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeFloors, i, target) + if !tile.Floors[i].Hidden && tile.Floors[i].Prop1 != 0 { + v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeFloors, i, target) + } + } + offX += 80 + offY += 40 + } + } + for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { + tile := v.regions[0].Region.DS1.Tiles[y][x] + for i := range tile.Shadows { + if tile.Shadows[i].Hidden || tile.Shadows[i].Prop1 == 0 { + continue + } + v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeShadows, i, target) } offX += 80 offY += 40 } } - for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { offX := -(y * 80) offY := y * 40 for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { tile := v.regions[0].Region.DS1.Tiles[y][x] for i := range tile.Walls { - // TODO: render back walls, then character, then fore walls - v.regions[0].Region.RenderTile(400+offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) + if tile.Walls[i].Hidden || tile.Walls[i].Orientation == 15 || tile.Walls[i].Orientation == 10 || tile.Walls[i].Orientation == 11 || tile.Walls[i].Orientation == 0 { + continue + } + v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) + } + offX += 80 + offY += 40 + } + } + for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { + offX := -(y * 80) + offY := y * 40 + for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { + tile := v.regions[0].Region.DS1.Tiles[y][x] + for i := range tile.Walls { + if tile.Walls[i].Hidden || tile.Walls[i].Orientation != 15 { + continue + } + v.regions[0].Region.RenderTile(offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) } offX += 80 offY += 40 } } - } diff --git a/Map/Region.go b/Map/Region.go index 316c3d82..08570eb0 100644 --- a/Map/Region.go +++ b/Map/Region.go @@ -1,6 +1,8 @@ package Map import ( + "image/color" + "log" "math" "math/rand" "strconv" @@ -26,14 +28,17 @@ type Region struct { Tiles []Tile DS1 *DS1 Palette Common.PaletteRec - TileCache map[uint32]*TileCacheRecord + FloorCache map[uint32]*TileCacheRecord + ShadowCache map[uint32]*TileCacheRecord + WallCache map[uint32]*TileCacheRecord } type RegionLayerType int const ( - RegionLayerTypeFloors RegionLayerType = 0 - RegionLayerTypeWalls RegionLayerType = 1 + RegionLayerTypeFloors RegionLayerType = 0 + RegionLayerTypeWalls RegionLayerType = 1 + RegionLayerTypeShadows RegionLayerType = 2 ) type RegionIdType int @@ -81,7 +86,9 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP levelType: Common.LevelTypes[levelType], levelPreset: Common.LevelPresets[levelPreset], Tiles: make([]Tile, 0), - TileCache: make(map[uint32]*TileCacheRecord), + FloorCache: make(map[uint32]*TileCacheRecord), + ShadowCache: make(map[uint32]*TileCacheRecord), + WallCache: make(map[uint32]*TileCacheRecord), } result.Palette = Common.Palettes[PaletteDefs.PaletteType("act"+strconv.Itoa(int(result.levelType.Act)))] //\bm := result.levelPreset.Dt1Mask @@ -122,6 +129,8 @@ func (v *Region) RenderTile(offsetX, offsetY, tileX, tileY int, layerType Region v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target) case RegionLayerTypeWalls: v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], offsetX, offsetY, target) + case RegionLayerTypeShadows: + v.renderShadow(v.DS1.Tiles[tileY][tileX].Shadows[layerIndex], offsetX, offsetY, target) } } @@ -138,17 +147,14 @@ func (v *Region) getTile(mainIndex, subIndex, orientation int32) *Tile { } func (v *Region) renderFloor(tile FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) { - if tile.Hidden { - return - } - tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) - tileCache := v.TileCache[tileCacheIndex] - if tileCache == nil { - v.TileCache[tileCacheIndex] = v.generateFloorCache(tile) - tileCache = v.TileCache[tileCacheIndex] - } - if tileCache == nil { - return + tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8) + tileCache, exists := v.FloorCache[tileCacheIndex] + if !exists { + v.FloorCache[tileCacheIndex] = v.generateFloorCache(tile) + tileCache = v.FloorCache[tileCacheIndex] + if tileCache == nil { + log.Fatal("Could not load floor tile") + } } opts := &ebiten.DrawImageOptions{} opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset)) @@ -156,30 +162,38 @@ func (v *Region) renderFloor(tile FloorShadowRecord, offsetX, offsetY int, targe } func (v *Region) renderWall(tile WallRecord, offsetX, offsetY int, target *ebiten.Image) { - if tile.Hidden { - return - } - if tile.Prop1 == 0 { - return - } - tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) + (uint32(tile.Orientation)) - tileCache := v.TileCache[tileCacheIndex] - if tileCache == nil { - v.TileCache[tileCacheIndex] = v.generateWallCache(tile) - // TODO: Temporary hack - if v.TileCache[tileCacheIndex] == nil { - return + tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8) | (uint32(tile.Orientation)) + tileCache, exists := v.WallCache[tileCacheIndex] + if !exists { + v.WallCache[tileCacheIndex] = v.generateWallCache(tile) + if v.WallCache[tileCacheIndex] == nil { + log.Fatal("Could not generate wall") } - tileCache = v.TileCache[tileCacheIndex] + tileCache = v.WallCache[tileCacheIndex] } opts := &ebiten.DrawImageOptions{} opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset)) target.DrawImage(tileCache.Image, opts) } -func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int32, tileWidth int32) { +func (v *Region) renderShadow(tile FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) { + tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) + 0 + tileCache, exists := v.ShadowCache[tileCacheIndex] + if !exists { + v.ShadowCache[tileCacheIndex] = v.generateShadowCache(tile) + tileCache = v.ShadowCache[tileCacheIndex] + if tileCache == nil { + log.Fatal("Could not load shadow tile") + } + } + opts := &ebiten.DrawImageOptions{} + opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset)) + opts.ColorM = Common.ColorToColorM(color.RGBA{255, 255, 255, 160}) + target.DrawImage(tileCache.Image, opts) +} + +func (v *Region) decodeTileGfxData(blocks []Block, pixels []byte, tileYOffset int32, tileWidth int32) { for _, block := range blocks { - // TODO: Move this to a less stupid place if block.Format == BlockFormatIsometric { // 3D isometric decoding xjump := []int32{14, 12, 10, 8, 6, 4, 2, 0, 2, 4, 6, 8, 10, 12, 14} @@ -198,12 +212,11 @@ func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int3 colorIndex := block.EncodedData[idx] if colorIndex != 0 { pixelColor := v.Palette.Colors[colorIndex] - pixels[(4 * (((blockY + y) * tileWidth) + (blockX + x)))] = pixelColor.R - pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+1] = pixelColor.G - pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+2] = pixelColor.B - pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+3] = 255 - } else { - pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+3] = 0 + offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)) + pixels[offset] = pixelColor.R + pixels[offset+1] = pixelColor.G + pixels[offset+2] = pixelColor.B + pixels[offset+3] = 255 } x++ n-- @@ -220,27 +233,27 @@ func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int3 idx := 0 length := block.Length for length > 0 { - length -= 2 - if (block.EncodedData[idx] + block.EncodedData[idx+1]) == 0 { - x = 0 - y++ - idx += 2 - continue - } - length -= int32(block.EncodedData[idx+1]) - x += int32(block.EncodedData[idx]) + b1 := block.EncodedData[idx] b2 := block.EncodedData[idx+1] idx += 2 + length -= 2 + if (b1 | b2) == 0 { + x = 0 + y++ + continue + } + x += int32(b1) + length -= int32(b2) for b2 > 0 { colorIndex := block.EncodedData[idx] if colorIndex != 0 { pixelColor := v.Palette.Colors[colorIndex] - pixels[(4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)))] = pixelColor.R - pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+1] = pixelColor.G - pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+2] = pixelColor.B - pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+3] = 255 - } else { - pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+3] = 0 + offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)) + pixels[offset] = pixelColor.R + pixels[offset+1] = pixelColor.G + pixels[offset+2] = pixelColor.B + pixels[offset+3] = 255 + } idx++ x++ @@ -254,7 +267,7 @@ func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int3 func (v *Region) generateFloorCache(tile FloorShadowRecord) *TileCacheRecord { tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 0) if tileData == nil { - return nil + log.Fatalf("Could not locate tile Idx:%d, Sub: %d, Ori: %d", tile.MainIndex, tile.SubIndex, 0) } tileYMinimum := int32(0) for _, block := range tileData.Blocks { @@ -264,37 +277,73 @@ func (v *Region) generateFloorCache(tile FloorShadowRecord) *TileCacheRecord { tileHeight := Common.AbsInt32(tileData.Height) image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest) pixels := make([]byte, 4*tileData.Width*tileHeight) - v.decodeFloorData(tileData.Blocks, pixels, tileYOffset, tileData.Width) + v.decodeTileGfxData(tileData.Blocks, pixels, tileYOffset, tileData.Width) image.ReplacePixels(pixels) return &TileCacheRecord{image, 0, 0} } +func (v *Region) generateShadowCache(tile FloorShadowRecord) *TileCacheRecord { + tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 13) + if tileData == nil { + return nil + } + tileMinY := int32(0) + tileMaxY := int32(0) + for _, block := range tileData.Blocks { + tileMinY = Common.MinInt32(tileMinY, int32(block.Y)) + tileMaxY = Common.MaxInt32(tileMaxY, int32(block.Y+32)) + } + tileYOffset := -tileMinY + tileHeight := int(tileMaxY - tileMinY) + image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest) + pixels := make([]byte, 4*tileData.Width*int32(tileHeight)) + v.decodeTileGfxData(tileData.Blocks, pixels, tileYOffset, tileData.Width) + image.ReplacePixels(pixels) + return &TileCacheRecord{image, 0, int(tileMinY) + 80} +} + func (v *Region) generateWallCache(tile WallRecord) *TileCacheRecord { tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(tile.Orientation)) if tileData == nil { return nil } - tileYMinimum := int32(0) - for _, block := range tileData.Blocks { - tileYMinimum = Common.MinInt32(tileYMinimum, int32(block.Y)) + var newTileData *Tile = nil + if tile.Orientation == 3 { + newTileData = v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(4)) + } + + tileMinY := int32(0) + tileMaxY := int32(0) + target := tileData + if newTileData != nil && newTileData.Height < tileData.Height { + target = newTileData + } + for _, block := range target.Blocks { + tileMinY = Common.MinInt32(tileMinY, int32(block.Y)) + tileMaxY = Common.MaxInt32(tileMaxY, int32(block.Y+32)) + } + realHeight := Common.MaxInt32(Common.AbsInt32(tileData.Height), tileMaxY-tileMinY) + tileYOffset := -tileMinY + //tileHeight := int(tileMaxY - tileMinY) + image, _ := ebiten.NewImage(160, int(realHeight), ebiten.FilterNearest) + pixels := make([]byte, 4*160*realHeight) + v.decodeTileGfxData(tileData.Blocks, pixels, tileYOffset, 160) + if newTileData != nil { + v.decodeTileGfxData(newTileData.Blocks, pixels, tileYOffset, 160) } - tileYOffset := -tileYMinimum - tileHeight := Common.AbsInt32(tileData.Height) - image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest) - pixels := make([]byte, 4*tileData.Width*tileHeight) - v.decodeFloorData(tileData.Blocks, pixels, tileYOffset, tileData.Width) - image.ReplacePixels(pixels) yAdjust := 0 - if tile.Orientation == 15 { + if tile.Orientation > 15 { + // Lower Walls + yAdjust = 80 + } else if tile.Orientation == 15 { // Roof yAdjust = -int(tileData.RoofHeight) - } else if tile.Orientation > 15 { - // Lower walls - yAdjust = int(tileYMinimum) + 80 } else { - // Upper Walls - yAdjust = int(tileYMinimum) + 80 + // Upper Walls, Special Tiles + yAdjust = int(tileMinY) + 80 } + + image.ReplacePixels(pixels) return &TileCacheRecord{ image, 0, diff --git a/Scenes/MapEngineTest.go b/Scenes/MapEngineTest.go index ce41091a..2a3c19f0 100644 --- a/Scenes/MapEngineTest.go +++ b/Scenes/MapEngineTest.go @@ -13,9 +13,8 @@ type MapEngineTest struct { soundManager *Sound.Manager fileProvider Common.FileProvider sceneProvider SceneProvider - //region *Map.Region - gameState *Common.GameState - mapEngine *Map.Engine + gameState *Common.GameState + mapEngine *Map.Engine } func CreateMapEngineTest( @@ -40,20 +39,19 @@ func (v *MapEngineTest) Load() []func() { return []func(){ func() { v.mapEngine = Map.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) - //v.mapEngine.GenerateMap(Map.RegionAct2Harem, 353) + v.mapEngine.GenerateMap(Map.RegionAct1Town, 1) //v.mapEngine.GenerateMap(Map.RegionAct1Tristram, 300) - - //v.mapEngine.GenerateMap(Map.RegionAct3Town, 529) //v.mapEngine.GenerateMap(Map.RegionAct1Cathedral, 257) - //v.mapEngine.GenerateMap(Map.RegionAct5IceCaves, 1038) + //v.mapEngine.GenerateMap(Map.RegionAct2Town, 301) // Broken rendering + //v.mapEngine.GenerateMap(Map.RegionAct2Harem, 353) + //v.mapEngine.GenerateMap(Map.RegionAct3Town, 529) //v.mapEngine.GenerateMap(Map.RegionAct3Jungle, 574) - - // Completely broken ---------------------------------------------- - //v.mapEngine.GenerateMap(Map.RegonAct5Town, 863) // CRASHES - // v.mapEngine.GenerateMap(Map.RegionAct5Siege, 879) // CRASHES + //v.mapEngine.GenerateMap(Map.RegonAct5Town, 863) + //v.mapEngine.GenerateMap(Map.RegionAct5IceCaves, 1038) + //v.mapEngine.GenerateMap(Map.RegionAct5Siege, 879) //v.mapEngine.GenerateMap(Map.RegionAct5Lava, 1057) // PALETTE ISSUE - // v.mapEngine.GenerateMap(Map.RegionAct2Town, 301) // Really broken rendering + //v.mapEngine.GenerateMap(Map.RegionAct5Barricade, 880) }, }