Files

393 lines
8.6 KiB
Go

package tween
import (
"strings"
"aego/contracts/scriptfields"
"aego/contracts/scriptwait"
"aego/contracts/signals"
"aego/core/curve"
"aego/core/errs"
"aego/core/mathx"
"aego/engine"
"aego/scene"
"aego/schema"
"aego/script/vm"
)
type Options struct {
Ease curve.Easing
Curve *curve.Curve
Delay float32
Legs int32
PingPong bool
Unscaled bool
}
type tween struct {
node scene.NodeHandle
tgt target
from [4]float32
to [4]float32
dur float32
t float32
delay float32
legs int32
ease curve.Easing
cv curve.Curve
hasCurve bool
ping bool
rev bool
started bool
unscaled bool
act Handle
held bool
moved bool
}
type Service struct {
scene *scene.Scene
world *engine.World
reg *schema.Registry
short map[string]schema.TypeID
tweens pool[tween]
timers pool[timer]
actions pool[action]
hub signals.Hub
}
func NewService(s *scene.Scene, w *engine.World) *Service {
svc := &Service{scene: s, world: w, reg: s.Registry(), short: map[string]schema.TypeID{}}
for _, td := range svc.reg.Components() {
n := td.FullName[strings.LastIndexByte(td.FullName, '.')+1:]
if _, dup := svc.short[n]; dup {
svc.short[n] = 0
continue
}
svc.short[n] = td.ID
}
if w != nil {
svc.hub, _ = engine.ServiceOf[signals.Hub](w, signals.ServiceID)
}
return svc
}
func handleSource(typ, key uint64) signals.Source {
return signals.Source{Kind: signals.KindHandle, A: key, B: typ}
}
func (s *Service) emitOne(src signals.Source, sig int, v vm.Value) {
if s.hub == nil || !s.hub.Wants(src) {
return
}
args := s.hub.Args(1)
args[0] = v
s.hub.Emit(src, sig, args)
}
func (s *Service) endHandle(src signals.Source) {
if s.hub != nil {
s.hub.End(src)
}
}
func (s *Service) tweenAlive(key uint64) bool {
return s.tweens.get(HandleOf(key)) != nil
}
func (s *Service) timerAlive(key uint64) bool {
return s.timers.get(HandleOf(key)) != nil
}
func (s *Service) OnCompleted(h Handle, fn func(finished bool)) vm.Value {
if s.hub == nil || fn == nil || s.tweens.get(h) == nil {
return vm.Value{}
}
return s.hub.Connect(handleSource(typeTween, h.Key()), sigCompleted, func(a []vm.Value) { fn(a[0].Bool()) })
}
func (s *Service) Disconnect(c vm.Value) {
if s.hub != nil {
s.hub.Disconnect(c)
}
}
func (s *Service) Close() {
s.tweens = pool[tween]{}
s.timers = pool[timer]{}
s.actions = pool[action]{}
}
func (s *Service) fields() scriptfields.Fields {
if s.world == nil {
return nil
}
f, _ := engine.ServiceOf[scriptfields.Fields](s.world, scriptfields.ServiceID)
return f
}
func (s *Service) wake(typ, key uint64) {
if s.world == nil {
return
}
if w, ok := engine.ServiceOf[scriptwait.Waker](s.world, scriptwait.ServiceID); ok {
w.Complete(typ, key)
}
}
func (s *Service) Tweens() int {
return s.tweens.n
}
func (s *Service) ToFloat(node scene.NodeHandle, path string, to, dur float32, o Options) (Handle, error) {
return s.start(node, path, shFloat, [4]float32{to}, dur, &o)
}
func (s *Service) ToVec2(node scene.NodeHandle, path string, to mathx.Vec2, dur float32, o Options) (Handle, error) {
return s.start(node, path, shVec2, [4]float32{to.X, to.Y}, dur, &o)
}
func (s *Service) ToVec3(node scene.NodeHandle, path string, to mathx.Vec3, dur float32, o Options) (Handle, error) {
return s.start(node, path, shVec3, [4]float32{to.X, to.Y, to.Z}, dur, &o)
}
func (s *Service) ToColor(node scene.NodeHandle, path string, to mathx.Color, dur float32, o Options) (Handle, error) {
return s.start(node, path, shVec4, [4]float32{to.R, to.G, to.B, to.A}, dur, &o)
}
func (s *Service) ToRect(node scene.NodeHandle, path string, to mathx.Rect, dur float32, o Options) (Handle, error) {
return s.start(node, path, shVec4, [4]float32{to.X, to.Y, to.W, to.H}, dur, &o)
}
func (s *Service) ToQuat(node scene.NodeHandle, path string, to mathx.Quat, dur float32, o Options) (Handle, error) {
return s.start(node, path, shQuat, [4]float32{to.X, to.Y, to.Z, to.W}, dur, &o)
}
func (s *Service) Position(node scene.NodeHandle, to mathx.Vec2, dur float32, o Options) (Handle, error) {
return s.ToVec2(node, ownerTransform+".position", to, dur, o)
}
func (s *Service) start(node scene.NodeHandle, path string, want shape, to [4]float32, dur float32, o *Options) (Handle, error) {
if !s.scene.IsAlive(node) {
return Handle{}, errs.New(errs.SceneDeadNode, "tween: the node is nil or destroyed")
}
if dur != dur || dur < 0 || o.Delay != o.Delay || o.Delay < 0 {
return Handle{}, badPath(path, "duration and delay must be zero or positive")
}
t, err := s.resolve(node, path)
if err != nil {
return Handle{}, err
}
if !fits(&t, want) {
return Handle{}, badPath(path, "the value does not match the type of the property")
}
s.replace(node, &t)
legs := normLegs(o.Legs)
h, tw := s.tweens.alloc()
keys := tw.cv.Keys[:0]
*tw = tween{
node: node, tgt: t, to: to, dur: dur, delay: o.Delay, legs: legs,
ease: o.Ease, ping: o.PingPong, unscaled: o.Unscaled,
}
tw.cv.Keys = keys
if o.Curve != nil {
tw.cv.Ease, tw.cv.Pre, tw.cv.Post = o.Curve.Ease, o.Curve.Pre, o.Curve.Post
tw.cv.Keys = append(keys, o.Curve.Keys...)
tw.hasCurve = true
}
return h, nil
}
func (s *Service) replace(node scene.NodeHandle, t *target) {
for i := range s.tweens.s {
e := &s.tweens.s[i]
if e.live && e.v.node == node && e.v.tgt.same(t) {
s.finish(uint32(i), false)
}
}
}
func (s *Service) Stop(h Handle) {
if s.tweens.get(h) != nil {
s.finish(h.Index, false)
}
}
func (s *Service) Done(h Handle) bool {
return s.tweens.get(h) == nil
}
func (s *Service) Running(h Handle) bool {
return !s.Done(h)
}
func (s *Service) finish(i uint32, finished bool) {
h := s.tweens.handle(i)
act := s.tweens.s[i].v.act
key := h.Key()
s.tweens.release(i)
src := handleSource(typeTween, key)
s.emitOne(src, sigCompleted, vm.Bool(finished))
s.endHandle(src)
s.wake(typeTween, key)
if !act.IsZero() {
s.stepDone(act, stepTween, h)
}
}
func normLegs(n int32) int32 {
switch {
case n == 0:
return 1
case n < 0:
return -1
}
return n
}
func (s *Service) SetDelay(h Handle, d float32) {
if tw := s.tweens.get(h); tw != nil && !tw.started && d >= 0 && d == d {
tw.delay = d
}
}
func (s *Service) SetLegs(h Handle, legs int32, ping bool) {
if tw := s.tweens.get(h); tw != nil {
tw.legs, tw.ping = normLegs(legs), ping
}
}
func (s *Service) SetUnscaled(h Handle, on bool) {
if tw := s.tweens.get(h); tw != nil {
tw.unscaled = on
}
}
func (s *Service) SetCurve(h Handle, c *curve.Curve) {
tw := s.tweens.get(h)
if tw == nil || c == nil {
return
}
tw.cv.Ease, tw.cv.Pre, tw.cv.Post = c.Ease, c.Pre, c.Post
tw.cv.Keys = append(tw.cv.Keys[:0], c.Keys...)
tw.hasCurve = true
}
func (s *Service) Step(dt float32, unscaled bool) {
if dt != dt || dt < 0 {
dt = 0
}
s.stepActions()
for i := range s.tweens.s {
e := &s.tweens.s[i]
if e.live && e.v.unscaled == unscaled {
s.advance(uint32(i), &e.v, dt)
}
}
s.stepTimers(dt, unscaled)
}
func (s *Service) advance(i uint32, tw *tween, dt float32) {
if !s.scene.IsAlive(tw.node) {
s.finish(i, false)
return
}
if tw.held {
return
}
tw.moved = true
if tw.delay > 0 {
tw.delay -= dt
if tw.delay > 0 {
return
}
dt = -tw.delay
tw.delay = 0
}
if !tw.started {
from, ok := s.read(&tw.tgt, tw.node)
if !ok {
s.finish(i, false)
return
}
tw.from, tw.started = from, true
}
tw.t += dt
for tw.dur <= 0 || tw.t >= tw.dur {
if tw.legs > 0 {
tw.legs--
}
if tw.legs == 0 || tw.dur <= 0 {
ok := s.write(&tw.tgt, tw.node, tw.end())
s.finish(i, ok)
return
}
tw.t -= tw.dur
if tw.ping {
tw.rev = !tw.rev
}
}
if !s.write(&tw.tgt, tw.node, tw.at(tw.t/tw.dur)) {
s.finish(i, false)
}
}
func (s *Service) actionAlive(key uint64) bool {
return s.actions.get(HandleOf(key)) != nil
}
func (tw *tween) end() [4]float32 {
if tw.rev {
return tw.from
}
return tw.to
}
func (tw *tween) eased(u float32) float32 {
if tw.hasCurve {
t0, t1 := tw.cv.Span()
return tw.cv.EvalClamped(t0 + (t1-t0)*u)
}
if tw.ease == curve.None {
return u
}
return curve.Ease(tw.ease, u)
}
func (tw *tween) at(u float32) [4]float32 {
e := tw.eased(u)
a, b := tw.from, tw.to
if tw.rev {
a, b = b, a
}
if tw.tgt.shape == shQuat && tw.tgt.lane < 0 {
return nlerp(a, b, e)
}
var out [4]float32
for i := range out {
out[i] = a[i] + (b[i]-a[i])*e
}
return out
}
func nlerp(a, b [4]float32, t float32) [4]float32 {
if a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3] < 0 {
for i := range b {
b[i] = -b[i]
}
}
var o [4]float32
var n float32
for i := range o {
o[i] = a[i] + (b[i]-a[i])*t
n += o[i] * o[i]
}
if n > 0 {
k := 1 / mathx.Sqrt(n)
for i := range o {
o[i] *= k
}
}
return o
}