OpenDiablo2/d2core/d2render/ebiten/ebiten_renderer.go

184 lines
4.8 KiB
Go

package ebiten
import (
"errors"
"image"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
)
const (
screenWidth = 800
screenHeight = 600
defaultSaturation = 1.0
defaultBrightness = 1.0
defaultSkewX = 0.0
defaultSkewY = 0.0
defaultScaleX = 1.0
defaultScaleY = 1.0
)
type renderCallback = func(surface d2interface.Surface) error
type updateCallback = func() error
// static check that we implement our renderer interface
var _ d2interface.Renderer = &Renderer{}
// Renderer is an implementation of a renderer
type Renderer struct {
updateCallback
renderCallback
*d2util.GlyphPrinter
lastRenderError error
}
// Update calls the game's logical update function (the `Advance` method)
func (r *Renderer) Update() error {
if r.updateCallback == nil {
return errors.New("no update callback defined for ebiten renderer")
}
return r.updateCallback()
}
const drawError = "no render callback defined for ebiten renderer"
// Draw updates the screen with the given *ebiten.Image
func (r *Renderer) Draw(screen *ebiten.Image) {
r.lastRenderError = nil
if r.renderCallback == nil {
r.lastRenderError = errors.New(drawError)
return
}
r.lastRenderError = r.renderCallback(createEbitenSurface(r, screen))
}
// Layout returns the renderer screen width and height
func (r *Renderer) Layout(_, _ int) (width, height int) {
return screenWidth, screenHeight
}
// CreateRenderer creates an ebiten renderer instance
func CreateRenderer(cfg *d2config.Configuration) (*Renderer, error) {
result := &Renderer{
GlyphPrinter: d2util.NewDebugPrinter(),
}
if cfg != nil {
config := cfg
ebiten.SetCursorMode(ebiten.CursorModeHidden)
ebiten.SetFullscreen(config.FullScreen)
ebiten.SetRunnableOnUnfocused(config.RunInBackground)
ebiten.SetVsyncEnabled(config.VsyncEnabled)
ebiten.SetMaxTPS(config.TicksPerSecond)
}
return result, nil
}
// GetRendererName returns the name of the renderer
func (*Renderer) GetRendererName() string {
return "Ebiten"
}
// SetWindowIcon sets the icon for the window, visible in the chrome of the window
func (*Renderer) SetWindowIcon(fileName string) {
_, iconImage, err := ebitenutil.NewImageFromFile(fileName)
if err == nil {
ebiten.SetWindowIcon([]image.Image{iconImage})
}
}
// IsDrawingSkipped returns a bool for whether or not the drawing has been skipped
func (r *Renderer) IsDrawingSkipped() bool {
return r.lastRenderError != nil
}
// Run initializes the renderer
func (r *Renderer) Run(f renderCallback, u updateCallback, width, height int, title string) error {
r.renderCallback = f
r.updateCallback = u
ebiten.SetWindowTitle(title)
ebiten.SetWindowResizable(true)
ebiten.SetWindowSize(width, height)
return ebiten.RunGame(r)
}
// CreateSurface creates a renderer surface from an existing surface
func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surface, error) {
img := surface.(*ebitenSurface).image
sfcState := surfaceState{
filter: ebiten.FilterNearest,
effect: d2enum.DrawEffectNone,
saturation: defaultSaturation,
brightness: defaultBrightness,
skewX: defaultSkewX,
skewY: defaultSkewY,
scaleX: defaultScaleX,
scaleY: defaultScaleY,
}
result := createEbitenSurface(r, img, sfcState)
return result, nil
}
// NewSurface creates a new surface
func (r *Renderer) NewSurface(width, height int) d2interface.Surface {
img := ebiten.NewImage(width, height)
return createEbitenSurface(r, img)
}
// IsFullScreen returns a boolean for whether or not the renderer is currently set to fullscreen
func (r *Renderer) IsFullScreen() bool {
return ebiten.IsFullscreen()
}
// SetFullScreen sets the renderer to fullscreen, given a boolean
func (r *Renderer) SetFullScreen(fullScreen bool) {
ebiten.SetFullscreen(fullScreen)
}
// SetVSyncEnabled enables vsync, given a boolean
func (r *Renderer) SetVSyncEnabled(vsync bool) {
ebiten.SetVsyncEnabled(vsync)
}
// GetVSyncEnabled returns a boolean for whether or not vsync is enabled
func (r *Renderer) GetVSyncEnabled() bool {
return ebiten.IsVsyncEnabled()
}
// GetCursorPos returns the current cursor position x,y coordinates
func (r *Renderer) GetCursorPos() (x, y int) {
return ebiten.CursorPosition()
}
// CurrentFPS returns the current frames per second of the renderer
func (r *Renderer) CurrentFPS() float64 {
return ebiten.CurrentFPS()
}
// ShowPanicScreen shows a panic message in a forever loop
func (r *Renderer) ShowPanicScreen(message string) {
errorScreen := CreatePanicScreen(message)
err := ebiten.RunGame(errorScreen)
if err != nil {
panic(err)
}
}