diff --git a/d2common/d2interface/animation.go b/d2common/d2interface/animation.go index 098ed610..6a229193 100644 --- a/d2common/d2interface/animation.go +++ b/d2common/d2interface/animation.go @@ -13,7 +13,7 @@ type Animation interface { SetSubLoop(startFrame, EndFrame int) Advance(elapsed float64) error Render(target Surface) error - RenderFromOrigin(target Surface) error + RenderFromOrigin(target Surface, shadow bool) error RenderSection(sfc Surface, bound image.Rectangle) error GetFrameSize(frameIndex int) (int, int, error) GetCurrentFrameSize() (int, int) diff --git a/d2common/d2interface/surface.go b/d2common/d2interface/surface.go index 55c04ae9..ff8e3ad0 100644 --- a/d2common/d2interface/surface.go +++ b/d2common/d2interface/surface.go @@ -21,7 +21,10 @@ type Surface interface { PushEffect(effect d2enum.DrawEffect) PushFilter(filter d2enum.Filter) PushTranslation(x, y int) + PushSkew(x, y float64) + PushScale(x, y float64) PushBrightness(brightness float64) + PushSaturation(saturation float64) Render(surface Surface) error // Renders a section of the surface enclosed by bounds RenderSection(surface Surface, bound image.Rectangle) error diff --git a/d2core/d2asset/animation.go b/d2core/d2asset/animation.go index 004443e3..c3359d9f 100644 --- a/d2core/d2asset/animation.go +++ b/d2core/d2asset/animation.go @@ -4,6 +4,7 @@ import ( "errors" "image" "image/color" + "math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" @@ -112,6 +113,24 @@ func (a *animation) Advance(elapsed float64) error { return nil } +func (a *animation) renderShadow(target d2iface.Surface) error { + //_, height := a.GetFrameBounds() + direction := a.directions[a.directionIndex] + frame := direction.frames[a.frameIndex] + target.PushFilter(d2enum.FilterLinear) + target.PushTranslation( + frame.offsetX, + int(float64(frame.offsetY)*0.5)) + target.PushScale(1.0, 0.5) + target.PushSkew(0.5, 0.0) + target.PushEffect(d2enum.DrawEffectPctTransparency25) + target.PushBrightness(0.0) + + defer target.PopN(6) + + return target.Render(frame.image) +} + // Render renders the animation to the given surface func (a *animation) Render(target d2iface.Surface) error { direction := a.directions[a.directionIndex] @@ -130,7 +149,7 @@ func (a *animation) Render(target d2iface.Surface) error { } // RenderFromOrigin renders the animation from the animation origin -func (a *animation) RenderFromOrigin(target d2iface.Surface) error { +func (a *animation) RenderFromOrigin(target d2iface.Surface, shadow bool) error { if a.originAtBottom { direction := a.directions[a.directionIndex] frame := direction.frames[a.frameIndex] @@ -139,6 +158,14 @@ func (a *animation) RenderFromOrigin(target d2iface.Surface) error { defer target.Pop() } + if shadow { + _, height := a.GetFrameBounds() + height = int(math.Abs(float64(height))) + target.PushTranslation((-height / 2), 0) + defer target.Pop() + return a.renderShadow(target) + } + return a.Render(target) } diff --git a/d2core/d2asset/composite.go b/d2core/d2asset/composite.go index 8a623440..d1b972f5 100644 --- a/d2core/d2asset/composite.go +++ b/d2core/d2asset/composite.go @@ -59,10 +59,20 @@ func (c *Composite) Render(target d2interface.Surface) error { } direction := d2cof.Dir64ToCof(c.direction, c.mode.cof.NumberOfDirections) + for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] { layer := c.mode.layers[layerIndex] if layer != nil { - if err := layer.RenderFromOrigin(target); err != nil { + if err := layer.RenderFromOrigin(target, true); err != nil { + return err + } + } + } + + for _, layerIndex := range c.mode.cof.Priority[direction][c.mode.frameIndex] { + layer := c.mode.layers[layerIndex] + if layer != nil { + if err := layer.RenderFromOrigin(target, false); err != nil { return err } } diff --git a/d2core/d2render/ebiten/ebiten_renderer.go b/d2core/d2render/ebiten/ebiten_renderer.go index 88de8db5..0b8a738c 100644 --- a/d2core/d2render/ebiten/ebiten_renderer.go +++ b/d2core/d2render/ebiten/ebiten_renderer.go @@ -85,8 +85,14 @@ func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surfa result := createEbitenSurface( surface.(*ebitenSurface).image, surfaceState{ - filter: ebiten.FilterNearest, - effect: d2enum.DrawEffectNone, + filter: ebiten.FilterNearest, + effect: d2enum.DrawEffectNone, + saturation: 1.0, + brightness: 1.0, + skewX: 0.0, + skewY: 0.0, + scaleX: 0.0, + scaleY: 0.0, }, ) diff --git a/d2core/d2render/ebiten/ebiten_surface.go b/d2core/d2render/ebiten/ebiten_surface.go index bdbc5368..8d9002db 100644 --- a/d2core/d2render/ebiten/ebiten_surface.go +++ b/d2core/d2render/ebiten/ebiten_surface.go @@ -35,7 +35,15 @@ type ebitenSurface struct { } func createEbitenSurface(img *ebiten.Image, currentState ...surfaceState) *ebitenSurface { - state := surfaceState{effect: d2enum.DrawEffectNone} + state := surfaceState{ + effect: d2enum.DrawEffectNone, + saturation: 1.0, + brightness: 1.0, + skewX: 0.0, + skewY: 0.0, + scaleX: 1.0, + scaleY: 1.0, + } if len(currentState) > 0 { state = currentState[0] } @@ -54,6 +62,20 @@ func (s *ebitenSurface) PushTranslation(x, y int) { s.stateCurrent.y += y } +// PushSkew pushes a skew to the state stack +func (s *ebitenSurface) PushSkew(skewX, skewY float64) { + s.stateStack = append(s.stateStack, s.stateCurrent) + s.stateCurrent.skewX = skewX + s.stateCurrent.skewY = skewY +} + +// PushScale pushes a scale to the state stack +func (s *ebitenSurface) PushScale(scaleX, scaleY float64) { + s.stateStack = append(s.stateStack, s.stateCurrent) + s.stateCurrent.scaleX = scaleX + s.stateCurrent.scaleY = scaleY +} + // PushEffect pushes an effect to the state stack func (s *ebitenSurface) PushEffect(effect d2enum.DrawEffect) { s.stateStack = append(s.stateStack, s.stateCurrent) @@ -78,6 +100,12 @@ func (s *ebitenSurface) PushBrightness(brightness float64) { s.stateCurrent.brightness = brightness } +// PushSaturation pushes a saturation value to the state stack +func (s *ebitenSurface) PushSaturation(saturation float64) { + s.stateStack = append(s.stateStack, s.stateCurrent) + s.stateCurrent.saturation = saturation +} + // Pop pops a state off of the state stack func (s *ebitenSurface) Pop() { count := len(s.stateStack) @@ -99,15 +127,25 @@ func (s *ebitenSurface) PopN(n int) { // Render renders the given surface func (s *ebitenSurface) Render(sfc d2interface.Surface) error { opts := &ebiten.DrawImageOptions{} + + if s.stateCurrent.skewX != 0 || s.stateCurrent.skewY != 0 { + opts.GeoM.Skew(s.stateCurrent.skewX, s.stateCurrent.skewY) + } + + if s.stateCurrent.scaleX != 1.0 || s.stateCurrent.scaleY != 1.0 { + opts.GeoM.Scale(s.stateCurrent.scaleX, s.stateCurrent.scaleY) + } + opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y)) + opts.Filter = s.stateCurrent.filter if s.stateCurrent.color != nil { opts.ColorM = s.colorToColorM(s.stateCurrent.color) } - if s.stateCurrent.brightness != 0 { - opts.ColorM.ChangeHSV(0, 1, s.stateCurrent.brightness) + if s.stateCurrent.brightness != 1 || s.stateCurrent.saturation != 1 { + opts.ColorM.ChangeHSV(0, s.stateCurrent.saturation, s.stateCurrent.brightness) } // Are these correct? who even knows @@ -137,7 +175,17 @@ func (s *ebitenSurface) Render(sfc d2interface.Surface) error { // Renders the section of the surface, given the bounds func (s *ebitenSurface) RenderSection(sfc d2interface.Surface, bound image.Rectangle) error { opts := &ebiten.DrawImageOptions{} + + if s.stateCurrent.skewX != 0 || s.stateCurrent.skewY != 0 { + opts.GeoM.Skew(s.stateCurrent.skewX, s.stateCurrent.skewY) + } + + if s.stateCurrent.scaleX != 1.0 || s.stateCurrent.scaleY != 1.0 { + opts.GeoM.Scale(s.stateCurrent.scaleX, s.stateCurrent.scaleY) + } + opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y)) + opts.Filter = s.stateCurrent.filter if s.stateCurrent.color != nil { @@ -145,7 +193,7 @@ func (s *ebitenSurface) RenderSection(sfc d2interface.Surface, bound image.Recta } if s.stateCurrent.brightness != 0 { - opts.ColorM.ChangeHSV(0, 1, s.stateCurrent.brightness) + opts.ColorM.ChangeHSV(0, s.stateCurrent.saturation, s.stateCurrent.brightness) } // Are these correct? who even knows diff --git a/d2core/d2render/ebiten/surface_state.go b/d2core/d2render/ebiten/surface_state.go index 5514a4b2..a48906de 100644 --- a/d2core/d2render/ebiten/surface_state.go +++ b/d2core/d2render/ebiten/surface_state.go @@ -9,10 +9,13 @@ import ( ) type surfaceState struct { - x int - y int - filter ebiten.Filter - color color.Color - brightness float64 - effect d2enum.DrawEffect + x int + y int + filter ebiten.Filter + color color.Color + brightness float64 + saturation float64 + effect d2enum.DrawEffect + skewX, skewY float64 + scaleX, scaleY float64 }