983 lines
21 KiB
Go
983 lines
21 KiB
Go
package world
|
|
|
|
import (
|
|
"aego/core/mathx"
|
|
)
|
|
|
|
type body struct {
|
|
typ BodyType
|
|
xf xform
|
|
angle float32
|
|
localCenter Vec2
|
|
center Vec2
|
|
v Vec2
|
|
w float32
|
|
force Vec2
|
|
torque float32
|
|
mass float32
|
|
invMass float32
|
|
inertia float32
|
|
invI float32
|
|
massMode MassMode
|
|
massOverride float32
|
|
gravityScale float32
|
|
linDamp float32
|
|
angDamp float32
|
|
fixedRot bool
|
|
bullet bool
|
|
allowSleep bool
|
|
awake bool
|
|
listed bool
|
|
sleepTime float32
|
|
layer uint8
|
|
mask uint32
|
|
userData uint64
|
|
shapes []ShapeHandle
|
|
joints []JointHandle
|
|
contacts []int32
|
|
index int32
|
|
solver int32
|
|
island int32
|
|
mark uint32
|
|
dead bool
|
|
}
|
|
|
|
type shape struct {
|
|
body BodyHandle
|
|
g geom
|
|
trigger bool
|
|
friction float32
|
|
restitution float32
|
|
density float32
|
|
oneSided bool
|
|
disabled bool
|
|
userData uint64
|
|
proxies []int32
|
|
dead bool
|
|
}
|
|
|
|
type proxyInfo struct {
|
|
shape ShapeHandle
|
|
child int32
|
|
tight AABB
|
|
alive bool
|
|
moved bool
|
|
}
|
|
|
|
type contact struct {
|
|
shapeA ShapeHandle
|
|
shapeB ShapeHandle
|
|
bodyA BodyHandle
|
|
bodyB BodyHandle
|
|
childA int32
|
|
childB int32
|
|
proxyA int32
|
|
proxyB int32
|
|
key uint64
|
|
m Manifold
|
|
friction float32
|
|
restitution float32
|
|
trigger bool
|
|
touching bool
|
|
alive bool
|
|
mark uint32
|
|
seen uint32
|
|
}
|
|
|
|
type candidate struct {
|
|
a, b int32
|
|
lo, hi uint64
|
|
}
|
|
|
|
type beginRec struct {
|
|
c int32
|
|
rel Vec2
|
|
}
|
|
|
|
type Contact struct {
|
|
ShapeA ShapeHandle
|
|
ShapeB ShapeHandle
|
|
BodyA BodyHandle
|
|
BodyB BodyHandle
|
|
Trigger bool
|
|
Touching bool
|
|
Manifold Manifold
|
|
}
|
|
|
|
type World 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
|
|
events []ContactEvent
|
|
pending []ContactEvent
|
|
began []beginRec
|
|
newPairs []candidate
|
|
dyingB []BodyHandle
|
|
deadB []BodyHandle
|
|
dyingS []ShapeHandle
|
|
deadS []ShapeHandle
|
|
queryProxy int32
|
|
pairFn func(int32) bool
|
|
inStep bool
|
|
mark uint32
|
|
seen uint32
|
|
awake []int32
|
|
cand []int32
|
|
isl []int32
|
|
islC []int32
|
|
islJ []int32
|
|
stack []int32
|
|
sb []solverBody
|
|
cc []contactConstraint
|
|
soft softness
|
|
staticSoft softness
|
|
liveBodies int
|
|
liveMovable int
|
|
liveShapes int
|
|
touching int
|
|
manifolds int
|
|
points int
|
|
stats Stats
|
|
steps uint64
|
|
q queryState
|
|
forces []ForceRecord
|
|
lastForces []ForceRecord
|
|
}
|
|
|
|
func New(cfg Config) (*World, error) {
|
|
cfg = cfg.withDefaults()
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
w := &World{
|
|
cfg: cfg,
|
|
bodies: newPool[body](cfg.InitialBodies),
|
|
shapes: newPool[shape](cfg.InitialShapes),
|
|
joints: newPool[joint](16),
|
|
tree: newTree(cfg.InitialShapes * 2),
|
|
prox: make([]proxyInfo, 0, cfg.InitialShapes*2),
|
|
moveBuf: make([]int32, 0, cfg.InitialShapes),
|
|
pairs: newPairMap(cfg.InitialContacts),
|
|
touch: newPairMap(cfg.InitialContacts),
|
|
contacts: make([]contact, 0, cfg.InitialContacts),
|
|
events: make([]ContactEvent, 0, 256),
|
|
began: make([]beginRec, 0, 256),
|
|
newPairs: make([]candidate, 0, 256),
|
|
awake: make([]int32, 0, cfg.InitialBodies),
|
|
cand: make([]int32, 0, cfg.InitialContacts),
|
|
isl: make([]int32, 0, cfg.InitialBodies),
|
|
islC: make([]int32, 0, cfg.InitialContacts),
|
|
stack: make([]int32, 0, cfg.InitialBodies),
|
|
sb: make([]solverBody, 0, cfg.InitialBodies),
|
|
cc: make([]contactConstraint, 0, cfg.InitialContacts),
|
|
}
|
|
for i := range w.layers {
|
|
w.layers[i] = ^uint32(0)
|
|
}
|
|
w.pairFn = w.pairQuery
|
|
w.q.init(w)
|
|
return w, nil
|
|
}
|
|
|
|
func (w *World) Config() Config {
|
|
return w.cfg
|
|
}
|
|
|
|
func (w *World) SetConfig(cfg Config) error {
|
|
cfg = cfg.withDefaults()
|
|
if err := cfg.Validate(); err != nil {
|
|
return err
|
|
}
|
|
cfg.InitialBodies, cfg.InitialShapes, cfg.InitialContacts = w.cfg.InitialBodies, w.cfg.InitialShapes, w.cfg.InitialContacts
|
|
cfg.DebugVectors = w.cfg.DebugVectors
|
|
w.cfg = cfg
|
|
return nil
|
|
}
|
|
|
|
func (w *World) Gravity() Vec2 {
|
|
return w.cfg.Gravity
|
|
}
|
|
|
|
func (w *World) SetGravity(g Vec2) error {
|
|
if !finite2(g) {
|
|
return bad("gravity is not finite")
|
|
}
|
|
w.cfg.Gravity = g
|
|
return nil
|
|
}
|
|
|
|
func (w *World) margin() float32 {
|
|
return 2 * w.cfg.ContactOffset
|
|
}
|
|
|
|
func (w *World) getBody(h BodyHandle) (*body, error) {
|
|
b, ok := w.bodies.get(h)
|
|
if !ok || b.dead {
|
|
return nil, ErrHandle
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (w *World) getShape(h ShapeHandle) (*shape, error) {
|
|
s, ok := w.shapes.get(h)
|
|
if !ok || s.dead {
|
|
return nil, ErrHandle
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func (w *World) BodyAlive(h BodyHandle) bool {
|
|
_, err := w.getBody(h)
|
|
return err == nil
|
|
}
|
|
|
|
func (w *World) Alive(h ShapeHandle) bool {
|
|
_, err := w.getShape(h)
|
|
return err == nil
|
|
}
|
|
|
|
func validBodyDef(d BodyDef) error {
|
|
switch {
|
|
case d.Type > Dynamic:
|
|
return bad("unknown body type %d", d.Type)
|
|
case !finite2(d.Position) || !finite(d.Angle):
|
|
return bad("body transform is not finite")
|
|
case !finite2(d.LinearVelocity) || !finite(d.AngularVelocity):
|
|
return bad("body velocity is not finite")
|
|
case !finite(d.GravityScale):
|
|
return bad("gravity scale is not finite")
|
|
case !finite(d.LinearDamping) || d.LinearDamping < 0 || !finite(d.AngularDamping) || d.AngularDamping < 0:
|
|
return bad("damping must be non-negative and finite")
|
|
case !finite(d.Mass) || d.Mass < 0:
|
|
return bad("mass must be non-negative and finite")
|
|
case d.MassMode > MassOverride:
|
|
return bad("unknown mass mode %d", d.MassMode)
|
|
case d.MassMode == MassOverride && d.Type == Dynamic && d.Mass <= 0:
|
|
return bad("mass override needs a positive mass")
|
|
case d.Layer >= 32:
|
|
return bad("layer %d is outside 0..31", d.Layer)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validShapeDef(d ShapeDef) error {
|
|
switch {
|
|
case !finite(d.Friction) || d.Friction < 0:
|
|
return bad("friction must be non-negative and finite")
|
|
case !finite(d.Restitution) || d.Restitution < 0:
|
|
return bad("restitution must be non-negative and finite")
|
|
case !finite(d.Density) || d.Density < 0:
|
|
return bad("density must be non-negative and finite")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *World) CreateBody(d BodyDef) (BodyHandle, error) {
|
|
if err := validBodyDef(d); err != nil {
|
|
return BodyHandle{}, err
|
|
}
|
|
h, b := w.bodies.alloc()
|
|
*b = body{
|
|
typ: d.Type,
|
|
xf: xform{p: d.Position, q: makeRot(d.Angle)},
|
|
angle: d.Angle,
|
|
center: d.Position,
|
|
massMode: d.MassMode,
|
|
massOverride: d.Mass,
|
|
gravityScale: d.GravityScale,
|
|
linDamp: d.LinearDamping,
|
|
angDamp: d.AngularDamping,
|
|
fixedRot: d.FixedRotation,
|
|
bullet: d.Bullet,
|
|
allowSleep: d.AllowSleep,
|
|
layer: d.Layer,
|
|
mask: d.Mask,
|
|
userData: d.UserData,
|
|
index: int32(h.Index),
|
|
solver: -1,
|
|
}
|
|
w.liveBodies++
|
|
if d.Type != Static {
|
|
w.liveMovable++
|
|
b.v, b.w = d.LinearVelocity, d.AngularVelocity
|
|
if d.FixedRotation {
|
|
b.w = 0
|
|
}
|
|
w.wake(b)
|
|
}
|
|
w.updateMass(b)
|
|
return h, nil
|
|
}
|
|
|
|
func (w *World) DestroyBody(h BodyHandle) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for len(b.joints) > 0 {
|
|
w.destroyJoint(b.joints[len(b.joints)-1])
|
|
}
|
|
for len(b.shapes) > 0 {
|
|
w.removeShape(b.shapes[len(b.shapes)-1], b)
|
|
}
|
|
w.liveBodies--
|
|
if b.typ != Static {
|
|
w.liveMovable--
|
|
}
|
|
b.dead, b.awake = true, false
|
|
w.dyingB = append(w.dyingB, h)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) AddShape(bh BodyHandle, d ShapeDef) (ShapeHandle, error) {
|
|
b, err := w.getBody(bh)
|
|
if err != nil {
|
|
return ShapeHandle{}, err
|
|
}
|
|
if err := validShapeDef(d); err != nil {
|
|
return ShapeHandle{}, err
|
|
}
|
|
g, err := buildGeom(d.Geometry, d.Local, w.cfg.LinearSlop)
|
|
if err != nil {
|
|
return ShapeHandle{}, err
|
|
}
|
|
sh, s := w.shapes.alloc()
|
|
*s = shape{
|
|
body: bh,
|
|
g: g,
|
|
trigger: d.IsTrigger,
|
|
friction: d.Friction,
|
|
restitution: d.Restitution,
|
|
density: d.Density,
|
|
oneSided: d.OneSided,
|
|
userData: d.UserData,
|
|
}
|
|
w.liveShapes++
|
|
b.shapes = append(b.shapes, sh)
|
|
w.createProxies(sh, s, b)
|
|
w.updateMass(b)
|
|
w.wake(b)
|
|
return sh, nil
|
|
}
|
|
|
|
func (w *World) RemoveShape(h ShapeHandle) error {
|
|
s, err := w.getShape(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b := &w.bodies.items[s.body.Index]
|
|
w.removeShape(h, b)
|
|
w.updateMass(b)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) removeShape(sh ShapeHandle, b *body) {
|
|
s := &w.shapes.items[sh.Index]
|
|
w.dropShapeContacts(sh)
|
|
w.destroyProxies(s)
|
|
s.dead = true
|
|
w.liveShapes--
|
|
for i, x := range b.shapes {
|
|
if x == sh {
|
|
b.shapes = append(b.shapes[:i], b.shapes[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
w.dyingS = append(w.dyingS, sh)
|
|
}
|
|
|
|
func (w *World) SetShape(h ShapeHandle, d ShapeDef) error {
|
|
s, err := w.getShape(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := validShapeDef(d); err != nil {
|
|
return err
|
|
}
|
|
g, err := buildGeom(d.Geometry, d.Local, w.cfg.LinearSlop)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b := &w.bodies.items[s.body.Index]
|
|
w.dropShapeContacts(h)
|
|
w.destroyProxies(s)
|
|
s.g, s.trigger, s.friction, s.restitution = g, d.IsTrigger, d.Friction, d.Restitution
|
|
s.density, s.oneSided, s.userData = d.Density, d.OneSided, d.UserData
|
|
if !s.disabled {
|
|
w.createProxies(h, s, b)
|
|
}
|
|
w.updateMass(b)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) SetShapeEnabled(h ShapeHandle, on bool) error {
|
|
s, err := w.getShape(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if s.disabled == !on {
|
|
return nil
|
|
}
|
|
b := &w.bodies.items[s.body.Index]
|
|
if on {
|
|
s.disabled = false
|
|
w.createProxies(h, s, b)
|
|
} else {
|
|
w.dropShapeContacts(h)
|
|
w.destroyProxies(s)
|
|
s.disabled = true
|
|
}
|
|
w.updateMass(b)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) ShapeEnabled(h ShapeHandle) bool {
|
|
s, err := w.getShape(h)
|
|
return err == nil && !s.disabled
|
|
}
|
|
|
|
// SetBodyProps updates everything except type, pose and velocity.
|
|
func (w *World) SetBodyProps(h BodyHandle, d BodyDef) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.Type = b.typ
|
|
d.Position, d.Angle = b.xf.p, b.angle
|
|
d.LinearVelocity, d.AngularVelocity = b.v, b.w
|
|
if err := validBodyDef(d); err != nil {
|
|
return err
|
|
}
|
|
b.gravityScale, b.linDamp, b.angDamp = d.GravityScale, d.LinearDamping, d.AngularDamping
|
|
b.bullet, b.allowSleep, b.userData = d.Bullet, d.AllowSleep, d.UserData
|
|
massChanged := b.massMode != d.MassMode || b.massOverride != d.Mass || b.fixedRot != d.FixedRotation
|
|
b.massMode, b.massOverride, b.fixedRot = d.MassMode, d.Mass, d.FixedRotation
|
|
if b.fixedRot {
|
|
b.w = 0
|
|
}
|
|
if massChanged {
|
|
w.updateMass(b)
|
|
}
|
|
if b.layer != d.Layer || b.mask != d.Mask {
|
|
b.layer, b.mask = d.Layer, d.Mask
|
|
w.refilter(h, false)
|
|
}
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) SetBodyTransform(h BodyHandle, p Vec2, angle float32) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite2(p) || !finite(angle) {
|
|
return bad("body transform is not finite")
|
|
}
|
|
b.xf = xform{p: p, q: makeRot(angle)}
|
|
b.angle = angle
|
|
b.center = b.xf.apply(b.localCenter)
|
|
w.wake(b)
|
|
w.syncBody(b, 0)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) SetBodyFilter(h BodyHandle, layer uint8, mask uint32) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if layer >= 32 {
|
|
return bad("layer %d is outside 0..31", layer)
|
|
}
|
|
b.layer, b.mask = layer, mask
|
|
w.refilter(h, false)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) SetLayerMatrix(m [32]uint32) error {
|
|
for i := 0; i < 32; i++ {
|
|
for j := 0; j < 32; j++ {
|
|
if (m[i]>>j)&1 != (m[j]>>i)&1 {
|
|
return bad("layer matrix is not symmetric at %d,%d", i, j)
|
|
}
|
|
}
|
|
}
|
|
w.layers = m
|
|
w.refilter(BodyHandle{}, true)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) LayerMatrix() [32]uint32 {
|
|
return w.layers
|
|
}
|
|
|
|
func (w *World) refilter(only BodyHandle, all bool) {
|
|
for i := range w.contacts {
|
|
c := &w.contacts[i]
|
|
if !c.alive || (!all && c.bodyA != only && c.bodyB != only) {
|
|
continue
|
|
}
|
|
if !w.shouldCollide(c.shapeA, c.shapeB) {
|
|
w.destroyContact(int32(i), true)
|
|
}
|
|
}
|
|
for id := range w.prox {
|
|
p := &w.prox[id]
|
|
if !p.alive || p.moved {
|
|
continue
|
|
}
|
|
if !all && w.shapes.items[p.shape.Index].body != only {
|
|
continue
|
|
}
|
|
p.moved = true
|
|
w.moveBuf = append(w.moveBuf, int32(id))
|
|
}
|
|
}
|
|
|
|
func (w *World) wake(b *body) {
|
|
if b.typ == Static || b.dead {
|
|
return
|
|
}
|
|
b.awake = true
|
|
b.sleepTime = 0
|
|
w.listAwake(b)
|
|
}
|
|
|
|
func (w *World) listAwake(b *body) {
|
|
if !b.listed {
|
|
b.listed = true
|
|
w.awake = append(w.awake, b.index)
|
|
}
|
|
}
|
|
|
|
func (w *World) ForEachBody(fn func(BodyHandle) bool) {
|
|
for i := range w.bodies.items {
|
|
if w.bodies.alive[i] && !w.bodies.items[i].dead && !fn(w.bodies.handleAt(i)) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *World) wakeHandle(h BodyHandle) {
|
|
if int(h.Index) < len(w.bodies.items) {
|
|
w.wake(&w.bodies.items[h.Index])
|
|
}
|
|
}
|
|
|
|
func (w *World) updateMass(b *body) {
|
|
old := b.center
|
|
b.mass, b.invMass, b.inertia, b.invI = 0, 0, 0, 0
|
|
b.localCenter = Vec2{}
|
|
if b.typ != Dynamic {
|
|
b.center = b.xf.p
|
|
return
|
|
}
|
|
var mass, rot float32
|
|
var c Vec2
|
|
for _, sh := range b.shapes {
|
|
s := &w.shapes.items[sh.Index]
|
|
if s.density == 0 || s.disabled {
|
|
continue
|
|
}
|
|
md := s.g.mass(s.density)
|
|
mass += md.mass
|
|
c = c.Add(md.center.Scale(md.mass))
|
|
rot += md.inertia + md.mass*md.center.Dot(md.center)
|
|
}
|
|
if mass > 0 {
|
|
c = c.Scale(1 / mass)
|
|
}
|
|
if b.massMode == MassOverride && b.massOverride > 0 {
|
|
if mass > 0 {
|
|
rot *= b.massOverride / mass
|
|
}
|
|
mass = b.massOverride
|
|
}
|
|
if mass <= 0 {
|
|
mass, rot = 1, 0
|
|
}
|
|
b.mass, b.invMass = mass, 1/mass
|
|
if !b.fixedRot && rot > 0 {
|
|
if in := rot - mass*c.Dot(c); in > 0 {
|
|
b.inertia, b.invI = in, 1/in
|
|
}
|
|
}
|
|
b.localCenter = c
|
|
b.center = b.xf.apply(c)
|
|
b.v = b.v.Add(crossSV(b.w, b.center.Sub(old)))
|
|
}
|
|
|
|
type ForceRecord struct {
|
|
Body BodyHandle
|
|
Point Vec2
|
|
Vector Vec2
|
|
Impulse bool
|
|
}
|
|
|
|
func (w *World) record(h BodyHandle, p, v Vec2, impulse bool) {
|
|
if w.cfg.DebugVectors {
|
|
w.forces = append(w.forces, ForceRecord{Body: h, Point: p, Vector: v, Impulse: impulse})
|
|
}
|
|
}
|
|
|
|
func (w *World) SetDebugVectors(on bool) {
|
|
w.cfg.DebugVectors = on
|
|
if !on {
|
|
w.forces, w.lastForces = w.forces[:0], w.lastForces[:0]
|
|
}
|
|
}
|
|
|
|
// Forces returns what was applied before the last Step, valid until the next.
|
|
func (w *World) Forces() []ForceRecord {
|
|
return w.lastForces
|
|
}
|
|
|
|
func (w *World) dynamicBody(h BodyHandle) (*body, error) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b.typ != Dynamic {
|
|
return nil, bad("body is %s, forces apply only to dynamic bodies", b.typ)
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (w *World) ApplyForce(h BodyHandle, f Vec2) error {
|
|
b, err := w.dynamicBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite2(f) {
|
|
return bad("force is not finite")
|
|
}
|
|
b.force = b.force.Add(f)
|
|
w.record(h, b.center, f, false)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) ApplyForceAtPoint(h BodyHandle, f, p Vec2) error {
|
|
b, err := w.dynamicBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite2(f) || !finite2(p) {
|
|
return bad("force or point is not finite")
|
|
}
|
|
b.force = b.force.Add(f)
|
|
b.torque += p.Sub(b.center).Cross(f)
|
|
w.record(h, p, f, false)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) ApplyImpulse(h BodyHandle, j Vec2) error {
|
|
b, err := w.dynamicBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite2(j) {
|
|
return bad("impulse is not finite")
|
|
}
|
|
b.v = b.v.Add(j.Scale(b.invMass))
|
|
w.record(h, b.center, j, true)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) ApplyImpulseAtPoint(h BodyHandle, j, p Vec2) error {
|
|
b, err := w.dynamicBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite2(j) || !finite2(p) {
|
|
return bad("impulse or point is not finite")
|
|
}
|
|
b.v = b.v.Add(j.Scale(b.invMass))
|
|
b.w += b.invI * p.Sub(b.center).Cross(j)
|
|
w.record(h, p, j, true)
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) ApplyTorque(h BodyHandle, t float32) error {
|
|
b, err := w.dynamicBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite(t) {
|
|
return bad("torque is not finite")
|
|
}
|
|
b.torque += t
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) SetLinearVelocity(h BodyHandle, v Vec2) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite2(v) {
|
|
return bad("velocity is not finite")
|
|
}
|
|
if b.typ == Static {
|
|
return bad("static bodies have no velocity")
|
|
}
|
|
b.v = v
|
|
if v.LenSq() > 0 {
|
|
w.wake(b)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *World) SetAngularVelocity(h BodyHandle, av float32) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !finite(av) {
|
|
return bad("angular velocity is not finite")
|
|
}
|
|
if b.typ == Static {
|
|
return bad("static bodies have no velocity")
|
|
}
|
|
if b.fixedRot {
|
|
av = 0
|
|
}
|
|
b.w = av
|
|
if av != 0 {
|
|
w.wake(b)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *World) MoveKinematic(h BodyHandle, target Transform, dt float32) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if b.typ != Kinematic {
|
|
return bad("MoveKinematic needs a kinematic body, got %s", b.typ)
|
|
}
|
|
if !finite2(target.Position) || !finite(target.Angle) || !finite(dt) || dt <= 0 {
|
|
return bad("kinematic target or step is not valid")
|
|
}
|
|
b.v = target.Position.Sub(b.xf.p).Scale(1 / dt)
|
|
b.w = (target.Angle - b.angle) / dt
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) WakeUp(h BodyHandle) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.wake(b)
|
|
return nil
|
|
}
|
|
|
|
func (w *World) Sleep(h BodyHandle) error {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if b.typ == Static {
|
|
return nil
|
|
}
|
|
b.awake, b.sleepTime = false, 0
|
|
b.v, b.w, b.force, b.torque = Vec2{}, 0, Vec2{}, 0
|
|
return nil
|
|
}
|
|
|
|
func (w *World) BodyTransform(h BodyHandle) (Vec2, float32, bool) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return Vec2{}, 0, false
|
|
}
|
|
return b.xf.p, b.angle, true
|
|
}
|
|
|
|
func (w *World) BodyCenter(h BodyHandle) (Vec2, bool) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return Vec2{}, false
|
|
}
|
|
return b.center, true
|
|
}
|
|
|
|
func (w *World) BodyVelocity(h BodyHandle) (Vec2, float32, bool) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return Vec2{}, 0, false
|
|
}
|
|
return b.v, b.w, true
|
|
}
|
|
|
|
func (w *World) BodyAwake(h BodyHandle) bool {
|
|
b, err := w.getBody(h)
|
|
return err == nil && b.awake
|
|
}
|
|
|
|
func (w *World) BodyType(h BodyHandle) (BodyType, bool) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
return b.typ, true
|
|
}
|
|
|
|
func (w *World) BodyMass(h BodyHandle) (mass, inertia float32, localCenter Vec2, ok bool) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return 0, 0, Vec2{}, false
|
|
}
|
|
return b.mass, b.inertia, b.localCenter, true
|
|
}
|
|
|
|
func (w *World) BodyUserData(h BodyHandle) (uint64, bool) {
|
|
b, ok := w.bodies.get(h)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
return b.userData, true
|
|
}
|
|
|
|
func (w *World) BodyShapes(h BodyHandle) []ShapeHandle {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return b.shapes
|
|
}
|
|
|
|
func (w *World) ShapeBody(h ShapeHandle) (BodyHandle, bool) {
|
|
s, ok := w.shapes.get(h)
|
|
if !ok {
|
|
return BodyHandle{}, false
|
|
}
|
|
return s.body, true
|
|
}
|
|
|
|
func (w *World) ShapeUserData(h ShapeHandle) (uint64, bool) {
|
|
s, ok := w.shapes.get(h)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
return s.userData, true
|
|
}
|
|
|
|
func (w *World) ShapeKind(h ShapeHandle) (GeometryKind, bool) {
|
|
s, err := w.getShape(h)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
return s.g.kind, true
|
|
}
|
|
|
|
func (w *World) ShapeTrigger(h ShapeHandle) bool {
|
|
s, err := w.getShape(h)
|
|
return err == nil && s.trigger
|
|
}
|
|
|
|
func (w *World) ShapeAABB(h ShapeHandle) (AABB, bool) {
|
|
s, err := w.getShape(h)
|
|
if err != nil || len(s.proxies) == 0 {
|
|
return AABB{}, false
|
|
}
|
|
box := w.prox[s.proxies[0]].tight
|
|
for _, id := range s.proxies[1:] {
|
|
box = boxUnion(box, w.prox[id].tight)
|
|
}
|
|
return box, true
|
|
}
|
|
|
|
func (w *World) Contacts(h BodyHandle, fn func(Contact) bool) {
|
|
b, err := w.getBody(h)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, ci := range b.contacts {
|
|
c := &w.contacts[ci]
|
|
if !c.alive || !c.touching {
|
|
continue
|
|
}
|
|
if !fn(Contact{ShapeA: c.shapeA, ShapeB: c.shapeB, BodyA: c.bodyA, BodyB: c.bodyB, Trigger: c.trigger, Touching: true, Manifold: c.m}) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *World) Events() Events {
|
|
return Events{Contacts: w.events}
|
|
}
|
|
|
|
func (w *World) Stats() Stats {
|
|
return w.stats
|
|
}
|
|
|
|
func sqrtf(v float32) float32 {
|
|
return mathx.Sqrt(v)
|
|
}
|
|
|
|
func (w *World) ContactBetween(a, b ShapeHandle) (ContactEvent, bool) {
|
|
s, err := w.getShape(a)
|
|
if err != nil {
|
|
return ContactEvent{}, false
|
|
}
|
|
bd := &w.bodies.items[s.body.Index]
|
|
for _, ci := range bd.contacts {
|
|
c := &w.contacts[ci]
|
|
if !c.alive || !c.touching {
|
|
continue
|
|
}
|
|
switch {
|
|
case c.shapeA == a && c.shapeB == b:
|
|
return w.event(c, &c.m, BeginContact), true
|
|
case c.shapeA == b && c.shapeB == a:
|
|
ev := w.event(c, &c.m, BeginContact)
|
|
ev.ShapeA, ev.ShapeB = ev.ShapeB, ev.ShapeA
|
|
ev.BodyA, ev.BodyB = ev.BodyB, ev.BodyA
|
|
ev.Normal, ev.RelativeVelocity = ev.Normal.Neg(), ev.RelativeVelocity.Neg()
|
|
return ev, true
|
|
}
|
|
}
|
|
return ContactEvent{}, false
|
|
}
|
|
|
|
func (w *World) DebugTree(fn func(box AABB, height int32, leaf bool)) {
|
|
for i := range w.tree.nodes {
|
|
n := &w.tree.nodes[i]
|
|
if n.height < 0 {
|
|
continue
|
|
}
|
|
fn(n.box, n.height, n.leaf())
|
|
}
|
|
}
|
|
|
|
func CheckShape(d ShapeDef, slop float32) error {
|
|
if slop <= 0 {
|
|
slop = DefaultConfig().LinearSlop
|
|
}
|
|
if err := validShapeDef(d); err != nil {
|
|
return err
|
|
}
|
|
_, err := buildGeom(d.Geometry, d.Local, slop)
|
|
return err
|
|
}
|