362 lines
7.4 KiB
Go
362 lines
7.4 KiB
Go
package world
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"math"
|
|
"unsafe"
|
|
)
|
|
|
|
type Snapshot struct {
|
|
cfg Config
|
|
bodies pool[body]
|
|
shapes pool[shape]
|
|
joints pool[joint]
|
|
tree tree
|
|
prox []proxyInfo
|
|
moveBuf []int32
|
|
pairs pairMap
|
|
touch pairMap
|
|
contacts []contact
|
|
freeC []int32
|
|
layers [32]uint32
|
|
pending []ContactEvent
|
|
dyingB []BodyHandle
|
|
deadB []BodyHandle
|
|
dyingS []ShapeHandle
|
|
deadS []ShapeHandle
|
|
mark uint32
|
|
steps uint64
|
|
awake []int32
|
|
raw []byte
|
|
}
|
|
|
|
func cloneBody(dst, src *body) {
|
|
sh, jo, co := dst.shapes, dst.joints, dst.contacts
|
|
*dst = *src
|
|
dst.shapes = append(sh[:0], src.shapes...)
|
|
dst.joints = append(jo[:0], src.joints...)
|
|
dst.contacts = append(co[:0], src.contacts...)
|
|
}
|
|
|
|
func cloneShape(dst, src *shape) {
|
|
px, ch := dst.proxies, dst.g.chain
|
|
*dst = *src
|
|
dst.proxies = append(px[:0], src.proxies...)
|
|
if src.g.chain == nil {
|
|
return
|
|
}
|
|
if ch == nil || ch == src.g.chain {
|
|
ch = &chainData{}
|
|
}
|
|
pts := ch.pts
|
|
*ch = *src.g.chain
|
|
ch.pts = append(pts[:0], src.g.chain.pts...)
|
|
dst.g.chain = ch
|
|
}
|
|
|
|
func (w *World) Snapshot() *Snapshot {
|
|
s := &Snapshot{}
|
|
w.SnapshotInto(s)
|
|
return s
|
|
}
|
|
|
|
func (w *World) SnapshotInto(s *Snapshot) {
|
|
s.cfg = w.cfg
|
|
w.bodies.cloneInto(&s.bodies, cloneBody)
|
|
w.shapes.cloneInto(&s.shapes, cloneShape)
|
|
w.joints.cloneInto(&s.joints, nil)
|
|
w.tree.cloneInto(&s.tree)
|
|
s.prox = append(s.prox[:0], w.prox...)
|
|
s.moveBuf = append(s.moveBuf[:0], w.moveBuf...)
|
|
w.pairs.cloneInto(&s.pairs)
|
|
w.touch.cloneInto(&s.touch)
|
|
s.contacts = append(s.contacts[:0], w.contacts...)
|
|
s.freeC = append(s.freeC[:0], w.freeC...)
|
|
s.layers = w.layers
|
|
s.pending = append(s.pending[:0], w.pending...)
|
|
s.dyingB = append(s.dyingB[:0], w.dyingB...)
|
|
s.deadB = append(s.deadB[:0], w.deadB...)
|
|
s.dyingS = append(s.dyingS[:0], w.dyingS...)
|
|
s.deadS = append(s.deadS[:0], w.deadS...)
|
|
s.awake = append(s.awake[:0], w.awake...)
|
|
s.mark = w.mark
|
|
s.steps = w.steps
|
|
s.raw = nil
|
|
}
|
|
|
|
func (w *World) Restore(s *Snapshot) {
|
|
if s == nil {
|
|
return
|
|
}
|
|
dv := w.cfg.DebugVectors
|
|
w.cfg = s.cfg
|
|
w.cfg.DebugVectors = dv
|
|
s.bodies.cloneInto(&w.bodies, cloneBody)
|
|
s.shapes.cloneInto(&w.shapes, cloneShape)
|
|
s.joints.cloneInto(&w.joints, nil)
|
|
s.tree.cloneInto(&w.tree)
|
|
w.prox = append(w.prox[:0], s.prox...)
|
|
w.moveBuf = append(w.moveBuf[:0], s.moveBuf...)
|
|
s.pairs.cloneInto(&w.pairs)
|
|
s.touch.cloneInto(&w.touch)
|
|
w.contacts = append(w.contacts[:0], s.contacts...)
|
|
w.freeC = append(w.freeC[:0], s.freeC...)
|
|
w.layers = s.layers
|
|
w.pending = append(w.pending[:0], s.pending...)
|
|
w.dyingB = append(w.dyingB[:0], s.dyingB...)
|
|
w.deadB = append(w.deadB[:0], s.deadB...)
|
|
w.dyingS = append(w.dyingS[:0], s.dyingS...)
|
|
w.deadS = append(w.deadS[:0], s.deadS...)
|
|
w.mark = s.mark
|
|
w.steps = s.steps
|
|
w.events = w.events[:0]
|
|
w.began = w.began[:0]
|
|
w.newPairs = w.newPairs[:0]
|
|
w.stats = Stats{Steps: s.steps}
|
|
w.rebuildDerived()
|
|
items := w.bodies.items
|
|
for _, i := range w.awake {
|
|
items[i].listed = false
|
|
}
|
|
w.awake = append(w.awake[:0], s.awake...)
|
|
for _, i := range w.awake {
|
|
items[i].listed = true
|
|
}
|
|
w.countStats()
|
|
}
|
|
|
|
func (s *Snapshot) Size() int {
|
|
return len(s.bodies.items)*int(unsafe.Sizeof(body{})) +
|
|
len(s.shapes.items)*int(unsafe.Sizeof(shape{})) +
|
|
len(s.joints.items)*int(unsafe.Sizeof(joint{})) +
|
|
len(s.tree.nodes)*int(unsafe.Sizeof(treeNode{})) +
|
|
len(s.prox)*int(unsafe.Sizeof(proxyInfo{})) +
|
|
len(s.contacts)*int(unsafe.Sizeof(contact{})) +
|
|
12*(len(s.pairs.keys)+len(s.touch.keys)) +
|
|
4*(len(s.awake)+len(s.moveBuf)+len(s.freeC))
|
|
}
|
|
|
|
type enc struct {
|
|
b []byte
|
|
}
|
|
|
|
func (e *enc) u32(v uint32) {
|
|
e.b = binary.LittleEndian.AppendUint32(e.b, v)
|
|
}
|
|
|
|
func (e *enc) u64(v uint64) {
|
|
e.b = binary.LittleEndian.AppendUint64(e.b, v)
|
|
}
|
|
|
|
func (e *enc) i32(v int32) {
|
|
e.u32(uint32(v))
|
|
}
|
|
|
|
func (e *enc) f32(v float32) {
|
|
e.u32(math.Float32bits(v))
|
|
}
|
|
|
|
func (e *enc) v2(v Vec2) {
|
|
e.f32(v.X)
|
|
e.f32(v.Y)
|
|
}
|
|
|
|
func (e *enc) flag(v bool) {
|
|
if v {
|
|
e.b = append(e.b, 1)
|
|
return
|
|
}
|
|
e.b = append(e.b, 0)
|
|
}
|
|
|
|
func (e *enc) box(a AABB) {
|
|
e.v2(a.Min)
|
|
e.v2(a.Max)
|
|
}
|
|
|
|
func (e *enc) handle(index, gen uint32) {
|
|
e.u32(index)
|
|
e.u32(gen)
|
|
}
|
|
|
|
func (e *enc) manifold(m *Manifold) {
|
|
e.v2(m.Normal)
|
|
e.u32(uint32(m.Count))
|
|
for i := 0; i < m.Count; i++ {
|
|
p := &m.Points[i]
|
|
e.v2(p.Point)
|
|
e.v2(p.AnchorA)
|
|
e.v2(p.AnchorB)
|
|
e.f32(p.Separation)
|
|
e.f32(p.NormalImpulse)
|
|
e.f32(p.TangentImpulse)
|
|
e.f32(p.TotalNormalImpulse)
|
|
e.u32(uint32(p.ID))
|
|
e.flag(p.Persisted)
|
|
}
|
|
}
|
|
|
|
func poolHeader[T any](e *enc, p *pool[T]) {
|
|
e.u32(uint32(len(p.items)))
|
|
for i := range p.items {
|
|
e.u32(p.gens[i])
|
|
e.flag(p.alive[i])
|
|
}
|
|
e.u32(uint32(len(p.free)))
|
|
for _, f := range p.free {
|
|
e.u32(f)
|
|
}
|
|
}
|
|
|
|
func (s *Snapshot) Bytes() []byte {
|
|
if s.raw != nil {
|
|
return s.raw
|
|
}
|
|
e := &enc{b: make([]byte, 0, 4096)}
|
|
e.u64(s.steps)
|
|
e.u32(s.mark)
|
|
poolHeader(e, &s.bodies)
|
|
for i := range s.bodies.items {
|
|
if !s.bodies.alive[i] {
|
|
continue
|
|
}
|
|
b := &s.bodies.items[i]
|
|
e.u32(uint32(b.typ))
|
|
e.v2(b.xf.p)
|
|
e.f32(b.xf.q.C)
|
|
e.f32(b.xf.q.S)
|
|
e.f32(b.angle)
|
|
e.v2(b.localCenter)
|
|
e.v2(b.center)
|
|
e.v2(b.v)
|
|
e.f32(b.w)
|
|
e.v2(b.force)
|
|
e.f32(b.torque)
|
|
e.f32(b.mass)
|
|
e.f32(b.inertia)
|
|
e.f32(b.sleepTime)
|
|
e.flag(b.awake)
|
|
e.flag(b.dead)
|
|
e.u32(uint32(b.layer))
|
|
e.u32(b.mask)
|
|
e.u32(b.mark)
|
|
e.u32(uint32(len(b.shapes)))
|
|
for _, h := range b.shapes {
|
|
e.handle(h.Index, h.Gen)
|
|
}
|
|
e.u32(uint32(len(b.joints)))
|
|
for _, h := range b.joints {
|
|
e.handle(h.Index, h.Gen)
|
|
}
|
|
e.u32(uint32(len(b.contacts)))
|
|
for _, c := range b.contacts {
|
|
e.i32(c)
|
|
}
|
|
}
|
|
poolHeader(e, &s.shapes)
|
|
for i := range s.shapes.items {
|
|
if !s.shapes.alive[i] {
|
|
continue
|
|
}
|
|
sh := &s.shapes.items[i]
|
|
e.handle(sh.body.Index, sh.body.Gen)
|
|
e.u32(uint32(sh.g.kind))
|
|
e.flag(sh.trigger)
|
|
e.flag(sh.dead)
|
|
e.flag(sh.disabled)
|
|
e.f32(sh.friction)
|
|
e.f32(sh.restitution)
|
|
e.f32(sh.density)
|
|
for _, id := range sh.proxies {
|
|
e.i32(id)
|
|
}
|
|
}
|
|
poolHeader(e, &s.joints)
|
|
for i := range s.joints.items {
|
|
if !s.joints.alive[i] {
|
|
continue
|
|
}
|
|
j := &s.joints.items[i]
|
|
e.u32(uint32(j.kind))
|
|
e.handle(j.a.Index, j.a.Gen)
|
|
e.handle(j.b.Index, j.b.Gen)
|
|
e.f32(j.impulse)
|
|
e.f32(j.lowerImp)
|
|
e.f32(j.upperImp)
|
|
e.v2(j.pImpulse)
|
|
e.f32(j.motorImp)
|
|
e.f32(j.speed)
|
|
e.u32(j.mark)
|
|
}
|
|
e.i32(s.tree.root)
|
|
e.i32(s.tree.free)
|
|
e.u32(uint32(len(s.tree.nodes)))
|
|
for i := range s.tree.nodes {
|
|
n := &s.tree.nodes[i]
|
|
e.box(n.box)
|
|
e.i32(n.parent)
|
|
e.i32(n.child1)
|
|
e.i32(n.child2)
|
|
e.i32(n.height)
|
|
e.i32(n.data)
|
|
}
|
|
for i := range s.prox {
|
|
p := &s.prox[i]
|
|
e.handle(p.shape.Index, p.shape.Gen)
|
|
e.i32(p.child)
|
|
e.box(p.tight)
|
|
e.flag(p.alive)
|
|
e.flag(p.moved)
|
|
}
|
|
e.u32(uint32(len(s.moveBuf)))
|
|
for _, id := range s.moveBuf {
|
|
e.i32(id)
|
|
}
|
|
e.u32(uint32(len(s.contacts)))
|
|
for i := range s.contacts {
|
|
c := &s.contacts[i]
|
|
e.flag(c.alive)
|
|
if !c.alive {
|
|
continue
|
|
}
|
|
e.handle(c.shapeA.Index, c.shapeA.Gen)
|
|
e.handle(c.shapeB.Index, c.shapeB.Gen)
|
|
e.i32(c.childA)
|
|
e.i32(c.childB)
|
|
e.u64(c.key)
|
|
e.flag(c.touching)
|
|
e.u32(c.mark)
|
|
e.manifold(&c.m)
|
|
}
|
|
e.u32(uint32(len(s.freeC)))
|
|
for _, f := range s.freeC {
|
|
e.i32(f)
|
|
}
|
|
for _, m := range [...]*pairMap{&s.pairs, &s.touch} {
|
|
e.u32(uint32(len(m.keys)))
|
|
for i, k := range m.keys {
|
|
e.u64(k)
|
|
e.i32(m.vals[i])
|
|
}
|
|
}
|
|
for _, l := range s.layers {
|
|
e.u32(l)
|
|
}
|
|
e.u32(uint32(len(s.awake)))
|
|
for _, i := range s.awake {
|
|
e.i32(i)
|
|
}
|
|
s.raw = e.b
|
|
return s.raw
|
|
}
|
|
|
|
func (s *Snapshot) Equal(o *Snapshot) bool {
|
|
return bytes.Equal(s.Bytes(), o.Bytes())
|
|
}
|
|
|
|
func (s *Snapshot) Hash() [32]byte {
|
|
return sha256.Sum256(s.Bytes())
|
|
}
|