mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-09 10:06:35 -05:00
scene rendering now works
* scene systems now render their objects to the main viewport * added SegmentedSprite component * added SegmentedSprite to sprite factory
This commit is contained in:
parent
1c8240d869
commit
05dae775e4
@ -36,4 +36,5 @@ const (
|
||||
SizeCID
|
||||
AnimationCID
|
||||
ScaleCID
|
||||
SegmentedSpriteCID
|
||||
)
|
||||
|
@ -19,7 +19,7 @@ type RenderableComponent struct {
|
||||
d2interface.Surface
|
||||
}
|
||||
|
||||
// RenderableMap is a map of entity ID's to Surface
|
||||
// RenderableMap is a map of entity ID's to Renderable
|
||||
type RenderableMap struct {
|
||||
*akara.BaseComponentMap
|
||||
}
|
||||
@ -41,8 +41,8 @@ func (cm *RenderableMap) GetRenderable(id akara.EID) (*RenderableComponent, bool
|
||||
return entry.(*RenderableComponent), found
|
||||
}
|
||||
|
||||
// Surface is a convenient reference to be used as a component identifier
|
||||
var Surface = newRenderable() // nolint:gochecknoglobals // global by design
|
||||
// Renderable is a convenient reference to be used as a component identifier
|
||||
var Renderable = newRenderable() // nolint:gochecknoglobals // global by design
|
||||
|
||||
func newRenderable() akara.Component {
|
||||
return &RenderableComponent{
|
||||
|
67
d2core/d2components/segmented_sprite.go
Normal file
67
d2core/d2components/segmented_sprite.go
Normal file
@ -0,0 +1,67 @@
|
||||
//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same
|
||||
package d2components
|
||||
|
||||
import (
|
||||
"github.com/gravestench/akara"
|
||||
)
|
||||
|
||||
// static check that SegmentedSpriteComponent implements Component
|
||||
var _ akara.Component = &SegmentedSpriteComponent{}
|
||||
|
||||
// static check that SegmentedSpriteMap implements ComponentMap
|
||||
var _ akara.ComponentMap = &SegmentedSpriteMap{}
|
||||
|
||||
// SegmentedSpriteComponent represents an entities x,y axis scale as a vector
|
||||
type SegmentedSpriteComponent struct {
|
||||
*akara.BaseComponent
|
||||
Xsegments int
|
||||
Ysegments int
|
||||
FrameOffset int
|
||||
}
|
||||
|
||||
// SegmentedSpriteMap is a map of entity ID's to SegmentedSprite
|
||||
type SegmentedSpriteMap struct {
|
||||
*akara.BaseComponentMap
|
||||
}
|
||||
|
||||
// AddSegmentedSprite adds a new SegmentedSpriteComponent for the given entity id and returns it.
|
||||
// this is a convenience method for the generic Add method, as it returns a
|
||||
// *SegmentedSpriteComponent instead of an akara.Component
|
||||
func (cm *SegmentedSpriteMap) AddSegmentedSprite(id akara.EID) *SegmentedSpriteComponent {
|
||||
c := cm.Add(id).(*SegmentedSpriteComponent)
|
||||
|
||||
c.Xsegments = 1
|
||||
c.Ysegments = 1
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// GetSegmentedSprite returns the SegmentedSpriteComponent associated with the given entity id
|
||||
func (cm *SegmentedSpriteMap) GetSegmentedSprite(id akara.EID) (*SegmentedSpriteComponent, bool) {
|
||||
entry, found := cm.Get(id)
|
||||
if entry == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return entry.(*SegmentedSpriteComponent), found
|
||||
}
|
||||
|
||||
// SegmentedSprite is a convenient reference to be used as a component identifier
|
||||
var SegmentedSprite = newSegmentedSprite() // nolint:gochecknoglobals // global by design
|
||||
|
||||
func newSegmentedSprite() akara.Component {
|
||||
return &SegmentedSpriteComponent{
|
||||
BaseComponent: akara.NewBaseComponent(SegmentedSpriteCID, newSegmentedSprite, newSegmentedSpriteMap),
|
||||
}
|
||||
}
|
||||
|
||||
func newSegmentedSpriteMap() akara.ComponentMap {
|
||||
baseComponent := akara.NewBaseComponent(SegmentedSpriteCID, newSegmentedSprite, newSegmentedSpriteMap)
|
||||
baseMap := akara.NewBaseComponentMap(baseComponent)
|
||||
|
||||
cm := &SegmentedSpriteMap{
|
||||
BaseComponentMap: baseMap,
|
||||
}
|
||||
|
||||
return cm
|
||||
}
|
@ -25,7 +25,7 @@ func NewRenderSystem() *RenderSystem {
|
||||
viewports := akara.NewFilter().
|
||||
Require(d2components.Viewport).
|
||||
Require(d2components.MainViewport).
|
||||
Require(d2components.Surface).
|
||||
Require(d2components.Renderable).
|
||||
Build()
|
||||
|
||||
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
|
||||
@ -73,7 +73,7 @@ func (m *RenderSystem) Init(_ *akara.World) {
|
||||
m.GameConfigMap = m.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
|
||||
m.ViewportMap = m.InjectMap(d2components.Viewport).(*d2components.ViewportMap)
|
||||
m.MainViewportMap = m.InjectMap(d2components.MainViewport).(*d2components.MainViewportMap)
|
||||
m.RenderableMap = m.InjectMap(d2components.Surface).(*d2components.RenderableMap)
|
||||
m.RenderableMap = m.InjectMap(d2components.Renderable).(*d2components.RenderableMap)
|
||||
}
|
||||
|
||||
// Update will initialize the renderer, start the game loop, and
|
||||
@ -156,17 +156,17 @@ func (m *RenderSystem) render(screen d2interface.Surface) error {
|
||||
return errors.New("main viewport not found")
|
||||
}
|
||||
|
||||
sfc, found := m.GetRenderable(id)
|
||||
renderable, found := m.GetRenderable(id)
|
||||
if !found {
|
||||
return errors.New("main viewport doesn't have a surface")
|
||||
}
|
||||
|
||||
if sfc.Surface == nil {
|
||||
sfc.Surface = m.renderer.NewSurface(vp.Width, vp.Height)
|
||||
if renderable.Surface == nil {
|
||||
renderable.Surface = m.renderer.NewSurface(vp.Width, vp.Height)
|
||||
}
|
||||
|
||||
screen.PushTranslation(vp.Left, vp.Top)
|
||||
screen.Render(sfc.Surface)
|
||||
screen.Render(renderable.Surface)
|
||||
screen.Pop()
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ package d2systems
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||
|
||||
"github.com/gravestench/akara"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
@ -128,7 +130,7 @@ func (s *BaseScene) injectComponentMaps() {
|
||||
s.ViewportMap = s.World.InjectMap(d2components.Viewport).(*d2components.ViewportMap)
|
||||
s.ViewportFilterMap = s.World.InjectMap(d2components.ViewportFilter).(*d2components.ViewportFilterMap)
|
||||
s.CameraMap = s.World.InjectMap(d2components.Camera).(*d2components.CameraMap)
|
||||
s.RenderableMap = s.World.InjectMap(d2components.Surface).(*d2components.RenderableMap)
|
||||
s.RenderableMap = s.World.InjectMap(d2components.Renderable).(*d2components.RenderableMap)
|
||||
s.PositionMap = s.World.InjectMap(d2components.Position).(*d2components.PositionMap)
|
||||
s.ScaleMap = s.World.InjectMap(d2components.Scale).(*d2components.ScaleMap)
|
||||
s.AnimationMap = s.World.InjectMap(d2components.Animation).(*d2components.AnimationMap)
|
||||
@ -240,9 +242,9 @@ func (s *BaseScene) renderViewport(idx int, objects []akara.EID) {
|
||||
sfc.Surface = s.systems.renderer.NewSurface(camera.Width, camera.Height)
|
||||
}
|
||||
|
||||
cx, cy := int(camera.X())+camera.Width>>1, int(camera.Y())+camera.Height>>1
|
||||
cx, cy := int(camera.X()), int(camera.Y())
|
||||
|
||||
sfc.Surface.PushTranslation(-cx, -cy) // negative because we're offsetting everything that gets rendered
|
||||
sfc.Surface.PushTranslation(cx, cy) // negative because we're offsetting everything that gets rendered
|
||||
sfc.Surface.PushScale(camera.Zoom, camera.Zoom)
|
||||
|
||||
for _, object := range objects {
|
||||
@ -254,7 +256,7 @@ func (s *BaseScene) renderViewport(idx int, objects []akara.EID) {
|
||||
}
|
||||
|
||||
func (s *BaseScene) renderObject(target d2interface.Surface, id akara.EID) {
|
||||
sfc, found := s.GetRenderable(id)
|
||||
renderable, found := s.GetRenderable(id)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
@ -269,13 +271,51 @@ func (s *BaseScene) renderObject(target d2interface.Surface, id akara.EID) {
|
||||
scale = s.AddScale(id)
|
||||
}
|
||||
|
||||
sfc.PushTranslation(int(position.X()), int(position.Y()))
|
||||
sfc.PushScale(scale.X(), scale.Y())
|
||||
x, y := int(position.X()), int(position.Y())
|
||||
|
||||
target.Render(sfc.Surface)
|
||||
target.PushTranslation(x, y)
|
||||
defer target.Pop()
|
||||
|
||||
sfc.Pop()
|
||||
sfc.Pop()
|
||||
target.PushScale(scale.X(), scale.Y())
|
||||
defer target.Pop()
|
||||
|
||||
segment, found := s.systems.SpriteFactory.GetSegmentedSprite(id)
|
||||
if found {
|
||||
animation, found := s.GetAnimation(id)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
var offsetY int
|
||||
|
||||
segmentsX, segmentsY := segment.Xsegments, segment.Ysegments
|
||||
frameOffset := segment.FrameOffset
|
||||
|
||||
for y := 0; y < segmentsY; y++ {
|
||||
var offsetX, maxFrameHeight int
|
||||
|
||||
for x := 0; x < segmentsX; x++ {
|
||||
idx := x + y*segmentsX + frameOffset*segmentsX*segmentsY
|
||||
if err := animation.SetCurrentFrame(idx); err != nil {
|
||||
s.Error("SetCurrentFrame error" + err.Error())
|
||||
}
|
||||
|
||||
target.PushTranslation(x+offsetX, y+offsetY)
|
||||
target.Render(animation.GetCurrentFrameSurface())
|
||||
target.Pop()
|
||||
|
||||
frameWidth, frameHeight := animation.GetCurrentFrameSize()
|
||||
maxFrameHeight = d2math.MaxInt(maxFrameHeight, frameHeight)
|
||||
offsetX += frameWidth - 1
|
||||
}
|
||||
|
||||
offsetY += maxFrameHeight - 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
target.Render(renderable.Surface)
|
||||
}
|
||||
|
||||
// responsible for wrapping the object factory calls and assigning the created object entity id's to the scene
|
||||
@ -291,3 +331,12 @@ func (s *sceneObjectAssigner) Sprite(x, y float64, imgPath, palPath string) akar
|
||||
|
||||
return eid
|
||||
}
|
||||
|
||||
func (s *sceneObjectAssigner) SegmentedSprite(x, y float64, imgPath, palPath string, xseg, yseg, frame int) akara.EID {
|
||||
s.Infof("creating segmented sprite: %s, %s", filepath.Base(imgPath), palPath)
|
||||
|
||||
eid := s.systems.SpriteFactory.SegmentedSprite(x, y, imgPath, palPath, xseg, yseg, frame)
|
||||
s.GameObjects = append(s.GameObjects, eid)
|
||||
|
||||
return eid
|
||||
}
|
||||
|
@ -41,16 +41,16 @@ func (s *MainMenuScene) boot() {
|
||||
return
|
||||
}
|
||||
|
||||
s.createBackground()
|
||||
s.createButtons()
|
||||
s.createTrademarkScreen()
|
||||
s.createButtons()
|
||||
s.createBackground()
|
||||
|
||||
s.booted = true
|
||||
}
|
||||
|
||||
func (s *MainMenuScene) createBackground() {
|
||||
s.Info("creating background")
|
||||
s.Add.Sprite(0, 0, d2resource.GameSelectScreen, d2resource.PaletteSky)
|
||||
s.Add.SegmentedSprite(0, 0, d2resource.GameSelectScreen, d2resource.PaletteSky, 4, 3, 0)
|
||||
}
|
||||
|
||||
func (s *MainMenuScene) createButtons() {
|
||||
@ -59,7 +59,7 @@ func (s *MainMenuScene) createButtons() {
|
||||
|
||||
func (s *MainMenuScene) createTrademarkScreen() {
|
||||
s.Info("creating trademark screen")
|
||||
s.Add.Sprite(0, 0, d2resource.TrademarkScreen, d2resource.PaletteSky)
|
||||
s.Add.SegmentedSprite(0, 0, d2resource.TrademarkScreen, d2resource.PaletteSky, 4, 3, 0)
|
||||
}
|
||||
|
||||
// Update the main menu scene
|
||||
|
@ -18,11 +18,16 @@ const (
|
||||
func NewSpriteFactorySubsystem(b *akara.BaseSystem, l *d2util.Logger) *SpriteFactory {
|
||||
spritesToRender := akara.NewFilter().
|
||||
Require(d2components.Animation). // we want to process entities that have an animation ...
|
||||
Forbid(d2components.Surface). // ... but are missing a surface
|
||||
Forbid(d2components.Renderable). // ... but are missing a surface
|
||||
Build()
|
||||
|
||||
spritesToUpdate := akara.NewFilter().
|
||||
Require(d2components.Animation). // we want to process entities that have an animation ...
|
||||
Require(d2components.Renderable). // ... but are missing a surface
|
||||
Build()
|
||||
|
||||
sys := &SpriteFactory{
|
||||
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(spritesToRender),
|
||||
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(spritesToRender, spritesToUpdate),
|
||||
Logger: l,
|
||||
}
|
||||
|
||||
@ -52,8 +57,10 @@ type SpriteFactory struct {
|
||||
*d2components.PaletteMap
|
||||
*d2components.AnimationMap
|
||||
*d2components.RenderableMap
|
||||
*d2components.SegmentedSpriteMap
|
||||
loadQueue spriteLoadQueue
|
||||
spritesToRender *akara.Subscription
|
||||
spritesToUpdate *akara.Subscription
|
||||
}
|
||||
|
||||
// Init the sprite factory, injecting the necessary components
|
||||
@ -63,6 +70,7 @@ func (t *SpriteFactory) Init(world *akara.World) {
|
||||
t.loadQueue = make(spriteLoadQueue)
|
||||
|
||||
t.spritesToRender = t.Subscriptions[0]
|
||||
t.spritesToUpdate = t.Subscriptions[1]
|
||||
|
||||
t.FilePathMap = t.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
|
||||
t.PositionMap = t.InjectMap(d2components.Position).(*d2components.PositionMap)
|
||||
@ -70,12 +78,17 @@ func (t *SpriteFactory) Init(world *akara.World) {
|
||||
t.DccMap = t.InjectMap(d2components.Dcc).(*d2components.DccMap)
|
||||
t.PaletteMap = t.InjectMap(d2components.Palette).(*d2components.PaletteMap)
|
||||
t.AnimationMap = t.InjectMap(d2components.Animation).(*d2components.AnimationMap)
|
||||
t.RenderableMap = t.InjectMap(d2components.Surface).(*d2components.RenderableMap)
|
||||
t.RenderableMap = t.InjectMap(d2components.Renderable).(*d2components.RenderableMap)
|
||||
t.SegmentedSpriteMap = t.InjectMap(d2components.SegmentedSprite).(*d2components.SegmentedSpriteMap)
|
||||
}
|
||||
|
||||
// Update processes the load queue which attempting to create animations, as well as
|
||||
// binding existing animations to a renderer if one is present.
|
||||
func (t *SpriteFactory) Update() {
|
||||
for _, eid := range t.spritesToUpdate.GetEntities() {
|
||||
t.updateSprite(eid)
|
||||
}
|
||||
|
||||
for _, eid := range t.spritesToRender.GetEntities() {
|
||||
t.tryRenderingSprite(eid)
|
||||
}
|
||||
@ -103,6 +116,19 @@ func (t *SpriteFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID
|
||||
return spriteID
|
||||
}
|
||||
|
||||
// SegmentedSprite queues a segmented sprite animation to be loaded.
|
||||
// A segmented sprite is a sprite that has many frames that form the entire sprite.
|
||||
func (t *SpriteFactory) SegmentedSprite(x, y float64, imgPath, palPath string, xseg, yseg, frame int) akara.EID {
|
||||
spriteID := t.Sprite(x, y, imgPath, palPath)
|
||||
|
||||
s := t.AddSegmentedSprite(spriteID)
|
||||
s.Xsegments = xseg
|
||||
s.Ysegments = yseg
|
||||
s.FrameOffset = frame
|
||||
|
||||
return spriteID
|
||||
}
|
||||
|
||||
func (t *SpriteFactory) tryCreatingAnimation(id akara.EID) {
|
||||
entry := t.loadQueue[id]
|
||||
imageID, paletteID := entry.spriteImage, entry.spritePalette
|
||||
@ -172,6 +198,32 @@ func (t *SpriteFactory) tryRenderingSprite(eid akara.EID) {
|
||||
t.AddRenderable(eid).Surface = sfc
|
||||
}
|
||||
|
||||
func (t *SpriteFactory) updateSprite(eid akara.EID) {
|
||||
if t.RenderSystem == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if t.RenderSystem.renderer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
anim, found := t.GetAnimation(eid)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
if anim.Animation == nil {
|
||||
return
|
||||
}
|
||||
|
||||
renderable, found := t.GetRenderable(eid)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
renderable.Surface = anim.GetCurrentFrameSurface()
|
||||
}
|
||||
|
||||
func (t *SpriteFactory) createDc6Animation(
|
||||
dc6 *d2components.Dc6Component,
|
||||
pal *d2components.PaletteComponent,
|
||||
|
Loading…
Reference in New Issue
Block a user