1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-09-28 06:06:29 -04:00

Lots of map rendering changes.

This commit is contained in:
Tim Sarbin 2019-11-01 22:12:07 -04:00
parent 07709e2ddf
commit b05e887d8a
4 changed files with 173 additions and 86 deletions

View File

@ -24,6 +24,14 @@ func MaxInt32(a, b int32) int32 {
return b return b
} }
func NextPow2(x int32) int32 {
result := int32(1)
for result < x {
result *= 2
}
return result
}
func AbsInt32(a int32) int32 { func AbsInt32(a int32) int32 {
if a < 0 { if a < 0 {
return -a return -a

View File

@ -51,25 +51,57 @@ func (v *Engine) Render(target *ebiten.Image) {
for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { for x := 0; x < int(v.regions[0].Region.TileWidth); x++ {
tile := v.regions[0].Region.DS1.Tiles[y][x] tile := v.regions[0].Region.DS1.Tiles[y][x]
for i := range tile.Floors { 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 offX += 80
offY += 40 offY += 40
} }
} }
for y := 0; y < int(v.regions[0].Region.TileHeight); y++ { for y := 0; y < int(v.regions[0].Region.TileHeight); y++ {
offX := -(y * 80) offX := -(y * 80)
offY := y * 40 offY := y * 40
for x := 0; x < int(v.regions[0].Region.TileWidth); x++ { for x := 0; x < int(v.regions[0].Region.TileWidth); x++ {
tile := v.regions[0].Region.DS1.Tiles[y][x] tile := v.regions[0].Region.DS1.Tiles[y][x]
for i := range tile.Walls { for i := range tile.Walls {
// TODO: render back walls, then character, then fore walls 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 {
v.regions[0].Region.RenderTile(400+offX+int(v.OffsetX), offY+int(v.OffsetY), x, y, RegionLayerTypeWalls, i, target) 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 offX += 80
offY += 40 offY += 40
} }
} }
} }

View File

@ -1,6 +1,8 @@
package Map package Map
import ( import (
"image/color"
"log"
"math" "math"
"math/rand" "math/rand"
"strconv" "strconv"
@ -26,14 +28,17 @@ type Region struct {
Tiles []Tile Tiles []Tile
DS1 *DS1 DS1 *DS1
Palette Common.PaletteRec Palette Common.PaletteRec
TileCache map[uint32]*TileCacheRecord FloorCache map[uint32]*TileCacheRecord
ShadowCache map[uint32]*TileCacheRecord
WallCache map[uint32]*TileCacheRecord
} }
type RegionLayerType int type RegionLayerType int
const ( const (
RegionLayerTypeFloors RegionLayerType = 0 RegionLayerTypeFloors RegionLayerType = 0
RegionLayerTypeWalls RegionLayerType = 1 RegionLayerTypeWalls RegionLayerType = 1
RegionLayerTypeShadows RegionLayerType = 2
) )
type RegionIdType int type RegionIdType int
@ -81,7 +86,9 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
levelType: Common.LevelTypes[levelType], levelType: Common.LevelTypes[levelType],
levelPreset: Common.LevelPresets[levelPreset], levelPreset: Common.LevelPresets[levelPreset],
Tiles: make([]Tile, 0), 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)))] result.Palette = Common.Palettes[PaletteDefs.PaletteType("act"+strconv.Itoa(int(result.levelType.Act)))]
//\bm := result.levelPreset.Dt1Mask //\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) v.renderFloor(v.DS1.Tiles[tileY][tileX].Floors[layerIndex], offsetX, offsetY, target)
case RegionLayerTypeWalls: case RegionLayerTypeWalls:
v.renderWall(v.DS1.Tiles[tileY][tileX].Walls[layerIndex], offsetX, offsetY, target) 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) { func (v *Region) renderFloor(tile FloorShadowRecord, offsetX, offsetY int, target *ebiten.Image) {
if tile.Hidden { tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8)
return tileCache, exists := v.FloorCache[tileCacheIndex]
} if !exists {
tileCacheIndex := (uint32(tile.MainIndex) << 16) + (uint32(tile.SubIndex) << 8) v.FloorCache[tileCacheIndex] = v.generateFloorCache(tile)
tileCache := v.TileCache[tileCacheIndex] tileCache = v.FloorCache[tileCacheIndex]
if tileCache == nil { if tileCache == nil {
v.TileCache[tileCacheIndex] = v.generateFloorCache(tile) log.Fatal("Could not load floor tile")
tileCache = v.TileCache[tileCacheIndex] }
}
if tileCache == nil {
return
} }
opts := &ebiten.DrawImageOptions{} opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset)) 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) { func (v *Region) renderWall(tile WallRecord, offsetX, offsetY int, target *ebiten.Image) {
if tile.Hidden { tileCacheIndex := (uint32(tile.MainIndex) << 16) | (uint32(tile.SubIndex) << 8) | (uint32(tile.Orientation))
return tileCache, exists := v.WallCache[tileCacheIndex]
} if !exists {
if tile.Prop1 == 0 { v.WallCache[tileCacheIndex] = v.generateWallCache(tile)
return if v.WallCache[tileCacheIndex] == nil {
} log.Fatal("Could not generate wall")
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
} }
tileCache = v.TileCache[tileCacheIndex] tileCache = v.WallCache[tileCacheIndex]
} }
opts := &ebiten.DrawImageOptions{} opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset)) opts.GeoM.Translate(float64(offsetX+tileCache.XOffset), float64(offsetY+tileCache.YOffset))
target.DrawImage(tileCache.Image, opts) 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 { for _, block := range blocks {
// TODO: Move this to a less stupid place
if block.Format == BlockFormatIsometric { if block.Format == BlockFormatIsometric {
// 3D isometric decoding // 3D isometric decoding
xjump := []int32{14, 12, 10, 8, 6, 4, 2, 0, 2, 4, 6, 8, 10, 12, 14} 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] colorIndex := block.EncodedData[idx]
if colorIndex != 0 { if colorIndex != 0 {
pixelColor := v.Palette.Colors[colorIndex] pixelColor := v.Palette.Colors[colorIndex]
pixels[(4 * (((blockY + y) * tileWidth) + (blockX + x)))] = pixelColor.R offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+1] = pixelColor.G pixels[offset] = pixelColor.R
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+2] = pixelColor.B pixels[offset+1] = pixelColor.G
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+3] = 255 pixels[offset+2] = pixelColor.B
} else { pixels[offset+3] = 255
pixels[(4*(((blockY+y)*tileWidth)+(blockX+x)))+3] = 0
} }
x++ x++
n-- n--
@ -220,27 +233,27 @@ func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int3
idx := 0 idx := 0
length := block.Length length := block.Length
for length > 0 { for length > 0 {
length -= 2 b1 := block.EncodedData[idx]
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])
b2 := block.EncodedData[idx+1] b2 := block.EncodedData[idx+1]
idx += 2 idx += 2
length -= 2
if (b1 | b2) == 0 {
x = 0
y++
continue
}
x += int32(b1)
length -= int32(b2)
for b2 > 0 { for b2 > 0 {
colorIndex := block.EncodedData[idx] colorIndex := block.EncodedData[idx]
if colorIndex != 0 { if colorIndex != 0 {
pixelColor := v.Palette.Colors[colorIndex] pixelColor := v.Palette.Colors[colorIndex]
pixels[(4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)))] = pixelColor.R offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+1] = pixelColor.G pixels[offset] = pixelColor.R
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+2] = pixelColor.B pixels[offset+1] = pixelColor.G
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+3] = 255 pixels[offset+2] = pixelColor.B
} else { pixels[offset+3] = 255
pixels[(4*(((blockY+y+tileYOffset)*tileWidth)+(blockX+x)))+3] = 0
} }
idx++ idx++
x++ x++
@ -254,7 +267,7 @@ func (v *Region) decodeFloorData(blocks []Block, pixels []byte, tileYOffset int3
func (v *Region) generateFloorCache(tile FloorShadowRecord) *TileCacheRecord { func (v *Region) generateFloorCache(tile FloorShadowRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 0) tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), 0)
if tileData == nil { 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) tileYMinimum := int32(0)
for _, block := range tileData.Blocks { for _, block := range tileData.Blocks {
@ -264,37 +277,73 @@ func (v *Region) generateFloorCache(tile FloorShadowRecord) *TileCacheRecord {
tileHeight := Common.AbsInt32(tileData.Height) tileHeight := Common.AbsInt32(tileData.Height)
image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest) image, _ := ebiten.NewImage(int(tileData.Width), int(tileHeight), ebiten.FilterNearest)
pixels := make([]byte, 4*tileData.Width*tileHeight) 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) image.ReplacePixels(pixels)
return &TileCacheRecord{image, 0, 0} 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 { func (v *Region) generateWallCache(tile WallRecord) *TileCacheRecord {
tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(tile.Orientation)) tileData := v.getTile(int32(tile.MainIndex), int32(tile.SubIndex), int32(tile.Orientation))
if tileData == nil { if tileData == nil {
return nil return nil
} }
tileYMinimum := int32(0) var newTileData *Tile = nil
for _, block := range tileData.Blocks { if tile.Orientation == 3 {
tileYMinimum = Common.MinInt32(tileYMinimum, int32(block.Y)) 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 yAdjust := 0
if tile.Orientation == 15 { if tile.Orientation > 15 {
// Lower Walls
yAdjust = 80
} else if tile.Orientation == 15 {
// Roof // Roof
yAdjust = -int(tileData.RoofHeight) yAdjust = -int(tileData.RoofHeight)
} else if tile.Orientation > 15 {
// Lower walls
yAdjust = int(tileYMinimum) + 80
} else { } else {
// Upper Walls // Upper Walls, Special Tiles
yAdjust = int(tileYMinimum) + 80 yAdjust = int(tileMinY) + 80
} }
image.ReplacePixels(pixels)
return &TileCacheRecord{ return &TileCacheRecord{
image, image,
0, 0,

View File

@ -13,9 +13,8 @@ type MapEngineTest struct {
soundManager *Sound.Manager soundManager *Sound.Manager
fileProvider Common.FileProvider fileProvider Common.FileProvider
sceneProvider SceneProvider sceneProvider SceneProvider
//region *Map.Region gameState *Common.GameState
gameState *Common.GameState mapEngine *Map.Engine
mapEngine *Map.Engine
} }
func CreateMapEngineTest( func CreateMapEngineTest(
@ -40,20 +39,19 @@ func (v *MapEngineTest) Load() []func() {
return []func(){ return []func(){
func() { func() {
v.mapEngine = Map.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) 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.RegionAct1Town, 1)
//v.mapEngine.GenerateMap(Map.RegionAct1Tristram, 300) //v.mapEngine.GenerateMap(Map.RegionAct1Tristram, 300)
//v.mapEngine.GenerateMap(Map.RegionAct3Town, 529)
//v.mapEngine.GenerateMap(Map.RegionAct1Cathedral, 257) //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) //v.mapEngine.GenerateMap(Map.RegionAct3Jungle, 574)
//v.mapEngine.GenerateMap(Map.RegonAct5Town, 863)
// Completely broken ---------------------------------------------- //v.mapEngine.GenerateMap(Map.RegionAct5IceCaves, 1038)
//v.mapEngine.GenerateMap(Map.RegonAct5Town, 863) // CRASHES //v.mapEngine.GenerateMap(Map.RegionAct5Siege, 879)
// v.mapEngine.GenerateMap(Map.RegionAct5Siege, 879) // CRASHES
//v.mapEngine.GenerateMap(Map.RegionAct5Lava, 1057) // PALETTE ISSUE //v.mapEngine.GenerateMap(Map.RegionAct5Lava, 1057) // PALETTE ISSUE
// v.mapEngine.GenerateMap(Map.RegionAct2Town, 301) // Really broken rendering //v.mapEngine.GenerateMap(Map.RegionAct5Barricade, 880)
}, },
} }