mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-06-12 18:50:42 +00:00
Animation manager abstraction (#544)
* abstracted palettes, colors, and palette manager * make asset manager use the palette manager interface * added BGRA setter/getter, fixed lint errors * abstraction for animation and animation manager
This commit is contained in:
parent
8809fb2c22
commit
c1a88c9cf7
|
@ -1,11 +1,22 @@
|
||||||
package d2dat
|
package d2dat
|
||||||
|
|
||||||
|
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// index offset helpers
|
||||||
|
b = iota
|
||||||
|
g
|
||||||
|
r
|
||||||
|
o
|
||||||
|
)
|
||||||
|
|
||||||
// Load loads a DAT file.
|
// Load loads a DAT file.
|
||||||
func Load(data []byte) (*DATPalette, error) {
|
func Load(data []byte) (d2interface.Palette, error) {
|
||||||
palette := &DATPalette{}
|
palette := &DATPalette{}
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
palette.Colors[i] = DATColor{B: data[i*3], G: data[i*3+1], R: data[i*3+2]}
|
// offsets look like i*3+n, where n is 0,1,2
|
||||||
|
palette.colors[i] = &DATColor{b: data[i*o+b], g: data[i*o+g], r: data[i*o+r]}
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette, nil
|
return palette, nil
|
||||||
|
|
|
@ -2,7 +2,76 @@ package d2dat
|
||||||
|
|
||||||
// DATColor represents a single color in a DAT file.
|
// DATColor represents a single color in a DAT file.
|
||||||
type DATColor struct {
|
type DATColor struct {
|
||||||
R uint8
|
r uint8
|
||||||
G uint8
|
g uint8
|
||||||
B uint8
|
b uint8
|
||||||
|
a uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
colorBits = 8
|
||||||
|
mask = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bitShift0 = iota*colorBits
|
||||||
|
bitShift8
|
||||||
|
bitShift16
|
||||||
|
bitShift24
|
||||||
|
)
|
||||||
|
|
||||||
|
// R gets the red component
|
||||||
|
func (c *DATColor) R() uint8 {
|
||||||
|
return c.r
|
||||||
|
}
|
||||||
|
|
||||||
|
// G gets the green component
|
||||||
|
func (c *DATColor) G() uint8 {
|
||||||
|
return c.g
|
||||||
|
}
|
||||||
|
|
||||||
|
// B gets the blue component
|
||||||
|
func (c *DATColor) B() uint8 {
|
||||||
|
return c.b
|
||||||
|
}
|
||||||
|
|
||||||
|
// A gets the alpha component
|
||||||
|
func (c *DATColor) A() uint8 {
|
||||||
|
return mask
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGBA gets the combination of the color components (0xRRGGBBAA)
|
||||||
|
func (c *DATColor) RGBA() uint32 {
|
||||||
|
return toComposite(c.r, c.g, c.b, c.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRGBA sets the color components using the given RGBA form
|
||||||
|
func (c *DATColor) SetRGBA(rgba uint32) {
|
||||||
|
c.r, c.g, c.b, c.a = toComponent(rgba)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DATColor) BGRA() uint32 {
|
||||||
|
return toComposite(c.b, c.g, c.r, c.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DATColor) SetBGRA(bgra uint32) {
|
||||||
|
c.b, c.g, c.r, c.a = toComponent(bgra)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toComposite (w,x,y,z uint8) uint32 {
|
||||||
|
composite := uint32(w)<<bitShift24
|
||||||
|
composite += uint32(x)<<bitShift16
|
||||||
|
composite += uint32(y)<<bitShift8
|
||||||
|
composite += uint32(z)<<bitShift0
|
||||||
|
|
||||||
|
return composite
|
||||||
|
}
|
||||||
|
|
||||||
|
func toComponent (wxyz uint32) (w,x,y,z uint8){
|
||||||
|
w = uint8(wxyz>>bitShift24 & mask)
|
||||||
|
x = uint8(wxyz>>bitShift16 & mask)
|
||||||
|
y = uint8(wxyz>>bitShift8 & mask)
|
||||||
|
z = uint8(wxyz>>bitShift0 & mask)
|
||||||
|
return w, x, y, z
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,34 @@
|
||||||
package d2dat
|
package d2dat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
numColors = 256
|
||||||
|
)
|
||||||
|
|
||||||
// DATPalette represents a 256 color palette.
|
// DATPalette represents a 256 color palette.
|
||||||
type DATPalette struct {
|
type DATPalette struct {
|
||||||
Colors [256]DATColor
|
colors [numColors]d2interface.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumColors returns the number of colors in the palette
|
||||||
|
func (p *DATPalette) NumColors() int {
|
||||||
|
return len(p.colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetColors returns the slice of colors in the palette
|
||||||
|
func (p *DATPalette) GetColors() [numColors]d2interface.Color {
|
||||||
|
return p.colors
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetColor returns a color by index
|
||||||
|
func (p *DATPalette) GetColor(idx int) (d2interface.Color, error) {
|
||||||
|
if color := p.colors[idx]; color != nil {
|
||||||
|
return color, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("cannot find color index '%d in palette'", idx)
|
||||||
}
|
}
|
||||||
|
|
39
d2common/d2interface/animation.go
Normal file
39
d2common/d2interface/animation.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package d2interface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Animation is an animation
|
||||||
|
type Animation interface {
|
||||||
|
Clone() Animation
|
||||||
|
SetSubLoop(startFrame, EndFrame int)
|
||||||
|
Advance(elapsed float64) error
|
||||||
|
Render(target Surface) error
|
||||||
|
RenderFromOrigin(target Surface) error
|
||||||
|
RenderSection(sfc Surface, bound image.Rectangle) error
|
||||||
|
GetFrameSize(frameIndex int) (int, int, error)
|
||||||
|
GetCurrentFrameSize() (int, int)
|
||||||
|
GetFrameBounds() (int, int)
|
||||||
|
GetCurrentFrame() int
|
||||||
|
GetFrameCount() int
|
||||||
|
IsOnFirstFrame() bool
|
||||||
|
IsOnLastFrame() bool
|
||||||
|
GetDirectionCount() int
|
||||||
|
SetDirection(directionIndex int) error
|
||||||
|
GetDirection() int
|
||||||
|
SetCurrentFrame(frameIndex int) error
|
||||||
|
Rewind()
|
||||||
|
PlayForward()
|
||||||
|
PlayBackward()
|
||||||
|
Pause()
|
||||||
|
SetPlayLoop(loop bool)
|
||||||
|
SetPlaySpeed(playSpeed float64)
|
||||||
|
SetPlayLength(playLength float64)
|
||||||
|
SetPlayLengthMs(playLengthMs int)
|
||||||
|
SetColorMod(colorMod color.Color)
|
||||||
|
GetPlayedCount() int
|
||||||
|
ResetPlayedCount()
|
||||||
|
SetBlend(blend bool)
|
||||||
|
}
|
6
d2common/d2interface/archived_animation_manager.go
Normal file
6
d2common/d2interface/archived_animation_manager.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package d2interface
|
||||||
|
|
||||||
|
type ArchivedAnimationManager interface {
|
||||||
|
Cacher
|
||||||
|
LoadAnimation(animationPath, palettePath string, transparency int) (Animation, error)
|
||||||
|
}
|
5
d2common/d2interface/archived_palette_manager.go
Normal file
5
d2common/d2interface/archived_palette_manager.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package d2interface
|
||||||
|
|
||||||
|
type ArchivedPaletteManager interface {
|
||||||
|
LoadPalette(palettePath string) (Palette, error)
|
||||||
|
}
|
22
d2common/d2interface/palette.go
Normal file
22
d2common/d2interface/palette.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package d2interface
|
||||||
|
|
||||||
|
const numColors = 256
|
||||||
|
|
||||||
|
// Color represents a color
|
||||||
|
type Color interface {
|
||||||
|
R() uint8
|
||||||
|
G() uint8
|
||||||
|
B() uint8
|
||||||
|
A() uint8
|
||||||
|
RGBA() uint32
|
||||||
|
SetRGBA(uint32)
|
||||||
|
BGRA() uint32
|
||||||
|
SetBGRA(uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Palette is a color palette
|
||||||
|
type Palette interface {
|
||||||
|
NumColors() int
|
||||||
|
GetColors() [256]Color
|
||||||
|
GetColor(idx int) (Color, error)
|
||||||
|
}
|
|
@ -8,10 +8,9 @@ import (
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
d2iface "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dat"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
|
||||||
)
|
)
|
||||||
|
@ -32,7 +31,7 @@ type animationFrame struct {
|
||||||
offsetX int
|
offsetX int
|
||||||
offsetY int
|
offsetY int
|
||||||
|
|
||||||
image d2interface.Surface
|
image d2iface.Surface
|
||||||
}
|
}
|
||||||
|
|
||||||
type animationDirection struct {
|
type animationDirection struct {
|
||||||
|
@ -41,27 +40,25 @@ type animationDirection struct {
|
||||||
|
|
||||||
// Animation has directionality, play modes, and frame counting
|
// Animation has directionality, play modes, and frame counting
|
||||||
type Animation struct {
|
type Animation struct {
|
||||||
directions []*animationDirection
|
directions []*animationDirection
|
||||||
frameIndex int
|
colorMod color.Color
|
||||||
directionIndex int
|
frameIndex int
|
||||||
lastFrameTime float64
|
directionIndex int
|
||||||
playedCount int
|
lastFrameTime float64
|
||||||
|
playedCount int
|
||||||
compositeMode d2enum.CompositeMode
|
compositeMode d2enum.CompositeMode
|
||||||
colorMod color.Color
|
|
||||||
originAtBottom bool
|
|
||||||
|
|
||||||
playMode playMode
|
playMode playMode
|
||||||
playLength float64
|
playLength float64
|
||||||
playLoop bool
|
|
||||||
hasSubLoop bool // runs after first animation ends
|
|
||||||
subStartingFrame int
|
subStartingFrame int
|
||||||
subEndingFrame int
|
subEndingFrame int
|
||||||
|
originAtBottom bool
|
||||||
|
playLoop bool
|
||||||
|
hasSubLoop bool // runs after first animation ends
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAnimationFromDCC creates an animation from d2dcc.DCC and d2dat.DATPalette
|
// CreateAnimationFromDCC creates an animation from d2dcc.DCC and d2dat.DATPalette
|
||||||
func CreateAnimationFromDCC(renderer d2interface.Renderer, dcc *d2dcc.DCC, palette *d2dat.DATPalette,
|
func CreateAnimationFromDCC(renderer d2iface.Renderer, dcc *d2dcc.DCC, palette d2iface.Palette,
|
||||||
transparency int) (*Animation, error) {
|
transparency int) (d2iface.Animation, error) {
|
||||||
animation := &Animation{
|
animation := &Animation{
|
||||||
playLength: defaultPlayLength,
|
playLength: defaultPlayLength,
|
||||||
playLoop: true,
|
playLoop: true,
|
||||||
|
@ -87,18 +84,22 @@ func CreateAnimationFromDCC(renderer d2interface.Renderer, dcc *d2dcc.DCC, palet
|
||||||
|
|
||||||
for y := 0; y < frameHeight; y++ {
|
for y := 0; y < frameHeight; y++ {
|
||||||
for x := 0; x < frameWidth; x++ {
|
for x := 0; x < frameWidth; x++ {
|
||||||
if paletteIndex := dccFrame.PixelData[y*frameWidth+x]; paletteIndex != 0 {
|
paletteIndex := dccFrame.PixelData[y*frameWidth+x]
|
||||||
palColor := palette.Colors[paletteIndex]
|
|
||||||
offset := (x + y*frameWidth) * bytesPerPixel
|
if paletteIndex == 0 {
|
||||||
pixels[offset] = palColor.R
|
continue
|
||||||
pixels[offset+1] = palColor.G
|
|
||||||
pixels[offset+2] = palColor.B
|
|
||||||
pixels[offset+3] = byte(transparency)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
palColor := 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(transparency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sfc, err := renderer.NewSurface(frameWidth, frameHeight, d2interface.FilterNearest)
|
sfc, err := renderer.NewSurface(frameWidth, frameHeight, d2iface.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -126,7 +127,8 @@ func CreateAnimationFromDCC(renderer d2interface.Renderer, dcc *d2dcc.DCC, palet
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAnimationFromDC6 creates an Animation from d2dc6.DC6 and d2dat.DATPalette
|
// CreateAnimationFromDC6 creates an Animation from d2dc6.DC6 and d2dat.DATPalette
|
||||||
func CreateAnimationFromDC6(renderer d2interface.Renderer, dc6 *d2dc6.DC6, palette *d2dat.DATPalette) (*Animation, error) {
|
func CreateAnimationFromDC6(renderer d2iface.Renderer, dc6 *d2dc6.DC6,
|
||||||
|
palette d2iface.Palette) (d2iface.Animation, error) {
|
||||||
animation := &Animation{
|
animation := &Animation{
|
||||||
playLength: defaultPlayLength,
|
playLength: defaultPlayLength,
|
||||||
playLoop: true,
|
playLoop: true,
|
||||||
|
@ -134,7 +136,8 @@ func CreateAnimationFromDC6(renderer d2interface.Renderer, dc6 *d2dc6.DC6, palet
|
||||||
}
|
}
|
||||||
|
|
||||||
for frameIndex, dc6Frame := range dc6.Frames {
|
for frameIndex, dc6Frame := range dc6.Frames {
|
||||||
sfc, err := renderer.NewSurface(int(dc6Frame.Width), int(dc6Frame.Height), d2interface.FilterNearest)
|
sfc, err := renderer.NewSurface(int(dc6Frame.Width), int(dc6Frame.Height),
|
||||||
|
d2iface.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -177,14 +180,16 @@ func CreateAnimationFromDC6(renderer d2interface.Renderer, dc6 *d2dc6.DC6, palet
|
||||||
colorData := make([]byte, int(dc6Frame.Width)*int(dc6Frame.Height)*bytesPerPixel)
|
colorData := make([]byte, int(dc6Frame.Width)*int(dc6Frame.Height)*bytesPerPixel)
|
||||||
|
|
||||||
for i := 0; i < int(dc6Frame.Width*dc6Frame.Height); i++ {
|
for i := 0; i < int(dc6Frame.Width*dc6Frame.Height); i++ {
|
||||||
if indexData[i] < 1 { // TODO: Is this == -1 or < 1?
|
// TODO: Is this == -1 or < 1?
|
||||||
|
if indexData[i] < 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
colorData[i*bytesPerPixel] = palette.Colors[indexData[i]].R
|
c := palette.GetColors()[indexData[i]]
|
||||||
colorData[i*bytesPerPixel+1] = palette.Colors[indexData[i]].G
|
colorData[i*bytesPerPixel] = c.R()
|
||||||
colorData[i*bytesPerPixel+2] = palette.Colors[indexData[i]].B
|
colorData[i*bytesPerPixel+1] = c.G()
|
||||||
colorData[i*bytesPerPixel+3] = 0xff
|
colorData[i*bytesPerPixel+2] = c.B()
|
||||||
|
colorData[i*bytesPerPixel+3] = c.A()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sfc.ReplacePixels(colorData); err != nil {
|
if err := sfc.ReplacePixels(colorData); err != nil {
|
||||||
|
@ -209,17 +214,20 @@ func CreateAnimationFromDC6(renderer d2interface.Renderer, dc6 *d2dc6.DC6, palet
|
||||||
return animation, nil
|
return animation, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Animation) Clone() *Animation {
|
// Clone creates a copy of the animation
|
||||||
|
func (a *Animation) Clone() d2iface.Animation {
|
||||||
animation := *a
|
animation := *a
|
||||||
return &animation
|
return &animation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Animation) SetSubLoop(startFrame, EndFrame int) {
|
// SetSubLoop sets a sub loop for the animation
|
||||||
|
func (a *Animation) SetSubLoop(startFrame, endFrame int) {
|
||||||
a.subStartingFrame = startFrame
|
a.subStartingFrame = startFrame
|
||||||
a.subEndingFrame = EndFrame
|
a.subEndingFrame = endFrame
|
||||||
a.hasSubLoop = true
|
a.hasSubLoop = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advance advances the animation state
|
||||||
func (a *Animation) Advance(elapsed float64) error {
|
func (a *Animation) Advance(elapsed float64) error {
|
||||||
if a.playMode == playModePause {
|
if a.playMode == playModePause {
|
||||||
return nil
|
return nil
|
||||||
|
@ -234,6 +242,7 @@ func (a *Animation) Advance(elapsed float64) error {
|
||||||
for i := 0; i < framesAdvanced; i++ {
|
for i := 0; i < framesAdvanced; i++ {
|
||||||
startIndex := 0
|
startIndex := 0
|
||||||
endIndex := frameCount
|
endIndex := frameCount
|
||||||
|
|
||||||
if a.hasSubLoop && a.playedCount > 0 {
|
if a.hasSubLoop && a.playedCount > 0 {
|
||||||
startIndex = a.subStartingFrame
|
startIndex = a.subStartingFrame
|
||||||
endIndex = a.subEndingFrame
|
endIndex = a.subEndingFrame
|
||||||
|
@ -268,7 +277,8 @@ func (a *Animation) Advance(elapsed float64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Animation) Render(target d2interface.Surface) error {
|
// Render renders the animation to the given surface
|
||||||
|
func (a *Animation) Render(target d2iface.Surface) error {
|
||||||
direction := a.directions[a.directionIndex]
|
direction := a.directions[a.directionIndex]
|
||||||
frame := direction.frames[a.frameIndex]
|
frame := direction.frames[a.frameIndex]
|
||||||
|
|
||||||
|
@ -284,11 +294,13 @@ func (a *Animation) Render(target d2interface.Surface) error {
|
||||||
return target.Render(frame.image)
|
return target.Render(frame.image)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Animation) RenderFromOrigin(target d2interface.Surface) error {
|
// RenderFromOrigin renders the animation from the animation origin
|
||||||
|
func (a *Animation) RenderFromOrigin(target d2iface.Surface) error {
|
||||||
if a.originAtBottom {
|
if a.originAtBottom {
|
||||||
direction := a.directions[a.directionIndex]
|
direction := a.directions[a.directionIndex]
|
||||||
frame := direction.frames[a.frameIndex]
|
frame := direction.frames[a.frameIndex]
|
||||||
target.PushTranslation(0, -frame.height)
|
target.PushTranslation(0, -frame.height)
|
||||||
|
|
||||||
defer target.Pop()
|
defer target.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,37 +308,40 @@ func (a *Animation) RenderFromOrigin(target d2interface.Surface) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderSection renders the section of the animation frame enclosed by bounds
|
// RenderSection renders the section of the animation frame enclosed by bounds
|
||||||
func (a *Animation) RenderSection(sfc d2interface.Surface, bound image.Rectangle) error {
|
func (a *Animation) RenderSection(sfc d2iface.Surface, bound image.Rectangle) error {
|
||||||
direction := a.directions[a.directionIndex]
|
direction := a.directions[a.directionIndex]
|
||||||
frame := direction.frames[a.frameIndex]
|
frame := direction.frames[a.frameIndex]
|
||||||
|
|
||||||
sfc.PushTranslation(frame.offsetX, frame.offsetY)
|
sfc.PushTranslation(frame.offsetX, frame.offsetY)
|
||||||
sfc.PushCompositeMode(a.compositeMode)
|
sfc.PushCompositeMode(a.compositeMode)
|
||||||
sfc.PushColor(a.colorMod)
|
sfc.PushColor(a.colorMod)
|
||||||
|
|
||||||
defer sfc.PopN(3)
|
defer sfc.PopN(3)
|
||||||
|
|
||||||
return sfc.RenderSection(frame.image, bound)
|
return sfc.RenderSection(frame.image, bound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFrameSize gets the Size(width, height) of a indexed frame.
|
// GetFrameSize gets the Size(width, height) of a indexed frame.
|
||||||
func (a *Animation) GetFrameSize(frameIndex int) (int, int, error) {
|
func (a *Animation) GetFrameSize(frameIndex int) (width, height int, err error) {
|
||||||
direction := a.directions[a.directionIndex]
|
direction := a.directions[a.directionIndex]
|
||||||
if frameIndex >= len(direction.frames) {
|
if frameIndex >= len(direction.frames) {
|
||||||
return 0, 0, errors.New("invalid frame index")
|
return 0, 0, errors.New("invalid frame index")
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := direction.frames[frameIndex]
|
frame := direction.frames[frameIndex]
|
||||||
|
|
||||||
return frame.width, frame.height, nil
|
return frame.width, frame.height, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentFrameSize gets the Size(width, height) of the current frame.
|
// GetCurrentFrameSize gets the Size(width, height) of the current frame.
|
||||||
func (a *Animation) GetCurrentFrameSize() (int, int) {
|
func (a *Animation) GetCurrentFrameSize() (width, height int) {
|
||||||
width, height, _ := a.GetFrameSize(a.frameIndex)
|
width, height, _ = a.GetFrameSize(a.frameIndex)
|
||||||
return width, height
|
return width, height
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFrameBounds gets maximum Size(width, height) of all frame.
|
// GetFrameBounds gets maximum Size(width, height) of all frame.
|
||||||
func (a *Animation) GetFrameBounds() (int, int) {
|
func (a *Animation) GetFrameBounds() (maxWidth, maxHeight int) {
|
||||||
maxWidth, maxHeight := 0, 0
|
maxWidth, maxHeight = 0, 0
|
||||||
|
|
||||||
direction := a.directions[a.directionIndex]
|
direction := a.directions[a.directionIndex]
|
||||||
for _, frame := range direction.frames {
|
for _, frame := range direction.frames {
|
||||||
|
@ -427,13 +442,15 @@ func (a *Animation) SetPlaySpeed(playSpeed float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPlayLength sets the Animation's play length in seconds
|
// SetPlayLength sets the Animation's play length in seconds
|
||||||
func (a *Animation) SetPlayLength(playLength float64) { // TODO refactor to use time.Duration instead of float64
|
func (a *Animation) SetPlayLength(playLength float64) {
|
||||||
|
// TODO refactor to use time.Duration instead of float64
|
||||||
a.playLength = playLength
|
a.playLength = playLength
|
||||||
a.lastFrameTime = 0
|
a.lastFrameTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPlayLengthMs sets the Animation's play length in milliseconds
|
// SetPlayLengthMs sets the Animation's play length in milliseconds
|
||||||
func (a *Animation) SetPlayLengthMs(playLengthMs int) { // TODO remove this method
|
func (a *Animation) SetPlayLengthMs(playLengthMs int) {
|
||||||
|
// TODO remove this method
|
||||||
const millisecondsPerSecond = 1000.0
|
const millisecondsPerSecond = 1000.0
|
||||||
a.SetPlayLength(float64(playLengthMs) / millisecondsPerSecond)
|
a.SetPlayLength(float64(playLengthMs) / millisecondsPerSecond)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,14 @@ type animationManager struct {
|
||||||
renderer d2interface.Renderer
|
renderer d2interface.Renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *animationManager) ClearCache() {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *animationManager) GetCache() d2interface.Cache {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
func createAnimationManager(renderer d2interface.Renderer) *animationManager {
|
func createAnimationManager(renderer d2interface.Renderer) *animationManager {
|
||||||
return &animationManager{
|
return &animationManager{
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
|
@ -26,13 +34,15 @@ func createAnimationManager(renderer d2interface.Renderer) *animationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *animationManager) loadAnimation(animationPath, palettePath string, transparency int) (*Animation, error) {
|
func (am *animationManager) LoadAnimation(
|
||||||
|
animationPath, palettePath string,
|
||||||
|
transparency int ) (d2interface.Animation, error) {
|
||||||
cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, transparency)
|
cachePath := fmt.Sprintf("%s;%s;%d", animationPath, palettePath, transparency)
|
||||||
if animation, found := am.cache.Retrieve(cachePath); found {
|
if animation, found := am.cache.Retrieve(cachePath); found {
|
||||||
return animation.(*Animation).Clone(), nil
|
return animation.(*Animation).Clone(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var animation *Animation
|
var animation d2interface.Animation
|
||||||
|
|
||||||
ext := strings.ToLower(filepath.Ext(animationPath))
|
ext := strings.ToLower(filepath.Ext(animationPath))
|
||||||
switch ext {
|
switch ext {
|
||||||
|
|
|
@ -10,9 +10,9 @@ import (
|
||||||
type assetManager struct {
|
type assetManager struct {
|
||||||
archiveManager d2interface.ArchiveManager
|
archiveManager d2interface.ArchiveManager
|
||||||
archivedFileManager d2interface.ArchivedFileManager
|
archivedFileManager d2interface.ArchivedFileManager
|
||||||
paletteManager *paletteManager
|
paletteManager d2interface.ArchivedPaletteManager
|
||||||
paletteTransformManager *paletteTransformManager
|
paletteTransformManager *paletteTransformManager
|
||||||
animationManager *animationManager
|
animationManager d2interface.ArchivedAnimationManager
|
||||||
fontManager *fontManager
|
fontManager *fontManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ type compositeMode struct {
|
||||||
weaponClass string
|
weaponClass string
|
||||||
playedCount int
|
playedCount int
|
||||||
|
|
||||||
layers []*Animation
|
layers []d2interface.Animation
|
||||||
|
|
||||||
frameCount int
|
frameCount int
|
||||||
frameIndex int
|
frameIndex int
|
||||||
|
@ -194,7 +194,7 @@ func (c *Composite) createMode(animationMode, weaponClass string) (*compositeMod
|
||||||
cof: cof,
|
cof: cof,
|
||||||
animationMode: animationMode,
|
animationMode: animationMode,
|
||||||
weaponClass: weaponClass,
|
weaponClass: weaponClass,
|
||||||
layers: make([]*Animation, d2enum.CompositeTypeMax),
|
layers: make([]d2interface.Animation, d2enum.CompositeTypeMax),
|
||||||
frameCount: animationData[0].FramesPerDirection,
|
frameCount: animationData[0].FramesPerDirection,
|
||||||
animationSpeed: 1.0 / ((float64(animationData[0].AnimationSpeed) * 25.0) / 256.0),
|
animationSpeed: 1.0 / ((float64(animationData[0].AnimationSpeed) * 25.0) / 256.0),
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,7 @@ func (c *Composite) createMode(animationMode, weaponClass string) (*compositeMod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weaponClass,
|
func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weaponClass,
|
||||||
palettePath string, transparency int) (*Animation, error) {
|
palettePath string, transparency int) (d2interface.Animation, error) {
|
||||||
animationPaths := []string{
|
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.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),
|
fmt.Sprintf("%s/%s/%s/%s%s%s%s%s.dc6", c.basePath, c.token, layerKey, c.token, layerKey, layerValue, animationMode, weaponClass),
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dat"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -106,13 +105,14 @@ func FileExists(filePath string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAnimation loads an animation by its resource path and its palette path
|
// LoadAnimation loads an animation by its resource path and its palette path
|
||||||
func LoadAnimation(animationPath, palettePath string) (*Animation, error) {
|
func LoadAnimation(animationPath, palettePath string) (d2interface.Animation, error) {
|
||||||
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
|
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAnimationWithTransparency loads an animation by its resource path and its palette path with a given transparency value
|
// LoadAnimationWithTransparency loads an animation by its resource path and its palette path with a given transparency value
|
||||||
func LoadAnimationWithTransparency(animationPath, palettePath string, transparency int) (*Animation, error) {
|
func LoadAnimationWithTransparency(animationPath, palettePath string,
|
||||||
return singleton.animationManager.loadAnimation(animationPath, palettePath, transparency)
|
transparency int) (d2interface.Animation, error) {
|
||||||
|
return singleton.animationManager.LoadAnimation(animationPath, palettePath, transparency)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadComposite creates a composite object from a ObjectLookupRecord and palettePath describing it
|
// LoadComposite creates a composite object from a ObjectLookupRecord and palettePath describing it
|
||||||
|
@ -126,6 +126,6 @@ func LoadFont(tablePath, spritePath, palettePath string) (*Font, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPalette loads a palette from a given palette path
|
// LoadPalette loads a palette from a given palette path
|
||||||
func LoadPalette(palettePath string) (*d2dat.DATPalette, error) {
|
func LoadPalette(palettePath string) (d2interface.Palette, error) {
|
||||||
return singleton.paletteManager.loadPalette(palettePath)
|
return singleton.paletteManager.LoadPalette(palettePath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type fontGlyph struct {
|
||||||
|
|
||||||
// Font represents a displayable font
|
// Font represents a displayable font
|
||||||
type Font struct {
|
type Font struct {
|
||||||
sheet *Animation
|
sheet d2interface.Animation
|
||||||
glyphs map[rune]fontGlyph
|
glyphs map[rune]fontGlyph
|
||||||
color color.Color
|
color color.Color
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ func createPaletteManager() *paletteManager {
|
||||||
return &paletteManager{d2common.CreateCache(paletteBudget)}
|
return &paletteManager{d2common.CreateCache(paletteBudget)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *paletteManager) loadPalette(palettePath string) (*d2dat.DATPalette, error) {
|
func (pm *paletteManager) LoadPalette(palettePath string) (d2interface.Palette, error) {
|
||||||
if palette, found := pm.cache.Retrieve(palettePath); found {
|
if palette, found := pm.cache.Retrieve(palettePath); found {
|
||||||
return palette.(*d2dat.DATPalette), nil
|
return palette.(d2interface.Palette), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
paletteData, err := LoadFile(palettePath)
|
paletteData, err := LoadFile(palettePath)
|
||||||
|
|
|
@ -18,7 +18,8 @@ func loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
|
||||||
return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath)
|
return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderSegmented(animation *d2asset.Animation, segmentsX, segmentsY, frameOffset int, target d2interface.Surface) error {
|
func renderSegmented(animation d2interface.Animation, segmentsX, segmentsY, frameOffset int,
|
||||||
|
target d2interface.Surface) error {
|
||||||
var currentY int
|
var currentY int
|
||||||
for y := 0; y < segmentsY; y++ {
|
for y := 0; y < segmentsY; y++ {
|
||||||
var currentX int
|
var currentX int
|
||||||
|
|
|
@ -13,10 +13,10 @@ import (
|
||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
layout *Layout
|
layout *Layout
|
||||||
cursorAnim *d2asset.Animation
|
cursorAnim d2interface.Animation
|
||||||
cursorX int
|
cursorX int
|
||||||
cursorY int
|
cursorY int
|
||||||
loadingAnim *d2asset.Animation
|
loadingAnim d2interface.Animation
|
||||||
cursorVisible bool
|
cursorVisible bool
|
||||||
loading bool
|
loading bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type Sprite struct {
|
||||||
segmentsY int
|
segmentsY int
|
||||||
frameOffset int
|
frameOffset int
|
||||||
|
|
||||||
animation *d2asset.Animation
|
animation d2interface.Animation
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnimatedSprite struct {
|
type AnimatedSprite struct {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package d2mapentity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AnimatedEntity represents an animation that can be projected onto the map.
|
// AnimatedEntity represents an animation that can be projected onto the map.
|
||||||
|
@ -12,11 +11,11 @@ type AnimatedEntity struct {
|
||||||
action int
|
action int
|
||||||
repetitions int
|
repetitions int
|
||||||
|
|
||||||
animation *d2asset.Animation
|
animation d2interface.Animation
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
||||||
func CreateAnimatedEntity(x, y int, animation *d2asset.Animation) *AnimatedEntity {
|
func CreateAnimatedEntity(x, y int, animation d2interface.Animation) *AnimatedEntity {
|
||||||
entity := &AnimatedEntity{
|
entity := &AnimatedEntity{
|
||||||
mapEntity: createMapEntity(x, y),
|
mapEntity: createMapEntity(x, y),
|
||||||
animation: animation,
|
animation: animation,
|
||||||
|
|
|
@ -21,11 +21,11 @@ func (mr *MapRenderer) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, t
|
||||||
for n > 0 {
|
for n > 0 {
|
||||||
colorIndex := block.EncodedData[idx]
|
colorIndex := block.EncodedData[idx]
|
||||||
if colorIndex != 0 {
|
if colorIndex != 0 {
|
||||||
pixelColor := mr.palette.Colors[colorIndex]
|
pixelColor := mr.palette.GetColors()[colorIndex]
|
||||||
offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
|
offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
|
||||||
(*pixels)[offset] = pixelColor.R
|
(*pixels)[offset] = pixelColor.R()
|
||||||
(*pixels)[offset+1] = pixelColor.G
|
(*pixels)[offset+1] = pixelColor.G()
|
||||||
(*pixels)[offset+2] = pixelColor.B
|
(*pixels)[offset+2] = pixelColor.B()
|
||||||
(*pixels)[offset+3] = 255
|
(*pixels)[offset+3] = 255
|
||||||
}
|
}
|
||||||
x++
|
x++
|
||||||
|
@ -58,12 +58,12 @@ func (mr *MapRenderer) decodeTileGfxData(blocks []d2dt1.Block, pixels *[]byte, t
|
||||||
for b2 > 0 {
|
for b2 > 0 {
|
||||||
colorIndex := block.EncodedData[idx]
|
colorIndex := block.EncodedData[idx]
|
||||||
if colorIndex != 0 {
|
if colorIndex != 0 {
|
||||||
pixelColor := mr.palette.Colors[colorIndex]
|
pixelColor := mr.palette.GetColors()[colorIndex]
|
||||||
|
|
||||||
offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
|
offset := 4 * (((blockY + y + tileYOffset) * tileWidth) + (blockX + x))
|
||||||
(*pixels)[offset] = pixelColor.R
|
(*pixels)[offset] = pixelColor.R()
|
||||||
(*pixels)[offset+1] = pixelColor.G
|
(*pixels)[offset+1] = pixelColor.G()
|
||||||
(*pixels)[offset+2] = pixelColor.B
|
(*pixels)[offset+2] = pixelColor.B()
|
||||||
(*pixels)[offset+3] = 255
|
(*pixels)[offset+3] = 255
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dat"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||||
|
@ -19,7 +18,7 @@ import (
|
||||||
type MapRenderer struct {
|
type MapRenderer struct {
|
||||||
renderer d2interface.Renderer // The renderer to use for drawing operations
|
renderer d2interface.Renderer // The renderer to use for drawing operations
|
||||||
mapEngine *d2mapengine.MapEngine // The map engine that is being rendered
|
mapEngine *d2mapengine.MapEngine // The map engine that is being rendered
|
||||||
palette *d2dat.DATPalette // The palette used for this map
|
palette d2interface.Palette // The palette used for this map
|
||||||
viewport *Viewport // The viewport for the map renderer (used for rendering offsets)
|
viewport *Viewport // The viewport for the map renderer (used for rendering offsets)
|
||||||
camera Camera // The camera for this map renderer (used to determine where on the map we are rendering)
|
camera Camera // The camera for this map renderer (used to determine where on the map we are rendering)
|
||||||
debugVisLevel int // Debug visibility index (0=none, 1=tiles, 2=sub-tiles)
|
debugVisLevel int // Debug visibility index (0=none, 1=tiles, 2=sub-tiles)
|
||||||
|
@ -354,7 +353,7 @@ func (mr *MapRenderer) Advance(elapsed float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPaletteForAct(levelType d2enum.RegionIdType) (*d2dat.DATPalette, error) {
|
func loadPaletteForAct(levelType d2enum.RegionIdType) (d2interface.Palette, error) {
|
||||||
var palettePath string
|
var palettePath string
|
||||||
switch levelType {
|
switch levelType {
|
||||||
case d2enum.RegionAct1Town, d2enum.RegionAct1Wilderness, d2enum.RegionAct1Cave, d2enum.RegionAct1Crypt,
|
case d2enum.RegionAct1Town, d2enum.RegionAct1Wilderness, d2enum.RegionAct1Cave, d2enum.RegionAct1Crypt,
|
||||||
|
|
|
@ -8,21 +8,20 @@ import (
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sprite is a positioned visual object.
|
// Sprite is a positioned visual object.
|
||||||
type Sprite struct {
|
type Sprite struct {
|
||||||
x int
|
x int
|
||||||
y int
|
y int
|
||||||
animation *d2asset.Animation
|
animation d2interface.Animation
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoAnimation = errors.New("no animation was specified")
|
ErrNoAnimation = errors.New("no animation was specified")
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadSprite(animation *d2asset.Animation) (*Sprite, error) {
|
func LoadSprite(animation d2interface.Animation) (*Sprite, error) {
|
||||||
if animation == nil {
|
if animation == nil {
|
||||||
return nil, ErrNoAnimation
|
return nil, ErrNoAnimation
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user