Files

372 lines
7.1 KiB
Go

package systems
import (
"aego/core/log"
"aego/core/mathx"
"aego/modules/physics/components"
"aego/modules/physics/world"
"aego/scene"
)
const (
ServiceID = "physics.sim"
Channel log.Channel = "physics"
)
type bodyRec struct {
node scene.NodeHandle
parent scene.NodeHandle
h world.BodyHandle
implicit bool
cfg components.Body2D
local scene.Transform2D
world mathx.Mat3
prev pose
cur pose
excluded bool
warned bool
mark uint32
}
func (b *bodyRec) moves() bool {
return !b.implicit && b.cfg.Type != components.Static
}
type shapeKey struct {
node scene.NodeHandle
kind components.Kind
}
type shapeRec struct {
key shapeKey
owner scene.NodeHandle
body world.BodyHandle
h world.ShapeHandle
sig colliderSig
rel mathx.Mat3
built bool
bad bool
enabled bool
mark uint32
}
type jointKind uint8
const (
jointDistance jointKind = iota
jointRevolute
)
type jointKey struct {
node scene.NodeHandle
kind jointKind
}
type jointRec struct {
key jointKey
h world.JointHandle
a, b world.BodyHandle
dist components.DistanceJoint2D
rev components.RevoluteJoint2D
built bool
failed bool
mark uint32
}
type Sim struct {
w *world.World
s *scene.Scene
log *log.ChannelLogger
unsub func()
dirty bool
mark uint32
bodies []*bodyRec
nb []*bodyRec
byBody map[scene.NodeHandle]*bodyRec
shapes []*shapeRec
ns []*shapeRec
byShape map[shapeKey]*shapeRec
joints []*jointRec
nj []*jointRec
byJoint map[jointKey]*jointRec
diags []Diag
diagRev uint64
col components.Collider
pts []mathx.Vec2
touch []Touch
layers [32]string
qhits []world.RayHit
qshapes []world.ShapeHandle
}
// Touch is a shape pair in contact; Fresh marks pairs that began this step.
type Touch struct {
ShapeA world.ShapeHandle
ShapeB world.ShapeHandle
Fresh bool
}
func New(s *scene.Scene, cfg world.Config, matrix [32]uint32, lg *log.Logger) (*Sim, error) {
w, err := world.New(cfg)
if err != nil {
return nil, err
}
if err := w.SetLayerMatrix(matrix); err != nil {
return nil, err
}
if lg == nil {
lg = log.Nop()
}
m := &Sim{
w: w,
s: s,
log: lg.With(Channel),
dirty: true,
byBody: map[scene.NodeHandle]*bodyRec{},
byShape: map[shapeKey]*shapeRec{},
byJoint: map[jointKey]*jointRec{},
}
m.unsub = s.Events().Subscribe(m.onEvent)
return m, nil
}
func (m *Sim) Close() {
if m.unsub != nil {
m.unsub()
m.unsub = nil
}
}
func (m *Sim) World() *world.World {
return m.w
}
func (m *Sim) Scene() *scene.Scene {
return m.s
}
func (m *Sim) Stats() world.Stats {
return m.w.Stats()
}
func (m *Sim) BodyOf(h scene.NodeHandle) (world.BodyHandle, bool) {
if b := m.byBody[h]; b != nil && !b.h.IsZero() {
return b.h, true
}
return world.BodyHandle{}, false
}
func (m *Sim) onEvent(ev scene.Event) {
switch ev.Kind {
case scene.EvNodeDestroyed, scene.EvNodeReparented, scene.EvSpaceChanged:
m.dirty = true
case scene.EvComponentAdded, scene.EvComponentRemoved:
if components.Is(ev.Type) {
m.dirty = true
}
}
}
func (m *Sim) Sync() {
m.s.UpdateTransforms()
if m.dirty {
m.reconcile()
}
for _, b := range m.bodies {
m.syncBody(b)
b.excluded = false
}
for _, sh := range m.shapes {
m.buildShape(sh)
}
for _, sh := range m.shapes {
m.enableShape(sh)
}
for _, b := range m.bodies {
if b.excluded && !b.h.IsZero() {
m.w.Sleep(b.h)
}
}
for _, j := range m.joints {
m.syncJoint(j)
}
}
func (m *Sim) Step(dt float32) {
for i := range m.touch {
m.touch[i].Fresh = false
}
m.w.Step(dt)
for _, e := range m.w.Events().Contacts {
switch e.Kind {
case world.BeginContact:
m.touch = append(m.touch, Touch{ShapeA: e.ShapeA, ShapeB: e.ShapeB, Fresh: true})
case world.EndContact:
for i, t := range m.touch {
if t.ShapeA == e.ShapeA && t.ShapeB == e.ShapeB {
last := len(m.touch) - 1
m.touch[i] = m.touch[last]
m.touch = m.touch[:last]
break
}
}
}
}
}
func (m *Sim) Touches() []Touch {
return m.touch
}
func (m *Sim) SetLayers(names [32]string) {
m.layers = names
}
func (m *Sim) Layers() [32]string {
return m.layers
}
func (m *Sim) LayerIndex(name string) (int, bool) {
for i, n := range m.layers {
if n != "" && n == name {
return i, true
}
}
return -1, false
}
// Body is BodyOf after a pending reconcile, for callers outside the step.
func (m *Sim) Body(h scene.NodeHandle) (world.BodyHandle, bool) {
if m.dirty {
m.Sync()
}
return m.BodyOf(h)
}
func (m *Sim) reconcile() {
m.dirty = false
m.mark++
s := m.s
root := s.Root()
m.nb, m.ns, m.nj = m.nb[:0], m.ns[:0], m.nj[:0]
order := s.PreOrder()
for _, h := range order {
if h != root && s.IsAlive(h) && scene.Has[components.Body2D](s, h) {
m.keepBody(h, false)
}
}
for _, h := range order {
if h == root || !s.IsAlive(h) {
continue
}
for k := components.Kind(0); k < components.KindCount; k++ {
if components.HasCollider(s, h, k) {
m.keepShape(shapeKey{node: h, kind: k})
}
}
if scene.Has[components.DistanceJoint2D](s, h) {
m.keepJoint(jointKey{node: h, kind: jointDistance})
}
if scene.Has[components.RevoluteJoint2D](s, h) {
m.keepJoint(jointKey{node: h, kind: jointRevolute})
}
}
m.sweep()
}
func (m *Sim) sweep() {
for _, j := range m.joints {
if j.mark != m.mark {
m.dropJoint(j)
delete(m.byJoint, j.key)
m.resolve(j.key.node, jointKeyName(j.key.kind))
}
}
for _, sh := range m.shapes {
if sh.mark != m.mark {
m.dropShape(sh)
delete(m.byShape, sh.key)
m.resolve(sh.key.node, shapeKeyName(sh.key.kind))
}
}
for _, b := range m.bodies {
if b.mark != m.mark {
m.dropBody(b)
delete(m.byBody, b.node)
m.forget(b.node)
}
}
m.bodies, m.nb = m.nb, m.bodies[:0]
m.shapes, m.ns = m.ns, m.shapes[:0]
m.joints, m.nj = m.nj, m.joints[:0]
}
func (m *Sim) keepBody(h scene.NodeHandle, implicit bool) *bodyRec {
b := m.byBody[h]
if b != nil && b.mark == m.mark {
return b
}
if b == nil {
b = &bodyRec{node: h, implicit: implicit}
m.byBody[h] = b
} else if b.implicit != implicit {
m.dropBody(b)
b.implicit = implicit
}
b.mark = m.mark
m.nb = append(m.nb, b)
parent := m.s.Parent(h)
if !b.h.IsZero() && b.moves() && !b.parent.IsZero() && parent != b.parent {
m.reparent(b)
}
b.parent = parent
if b.h.IsZero() {
m.createBody(b, mathx.Vec2{}, 0)
}
return b
}
func (m *Sim) ownerOf(h scene.NodeHandle) scene.NodeHandle {
root := m.s.Root()
for x := h; !x.IsZero() && x != root; x = m.s.Parent(x) {
if b := m.byBody[x]; b != nil && b.mark == m.mark && !b.implicit {
return x
}
}
return scene.NodeHandle{}
}
func (m *Sim) keepShape(k shapeKey) {
var b *bodyRec
if owner := m.ownerOf(k.node); owner.IsZero() {
b = m.keepBody(k.node, true)
} else {
b = m.byBody[owner]
}
sh := m.byShape[k]
if sh == nil {
sh = &shapeRec{key: k}
m.byShape[k] = sh
}
if sh.mark != m.mark {
sh.mark = m.mark
m.ns = append(m.ns, sh)
}
if sh.owner != b.node {
m.dropShape(sh)
sh.owner = b.node
}
}
func (m *Sim) keepJoint(k jointKey) {
j := m.byJoint[k]
if j == nil {
j = &jointRec{key: k}
m.byJoint[k] = j
}
if j.mark != m.mark {
j.mark = m.mark
m.nj = append(m.nj, j)
}
}