diff --git a/d2common/d2enum/draw_effect.go b/d2common/d2enum/draw_effect.go index 2d4b328b..1009d9d5 100644 --- a/d2common/d2enum/draw_effect.go +++ b/d2common/d2enum/draw_effect.go @@ -8,7 +8,7 @@ const ( // DrawEffectPctTransparency25 is a draw effect that implements the following function: // GL_MODULATE; GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 25 % transparency (colormaps 49-304 in a .pl2) - DrawEffectPctTransparency25 = iota + DrawEffectPctTransparency25 DrawEffect = iota // DrawEffectPctTransparency50 is a draw effect that implements the following function: // GL_MODULATE; GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 50 % transparency (colormaps 305-560 in a .pl2) @@ -37,4 +37,7 @@ const ( // DrawEffectMod2X is a draw effect that implements the following function: // GL_COMBINE_ARB; GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA (colormaps 1457-1712 in a .pl2) DrawEffectMod2X + + // no effect + DrawEffectNone ) diff --git a/d2common/d2interface/archived_animation_manager.go b/d2common/d2interface/archived_animation_manager.go index 3315f128..6e72928f 100644 --- a/d2common/d2interface/archived_animation_manager.go +++ b/d2common/d2interface/archived_animation_manager.go @@ -1,6 +1,8 @@ package d2interface +import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + type ArchivedAnimationManager interface { Cacher - LoadAnimation(animationPath, palettePath string, transparency int) (Animation, error) + LoadAnimation(animationPath, palettePath string, drawEffect d2enum.DrawEffect) (Animation, error) } diff --git a/d2core/d2asset/animation_manager.go b/d2core/d2asset/animation_manager.go index d8732dc3..8b5cc3d6 100644 --- a/d2core/d2asset/animation_manager.go +++ b/d2core/d2asset/animation_manager.go @@ -5,6 +5,7 @@ import ( "path/filepath" "strings" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common" @@ -36,8 +37,8 @@ func createAnimationManager(renderer d2interface.Renderer) *animationManager { func (am *animationManager) LoadAnimation( animationPath, palettePath string, - transparency int ) (d2interface.Animation, error) { - cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, transparency) + effect d2enum.DrawEffect ) (d2interface.Animation, error) { + cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, effect) if animation, found := am.cache.Retrieve(cachePath); found { return animation.(d2interface.Animation).Clone(), nil } @@ -62,7 +63,7 @@ func (am *animationManager) LoadAnimation( return nil, err } - animation, err = CreateDCCAnimation(am.renderer, animationPath, palette, transparency) + animation, err = CreateDCCAnimation(am.renderer, animationPath, palette, effect) if err != nil { return nil, err } diff --git a/d2core/d2asset/composite.go b/d2core/d2asset/composite.go index 0d5df18f..2b59168e 100644 --- a/d2core/d2asset/composite.go +++ b/d2core/d2asset/composite.go @@ -3,7 +3,6 @@ package d2asset import ( "errors" "fmt" - "log" "strings" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data" @@ -237,30 +236,17 @@ func (c *Composite) createMode(animationMode, weaponClass string) (*compositeMod layerValue = "lit" } - blend := false - transparency := 255 + drawEffect := d2enum.DrawEffectNone if cofLayer.Transparent { - switch cofLayer.DrawEffect { - case d2enum.DrawEffectPctTransparency25: - transparency = 64 - case d2enum.DrawEffectPctTransparency50: - transparency = 128 - case d2enum.DrawEffectPctTransparency75: - transparency = 192 - case d2enum.DrawEffectModulate: - blend = true - default: - log.Println("Unhandled DrawEffect ", cofLayer.DrawEffect, " requested for layer ", cofLayer.Type.String(), " of ", c.token) - } + drawEffect = cofLayer.DrawEffect } layer, err := c.loadCompositeLayer(cofLayer.Type.String(), layerValue, animationMode, - cofLayer.WeaponClass.String(), c.palettePath, transparency) + cofLayer.WeaponClass.String(), c.palettePath, drawEffect) if err == nil { layer.SetPlaySpeed(mode.animationSpeed) layer.PlayForward() - layer.SetBlend(blend) if err := layer.SetDirection(c.direction); err != nil { return nil, err @@ -274,7 +260,7 @@ func (c *Composite) createMode(animationMode, weaponClass string) (*compositeMod } func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weaponClass, - palettePath string, transparency int) (d2interface.Animation, error) { + palettePath string, drawEffect d2enum.DrawEffect) (d2interface.Animation, error) { animationPaths := []string{ fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dcc", c.basePath, c.token, layerKey, c.token, layerKey, layerValue, animationMode, weaponClass), fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dc6", c.basePath, c.token, layerKey, c.token, layerKey, layerValue, animationMode, weaponClass), @@ -282,7 +268,7 @@ func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weap for _, animationPath := range animationPaths { if exists, _ := FileExists(animationPath); exists { - animation, err := LoadAnimationWithTransparency(animationPath, palettePath, transparency) + animation, err := LoadAnimationWithEffect(animationPath, palettePath, drawEffect) if err == nil { return animation, nil } diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index c998bc52..3d1500a6 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -104,13 +104,13 @@ func FileExists(filePath string) (bool, error) { // LoadAnimation loads an animation by its resource path and its palette path func LoadAnimation(animationPath, palettePath string) (d2interface.Animation, error) { - return LoadAnimationWithTransparency(animationPath, palettePath, 255) + return LoadAnimationWithEffect(animationPath, palettePath, d2enum.DrawEffectNone) } -// LoadAnimationWithTransparency loads an animation by its resource path and its palette path with a given transparency value -func LoadAnimationWithTransparency(animationPath, palettePath string, - transparency int) (d2interface.Animation, error) { - return singleton.animationManager.LoadAnimation(animationPath, palettePath, transparency) +// LoadAnimationWithEffect loads an animation by its resource path and its palette path with a given transparency value +func LoadAnimationWithEffect(animationPath, palettePath string, + drawEffect d2enum.DrawEffect) (d2interface.Animation, error) { + return singleton.animationManager.LoadAnimation(animationPath, palettePath, drawEffect) } // LoadComposite creates a composite object from a ObjectLookupRecord and palettePath describing it diff --git a/d2core/d2asset/dc6_animation.go b/d2core/d2asset/dc6_animation.go index 4a700ac1..ba9411d1 100644 --- a/d2core/d2asset/dc6_animation.go +++ b/d2core/d2asset/dc6_animation.go @@ -82,22 +82,7 @@ func (a *DC6Animation) decodeDirection(directionIndex int) error { } indexData := dc6.DecodeFrame(startFrame + i) - - bytesPerPixel := 4 - colorData := make([]byte, int(dc6Frame.Width)*int(dc6Frame.Height)*bytesPerPixel) - - for i := 0; i < int(dc6Frame.Width*dc6Frame.Height); i++ { - // Index zero is hardcoded transparent regardless of palette - if indexData[i] == 0 { - continue - } - - c := a.palette.GetColors()[indexData[i]] - colorData[i*bytesPerPixel] = c.R() - colorData[i*bytesPerPixel+1] = c.G() - colorData[i*bytesPerPixel+2] = c.B() - colorData[i*bytesPerPixel+3] = c.A() - } + colorData := ImgIndexToRGBA(indexData, a.palette) if err := sfc.ReplacePixels(colorData); err != nil { return err diff --git a/d2core/d2asset/dcc_animation.go b/d2core/d2asset/dcc_animation.go index f513b7ca..e6fbeb05 100644 --- a/d2core/d2asset/dcc_animation.go +++ b/d2core/d2asset/dcc_animation.go @@ -2,9 +2,10 @@ package d2asset import ( "errors" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "math" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" d2iface "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -13,16 +14,15 @@ import ( // DCCAnimation represens an animation decoded from DCC type DCCAnimation struct { animation - dccPath string - transparency int - palette d2iface.Palette - renderer d2iface.Renderer + dccPath string + effect d2enum.DrawEffect + palette d2iface.Palette + renderer d2iface.Renderer } // CreateAnimationFromDCC creates an animation from d2dcc.DCC and d2dat.DATPalette func CreateDCCAnimation(renderer d2iface.Renderer, dccPath string, palette d2iface.Palette, - transparency int) (d2iface.Animation, error) { - + effect d2enum.DrawEffect) (d2iface.Animation, error) { dcc, err := loadDCC(dccPath) if err != nil { return nil, err @@ -35,13 +35,21 @@ func CreateDCCAnimation(renderer d2iface.Renderer, dccPath string, palette d2ifa } DCC := DCCAnimation{ - animation: anim, - dccPath: dccPath, - palette: palette, - renderer: renderer, - transparency: transparency, + animation: anim, + dccPath: dccPath, + palette: palette, + renderer: renderer, + effect: effect, } + // Really the renderer should take DrawEffects and do the right thing + if effect == d2enum.DrawEffectModulate { + DCC.SetBlend(true) + } + + // Transparency is now no longer handled, it should be done by using PL2 palette and + // picking the appropriate transform for the transparency level + err = DCC.SetDirection(0) if err != nil { return nil, err @@ -99,25 +107,7 @@ func (a *DCCAnimation) decodeDirection(directionIndex int) error { frameWidth := maxX - minX frameHeight := maxY - minY - const bytesPerPixel = 4 - pixels := make([]byte, frameWidth*frameHeight*bytesPerPixel) - - for y := 0; y < frameHeight; y++ { - for x := 0; x < frameWidth; x++ { - paletteIndex := dccFrame.PixelData[y*frameWidth+x] - - if paletteIndex == 0 { - continue - } - - palColor := a.palette.GetColors()[paletteIndex] - offset := (x + y*frameWidth) * bytesPerPixel - pixels[offset] = palColor.R() - pixels[offset+1] = palColor.G() - pixels[offset+2] = palColor.B() - pixels[offset+3] = byte(a.transparency) - } - } + pixels := ImgIndexToRGBA(dccFrame.PixelData, a.palette) sfc, err := a.renderer.NewSurface(frameWidth, frameHeight, d2enum.FilterNearest) if err != nil { diff --git a/d2core/d2asset/palette.go b/d2core/d2asset/palette.go new file mode 100644 index 00000000..0b66c165 --- /dev/null +++ b/d2core/d2asset/palette.go @@ -0,0 +1,23 @@ +package d2asset + +import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + +func ImgIndexToRGBA(indexData []byte, palette d2interface.Palette) []byte { + bytesPerPixel := 4 + colorData := make([]byte, len(indexData)*bytesPerPixel) + + for i := 0; i < len(indexData); i++ { + // Index zero is hardcoded transparent regardless of palette + if indexData[i] == 0 { + continue + } + + c := palette.GetColors()[indexData[i]] + colorData[i*bytesPerPixel] = c.R() + colorData[i*bytesPerPixel+1] = c.G() + colorData[i*bytesPerPixel+2] = c.B() + colorData[i*bytesPerPixel+3] = c.A() + } + + return colorData +} diff --git a/d2core/d2map/d2maprenderer/gfx_decode.go b/d2core/d2map/d2maprenderer/gfx_decode.go index f8efe397..35ebbc24 100644 --- a/d2core/d2map/d2maprenderer/gfx_decode.go +++ b/d2core/d2map/d2maprenderer/gfx_decode.go @@ -19,15 +19,8 @@ func (mr *MapRenderer) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, t n := nbpix[y] length -= n for n > 0 { - colorIndex := block.EncodedData[idx] - if colorIndex != 0 { - pixelColor := mr.palette.GetColors()[colorIndex] - 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 - } + offset := (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)) + (*pixels)[offset] = block.EncodedData[idx] x++ n-- idx++ @@ -56,17 +49,8 @@ func (mr *MapRenderer) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, t x += int32(b1) length -= int32(b2) for b2 > 0 { - colorIndex := block.EncodedData[idx] - if colorIndex != 0 { - pixelColor := mr.palette.GetColors()[colorIndex] - - 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 - - } + offset := (((blockY + y + tileYOffset) * tileWidth) + (blockX + x)) + (*pixels)[offset] = block.EncodedData[idx] idx++ x++ b2-- diff --git a/d2core/d2map/d2maprenderer/tile_cache.go b/d2core/d2map/d2maprenderer/tile_cache.go index 8680a8dc..7920cbcc 100644 --- a/d2core/d2map/d2maprenderer/tile_cache.go +++ b/d2core/d2map/d2maprenderer/tile_cache.go @@ -7,6 +7,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) func (mr *MapRenderer) generateTileCache() { @@ -73,8 +74,10 @@ func (mr *MapRenderer) generateFloorCache(tile *d2ds1.FloorShadowRecord, tileX, tileYOffset := d2common.AbsInt32(tileYMinimum) tileHeight := d2common.AbsInt32(tileData[i].Height) image, _ := mr.renderer.NewSurface(int(tileData[i].Width), int(tileHeight), d2enum.FilterNearest) - pixels := make([]byte, 4*tileData[i].Width*tileHeight) - mr.decodeTileGfxData(tileData[i].Blocks, &pixels, tileYOffset, tileData[i].Width) + indexData := make([]byte, tileData[i].Width*tileHeight) + mr.decodeTileGfxData(tileData[i].Blocks, &indexData, tileYOffset, tileData[i].Width) + pixels := d2asset.ImgIndexToRGBA(indexData, mr.palette) + _ = image.ReplacePixels(pixels) mr.setImageCacheRecord(tile.Style, tile.Sequence, 0, tileIndex, image) } @@ -112,8 +115,9 @@ func (mr *MapRenderer) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, } image, _ := mr.renderer.NewSurface(int(tileData.Width), tileHeight, d2enum.FilterNearest) - pixels := make([]byte, 4*tileData.Width*int32(tileHeight)) - mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, tileData.Width) + indexData := make([]byte, tileData.Width*int32(tileHeight)) + mr.decodeTileGfxData(tileData.Blocks, &indexData, tileYOffset, tileData.Width) + pixels := d2asset.ImgIndexToRGBA(indexData, mr.palette) _ = image.ReplacePixels(pixels) mr.setImageCacheRecord(tile.Style, tile.Sequence, 13, tileIndex, image) } @@ -173,14 +177,16 @@ func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord, tileX, tileY in } image, _ := mr.renderer.NewSurface(160, int(realHeight), d2enum.FilterNearest) - pixels := make([]byte, 4*160*realHeight) + indexData := make([]byte, 160*realHeight) - mr.decodeTileGfxData(tileData.Blocks, &pixels, tileYOffset, 160) + mr.decodeTileGfxData(tileData.Blocks, &indexData, tileYOffset, 160) if newTileData != nil { - mr.decodeTileGfxData(newTileData.Blocks, &pixels, tileYOffset, 160) + mr.decodeTileGfxData(newTileData.Blocks, &indexData, tileYOffset, 160) } + pixels := d2asset.ImgIndexToRGBA(indexData, mr.palette) + if err := image.ReplacePixels(pixels); err != nil { log.Panicf(err.Error()) }