1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-02 22:57:04 -05:00

transform component, scene testing

* removed position,scale,rotation components
* added Transform component that contains position, rotation, and scale
* scene graph update now regenerates the local mat4 using the transform
component
* akara bugfix: adding new subscriptions will process existing entities
* added `--testscene` arg for testing individual scenes in isolation
* added rotation support to d2interface.Surface
This commit is contained in:
gravestench 2020-12-05 23:16:16 -08:00
parent 13ae64af8c
commit 2e814f29b0
36 changed files with 615 additions and 543 deletions

11
d2app/app_ecs.go Normal file
View File

@ -0,0 +1,11 @@
package d2app
import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2systems"
"github.com/gravestench/akara"
)
func Run() {
cfg := akara.NewWorldConfig().With(&d2systems.AppBootstrap{})
akara.NewWorld(cfg)
}

View File

@ -1,10 +1,9 @@
package rectangle
import (
"github.com/gravestench/pho/geom/point"
)
import "github.com/gravestench/pho/geom/point"
type RectangleNamespace interface {
New(x, y, w, h float64) *Rectangle
Contains(r *Rectangle, x, y float64) bool
GetPoint(r *Rectangle, position float64, p *point.Point) *point.Point
GetPoints(r *Rectangle, quantity int, stepRate float64, points []*point.Point) []*point.Point
@ -31,6 +30,11 @@ type RectangleNamespace interface {
type Namespace struct{}
// New creates a new Rectangle instance.
func (*Namespace) New(x, y, w, h float64) *Rectangle {
return New(x, y, w, h)
}
// Contains checks if the given x, y is inside the Rectangle's bounds.
func (*Namespace) Contains(r *Rectangle, x, y float64) bool {
return Contains(r, x, y)

View File

@ -8,7 +8,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
// Animation is an animation
// Sprite is an Sprite
type Sprite interface {
BindRenderer(Renderer)
Clone() Sprite

View File

@ -24,6 +24,7 @@ type Surface interface {
PushTranslation(x, y int)
PushSkew(x, y float64)
PushScale(x, y float64)
PushRotate(theta float64)
PushBrightness(brightness float64)
PushSaturation(saturation float64)
Render(surface Surface)

View File

@ -20,6 +20,38 @@ type Matrix4 struct {
Values [numMatrix4Values]float64
}
// Decompose this matrix into vec3 for translation, rotation, and scale
func (m *Matrix4) Decompose() (t, r, s *Vector3) {
t = NewVector3(m.Values[12], m.Values[13], m.Values[14])
s = NewVector3(
NewVector3(m.Values[0], m.Values[1], m.Values[2]).Length(),
NewVector3(m.Values[4], m.Values[5], m.Values[6]).Length(),
NewVector3(m.Values[8], m.Values[9], m.Values[10]).Length(),
)
e := NewEuler(0, 0, 0, EulerOrderDefault)
rotmat := m.Clone()
rotmat.Values[0] /= s.X
rotmat.Values[1] /= s.Y
rotmat.Values[2] /= s.Z
rotmat.Values[4] /= s.X
rotmat.Values[5] /= s.Y
rotmat.Values[6] /= s.Z
rotmat.Values[8] /= s.X
rotmat.Values[9] /= s.Y
rotmat.Values[10] /= s.Z
e.SetFromRotationMatrix(rotmat, EulerOrderZYX)
r = NewVector3(e.XYZ())
return t, r, s
}
// Clone makes a clone of this Matrix4.
func (m *Matrix4) Clone() *Matrix4 {
return NewMatrix4(m)
@ -47,12 +79,10 @@ func (m *Matrix4) Set(other *Matrix4) *Matrix4 {
// SetValues sets the values of this Matrix4.
func (m *Matrix4) SetValues(a, b, c, d, e, f, g, h, i, j, k, l, mm, n, o, p float64) *Matrix4 {
v := m.Values[:]
v[0], v[1], v[2], v[3] = a, b, c, d
v[4], v[5], v[6], v[7] = e, f, g, h
v[8], v[9], v[10], v[11] = i, j, k, l
v[12], v[13], v[14], v[15] = mm, n, o, p
m.Values[0], m.Values[1], m.Values[2], m.Values[3] = a, b, c, d
m.Values[4], m.Values[5], m.Values[6], m.Values[7] = e, f, g, h
m.Values[8], m.Values[9], m.Values[10], m.Values[11] = i, j, k, l
m.Values[12], m.Values[13], m.Values[14], m.Values[15] = mm, n, o, p
return m
}
@ -130,27 +160,25 @@ func (m *Matrix4) SetScaling(x, y, z float64) *Matrix4 {
// Transpose this Matrix.
func (m *Matrix4) Transpose() *Matrix4 {
a := m.Values[:]
a01 := m.Values[1]
a02 := m.Values[2]
a03 := m.Values[3]
a12 := m.Values[6]
a13 := m.Values[7]
a23 := m.Values[11]
a01 := a[1]
a02 := a[2]
a03 := a[3]
a12 := a[6]
a13 := a[7]
a23 := a[11]
a[1] = a[4]
a[2] = a[8]
a[3] = a[12]
a[4] = a01
a[6] = a[9]
a[7] = a[13]
a[8] = a02
a[9] = a12
a[11] = a[14]
a[12] = a03
a[13] = a13
a[14] = a23
m.Values[1] = m.Values[4]
m.Values[2] = m.Values[8]
m.Values[3] = m.Values[12]
m.Values[4] = a01
m.Values[6] = m.Values[9]
m.Values[7] = m.Values[13]
m.Values[8] = a02
m.Values[9] = a12
m.Values[11] = m.Values[14]
m.Values[12] = a03
m.Values[13] = a13
m.Values[14] = a23
return m
}
@ -162,12 +190,10 @@ func (m *Matrix4) GetInverse(other *Matrix4) *Matrix4 {
// Invert this Matrix.
func (m *Matrix4) Invert() *Matrix4 {
a := m.Values[:]
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a30, a31, a32, a33 := a[12], a[13], a[14], a[15]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15]
b00 := a00*a11 - a01*a10
b01 := a00*a12 - a02*a10
@ -213,12 +239,10 @@ func (m *Matrix4) Invert() *Matrix4 {
// Adjoint calculates the adjoint, or adjugate, of this Matrix.
func (m *Matrix4) Adjoint() *Matrix4 {
a := m.Values[:]
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a30, a31, a32, a33 := a[12], a[13], a[14], a[15]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15]
return m.SetValues(
a11*(a22*a33-a23*a32)-a21*(a12*a33-a13*a32)+a31*(a12*a23-a13*a22),
@ -242,12 +266,10 @@ func (m *Matrix4) Adjoint() *Matrix4 {
// Determinant calculates the determinant of this Matrix.
func (m *Matrix4) Determinant() float64 {
a := m.Values[:]
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a30, a31, a32, a33 := a[12], a[13], a[14], a[15]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15]
b00 := a00*a11 - a01*a10
b01 := a00*a12 - a02*a10
@ -267,62 +289,58 @@ func (m *Matrix4) Determinant() float64 {
// Multiply this Matrix4 by the given Matrix4.
func (m *Matrix4) Multiply(other *Matrix4) *Matrix4 {
a, b := m.Values, other.Values
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a30, a31, a32, a33 := a[12], a[13], a[14], a[15]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15]
// Cache only the current line of the second matrix
b0, b1, b2, b3 := b[0], b[1], b[2], b[3]
a[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30
a[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31
a[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32
a[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 := other.Values[0], other.Values[1], other.Values[2], other.Values[3]
m.Values[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30
m.Values[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31
m.Values[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32
m.Values[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 = b[4], b[5], b[6], b[7]
a[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30
a[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31
a[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32
a[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 = other.Values[4], other.Values[5], other.Values[6], other.Values[7]
m.Values[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30
m.Values[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31
m.Values[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32
m.Values[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 = b[8], b[9], b[10], b[11]
a[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30
a[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31
a[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32
a[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 = other.Values[8], other.Values[9], other.Values[10], other.Values[11]
m.Values[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30
m.Values[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31
m.Values[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32
m.Values[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 = b[12], b[13], b[14], b[15]
a[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30
a[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31
a[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32
a[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33
b0, b1, b2, b3 = other.Values[12], other.Values[13], other.Values[14], other.Values[15]
m.Values[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30
m.Values[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31
m.Values[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32
m.Values[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33
return m
}
// MultiplyLocal multiplies the values of this Matrix4 by those given in the `other` argument.
func (m *Matrix4) MultiplyLocal(other *Matrix4) *Matrix4 {
a, b := m.Values, other.Values
return m.SetValues(
a[0]*b[0]+a[1]*b[4]+a[2]*b[8]+a[3]*b[12],
a[0]*b[1]+a[1]*b[5]+a[2]*b[9]+a[3]*b[13],
a[0]*b[2]+a[1]*b[6]+a[2]*b[10]+a[3]*b[14],
a[0]*b[3]+a[1]*b[7]+a[2]*b[11]+a[3]*b[15],
a[4]*b[0]+a[5]*b[4]+a[6]*b[8]+a[7]*b[12],
a[4]*b[1]+a[5]*b[5]+a[6]*b[9]+a[7]*b[13],
a[4]*b[2]+a[5]*b[6]+a[6]*b[10]+a[7]*b[14],
a[4]*b[3]+a[5]*b[7]+a[6]*b[11]+a[7]*b[15],
a[8]*b[0]+a[9]*b[4]+a[10]*b[8]+a[11]*b[12],
a[8]*b[1]+a[9]*b[5]+a[10]*b[9]+a[11]*b[13],
a[8]*b[2]+a[9]*b[6]+a[10]*b[10]+a[11]*b[14],
a[8]*b[3]+a[9]*b[7]+a[10]*b[11]+a[11]*b[15],
a[12]*b[0]+a[13]*b[4]+a[14]*b[8]+a[15]*b[12],
a[12]*b[1]+a[13]*b[5]+a[14]*b[9]+a[15]*b[13],
a[12]*b[2]+a[13]*b[6]+a[14]*b[10]+a[15]*b[14],
a[12]*b[3]+a[13]*b[7]+a[14]*b[11]+a[15]*b[15],
m.Values[0]*other.Values[0]+m.Values[1]*other.Values[4]+m.Values[2]*other.Values[8]+m.Values[3]*other.Values[12],
m.Values[0]*other.Values[1]+m.Values[1]*other.Values[5]+m.Values[2]*other.Values[9]+m.Values[3]*other.Values[13],
m.Values[0]*other.Values[2]+m.Values[1]*other.Values[6]+m.Values[2]*other.Values[10]+m.Values[3]*other.Values[14],
m.Values[0]*other.Values[3]+m.Values[1]*other.Values[7]+m.Values[2]*other.Values[11]+m.Values[3]*other.Values[15],
m.Values[4]*other.Values[0]+m.Values[5]*other.Values[4]+m.Values[6]*other.Values[8]+m.Values[7]*other.Values[12],
m.Values[4]*other.Values[1]+m.Values[5]*other.Values[5]+m.Values[6]*other.Values[9]+m.Values[7]*other.Values[13],
m.Values[4]*other.Values[2]+m.Values[5]*other.Values[6]+m.Values[6]*other.Values[10]+m.Values[7]*other.Values[14],
m.Values[4]*other.Values[3]+m.Values[5]*other.Values[7]+m.Values[6]*other.Values[11]+m.Values[7]*other.Values[15],
m.Values[8]*other.Values[0]+m.Values[9]*other.Values[4]+m.Values[10]*other.Values[8]+m.Values[11]*other.Values[12],
m.Values[8]*other.Values[1]+m.Values[9]*other.Values[5]+m.Values[10]*other.Values[9]+m.Values[11]*other.Values[13],
m.Values[8]*other.Values[2]+m.Values[9]*other.Values[6]+m.Values[10]*other.Values[10]+m.Values[11]*other.Values[14],
m.Values[8]*other.Values[3]+m.Values[9]*other.Values[7]+m.Values[10]*other.Values[11]+m.Values[11]*other.Values[15],
m.Values[12]*other.Values[0]+m.Values[13]*other.Values[4]+m.Values[14]*other.Values[8]+m.Values[15]*other.Values[12],
m.Values[12]*other.Values[1]+m.Values[13]*other.Values[5]+m.Values[14]*other.Values[9]+m.Values[15]*other.Values[13],
m.Values[12]*other.Values[2]+m.Values[13]*other.Values[6]+m.Values[14]*other.Values[10]+m.Values[15]*other.Values[14],
m.Values[12]*other.Values[3]+m.Values[13]*other.Values[7]+m.Values[14]*other.Values[11]+m.Values[15]*other.Values[15],
)
}
@ -333,41 +351,39 @@ func (m *Matrix4) PreMultiply(other *Matrix4) *Matrix4 {
// MultiplyMatrices multiplies the two given Matrix4 objects and stores the results in this Matrix.
func (m *Matrix4) MultiplyMatrices(a, b *Matrix4) *Matrix4 {
am, bm := a.Values, b.Values
a11 := a.Values[0]
a12 := a.Values[4]
a13 := a.Values[8]
a14 := a.Values[12]
a21 := a.Values[1]
a22 := a.Values[5]
a23 := a.Values[9]
a24 := a.Values[13]
a31 := a.Values[2]
a32 := a.Values[6]
a33 := a.Values[10]
a34 := a.Values[14]
a41 := a.Values[3]
a42 := a.Values[7]
a43 := a.Values[11]
a44 := a.Values[15]
a11 := am[0]
a12 := am[4]
a13 := am[8]
a14 := am[12]
a21 := am[1]
a22 := am[5]
a23 := am[9]
a24 := am[13]
a31 := am[2]
a32 := am[6]
a33 := am[10]
a34 := am[14]
a41 := am[3]
a42 := am[7]
a43 := am[11]
a44 := am[15]
b11 := bm[0]
b12 := bm[4]
b13 := bm[8]
b14 := bm[12]
b21 := bm[1]
b22 := bm[5]
b23 := bm[9]
b24 := bm[13]
b31 := bm[2]
b32 := bm[6]
b33 := bm[10]
b34 := bm[14]
b41 := bm[3]
b42 := bm[7]
b43 := bm[11]
b44 := bm[15]
b11 := b.Values[0]
b12 := b.Values[4]
b13 := b.Values[8]
b14 := b.Values[12]
b21 := b.Values[1]
b22 := b.Values[5]
b23 := b.Values[9]
b24 := b.Values[13]
b31 := b.Values[2]
b32 := b.Values[6]
b33 := b.Values[10]
b34 := b.Values[14]
b41 := b.Values[3]
b42 := b.Values[7]
b43 := b.Values[11]
b44 := b.Values[15]
return m.SetValues(
a11*b11+a12*b21+a13*b31+a14*b41,
@ -396,12 +412,10 @@ func (m *Matrix4) Translate(v Vector3Like) *Matrix4 {
// TranslateXYZ translates this Matrix using the given values.
func (m *Matrix4) TranslateXYZ(x, y, z float64) *Matrix4 {
a := m.Values[:]
a[12] = a[0]*x + a[4]*y + a[8]*z + a[12]
a[13] = a[1]*x + a[5]*y + a[9]*z + a[13]
a[14] = a[2]*x + a[6]*y + a[10]*z + a[14]
a[15] = a[3]*x + a[7]*y + a[11]*z + a[15]
m.Values[12] = m.Values[0]*x + m.Values[4]*y + m.Values[8]*z + m.Values[12]
m.Values[13] = m.Values[1]*x + m.Values[5]*y + m.Values[9]*z + m.Values[13]
m.Values[14] = m.Values[2]*x + m.Values[6]*y + m.Values[10]*z + m.Values[14]
m.Values[15] = m.Values[3]*x + m.Values[7]*y + m.Values[11]*z + m.Values[15]
return m
}
@ -413,11 +427,9 @@ func (m *Matrix4) Scale(v Vector3Like) *Matrix4 {
// ScaleXYZ applies a scale transformation to this Matrix.
func (m *Matrix4) ScaleXYZ(x, y, z float64) *Matrix4 {
a := m.Values[:]
a[0], a[1], a[2], a[3] = a[0]*x, a[1]*x, a[2]*x, a[3]*x
a[4], a[5], a[6], a[7] = a[4]*y, a[5]*y, a[6]*y, a[7]*y
a[8], a[9], a[10], a[11] = a[8]*z, a[9]*z, a[10]*z, a[11]*z
m.Values[0], m.Values[1], m.Values[2], m.Values[3] = m.Values[0]*x, m.Values[1]*x, m.Values[2]*x, m.Values[3]*x
m.Values[4], m.Values[5], m.Values[6], m.Values[7] = m.Values[4]*y, m.Values[5]*y, m.Values[6]*y, m.Values[7]*y
m.Values[8], m.Values[9], m.Values[10], m.Values[11] = m.Values[8]*z, m.Values[9]*z, m.Values[10]*z, m.Values[11]*z
return m
}
@ -439,7 +451,6 @@ func (m *Matrix4) MakeRotationAxis(axis Vector3Like, radians float64) *Matrix4 {
// Rotate applies a rotation transformation to this Matrix.
func (m *Matrix4) Rotate(radians float64, axis Vector3Like) *Matrix4 {
a := m.Values[:]
x, y, z := axis.XYZ()
length := math.Sqrt(x*x + y*y + z*z)
@ -455,10 +466,10 @@ func (m *Matrix4) Rotate(radians float64, axis Vector3Like) *Matrix4 {
c, s := math.Cos(radians), math.Sin(radians)
t := 1 - c
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a30, a31, a32, a33 := a[12], a[13], a[14], a[15]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15]
b00, b01, b02 := x*x*t+c, y*x*t+z*s, z*x*t-y*s
b10, b11, b12 := x*y*t-z*s, y*y*t+c, z*y*t+x*s
@ -483,63 +494,60 @@ func (m *Matrix4) Rotate(radians float64, axis Vector3Like) *Matrix4 {
// RotateX rotates this matrix on its X axis.
func (m *Matrix4) RotateX(radians float64) *Matrix4 {
a := m.Values[:]
c, s := math.Cos(radians), math.Sin(radians)
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
// Perform axis-specific matrix multiplication
a[4] = a10*c + a20*s
a[5] = a11*c + a21*s
a[6] = a12*c + a22*s
a[7] = a13*c + a23*s
a[8] = a20*c - a10*s
a[9] = a21*c - a11*s
a[10] = a22*c - a12*s
a[11] = a23*c - a13*s
m.Values[4] = a10*c + a20*s
m.Values[5] = a11*c + a21*s
m.Values[6] = a12*c + a22*s
m.Values[7] = a13*c + a23*s
m.Values[8] = a20*c - a10*s
m.Values[9] = a21*c - a11*s
m.Values[10] = a22*c - a12*s
m.Values[11] = a23*c - a13*s
return m
}
// RotateY rotates this matrix on its X axis.
func (m *Matrix4) RotateY(radians float64) *Matrix4 {
a := m.Values[:]
c, s := math.Cos(radians), math.Sin(radians)
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a20, a21, a22, a23 := a[8], a[9], a[10], a[11]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11]
// Perform axis-specific matrix multiplication
a[0] = a00*c + a20*s
a[1] = a01*c + a21*s
a[2] = a02*c + a22*s
a[3] = a03*c + a23*s
a[4] = a20*c - a00*s
a[5] = a21*c - a01*s
a[6] = a22*c - a02*s
a[7] = a23*c - a03*s
m.Values[0] = a00*c + a20*s
m.Values[1] = a01*c + a21*s
m.Values[2] = a02*c + a22*s
m.Values[3] = a03*c + a23*s
m.Values[4] = a20*c - a00*s
m.Values[5] = a21*c - a01*s
m.Values[6] = a22*c - a02*s
m.Values[7] = a23*c - a03*s
return m
}
// RotateZ rotates this matrix on its X axis.
func (m *Matrix4) RotateZ(radians float64) *Matrix4 {
a := m.Values[:]
c, s := math.Cos(radians), math.Sin(radians)
a00, a01, a02, a03 := a[0], a[1], a[2], a[3]
a10, a11, a12, a13 := a[4], a[5], a[6], a[7]
a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3]
a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7]
// Perform axis-specific matrix multiplication
a[0] = a00*c + a10*s
a[1] = a01*c + a11*s
a[2] = a02*c + a12*s
a[3] = a03*c + a13*s
a[4] = a10*c - a00*s
a[5] = a11*c - a01*s
a[6] = a12*c - a02*s
a[7] = a13*c - a03*s
m.Values[0] = a00*c + a10*s
m.Values[1] = a01*c + a11*s
m.Values[2] = a02*c + a12*s
m.Values[3] = a03*c + a13*s
m.Values[4] = a10*c - a00*s
m.Values[5] = a11*c - a01*s
m.Values[6] = a12*c - a02*s
m.Values[7] = a13*c - a03*s
return m
}

View File

@ -380,17 +380,15 @@ func (q *Quaternion) SetFromEuler(e *Euler) *Quaternion {
// SetFromRotationMatrix sets the rotation of this Quaternion from the given Matrix4.
func (q *Quaternion) SetFromRotationMatrix(m4 *Matrix4) *Quaternion {
m := m4.Values
m11 := m[0]
m12 := m[4]
m13 := m[8]
m21 := m[1]
m22 := m[5]
m23 := m[9]
m31 := m[2]
m32 := m[6]
m33 := m[10]
m11 := m4.Values[0]
m12 := m4.Values[4]
m13 := m4.Values[8]
m21 := m4.Values[1]
m22 := m4.Values[5]
m23 := m4.Values[9]
m31 := m4.Values[2]
m32 := m4.Values[6]
m33 := m4.Values[10]
trace := m11 + m22 + m33
var s float64

View File

@ -83,7 +83,7 @@ func (a *Sprite) Advance(elapsed time.Duration) error {
a.lastFrameTime += elapsed
framesAdvanced := int(float64(a.lastFrameTime) / float64(frameLength))
a.lastFrameTime -= time.Duration(float64(framesAdvanced) * float64(frameLength))
a.lastFrameTime -= time.Duration(float64(framesAdvanced)) * frameLength
for i := 0; i < framesAdvanced; i++ {
startIndex := 0

View File

@ -20,7 +20,6 @@ var _ akara.Component = &Camera{}
// Camera represents a camera that can be rendered to
type Camera struct {
Position *d2math.Vector3
PerspectiveMatrix *d2math.Matrix4
OrthogonalMatrix *d2math.Matrix4
Size *d2math.Vector2
@ -31,7 +30,6 @@ type Camera struct {
// The camera defaults to position (0,0), 800x600 resolution, and zoom of 1.0
func (*Camera) New() akara.Component {
c := &Camera{
Position: d2math.NewVector3(0, 0, defaultCameraZ),
Size: d2math.NewVector2(defaultCameraWidth, defaultCameraHeight),
Clip: d2math.NewVector2(defaultCameraNear, defaultCameraFar),
}

View File

@ -1,45 +0,0 @@
//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same
package d2components
import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
)
// static check that Position implements Component
var _ akara.Component = &Position{}
// Position contains an embedded d2vector.Position, which is a vector with
// helper methods for translating between screen, isometric, tile, and sub-tile space.
type Position struct {
*d2math.Vector3
}
// New creates a new Position. By default, the position is (0,0)
func (*Position) New() akara.Component {
return &Position{
Vector3: d2math.NewVector3(0, 0, 0),
}
}
// PositionFactory is a wrapper for the generic component factory that returns Position component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Position.
type PositionFactory struct {
Position *akara.ComponentFactory
}
// AddPosition adds a Position component to the given entity and returns it
func (m *PositionFactory) AddPosition(id akara.EID) *Position {
return m.Position.Add(id).(*Position)
}
// GetPosition returns the Position component for the given entity, and a bool for whether or not it exists
func (m *PositionFactory) GetPosition(id akara.EID) (*Position, bool) {
component, found := m.Position.Get(id)
if !found {
return nil, found
}
return component.(*Position), found
}

View File

@ -1,43 +0,0 @@
//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same
package d2components
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
"github.com/gravestench/akara"
)
// static check that Scale implements Component
var _ akara.Component = &Scale{}
// Scale represents an entities x,y axis scale as a vector
type Scale struct {
*d2math.Vector3
}
// New creates a new Scale instance. By default, the scale is (1,1)
func (*Scale) New() akara.Component {
return &Scale{
Vector3: d2math.NewVector3(1, 1, 1),
}
}
// ScaleFactory is a wrapper for the generic component factory that returns Scale component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Scale.
type ScaleFactory struct {
Scale *akara.ComponentFactory
}
// AddScale adds a Scale component to the given entity and returns it
func (m *ScaleFactory) AddScale(id akara.EID) *Scale {
return m.Scale.Add(id).(*Scale)
}
// GetScale returns the Scale component for the given entity, and a bool for whether or not it exists
func (m *ScaleFactory) GetScale(id akara.EID) (*Scale, bool) {
component, found := m.Scale.Get(id)
if !found {
return nil, found
}
return component.(*Scale), found
}

View File

@ -0,0 +1,57 @@
//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same
package d2components
import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
)
// static check that Transform implements Component
var _ akara.Component = &Transform{}
// Transform contains a vec3 for Translation, Rotation, and Scale
type Transform struct {
Translation *d2math.Vector3
Rotation *d2math.Vector3
Scale *d2math.Vector3
}
func (t *Transform) GetMatrix() *d2math.Matrix4 {
return d2math.NewMatrix4(nil).
Translate(t.Translation).
RotateX(t.Rotation.X).
RotateY(t.Rotation.Y).
RotateZ(t.Rotation.Z).
ScaleXYZ(t.Scale.XYZ())
}
// New creates a new Transform. By default, the position is (0,0)
func (*Transform) New() akara.Component {
return &Transform{
Translation: d2math.NewVector3(0, 0, 0),
Rotation: d2math.NewVector3(0, 0, 0),
Scale: d2math.NewVector3(1, 1, 1),
}
}
// TransformFactory is a wrapper for the generic component factory that returns Transform component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Transform.
type TransformFactory struct {
Transform *akara.ComponentFactory
}
// AddTransform adds a Transform component to the given entity and returns it
func (m *TransformFactory) AddTransform(id akara.EID) *Transform {
return m.Transform.Add(id).(*Transform)
}
// GetTransform returns the Transform component for the given entity, and a bool for whether or not it exists
func (m *TransformFactory) GetTransform(id akara.EID) (*Transform, bool) {
component, found := m.Transform.Get(id)
if !found {
return nil, found
}
return component.(*Transform), found
}

View File

@ -23,6 +23,7 @@ const (
defaultSkewY = 0.0
defaultScaleX = 1.0
defaultScaleY = 1.0
defaultRotate = 0.0
)
type renderCallback = func(surface d2interface.Surface) error
@ -129,6 +130,7 @@ func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surfa
skewY: defaultSkewY,
scaleX: defaultScaleX,
scaleY: defaultScaleY,
rotate: defaultRotate,
}
result := createEbitenSurface(r, img, sfcState)

View File

@ -89,6 +89,12 @@ func (s *ebitenSurface) PushScale(scaleX, scaleY float64) {
s.stateCurrent.scaleY = scaleY
}
// PushRotate pushes a rotation to the state stack
func (s *ebitenSurface) PushRotate(theta float64) {
s.stateStack = append(s.stateStack, s.stateCurrent)
s.stateCurrent.rotate = theta
}
// PushEffect pushes an effect to the state stack
func (s *ebitenSurface) PushEffect(effect d2enum.DrawEffect) {
s.stateStack = append(s.stateStack, s.stateCurrent)
@ -178,14 +184,9 @@ func (s *ebitenSurface) RenderSection(sfc d2interface.Surface, bound image.Recta
func (s *ebitenSurface) createDrawImageOptions() *ebiten.DrawImageOptions {
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.Skew(s.stateCurrent.skewX, s.stateCurrent.skewY)
opts.GeoM.Scale(s.stateCurrent.scaleX, s.stateCurrent.scaleY)
opts.GeoM.Rotate(s.stateCurrent.rotate)
opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y))
opts.Filter = s.stateCurrent.filter

View File

@ -18,4 +18,5 @@ type surfaceState struct {
effect d2enum.DrawEffect
skewX, skewY float64
scaleX, scaleY float64
rotate float64
}

View File

@ -1,6 +1,8 @@
package d2systems
import (
"fmt"
"gopkg.in/alecthomas/kingpin.v2"
"os"
"path"
@ -19,12 +21,26 @@ const (
logPrefixAppBootstrap = "App Bootstrap"
)
// static check that the game config system implements the system interface
var _ akara.System = &AppBootstrapSystem{}
const (
sceneTestArg = "testscene"
sceneTestDesc = "test a scene"
// AppBootstrapSystem is responsible for the common initialization process between
serverArg = "server"
serverDesc = "run dedicated server"
counterArg = "counter"
counterDesc = "print updates/sec"
skipSplashArg = "nosplash"
skipSplashDesc = "skip the ebiten splash screen"
)
// static check that the game config system implements the system interface
var _ akara.System = &AppBootstrap{}
// AppBootstrap is responsible for the common initialization process between
// the app modes (eg common to the game client as well as the headless server)
type AppBootstrapSystem struct {
type AppBootstrap struct {
akara.BaseSubscriberSystem
*d2util.Logger
subscribedFiles *akara.Subscription
@ -37,7 +53,7 @@ type AppBootstrapSystem struct {
}
// Init will inject (or use existing) components related to setting up the config sources
func (m *AppBootstrapSystem) Init(world *akara.World) {
func (m *AppBootstrap) Init(world *akara.World) {
m.World = world
m.setupLogger()
@ -49,16 +65,17 @@ func (m *AppBootstrapSystem) Init(world *akara.World) {
m.injectSystems()
m.setupConfigSources()
m.setupConfigFile()
m.parseCommandLineArgs()
m.Info("... initialization complete!")
}
func (m *AppBootstrapSystem) setupLogger() {
func (m *AppBootstrap) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixAppBootstrap)
}
func (m *AppBootstrapSystem) setupSubscriptions() {
func (m *AppBootstrap) setupSubscriptions() {
m.Info("setting up component subscriptions")
// we are going to check entities that dont yet have loaded asset types
@ -91,7 +108,7 @@ func (m *AppBootstrapSystem) setupSubscriptions() {
m.subscribedConfigs = m.World.AddSubscription(gameConfigs)
}
func (m *AppBootstrapSystem) setupFactories() {
func (m *AppBootstrap) setupFactories() {
m.Info("setting up component factories")
m.InjectComponent(&d2components.GameConfig{}, &m.GameConfig)
@ -101,7 +118,7 @@ func (m *AppBootstrapSystem) setupFactories() {
m.InjectComponent(&d2components.FileSource{}, &m.FileSource)
}
func (m *AppBootstrapSystem) injectSystems() {
func (m *AppBootstrap) injectSystems() {
m.Info("injecting file type resolution system")
m.AddSystem(&FileTypeResolver{})
@ -116,9 +133,6 @@ func (m *AppBootstrapSystem) injectSystems() {
m.Info("injecting asset loader system")
m.AddSystem(&AssetLoaderSystem{})
m.Info("injecting game object factory system")
m.AddSystem(&GameObjectFactory{})
}
// we make two entities and assign file paths for the two directories that
@ -126,7 +140,7 @@ func (m *AppBootstrapSystem) injectSystems() {
// the file type resolver system, and then the file source resolver system. At that point,
// there will be sources for these two directories that can possibly resolve a config file.
// A new config file is created if one is not found.
func (m *AppBootstrapSystem) setupConfigSources() {
func (m *AppBootstrap) setupConfigSources() {
// make the two entities, these will be the file sources
e1, e2 := m.NewEntity(), m.NewEntity()
@ -152,7 +166,7 @@ func (m *AppBootstrapSystem) setupConfigSources() {
}
}
func (m *AppBootstrapSystem) setupConfigFile() {
func (m *AppBootstrap) setupConfigFile() {
// add an entity that will get picked up by the game config system and loaded
m.AddFilePath(m.NewEntity()).Path = configFileName
m.Infof("setting up config file `%s` for processing", configFileName)
@ -160,7 +174,7 @@ func (m *AppBootstrapSystem) setupConfigFile() {
// Update will look for the first entity with a game config component
// and then add the mpq's as file sources
func (m *AppBootstrapSystem) Update() {
func (m *AppBootstrap) Update() {
configs := m.subscribedConfigs.GetEntities()
if len(configs) < 1 {
return
@ -182,7 +196,7 @@ func (m *AppBootstrapSystem) Update() {
m.SetActive(false)
}
func (m *AppBootstrapSystem) initMpqSources(cfg *d2components.GameConfig) {
func (m *AppBootstrap) initMpqSources(cfg *d2components.GameConfig) {
for _, mpqFileName := range cfg.MpqLoadOrder {
fullMpqFilePath := path.Join(cfg.MpqPath, mpqFileName)
@ -193,3 +207,44 @@ func (m *AppBootstrapSystem) initMpqSources(cfg *d2components.GameConfig) {
mpqSource.Path = fullMpqFilePath
}
}
func (m *AppBootstrap) parseCommandLineArgs() {
sceneTest := kingpin.Flag(sceneTestArg, sceneTestDesc).String()
server := kingpin.Flag(serverArg, serverDesc).Bool()
enableCounter := kingpin.Flag(counterArg, counterDesc).Bool()
_ = kingpin.Flag(skipSplashArg, skipSplashDesc).Bool() // see game client bootstrap
kingpin.Parse()
if *enableCounter {
m.World.AddSystem(&UpdateCounter{})
}
if *server {
fmt.Println("not yet implemented")
os.Exit(0)
}
m.World.AddSystem(&RenderSystem{})
m.World.AddSystem(&InputSystem{})
switch *sceneTest {
case "splash":
m.Info("running ebiten splash scene")
m.World.AddSystem(NewEbitenSplashScene())
case "load":
m.Info("running loading scene")
m.World.AddSystem(NewLoadingScene())
case "mouse":
m.Info("running mouse cursor scene")
m.World.AddSystem(NewMouseCursorScene())
case "main":
m.Info("running main menu scene")
m.World.AddSystem(NewMainMenuScene())
case "terminal":
m.Info("running terminal scene")
m.World.AddSystem(NewTerminalScene())
default:
m.World.AddSystem(&GameClientBootstrap{})
}
}

View File

@ -1,2 +1,2 @@
// Package d2systems provides all of the ECS baseSystems
// Package d2systems provides all of the ECS sceneSystems
package d2systems

View File

@ -1,9 +1,9 @@
package d2systems
import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/gravestench/akara"
"gopkg.in/alecthomas/kingpin.v2"
)
const (
@ -11,17 +11,17 @@ const (
)
// static check that the game config system implements the system interface
var _ akara.System = &GameClientBootstrapSystem{}
var _ akara.System = &GameClientBootstrap{}
// GameClientBootstrapSystem is responsible for setting up other
// baseSystems that are common to both the game client and the headless game server
type GameClientBootstrapSystem struct {
// GameClientBootstrap is responsible for setting up other
// sceneSystems that are common to both the game client and the headless game server
type GameClientBootstrap struct {
akara.BaseSubscriberSystem
*d2util.Logger
}
// Init injects the common baseSystems required by both the game client and headless server
func (m *GameClientBootstrapSystem) Init(world *akara.World) {
// Init injects the common sceneSystems required by both the game client and headless server
func (m *GameClientBootstrap) Init(world *akara.World) {
m.World = world
m.setupLogger()
@ -37,20 +37,20 @@ func (m *GameClientBootstrapSystem) Init(world *akara.World) {
}
}
func (m *GameClientBootstrapSystem) setupLogger() {
func (m *GameClientBootstrap) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixGameClientBootstrap)
}
func (m *GameClientBootstrapSystem) injectSystems() {
m.Info("injecting render system")
m.AddSystem(&RenderSystem{})
func (m *GameClientBootstrap) injectSystems() {
m.Info("injecting terminal scene")
m.AddSystem(NewTerminalScene())
m.Info("injecting input system")
m.AddSystem(&InputSystem{})
m.Info("injecting timescale scene")
m.AddSystem(&TimeScaleSystem{})
m.Info("injecting update counter system")
m.AddSystem(&UpdateCounter{})
m.Info("injecting game object factory system")
m.AddSystem(&GameObjectFactory{})
m.Info("injecting loading scene")
m.AddSystem(NewLoadingScene())
@ -58,18 +58,20 @@ func (m *GameClientBootstrapSystem) injectSystems() {
m.Info("injecting mouse cursor scene")
m.AddSystem(NewMouseCursorScene())
skipSplash := kingpin.Flag(skipSplashArg, skipSplashDesc).Bool()
kingpin.Parse()
if !*skipSplash {
m.Info("injecting ebiten splash scene")
m.AddSystem(NewEbitenSplashScene())
}
m.Info("injecting main menu scene")
m.AddSystem(NewMainMenuScene())
m.Info("injecting terminal scene")
m.AddSystem(NewTerminalScene())
m.Info("injecting ebiten splash scene")
m.AddSystem(NewEbitenSplashScene())
}
// Update does nothing, but exists to satisfy the `akara.System` interface
func (m *GameClientBootstrapSystem) Update() {
func (m *GameClientBootstrap) Update() {
m.Info("game client bootstrap complete, deactivating")
m.RemoveSystem(m)
}

View File

@ -24,9 +24,9 @@ const (
// to be found in, and it also adds an entity for the initial config file to be loaded.
//
// This system is dependant on the FileTypeResolver, FileSourceResolver, and
// FileHandleResolver baseSystems because this system subscribes to entities
// with components created by these other baseSystems. Nothing will break if these
// other baseSystems are not present in the world, but no config files will be loaded by
// FileHandleResolver sceneSystems because this system subscribes to entities
// with components created by these other sceneSystems. Nothing will break if these
// other sceneSystems are not present in the world, but no config files will be loaded by
// this system either...
type GameConfigSystem struct {
akara.BaseSubscriberSystem

View File

@ -26,7 +26,7 @@ func Test_GameConfigSystem_Bootstrap(t *testing.T) {
cfgSys.AddFilePath(world.NewEntity()).Path = testDataPath
cfgSys.AddFilePath(world.NewEntity()).Path = "config.json"
// at this point the world has initialized the baseSystems. when the world
// at this point the world has initialized the sceneSystems. when the world
// updates it should process the config dir to a source and then
// use the source to resolve a file handle, and finally the config file
// will get loaded by the config system.

View File

@ -13,7 +13,7 @@ const (
// static check that GameObjectFactory implements the System interface
var _ akara.System = &GameObjectFactory{}
// GameObjectFactory is a wrapper system for subordinate baseSystems that
// GameObjectFactory is a wrapper system for subordinate sceneSystems that
// do the actual object creation work.
type GameObjectFactory struct {
akara.BaseSystem
@ -44,7 +44,7 @@ func (t *GameObjectFactory) injectSubSystems() {
t.ShapeSystem = NewShapeSystem(t.BaseSystem, t.Logger)
}
// Update updates all the sub-baseSystems
// Update updates all the sub-sceneSystems
func (t *GameObjectFactory) Update() {
t.SpriteFactory.Update()
t.ShapeSystem.Update()

View File

@ -86,6 +86,8 @@ func (m *InputSystem) Update() {
}
func (m *InputSystem) updateInputState() {
m.inputState.Clear()
var keysToCheck = []d2input.Key{
d2input.Key0, d2input.Key1, d2input.Key2, d2input.Key3, d2input.Key4, d2input.Key5, d2input.Key6,
d2input.Key7, d2input.Key8, d2input.Key9, d2input.KeyA, d2input.KeyB, d2input.KeyC, d2input.KeyD,
@ -118,17 +120,17 @@ func (m *InputSystem) updateInputState() {
}
for _, key := range keysToCheck {
truth := m.InputService.IsKeyPressed(d2enum.Key(key))
truth := m.InputService.IsKeyJustPressed(d2enum.Key(key))
m.inputState.KeyVector.Set(key, truth)
}
for _, mod := range modifiersToCheck {
truth := m.InputService.IsKeyPressed(d2enum.Key(mod))
truth := m.InputService.IsKeyJustPressed(d2enum.Key(mod))
m.inputState.ModifierVector.Set(mod, truth)
}
for _, btn := range buttonsToCheck {
truth := m.InputService.IsMouseButtonPressed(d2enum.MouseButton(btn))
truth := m.InputService.IsMouseButtonJustPressed(d2enum.MouseButton(btn))
m.inputState.MouseButtonVector.Set(btn, truth)
}
}

View File

@ -21,7 +21,7 @@ var _ akara.System = &MovementSystem{}
type MovementSystem struct {
akara.BaseSubscriberSystem
*d2util.Logger
d2components.PositionFactory
d2components.TransformFactory
d2components.VelocityFactory
movableEntities *akara.Subscription
}
@ -35,11 +35,11 @@ func (m *MovementSystem) Init(world *akara.World) {
m.Info("initializing ...")
m.InjectComponent(&d2components.Position{}, &m.Position)
m.InjectComponent(&d2components.Transform{}, &m.Transform)
m.InjectComponent(&d2components.Velocity{}, &m.Velocity)
movable := m.NewComponentFilter().Require(
&d2components.Position{},
&d2components.Transform{},
&d2components.Velocity{},
).Build()
@ -56,7 +56,7 @@ func (m *MovementSystem) Update() {
}
func (m *MovementSystem) move(id akara.EID) {
position, found := m.GetPosition(id)
transform, found := m.GetTransform(id)
if !found {
return
}
@ -67,5 +67,5 @@ func (m *MovementSystem) move(id akara.EID) {
}
s := float64(m.World.TimeDelta) / float64(time.Second)
position.Add(velocity.Clone().Scale(s))
transform.Translation.Add(velocity.Clone().Scale(s))
}

View File

@ -40,6 +40,7 @@ type RenderSystem struct {
d2components.TextureFactory
d2components.PriorityFactory
d2components.AlphaFactory
d2components.CameraFactory
lastUpdate time.Time
}
@ -69,6 +70,7 @@ func (m *RenderSystem) setupFactories() {
m.InjectComponent(&d2components.Texture{}, &m.Texture)
m.InjectComponent(&d2components.Priority{}, &m.Priority)
m.InjectComponent(&d2components.Alpha{}, &m.Alpha)
m.InjectComponent(&d2components.Camera{}, &m.Camera)
}
func (m *RenderSystem) setupSubscriptions() {
@ -77,6 +79,7 @@ func (m *RenderSystem) setupSubscriptions() {
&d2components.Viewport{},
&d2components.MainViewport{},
&d2components.Texture{},
&d2components.Camera{},
).
Build()
@ -91,7 +94,7 @@ func (m *RenderSystem) setupSubscriptions() {
// Update will initialize the renderer, start the game loop, and
// disable the system (to prevent it from being called during the game loop).
//
// The reason why this isn't in the init step is because we use other baseSystems
// The reason why this isn't in the init step is because we use other sceneSystems
// for loading the config file, and it may take more than one iteration
func (m *RenderSystem) Update() {
if m.renderer != nil {
@ -108,7 +111,7 @@ func (m *RenderSystem) Update() {
// this system and start the run loop.
m.SetActive(false)
err := m.startGameLoop()
err := m.StartGameLoop()
if err != nil {
m.Fatal(err.Error())
}
@ -173,6 +176,11 @@ func (m *RenderSystem) render(screen d2interface.Surface) error {
return errors.New("main viewport not found")
}
cam, found := m.GetCamera(id)
if !found {
return errors.New("main viewport camera not found")
}
texture, found := m.GetTexture(id)
if !found {
return errors.New("main viewport doesn't have a surface")
@ -191,11 +199,13 @@ func (m *RenderSystem) render(screen d2interface.Surface) error {
screen.PushColor(color.Alpha{A: uint8(alpha.Alpha * maxAlpha)})
screen.PushTranslation(vp.Left, vp.Top)
screen.PushScale(float64(vp.Width)/cam.Size.X, float64(vp.Height)/cam.Size.Y)
screen.Render(texture.Texture)
screen.Pop()
screen.Pop()
screen.Pop()
}
return nil
@ -209,7 +219,7 @@ func (m *RenderSystem) updateWorld() error {
return m.World.Update(elapsed)
}
func (m *RenderSystem) startGameLoop() error {
func (m *RenderSystem) StartGameLoop() error {
m.Infof("starting game loop ...")
return m.renderer.Run(m.render, m.updateWorld, 800, 600, gameTitle)

View File

@ -29,8 +29,7 @@ func NewBaseScene(key string) *BaseScene {
Logger: d2util.NewLogger(),
key: key,
Viewports: make([]akara.EID, 0),
GameObjects: make([]akara.EID, 0),
baseSystems: &baseSystems{},
SceneObjects: make([]akara.EID, 0),
backgroundColor: color.Transparent,
}
@ -41,20 +40,41 @@ func NewBaseScene(key string) *BaseScene {
var _ akara.System = &BaseScene{}
type baseSystems struct {
type sceneSystems struct {
*RenderSystem
*InputSystem
*GameObjectFactory
}
// BaseScene encapsulates common behaviors for baseSystems that are considered "scenes",
type sceneComponents struct {
d2components.SceneGraphNodeFactory
d2components.ViewportFactory
d2components.MainViewportFactory
d2components.ViewportFilterFactory
d2components.PriorityFactory
d2components.CameraFactory
d2components.TextureFactory
d2components.InteractiveFactory
d2components.TransformFactory
d2components.SpriteFactory
d2components.OriginFactory
d2components.AlphaFactory
d2components.DrawEffectFactory
d2components.RectangleFactory
d2components.ColorFactory
d2components.CommandRegistrationFactory
d2components.DirtyFactory
}
// BaseScene encapsulates common behaviors for systems that are considered "scenes",
// such as the main menu, the in-game map, the console, etc.
//
// The base scene is responsible for generic behaviors common to all scenes,
// like initializing the default viewport, or rendering game objects to the viewports.
type BaseScene struct {
*akara.BaseSystem
*baseSystems
sceneSystems
sceneComponents
Geom struct {
Rectangle rectangle.Namespace
}
@ -64,27 +84,9 @@ type BaseScene struct {
paused bool
Add *sceneObjectFactory
Viewports []akara.EID
GameObjects []akara.EID
SceneObjects []akara.EID
Graph *d2scene.Node // the root node
backgroundColor color.Color
d2components.SceneGraphNodeFactory
d2components.ViewportFactory
d2components.MainViewportFactory
d2components.ViewportFilterFactory
d2components.PriorityFactory
d2components.CameraFactory
d2components.TextureFactory
d2components.InteractiveFactory
d2components.PositionFactory
d2components.ScaleFactory
d2components.SpriteFactory
d2components.OriginFactory
d2components.AlphaFactory
d2components.DrawEffectFactory
d2components.RectangleFactory
d2components.ColorFactory
d2components.CommandRegistrationFactory
d2components.DirtyFactory
}
// Booted returns whether or not the scene has booted
@ -118,46 +120,46 @@ func (s *BaseScene) boot() {
s.Add.SetPrefix(fmt.Sprintf("%s -> %s", s.key, "Object Factory"))
for idx := range s.Systems {
if rendersys, ok := s.Systems[idx].(*RenderSystem); ok && s.baseSystems.RenderSystem == nil {
s.baseSystems.RenderSystem = rendersys
if rendersys, ok := s.Systems[idx].(*RenderSystem); ok && s.sceneSystems.RenderSystem == nil {
s.sceneSystems.RenderSystem = rendersys
continue
}
if inputSys, ok := s.Systems[idx].(*InputSystem); ok && s.baseSystems.InputSystem == nil {
s.baseSystems.InputSystem = inputSys
if inputSys, ok := s.Systems[idx].(*InputSystem); ok && s.sceneSystems.InputSystem == nil {
s.sceneSystems.InputSystem = inputSys
continue
}
if objFactory, ok := s.Systems[idx].(*GameObjectFactory); ok && s.baseSystems.GameObjectFactory == nil {
s.baseSystems.GameObjectFactory = objFactory
if objFactory, ok := s.Systems[idx].(*GameObjectFactory); ok && s.sceneSystems.GameObjectFactory == nil {
s.sceneSystems.GameObjectFactory = objFactory
continue
}
}
if s.baseSystems.RenderSystem == nil {
if s.sceneSystems.RenderSystem == nil {
s.Info("waiting for render system ...")
return
}
if s.baseSystems.RenderSystem.renderer == nil {
if s.sceneSystems.RenderSystem.renderer == nil {
s.Info("waiting for renderer instance ...")
return
}
if s.baseSystems.InputSystem == nil {
if s.sceneSystems.InputSystem == nil {
s.Info("waiting for input system")
return
}
if s.baseSystems.GameObjectFactory == nil {
if s.sceneSystems.GameObjectFactory == nil {
s.Info("waiting for game object factory ...")
return
}
s.setupFactories()
s.baseSystems.SpriteFactory.RenderSystem = s.baseSystems.RenderSystem
s.baseSystems.ShapeSystem.RenderSystem = s.baseSystems.RenderSystem
s.sceneSystems.SpriteFactory.RenderSystem = s.sceneSystems.RenderSystem
s.sceneSystems.ShapeSystem.RenderSystem = s.sceneSystems.RenderSystem
const (
defaultWidth = 800
@ -180,8 +182,7 @@ func (s *BaseScene) setupFactories() {
s.InjectComponent(&d2components.Priority{}, &s.Priority)
s.InjectComponent(&d2components.Texture{}, &s.Texture)
s.InjectComponent(&d2components.Interactive{}, &s.Interactive)
s.InjectComponent(&d2components.Position{}, &s.Position)
s.InjectComponent(&d2components.Scale{}, &s.Scale)
s.InjectComponent(&d2components.Transform{}, &s.Transform)
s.InjectComponent(&d2components.Origin{}, &s.Origin)
s.InjectComponent(&d2components.Alpha{}, &s.Alpha)
s.InjectComponent(&d2components.SceneGraphNode{}, &s.SceneGraphNode)
@ -208,17 +209,35 @@ func (s *BaseScene) Update() {
return
}
s.Graph.UpdateWorldMatrix()
s.updateSceneGraph()
s.renderViewports()
}
func (s *BaseScene) updateSceneGraph() {
for _, eid := range s.SceneObjects {
node, found := s.GetSceneGraphNode(eid)
if !found {
continue
}
transform, found := s.GetTransform(eid)
if !found {
continue
}
node.Local = transform.GetMatrix()
}
s.Graph.UpdateWorldMatrix()
}
func (s *BaseScene) renderViewports() {
if s.baseSystems.RenderSystem == nil {
if s.sceneSystems.RenderSystem == nil {
s.Warning("render system not present")
return
}
if s.baseSystems.RenderSystem.renderer == nil {
if s.sceneSystems.RenderSystem.renderer == nil {
s.Warning("render system doesn't have a renderer instance")
return
}
@ -244,7 +263,7 @@ func (s *BaseScene) renderViewports() {
func (s *BaseScene) binGameObjectsByViewport() map[int][]akara.EID {
bins := make(map[int][]akara.EID)
for _, eid := range s.GameObjects {
for _, eid := range s.SceneObjects {
vpfilter, found := s.GetViewportFilter(eid)
if !found {
vpfilter = s.AddViewportFilter(eid)
@ -267,116 +286,108 @@ func (s *BaseScene) binGameObjectsByViewport() map[int][]akara.EID {
}
func (s *BaseScene) renderViewport(idx int, objects []akara.EID) {
id := s.Viewports[idx]
viewportEID := s.Viewports[idx]
// the first viewport is always the main viewport
if idx == mainViewport {
s.AddMainViewport(id)
s.AddMainViewport(viewportEID)
} else {
s.MainViewport.Remove(id)
s.MainViewport.Remove(viewportEID)
}
camera, found := s.GetCamera(id)
if !found {
sfc, found := s.GetTexture(viewportEID)
if !found || sfc.Texture == nil {
return
}
node, found := s.GetSceneGraphNode(id)
if !found {
node = s.AddSceneGraphNode(id)
}
// translate the camera position using the camera's scene graph node
cx, cy := camera.Position.Clone().ApplyMatrix4(node.Local).XY()
cw, ch := camera.Size.XY()
sfc, found := s.GetTexture(id)
if !found {
return
}
if sfc.Texture == nil {
sfc.Texture = s.baseSystems.RenderSystem.renderer.NewSurface(int(cw), int(ch))
}
if idx == mainViewport {
sfc.Texture.Clear(s.backgroundColor)
}
sfc.Texture.PushTranslation(int(-cx), int(-cy)) // negative because we're offsetting everything that gets rendered
for _, object := range objects {
s.renderObject(sfc.Texture, object)
for _, objectEID := range objects {
s.renderObject(viewportEID, objectEID)
}
sfc.Texture.Pop()
}
func (s *BaseScene) renderObject(target d2interface.Surface, id akara.EID) {
texture, found := s.GetTexture(id)
func (s *BaseScene) renderObject(viewportEID, objectEID akara.EID) {
vpTexture, found := s.GetTexture(viewportEID)
if !found || vpTexture.Texture == nil {
return
}
vpNode, found := s.GetSceneGraphNode(viewportEID)
if !found {
vpNode = s.AddSceneGraphNode(viewportEID)
}
// translation, rotation, and scale vec3's
vpTrans, vpRot, vpScale := vpNode.Local.Invert().Decompose()
objTexture, found := s.GetTexture(objectEID)
if !found {
return
}
position, found := s.GetPosition(id)
alpha, found := s.GetAlpha(objectEID)
if !found {
position = s.AddPosition(id)
alpha = s.AddAlpha(objectEID)
}
scale, found := s.GetScale(id)
origin, found := s.GetOrigin(objectEID)
if !found {
scale = s.AddScale(id)
origin = s.AddOrigin(objectEID)
}
alpha, found := s.GetAlpha(id)
node, found := s.GetSceneGraphNode(objectEID)
if !found {
alpha = s.AddAlpha(id)
}
origin, found := s.GetOrigin(id)
if !found {
origin = s.AddOrigin(id)
}
node, found := s.GetSceneGraphNode(id)
if !found {
node = s.AddSceneGraphNode(id)
node = s.AddSceneGraphNode(objectEID)
node.SetParent(s.Graph)
}
drawEffect, found := s.GetDrawEffect(id)
drawEffect, found := s.GetDrawEffect(objectEID)
if found {
target.PushEffect(drawEffect.DrawEffect)
defer target.Pop()
vpTexture.Texture.PushEffect(drawEffect.DrawEffect)
defer vpTexture.Texture.Pop()
}
// translate the entity position using the scene graph node
x, y := position.Clone().
Add(origin.Vector3).
ApplyMatrix4(node.Local).
XY()
objNode, found := s.GetSceneGraphNode(objectEID)
if !found {
objNode = s.AddSceneGraphNode(objectEID)
}
target.PushTranslation(int(x), int(y))
defer target.Pop()
// translation, rotation, and scale vec3's
objTrans, objRot, objScale := objNode.Local.Decompose()
target.PushScale(scale.Clone().ApplyMatrix4(node.Local).XY())
defer target.Pop()
ox, oy := origin.X, origin.Y
tx, ty := objTrans.Add(vpTrans).XY()
vpTexture.Texture.PushTranslation(int(tx+ox), int(ty+oy))
defer vpTexture.Texture.Pop()
vpTexture.Texture.PushScale(objScale.Multiply(vpScale).XY())
defer vpTexture.Texture.Pop()
vpTexture.Texture.PushRotate(objRot.Add(vpRot).Z)
defer vpTexture.Texture.Pop()
const maxAlpha = 255
target.PushColor(color.Alpha{A: uint8(alpha.Alpha * maxAlpha)})
defer target.Pop()
vpTexture.Texture.PushColor(color.Alpha{A: uint8(alpha.Alpha * maxAlpha)})
defer vpTexture.Texture.Pop()
segment, found := s.baseSystems.SpriteFactory.GetSegmentedSprite(id)
segment, found := s.sceneSystems.SpriteFactory.GetSegmentedSprite(objectEID)
if found {
s.renderSegmentedSprite(target, id, segment)
s.renderSegmentedSprite(vpTexture.Texture, objectEID, segment)
return
}
target.Render(texture.Texture)
vpTexture.Texture.Render(objTexture.Texture)
}
func (s *BaseScene) renderSegmentedSprite(target d2interface.Surface, id akara.EID, seg *d2components.SegmentedSprite) {
func (s *BaseScene) renderSegmentedSprite(screen d2interface.Surface, id akara.EID, seg *d2components.SegmentedSprite) {
target := screen.Renderer().NewSurface(screen.GetSize())
animation, found := s.GetSprite(id)
if !found {
return
@ -393,7 +404,7 @@ func (s *BaseScene) renderSegmentedSprite(target d2interface.Surface, id akara.E
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())
s.Error("SetCurrentFrame error " + err.Error())
}
target.PushTranslation(x+offsetX, y+offsetY)
@ -407,6 +418,8 @@ func (s *BaseScene) renderSegmentedSprite(target d2interface.Surface, id akara.E
offsetY += maxFrameHeight - 1
}
screen.Render(target)
}
func (s *BaseScene) renderViewportsToMainViewport() {

View File

@ -1,11 +1,11 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2input"
"github.com/gravestench/akara"
"image/color"
"math"
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
@ -121,6 +121,20 @@ func (s *EbitenSplashScene) createSplash() {
}
}
interactive := s.AddInteractive(s.NewEntity())
interactive.InputVector.SetMouseButton(d2input.MouseButtonLeft)
interactive.Callback = func() bool {
s.Info("hiding splash scene")
s.timeElapsed = splashTimeout
interactive.Enabled = false
return true // prevent propagation
}
s.squares = squares
}
@ -133,9 +147,11 @@ func (s *EbitenSplashScene) updateSplash() {
// fade out after timeout
if s.timeElapsed >= splashTimeout {
vpAlpha, _ := s.GetAlpha(s.Viewports[0])
vpAlpha.Alpha -= 0.1225
vpAlpha.Alpha -= 0.0425
if vpAlpha.Alpha <= 0 {
vpAlpha.Alpha = 0
s.Info("finished, deactivating")
s.SetActive(false)
}
}
@ -145,7 +161,7 @@ func (s *EbitenSplashScene) updateSplash() {
// fade all of the squares
for idx, id := range s.squares {
a := math.Sin(s.timeElapsed + -90 + (float64(idx)/numSquares))
a := math.Sin(s.timeElapsed*2 + -90 + (float64(idx)/numSquares))
a = (a+1)/2 // clamp between 0..1
alpha, found := s.GetAlpha(id)

View File

@ -0,0 +1 @@
package d2systems

View File

@ -234,7 +234,7 @@ func (s *LoadingScene) updateLoadingSpritePosition() {
return
}
position, found := s.GetPosition(s.loadingSprite)
transform, found := s.GetTransform(s.loadingSprite)
if !found {
return
}
@ -243,7 +243,11 @@ func (s *LoadingScene) updateLoadingSpritePosition() {
frameW, frameH := sprite.GetCurrentFrameSize()
// we add the frameH in the Y because sprites are supposed to be drawn from bottom to top
position.X, position.Y = float64(centerX-(frameW/2)), float64(centerY+(frameH/2))
transform.Translation.Set(
float64(centerX-(frameW/2)),
float64(centerY+(frameH/2)),
transform.Translation.Z,
)
}
func (s *LoadingScene) updateLoadingSpriteFrame() {

View File

@ -38,6 +38,7 @@ type MouseCursorScene struct {
debug struct {
enabled bool
}
test bool
}
func (s *MouseCursorScene) Init(world *akara.World) {
@ -77,32 +78,33 @@ func (s *MouseCursorScene) Update() {
s.boot()
}
s.updateCursorPosition()
s.updateCursorTransform()
s.handleCursorFade()
s.BaseScene.Update()
}
func (s *MouseCursorScene) updateCursorPosition() {
position, found := s.GetPosition(s.cursor)
func (s *MouseCursorScene) updateCursorTransform() {
transform, found := s.GetTransform(s.cursor)
if !found {
return
}
cx, cy := s.CursorPosition()
tx, ty := transform.Translation.XY()
if int(position.X) != cx || int(position.Y) != cy {
if int(tx) != cx || int(ty) != cy {
s.lastTimeMoved = time.Now()
switch s.debug.enabled {
case true:
s.Infof("position: (%d, %d)", int(position.X), int(position.Y))
s.Infof("transform: (%d, %d)", int(tx), int(ty))
default:
s.Debugf("position: (%d, %d)", int(position.X), int(position.Y))
s.Debugf("transform: (%d, %d)", int(tx), int(ty))
}
}
position.X, position.Y = float64(cx), float64(cy)
transform.Translation.X, transform.Translation.Y = float64(cx), float64(cy)
}
func (s *MouseCursorScene) handleCursorFade() {

View File

@ -26,8 +26,8 @@ func (s *sceneObjectFactory) addBasicComponents(id akara.EID) {
func (s *sceneObjectFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID {
s.Debugf("creating sprite: %s, %s", filepath.Base(imgPath), palPath)
eid := s.baseSystems.SpriteFactory.Sprite(x, y, imgPath, palPath)
s.GameObjects = append(s.GameObjects, eid)
eid := s.sceneSystems.SpriteFactory.Sprite(x, y, imgPath, palPath)
s.SceneObjects = append(s.SceneObjects, eid)
s.addBasicComponents(eid)
@ -37,8 +37,8 @@ func (s *sceneObjectFactory) Sprite(x, y float64, imgPath, palPath string) akara
func (s *sceneObjectFactory) SegmentedSprite(x, y float64, imgPath, palPath string, xseg, yseg, frame int) akara.EID {
s.Debugf("creating segmented sprite: %s, %s", filepath.Base(imgPath), palPath)
eid := s.baseSystems.SpriteFactory.SegmentedSprite(x, y, imgPath, palPath, xseg, yseg, frame)
s.GameObjects = append(s.GameObjects, eid)
eid := s.sceneSystems.SpriteFactory.SegmentedSprite(x, y, imgPath, palPath, xseg, yseg, frame)
s.SceneObjects = append(s.SceneObjects, eid)
s.addBasicComponents(eid)
@ -60,7 +60,7 @@ func (s *sceneObjectFactory) Viewport(priority, width, height int) akara.EID {
camera.Size.X = float64(width)
camera.Size.Y = float64(height)
sfc := s.baseSystems.RenderSystem.renderer.NewSurface(width, height)
sfc := s.sceneSystems.RenderSystem.renderer.NewSurface(width, height)
sfc.Clear(color.Transparent)
@ -73,17 +73,17 @@ func (s *sceneObjectFactory) Viewport(priority, width, height int) akara.EID {
return eid
}
func (s *sceneObjectFactory) Rectangle(x, y, width, height int, color color.Color) akara.EID {
func (s *sceneObjectFactory) Rectangle(x, y, width, height int, c color.Color) akara.EID {
s.Debug("creating rectangle")
eid := s.baseSystems.ShapeSystem.Rectangle(x, y, width, height, color)
eid := s.sceneSystems.ShapeSystem.Rectangle(x, y, width, height, c)
s.addBasicComponents(eid)
position := s.AddPosition(eid)
position.X, position.Y = float64(x), float64(y)
transform := s.AddTransform(eid)
transform.Translation.X, transform.Translation.Y = float64(x), float64(y)
s.GameObjects = append(s.GameObjects, eid)
s.SceneObjects = append(s.SceneObjects, eid)
return eid
}

View File

@ -3,7 +3,7 @@ package d2systems
const (
scenePriorityMainMenu = iota
scenePriorityLoading
scenePriorityMouseCursor
scenePriorityTerminal
scenePriorityMouseCursor
scenePriorityEbitenSplash
)

View File

@ -29,7 +29,7 @@ type ShapeSystem struct {
akara.BaseSubscriberSystem
*d2util.Logger
RenderSystem *RenderSystem
d2components.PositionFactory
d2components.TransformFactory
d2components.ColorFactory
d2components.RectangleFactory
d2components.TextureFactory
@ -54,7 +54,7 @@ func (t *ShapeSystem) Init(world *akara.World) {
func (t *ShapeSystem) setupFactories() {
t.InjectComponent(&d2components.Color{}, &t.ColorFactory.Color)
t.InjectComponent(&d2components.Position{}, &t.PositionFactory.Position)
t.InjectComponent(&d2components.Transform{}, &t.TransformFactory.Transform)
t.InjectComponent(&d2components.Texture{}, &t.TextureFactory.Texture)
t.InjectComponent(&d2components.Origin{}, &t.OriginFactory.Origin)
t.InjectComponent(&d2components.Size{}, &t.SizeFactory.Size)
@ -69,7 +69,7 @@ func (t *ShapeSystem) setupSubscriptions() {
shapesToUpdate := t.NewComponentFilter().
RequireOne(&d2components.Rectangle{}).
Require(&d2components.Position{}, &d2components.Size{}).
Require(&d2components.Transform{}, &d2components.Size{}).
Build()
t.shapesToRender = t.AddSubscription(shapesToRender)
@ -109,7 +109,7 @@ func (t *ShapeSystem) Rectangle(x, y, width, height int, color color.Color) akar
}
func (t *ShapeSystem) updateShape(eid akara.EID) {
position, found := t.GetPosition(eid)
transform, found := t.GetTransform(eid)
if !found {
return
}
@ -126,7 +126,7 @@ func (t *ShapeSystem) updateShape(eid akara.EID) {
rectangle, rectangleFound := t.GetRectangle(eid)
if rectangleFound {
position.X, position.Y = rectangle.X, rectangle.Y
transform.Translation.X, transform.Translation.Y = rectangle.X, rectangle.Y
size.X, size.Y = rectangle.Width, rectangle.Height
tw, th := texture.Texture.GetSize()

View File

@ -40,7 +40,7 @@ type SpriteFactory struct {
*d2util.Logger
RenderSystem *RenderSystem
d2components.FilePathFactory
d2components.PositionFactory
d2components.TransformFactory
d2components.Dc6Factory
d2components.DccFactory
d2components.PaletteFactory
@ -67,7 +67,7 @@ func (t *SpriteFactory) Init(world *akara.World) {
func (t *SpriteFactory) setupFactories() {
t.InjectComponent(&d2components.FilePath{}, &t.FilePath)
t.InjectComponent(&d2components.Position{}, &t.Position)
t.InjectComponent(&d2components.Transform{}, &t.Transform)
t.InjectComponent(&d2components.Dc6{}, &t.Dc6)
t.InjectComponent(&d2components.Dcc{}, &t.Dcc)
t.InjectComponent(&d2components.Palette{}, &t.Palette)
@ -112,8 +112,8 @@ func (t *SpriteFactory) Update() {
func (t *SpriteFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID {
spriteID := t.NewEntity()
position := t.AddPosition(spriteID)
position.X, position.Y = x, y
transform := t.AddTransform(spriteID)
transform.Translation.X, transform.Translation.Y = x, y
imgID, palID := t.NewEntity(), t.NewEntity()
t.AddFilePath(imgID).Path = imgPath

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
@ -16,18 +17,6 @@ const (
logPrefixTimeScaleSystem = "Time Scale"
)
// NewTimeScaleSystem creates a timescale system
func NewTimeScaleSystem() *TimeScaleSystem {
m := &TimeScaleSystem{
BaseSystem: &akara.BaseSystem{},
Logger: d2util.NewLogger(),
}
m.SetPrefix(logPrefixTimeScaleSystem)
return m
}
// static check that TimeScaleSystem implements the System interface
var _ akara.System = &TimeScaleSystem{}
@ -35,29 +24,50 @@ var _ akara.System = &TimeScaleSystem{}
// apply a scalar the world's TimeDelta between frames. It's useful for slowing down or speeding
// up the game time without affecting the render rate.
type TimeScaleSystem struct {
*akara.BaseSystem
akara.BaseSystem
*d2util.Logger
scale float64
lastScale float64
d2components.DirtyFactory
d2components.CommandRegistrationFactory
}
// Init will initialize the TimeScale system
func (t *TimeScaleSystem) Init(world *akara.World) {
t.World = world
t.Logger = d2util.NewLogger()
t.SetPrefix(logPrefixTimeScaleSystem)
t.Info("initializing ...")
t.InjectComponent(&d2components.CommandRegistration{}, &t.CommandRegistration)
t.InjectComponent(&d2components.Dirty{}, &t.Dirty)
t.registerCommands()
t.scale = defaultScale
}
// Update scales the worlds time delta for this frame
func (t *TimeScaleSystem) Update() {
if !t.Active() || t.scale == t.lastScale {
if !t.Active() {
return
}
t.Infof("setting time scale to %.1f", t.scale)
t.lastScale = t.scale
t.World.TimeDelta *= time.Duration(t.scale)
t.World.TimeDelta = time.Duration(float64(t.World.TimeDelta) * t.scale)
}
func (t *TimeScaleSystem) registerCommands() {
e := t.NewEntity()
reg := t.AddCommandRegistration(e)
t.AddDirty(e)
reg.Name = "timescale"
reg.Description = "set the time scale of the game (default is 1.0)"
reg.Callback = func(scale float64) {
t.Infof("setting time scale to %.1f", scale)
t.scale = scale
}
}

2
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4 // indirect
github.com/go-restruct/restruct v1.2.0-alpha
github.com/google/uuid v1.1.2
github.com/gravestench/akara v0.0.0-20201203202918-85b8a01d1130
github.com/gravestench/akara v0.0.0-20201206061149-9be03b4110f2
github.com/gravestench/pho v0.0.0-20201029002250-f9afbd637e4d
github.com/hajimehoshi/ebiten/v2 v2.0.1
github.com/pkg/profile v1.5.0

2
go.sum
View File

@ -21,6 +21,8 @@ github.com/gravestench/akara v0.0.0-20201203202116-00c2a0dbae5d h1:8xg2B0KGOHLwp
github.com/gravestench/akara v0.0.0-20201203202116-00c2a0dbae5d/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/gravestench/akara v0.0.0-20201203202918-85b8a01d1130 h1:09fkM2hfORgZJ5hhtHzOMszOgx3Xs14+z7kpto8w7Ys=
github.com/gravestench/akara v0.0.0-20201203202918-85b8a01d1130/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/gravestench/akara v0.0.0-20201206061149-9be03b4110f2 h1:mOIIK6AgIyaEslKsu+tsguzFWaMLGjlMuUKqOlABhGk=
github.com/gravestench/akara v0.0.0-20201206061149-9be03b4110f2/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/gravestench/pho v0.0.0-20201029002250-f9afbd637e4d h1:CP+/y9SAdv9LifYvicxYdQNmzugykEahAiUhYolMROM=
github.com/gravestench/pho v0.0.0-20201029002250-f9afbd637e4d/go.mod h1:yi5GHMLLWtHhs9tz3q1csUlgGKz5MhZoJcxV8NFBtkk=
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=

42
main.go
View File

@ -1,45 +1,7 @@
package main
import (
"log"
"github.com/gravestench/akara"
"gopkg.in/alecthomas/kingpin.v2"
"github.com/OpenDiablo2/OpenDiablo2/d2app"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2systems"
)
// GitBranch is set by the CI build process to the name of the branch
//nolint:gochecknoglobals // This is filled in by the build system
var GitBranch string
// GitCommit is set by the CI build process to the commit hash
//nolint:gochecknoglobals // This is filled in by the build system
var GitCommit string
import "github.com/OpenDiablo2/OpenDiablo2/d2app"
func main() {
ecs := kingpin.Flag("ecs", "start the ecs implementation").Bool()
kingpin.Parse()
if *ecs {
cfg := akara.NewWorldConfig()
cfg.
With(&d2systems.AppBootstrapSystem{}).
With(&d2systems.GameClientBootstrapSystem{})
akara.NewWorld(cfg)
return
}
log.SetFlags(log.Lshortfile)
log.Println("OpenDiablo2 - Open source Diablo 2 engine")
instance := d2app.Create(GitBranch, GitCommit)
if err := instance.Run(); err != nil {
return
}
d2app.Run()
}