mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-10-31 16:27:18 -04:00
Cache each cof layer individually and set blending (#235)
* Cache each cof layer individually and set blending * Draw layers in their correct order
This commit is contained in:
parent
19257abddb
commit
1bfbffc48d
@ -28,6 +28,12 @@ var DccLayerNames = []string{"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1
|
|||||||
// DirectionLookup is used to decode the direction offset indexes
|
// DirectionLookup is used to decode the direction offset indexes
|
||||||
var DirectionLookup = []int{9, 15, 5, 6, 4, 12, 10, 2, 8, 13, 1, 7, 0, 14, 11, 3}
|
var DirectionLookup = []int{9, 15, 5, 6, 4, 12, 10, 2, 8, 13, 1, 7, 0, 14, 11, 3}
|
||||||
|
|
||||||
|
type LayerCacheEntry struct {
|
||||||
|
frames []*ebiten.Image
|
||||||
|
compositeMode ebiten.CompositeMode
|
||||||
|
offsetX, offsetY int32
|
||||||
|
}
|
||||||
|
|
||||||
// AnimatedEntity represents an entity on the map that can be animated
|
// AnimatedEntity represents an entity on the map that can be animated
|
||||||
type AnimatedEntity struct {
|
type AnimatedEntity struct {
|
||||||
fileProvider d2interface.FileProvider
|
fileProvider d2interface.FileProvider
|
||||||
@ -49,9 +55,10 @@ type AnimatedEntity struct {
|
|||||||
direction int
|
direction int
|
||||||
currentFrame int
|
currentFrame int
|
||||||
offsetX, offsetY int32
|
offsetX, offsetY int32
|
||||||
frames []*ebiten.Image
|
|
||||||
//frameLocations []d2common.Rectangle
|
//frameLocations []d2common.Rectangle
|
||||||
object *d2datadict.ObjectLookupRecord
|
object *d2datadict.ObjectLookupRecord
|
||||||
|
layerCache []LayerCacheEntry
|
||||||
|
drawOrder [][]d2enum.CompositeType
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
||||||
@ -62,7 +69,7 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fil
|
|||||||
token: object.Token,
|
token: object.Token,
|
||||||
object: object,
|
object: object,
|
||||||
palette: palette,
|
palette: palette,
|
||||||
frames: []*ebiten.Image{},
|
layerCache: make([]LayerCacheEntry, d2enum.CompositeTypeMax),
|
||||||
//frameLocations: []d2common.Rectangle{},
|
//frameLocations: []d2common.Rectangle{},
|
||||||
}
|
}
|
||||||
result.dccLayers = make(map[string]d2dcc.DCC)
|
result.dccLayers = make(map[string]d2dcc.DCC)
|
||||||
@ -85,8 +92,6 @@ func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction in
|
|||||||
if v.direction >= v.Cof.NumberOfDirections {
|
if v.direction >= v.Cof.NumberOfDirections {
|
||||||
v.direction = v.Cof.NumberOfDirections - 1
|
v.direction = v.Cof.NumberOfDirections - 1
|
||||||
}
|
}
|
||||||
//v.frames = make(map[string][]*ebiten.Image)
|
|
||||||
//v.frameLocations = make(map[string][]d2common.Rectangle)
|
|
||||||
v.dccLayers = make(map[string]d2dcc.DCC)
|
v.dccLayers = make(map[string]d2dcc.DCC)
|
||||||
for _, cofLayer := range v.Cof.CofLayers {
|
for _, cofLayer := range v.Cof.CofLayers {
|
||||||
layerName := DccLayerNames[cofLayer.Type]
|
layerName := DccLayerNames[cofLayer.Type]
|
||||||
@ -97,7 +102,6 @@ func (v *AnimatedEntity) SetMode(animationMode, weaponClass string, direction in
|
|||||||
}
|
}
|
||||||
|
|
||||||
v.updateFrameCache()
|
v.updateFrameCache()
|
||||||
//v.cacheFrames()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FileProvider) d2dcc.DCC {
|
func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FileProvider) d2dcc.DCC {
|
||||||
@ -161,38 +165,22 @@ func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v.currentFrame < 0 || v.frames == nil || v.currentFrame >= len(v.frames) || v.frames[v.currentFrame] == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
localX := (v.subcellX - v.subcellY) * 16
|
localX := (v.subcellX - v.subcellY) * 16
|
||||||
localY := ((v.subcellX + v.subcellY) * 8) - 5
|
localY := ((v.subcellX + v.subcellY) * 8) - 5
|
||||||
|
|
||||||
|
for _, layerIdx := range v.drawOrder[v.currentFrame] {
|
||||||
|
if v.currentFrame < 0 || v.layerCache[layerIdx].frames == nil || v.currentFrame >= len(v.layerCache[layerIdx].frames) || v.layerCache[layerIdx].frames[v.currentFrame] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
opts := &ebiten.DrawImageOptions{}
|
opts := &ebiten.DrawImageOptions{}
|
||||||
opts.GeoM.Translate(float64(v.offsetX)+float64(offsetX)+localX, float64(v.offsetY)+float64(offsetY)+localY)
|
x := float64(v.offsetX) + float64(offsetX) + localX + float64(v.layerCache[layerIdx].offsetX)
|
||||||
if err := target.DrawImage(v.frames[v.currentFrame], opts); err != nil {
|
y := float64(v.offsetY) + float64(offsetY) + localY + float64(v.layerCache[layerIdx].offsetY)
|
||||||
|
opts.GeoM.Translate(x, y)
|
||||||
|
opts.CompositeMode = v.layerCache[layerIdx].compositeMode
|
||||||
|
if err := target.DrawImage(v.layerCache[layerIdx].frames[v.currentFrame], opts); err != nil {
|
||||||
log.Panic(err.Error())
|
log.Panic(err.Error())
|
||||||
}
|
}
|
||||||
//for idx := 0; idx < v.Cof.NumberOfLayers; idx++ {
|
}
|
||||||
// priority := v.Cof.Priority[v.direction][v.currentFrame][idx]
|
|
||||||
// if int(priority) >= len(DccLayerNames) {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// frameName := DccLayerNames[priority]
|
|
||||||
// if v.frames[frameName] == nil {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Location within the current tile
|
|
||||||
// localX := (v.subcellX - v.subcellY) * 16
|
|
||||||
// localY := ((v.subcellX + v.subcellY) * 8) - 5
|
|
||||||
//
|
|
||||||
// // TODO: Transparency op maybe, but it'l murder batch calls
|
|
||||||
// opts := &ebiten.DrawImageOptions{}
|
|
||||||
// opts.GeoM.Translate(float64(v.frameLocations[frameName][v.currentFrame].Left+offsetX)+localX,
|
|
||||||
// float64(v.frameLocations[frameName][v.currentFrame].Top+offsetY)+localY)
|
|
||||||
// if err := target.DrawImage(v.frames[frameName][v.currentFrame], opts); err != nil {
|
|
||||||
// log.Panic(err.Error())
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *AnimatedEntity) updateFrameCache() {
|
func (v *AnimatedEntity) updateFrameCache() {
|
||||||
@ -206,52 +194,71 @@ func (v *AnimatedEntity) updateFrameCache() {
|
|||||||
v.animationSpeed = 1.0 / ((float64(animationData.AnimationSpeed) * 25.0) / 256.0)
|
v.animationSpeed = 1.0 / ((float64(animationData.AnimationSpeed) * 25.0) / 256.0)
|
||||||
v.framesToAnimate = animationData.FramesPerDirection
|
v.framesToAnimate = animationData.FramesPerDirection
|
||||||
v.lastFrameTime = d2helper.Now()
|
v.lastFrameTime = d2helper.Now()
|
||||||
minX := int32(10000)
|
|
||||||
minY := int32(10000)
|
v.drawOrder = make([][]d2enum.CompositeType, v.framesToAnimate)
|
||||||
maxX := int32(-10000)
|
for frame := 0; frame < v.framesToAnimate; frame++ {
|
||||||
maxY := int32(-10000)
|
v.drawOrder[frame] = v.Cof.Priority[v.direction][frame]
|
||||||
|
}
|
||||||
|
|
||||||
for cofLayerIdx := range v.Cof.CofLayers {
|
for cofLayerIdx := range v.Cof.CofLayers {
|
||||||
layerName := DccLayerNames[v.Cof.CofLayers[cofLayerIdx].Type]
|
layerType := v.Cof.CofLayers[cofLayerIdx].Type
|
||||||
|
layerName := DccLayerNames[layerType]
|
||||||
dccLayer := v.dccLayers[layerName]
|
dccLayer := v.dccLayers[layerName]
|
||||||
if !dccLayer.IsValid() {
|
if !dccLayer.IsValid() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
v.layerCache[layerType].frames = make([]*ebiten.Image, v.framesToAnimate)
|
||||||
|
|
||||||
|
minX := int32(10000)
|
||||||
|
minY := int32(10000)
|
||||||
|
maxX := int32(-10000)
|
||||||
|
maxY := int32(-10000)
|
||||||
for frameIdx := range dccLayer.Directions[v.direction].Frames {
|
for frameIdx := range dccLayer.Directions[v.direction].Frames {
|
||||||
minX = d2helper.MinInt32(minX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Left))
|
minX = d2helper.MinInt32(minX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Left))
|
||||||
minY = d2helper.MinInt32(minY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Top))
|
minY = d2helper.MinInt32(minY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Top))
|
||||||
maxX = d2helper.MaxInt32(maxX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Right()))
|
maxX = d2helper.MaxInt32(maxX, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Right()))
|
||||||
maxY = d2helper.MaxInt32(maxY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Bottom()))
|
maxY = d2helper.MaxInt32(maxY, int32(dccLayer.Directions[v.direction].Frames[frameIdx].Box.Bottom()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
v.offsetX = minX
|
v.layerCache[layerType].offsetX = minX
|
||||||
v.offsetY = minY
|
v.layerCache[layerType].offsetY = minY
|
||||||
actualWidth := maxX - minX
|
actualWidth := maxX - minX
|
||||||
actualHeight := maxY - minY
|
actualHeight := maxY - minY
|
||||||
|
|
||||||
if (actualWidth <= 0) || (actualHeight < 0) {
|
if (actualWidth <= 0) || (actualHeight < 0) {
|
||||||
log.Printf("Animated entity created with an invalid size of (%d, %d)", actualWidth, actualHeight)
|
log.Printf("Animated entity created with an invalid size of (%d, %d)", actualWidth, actualHeight)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v.frames = make([]*ebiten.Image, v.framesToAnimate)
|
|
||||||
pixels := make([]byte, actualWidth*actualHeight*4)
|
|
||||||
for animationIdx := 0; animationIdx < v.framesToAnimate; animationIdx++ {
|
|
||||||
// This should be faster than allocating all the bytes all over again...
|
|
||||||
for i := 0; i < int(actualWidth*actualHeight); i++ {
|
|
||||||
pixels[(i*4)+3] = 0
|
|
||||||
}
|
|
||||||
for cofLayerIdx := range v.Cof.CofLayers {
|
|
||||||
layerName := DccLayerNames[v.Cof.CofLayers[cofLayerIdx].Type]
|
|
||||||
dccLayer := v.dccLayers[layerName]
|
|
||||||
if !dccLayer.IsValid() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
transparency := byte(255)
|
transparency := byte(255)
|
||||||
if v.Cof.CofLayers[cofLayerIdx].Transparent {
|
if v.Cof.CofLayers[cofLayerIdx].Transparent {
|
||||||
|
switch v.Cof.CofLayers[cofLayerIdx].DrawEffect {
|
||||||
|
//Lets pick whatever we have that's closest.
|
||||||
|
case d2enum.DrawEffectPctTransparency25:
|
||||||
|
transparency = byte(64)
|
||||||
|
case d2enum.DrawEffectPctTransparency50:
|
||||||
transparency = byte(128)
|
transparency = byte(128)
|
||||||
|
case d2enum.DrawEffectPctTransparency75:
|
||||||
|
transparency = byte(192)
|
||||||
|
case d2enum.DrawEffectModulate:
|
||||||
|
v.layerCache[layerType].compositeMode = ebiten.CompositeModeLighter
|
||||||
|
case d2enum.DrawEffectBurn:
|
||||||
|
// Flies in tal rasha's tomb use this
|
||||||
|
case d2enum.DrawEffectNormal:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels := make([]byte, actualWidth*actualHeight*4)
|
||||||
|
|
||||||
|
for animationIdx := 0; animationIdx < v.framesToAnimate; animationIdx++ {
|
||||||
|
for i := 0; i < int(actualWidth*actualHeight); i++ {
|
||||||
|
pixels[(i*4)+3] = 0
|
||||||
}
|
}
|
||||||
if animationIdx >= len(dccLayer.Directions[v.direction].Frames) {
|
if animationIdx >= len(dccLayer.Directions[v.direction].Frames) {
|
||||||
log.Printf("Invalid animation index of %d for animated entity", animationIdx)
|
log.Printf("Invalid animation index of %d for animated entity", animationIdx)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := dccLayer.Directions[v.direction].Frames[animationIdx]
|
frame := dccLayer.Directions[v.direction].Frames[animationIdx]
|
||||||
for y := 0; y < dccLayer.Directions[v.direction].Box.Height; y++ {
|
for y := 0; y < dccLayer.Directions[v.direction].Box.Height; y++ {
|
||||||
for x := 0; x < dccLayer.Directions[v.direction].Box.Width; x++ {
|
for x := 0; x < dccLayer.Directions[v.direction].Box.Width; x++ {
|
||||||
@ -268,76 +275,12 @@ func (v *AnimatedEntity) updateFrameCache() {
|
|||||||
pixels[(actualX*4)+(actualY*int(actualWidth)*4)+3] = transparency
|
pixels[(actualX*4)+(actualY*int(actualWidth)*4)+3] = transparency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v.layerCache[layerType].frames[animationIdx], _ = ebiten.NewImage(int(actualWidth), int(actualHeight), ebiten.FilterNearest)
|
||||||
|
_ = v.layerCache[layerType].frames[animationIdx].ReplacePixels(pixels)
|
||||||
}
|
}
|
||||||
v.frames[animationIdx], _ = ebiten.NewImage(int(actualWidth), int(actualHeight), ebiten.FilterNearest)
|
|
||||||
_ = v.frames[animationIdx].ReplacePixels(pixels)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (v *AnimatedEntity) cacheFrames(layerName string) {
|
|
||||||
// dcc := v.dccLayers[layerName]
|
|
||||||
// v.currentFrame = 0
|
|
||||||
// animationData := d2data.AnimationData[strings.ToLower(v.token+v.animationMode+v.weaponClass)][0]
|
|
||||||
// v.animationSpeed = 1.0 / ((float64(animationData.AnimationSpeed) * 25.0) / 256.0)
|
|
||||||
// v.framesToAnimate = animationData.FramesPerDirection
|
|
||||||
// v.lastFrameTime = d2helper.Now()
|
|
||||||
// minX := int32(10000)
|
|
||||||
// minY := int32(10000)
|
|
||||||
// maxX := int32(-10000)
|
|
||||||
// maxY := int32(-10000)
|
|
||||||
// for _, layer := range dcc.Directions {
|
|
||||||
// minX = d2helper.MinInt32(minX, int32(layer.Box.Left))
|
|
||||||
// minY = d2helper.MinInt32(minY, int32(layer.Box.Top))
|
|
||||||
// maxX = d2helper.MaxInt32(maxX, int32(layer.Box.Right()))
|
|
||||||
// maxY = d2helper.MaxInt32(maxY, int32(layer.Box.Bottom()))
|
|
||||||
// }
|
|
||||||
// frameW := maxX - minX
|
|
||||||
// frameH := maxY - minY
|
|
||||||
// v.frames[layerName] = make([]*ebiten.Image, v.framesToAnimate)
|
|
||||||
// v.frameLocations[layerName] = make([]d2common.Rectangle, v.framesToAnimate)
|
|
||||||
// for frameIndex := range v.frames[layerName] {
|
|
||||||
// v.frames[layerName][frameIndex], _ = ebiten.NewImage(int(frameW), int(frameH), ebiten.FilterNearest)
|
|
||||||
// for layerIdx := 0; layerIdx < v.Cof.NumberOfLayers; layerIdx++ {
|
|
||||||
// transparency := byte(255)
|
|
||||||
// if v.Cof.CofLayers[layerIdx].Transparent {
|
|
||||||
// transparency = byte(128)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// direction := dcc.Directions[v.direction]
|
|
||||||
// if frameIndex >= len(direction.Frames) {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// frame := direction.Frames[frameIndex]
|
|
||||||
// img := image.NewRGBA(image.Rect(0, 0, int(frameW), int(frameH)))
|
|
||||||
// for y := 0; y < direction.Box.Height; y++ {
|
|
||||||
// for x := 0; x < direction.Box.Width; x++ {
|
|
||||||
// paletteIndex := frame.PixelData[x+(y*direction.Box.Width)]
|
|
||||||
//
|
|
||||||
// if paletteIndex == 0 {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// color := d2datadict.Palettes[v.palette].Colors[paletteIndex]
|
|
||||||
// actualX := x + direction.Box.Left - int(minX)
|
|
||||||
// actualY := y + direction.Box.Top - int(minY)
|
|
||||||
// img.Pix[(actualX*4)+(actualY*int(frameW)*4)] = color.R
|
|
||||||
// img.Pix[(actualX*4)+(actualY*int(frameW)*4)+1] = color.G
|
|
||||||
// img.Pix[(actualX*4)+(actualY*int(frameW)*4)+2] = color.B
|
|
||||||
// img.Pix[(actualX*4)+(actualY*int(frameW)*4)+3] = transparency
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// newImage, _ := ebiten.NewImageFromImage(img, ebiten.FilterNearest)
|
|
||||||
// img = nil
|
|
||||||
// v.frames[layerName][frameIndex] = newImage
|
|
||||||
// v.frameLocations[layerName][frameIndex] = d2common.Rectangle{
|
|
||||||
// Left: int(minX),
|
|
||||||
// Top: int(minY),
|
|
||||||
// Width: int(frameW),
|
|
||||||
// Height: int(frameH),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
func (v AnimatedEntity) GetDirection() int {
|
func (v AnimatedEntity) GetDirection() int {
|
||||||
return v.direction
|
return v.direction
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user