Files

214 lines
4.9 KiB
Go

package systems
import (
"errors"
"aego/core/mathx"
"aego/modules/physics/components"
"aego/modules/physics/world"
"aego/scene"
)
func configOf(c components.Body2D) components.Body2D {
c.Velocity, c.AngularVelocity, c.Awake = mathx.Vec2{}, 0, false
return c
}
func bodyDef(c components.Body2D, node scene.NodeHandle) world.BodyDef {
d := world.DefaultBodyDef()
d.Type = world.BodyType(c.Type)
d.MassMode = world.MassMode(c.MassMode)
d.Mass = c.Mass
d.GravityScale = c.GravityScale
d.LinearDamping, d.AngularDamping = c.LinearDamping, c.AngularDamping
d.FixedRotation, d.Bullet, d.AllowSleep = c.FixedRotation, c.Bullet, c.AllowSleep
d.Layer = uint8(min(max(c.Layer, 0), 31))
d.Mask = uint32(c.Mask)
d.UserData = Pack(node)
return d
}
func (m *Sim) velocity(b *bodyRec) (mathx.Vec2, float32) {
if b.h.IsZero() {
return mathx.Vec2{}, 0
}
v, av, _ := m.w.BodyVelocity(b.h)
return v, av
}
func (m *Sim) createBody(b *bodyRec, v mathx.Vec2, av float32) {
s := m.s
if s.Under3D(b.node) {
m.report(b.node, keySpace, CodeSpace, "physics needs a 2D node that is not under a 3D parent", true)
return
}
m.resolve(b.node, keySpace)
def := world.DefaultBodyDef()
if b.implicit {
def.Type = world.Static
def.UserData = Pack(b.node)
b.cfg = components.Body2D{}
} else {
c, ok := scene.Get[components.Body2D](s, b.node)
if !ok {
return
}
b.cfg = configOf(*c)
def = bodyDef(b.cfg, b.node)
def.LinearVelocity, def.AngularVelocity = v, av
}
wm := s.World(b.node)
f := frame(wm)
def.Position, def.Angle = f.p, f.a
h, err := m.w.CreateBody(def)
if err != nil {
m.report(b.node, keyBody, CodeBody, "Body2D: "+err.Error(), true)
return
}
m.resolve(b.node, keyBody)
b.h = h
b.local, b.world = s.Local(b.node), wm
b.prev, b.cur = f, f
}
func (m *Sim) dropBody(b *bodyRec) {
if !b.h.IsZero() {
m.w.DestroyBody(b.h)
}
b.h = world.BodyHandle{}
b.excluded = false
}
func (m *Sim) syncBody(b *bodyRec) {
s := m.s
if !b.implicit {
c, ok := scene.Get[components.Body2D](s, b.node)
if !ok {
m.dirty = true
return
}
if cfg := configOf(*c); cfg != b.cfg {
switch {
case b.h.IsZero() || cfg.Type != b.cfg.Type:
v, av := m.velocity(b)
m.dropBody(b)
m.createBody(b, v, av)
default:
b.cfg = cfg
if err := m.w.SetBodyProps(b.h, bodyDef(cfg, b.node)); err != nil {
m.report(b.node, keyBody, CodeBody, "Body2D: "+err.Error(), true)
} else {
m.resolve(b.node, keyBody)
}
}
}
}
if b.h.IsZero() {
return
}
if b.moves() {
if s.Local(b.node) != b.local {
m.teleport(b)
}
return
}
if s.World(b.node) != b.world {
m.teleport(b)
}
}
func (m *Sim) teleport(b *bodyRec) {
s := m.s
wm := s.World(b.node)
f := frame(wm)
if err := m.w.SetBodyTransform(b.h, f.p, f.a); err != nil {
m.report(b.node, keyBody, CodeBody, "Body2D: "+err.Error(), true)
return
}
b.local, b.world = s.Local(b.node), wm
b.prev, b.cur = f, f
}
func (m *Sim) reparent(b *bodyRec) {
v, av := m.velocity(b)
p, a, ok := m.w.BodyTransform(b.h)
m.dropBody(b)
if ok {
m.s.SetLocal(b.node, m.toLocal(b.node, pose{p: p, a: a}))
}
m.report(b.node, keyReparent, CodeReparent, "Body2D was reparented during Play; the body was recreated with its world pose and velocity", false)
m.createBody(b, v, av)
}
func (m *Sim) toLocal(h scene.NodeHandle, f pose) scene.Transform2D {
s := m.s
l := s.Local(h)
pw := s.World(s.Parent(h))
inv, ok := pw.Inverse()
if !ok {
return l
}
l.Position = inv.Apply(f.p)
l.Rotation = f.a - mathx.Atan2(pw[1], pw[0])
return l
}
func (m *Sim) Writeback() {
s := m.s
for _, b := range m.bodies {
if !b.moves() || b.h.IsZero() {
continue
}
v, av, _ := m.w.BodyVelocity(b.h)
awake := m.w.BodyAwake(b.h)
if c, ok := scene.Get[components.Body2D](s, b.node); ok {
c.Velocity, c.AngularVelocity, c.Awake = v, av, awake
}
p, a, _ := m.w.BodyTransform(b.h)
b.prev, b.cur = b.cur, pose{p: p, a: a}
if b.prev == b.cur {
continue
}
l := m.toLocal(b.node, b.cur)
s.SetLocal(b.node, l)
b.local = l
}
}
func (m *Sim) Present(alpha float32) {
s := m.s
s.ClearPresentation()
for _, b := range m.bodies {
if !b.moves() || b.h.IsZero() {
continue
}
if s.Local(b.node) != b.local {
if !b.warned {
b.warned = true
m.report(b.node, keyUpdate, CodeUpdate, "Transform2D of a moving physics body was changed outside FixedUpdate; it is applied as a teleport at the next physics step", false)
}
continue
}
if !b.cfg.Interpolate || b.prev == b.cur {
continue
}
s.SetPresentationLocal(b.node, m.toLocal(b.node, lerpPose(b.prev, b.cur, alpha)))
}
}
func (m *Sim) Teleport(h scene.NodeHandle, p mathx.Vec2, a float32) error {
b := m.byBody[h]
if b == nil || b.h.IsZero() {
return errors.New("node has no simulated physics body")
}
if err := m.w.SetBodyTransform(b.h, p, a); err != nil {
return err
}
f := pose{p: p, a: a}
l := m.toLocal(h, f)
m.s.SetLocal(h, l)
b.local, b.world = l, m.s.World(h)
b.prev, b.cur = f, f
return nil
}