func TestCollideBoxBox(t *testing.T) { a, b, cons := NewBody(NewBox(0.5, 0.5, 0.5)), NewBody(NewBox(1, 1, 1)), newManifold() if _, _, cs := collideBoxBox(a, b, cons); len(cs) == 0 || cs[0].depth != -1.58 || dumpV3(cs[0].point) != "{-1.0 0.5 0.5}" || dumpV3(cs[0].normal) != "{-1.0 -0.0 -0.0}" { depth, point, norm := cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal) t.Errorf("Boxes should collide since one is inside the other %f %s %s", depth, point, norm) } // just inside of contact range. a.World().Loc.SetS(0, 0, 1.49) if _, _, cs := collideBoxBox(a, b, cons); len(cs) != 4 || !lin.Aeq(cs[0].depth, -0.09) || dumpV3(cs[0].point) != "{0.5 0.5 1.0}" || dumpV3(cs[0].normal) != "{0.0 0.0 1.0}" { depth, point, norm := cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal) t.Errorf("Boxes should collide %f %s %s", depth, point, norm) } a.World().Loc.SetS(0, 1.49, 0) if _, _, cs := collideBoxBox(a, b, cons); len(cs) != 4 || !lin.Aeq(cs[0].depth, -0.09) || dumpV3(cs[0].point) != "{0.5 1.0 0.5}" || dumpV3(cs[0].normal) != "{0.0 1.0 0.0}" { depth, point, norm := cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal) t.Errorf("Boxes should collide %f %s %s", depth, point, norm) } a.World().Loc.SetS(1.49, 0, 0) if _, _, cs := collideBoxBox(a, b, cons); len(cs) != 4 || !lin.Aeq(cs[0].depth, -0.09) || dumpV3(cs[0].point) != "{1.0 0.5 0.5}" || dumpV3(cs[0].normal) != "{1.0 0.0 0.0}" { depth, point, norm := cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal) t.Errorf("Boxes should collide %f %s %s", depth, point, norm) } // just outside of contact range. a.World().Loc.SetS(0, 0, 1.6) if _, _, cs := collideBoxBox(a, b, cons); len(cs) != 0 { t.Errorf("Boxes should not collide") } }
func TestRoundTrip(t *testing.T) { cam, _, _ := initScene() cx, cy, cz := 0.0, 0.0, 14.0 // camera location to cam.SetLocation(cx, cy, cz) // ...point directly at 0, 0, 0 // Create the matricies to go between clip and world space. toClip := lin.NewM4().Mult(cam.vm, cam.pm) toWorld := lin.NewM4().Mult(cam.ipm, cam.ivm) if !lin.NewM4().Mult(toClip, toWorld).Aeq(lin.M4I) { t.Errorf("Invalid world<->clip matricies") } // start with world coordinates carefully chosen to give x=1, y=1 clip values px, py := 6.002062, 3.751289 pnt := lin.NewV4().SetS(px, py, 0, 1) pnt.MultMv(toClip, pnt) if !lin.Aeq(pnt.X/pnt.W, 1) || !lin.Aeq(pnt.Y/pnt.W, 1) { t.Errorf("%f %f gave clip %f %f %f, expected (1 1 -0.071429)", px, py, pnt.X, pnt.Y, pnt.Z) } // now reverse back to world coordinates. pnt.MultMv(toWorld, pnt) if !lin.Aeq(pnt.X, px) || !lin.Aeq(pnt.Y, py) { t.Errorf("got point %f %f %f, expected x=%f y=%f", pnt.X, pnt.Y, pnt.Z, px, py) } }
func TestCastRaySphere(t *testing.T) { r := newBody(NewRay(0.70710678, 0.70710678, 0.70710678)) // 45 degrees from each axis. s := newBody(NewSphere(1)) // sphere of radius 1 s.World().Loc.SetS(20, 20, 20) hit, x, y, z := castRaySphere(r, s) cx, cy, cz := 19.4226497, 19.4226497, 19.4226497 // expected contact location. if !hit || !lin.Aeq(x, cx) || !lin.Aeq(y, cy) || !lin.Aeq(z, cz) { t.Errorf("%t Expected ray-plane hit at %2.7f %2.7f %2.7f, got %2.7f %2.7f %2.7f", hit, cx, cy, cz, x, y, z) } }
func TestCastRotatedRayPlane(t *testing.T) { r := newBody(NewRay(0, 0.70710678, -0.70710678)) // ray at origin pointing down +Y -Z r.World().Loc.SetS(0, 0, 20) // move ray origin +20 on Z axis. p := newBody(NewPlane(0, 0, -1)) // plane at origin with normal -Z hit, x, y, z := castRayPlane(r, p) cx, cy, cz := 0.0, 20.0, 0.0 // expected contact location. if !hit || !lin.Aeq(x, cx) || !lin.Aeq(y, cy) || !lin.Aeq(z, cz) { t.Errorf("%t Expected ray-plane hit at %f %f %f, got %f %f %f", hit, cx, cy, cz, x, y, z) } }
func TestCastRayPlane(t *testing.T) { r := newBody(NewRay(0, 0.70710678, 0.70710678)) // ray at origin pointing down +Y +Z p := newBody(NewPlane(0, 0, 1)) // normal +Z p.World().Loc.SetS(0, 0, 20) // move plane 20 +Z hit, x, y, z := castRayPlane(r, p) cx, cy, cz := 0.0, 20.0, 20.0 // expected contact location. if !hit || !lin.Aeq(x, cx) || !lin.Aeq(y, cy) || !lin.Aeq(z, cz) { t.Errorf("%t Expected ray-plane hit at %f %f %f, got %f %f %f", hit, cx, cy, cz, x, y, z) } }
// Test a ray cast with perspective inverse and angled view inverse. func TestAngledRay(t *testing.T) { cam, ww, wh := initScene() cam.AdjustPitch(45) cam.SetLocation(0, -15, 15) rx, ry, rz := cam.Ray(ww/2, wh/2, ww, wh) // center of screen. ex, ey, ez := 0.0, 0.7071068, -0.7071068 if !lin.Aeq(rx, ex) || !lin.Aeq(ry, ey) || !lin.Aeq(rz, ez) { t.Errorf("Expected %f %f %f got %f %f %f", ex, ey, ez, rx, ry, rz) } }
func TestCastRotatedRaySphere(t *testing.T) { r := newBody(NewRay(0, 0.70710678, -0.70710678)) // ray at origin pointing down +Y -Z r.World().Loc.SetS(0, 0, 20) // move ray origin +20 on Z axis. s := newBody(NewSphere(1)) // sphere of radius 1. s.World().Loc.SetS(0, 20, 0) // put sphere up the y-axis. hit, x, y, z := castRaySphere(r, s) cx, cy, cz := 0.0, 19.2928932, 0.7071068 // expected contact location. if !hit || !lin.Aeq(x, cx) || !lin.Aeq(y, cy) || !lin.Aeq(z, cz) { t.Errorf("%t Expected ray-plane hit at %2.7f %2.7f %2.7f, got %2.7f %2.7f %2.7f", hit, cx, cy, cz, x, y, z) } }
func (c *cam) update(camera vu.Camera) { fraction := 0.25 pitch := camera.Pitch() if !lin.Aeq(pitch, c.pitch) { pitch = (c.pitch-pitch)*fraction + pitch camera.SetPitch(pitch) } yaw := camera.Yaw() if !lin.Aeq(yaw, c.yaw) { yaw = (c.yaw-yaw)*fraction + yaw camera.SetYaw(yaw) } }
// TestChildWorldTransform checks that a child object can calculate its // world space location. func TestChildWorldTransform(t *testing.T) { eng := newEngine(nil) parent := eng.Root().NewPov().SetLocation(0, -8, 0).SetScale(4, 4, 4) parent.Spin(-90, 0, 0) child := parent.NewPov().SetLocation(0, 0.78, 0.01).SetScale(0.1, 0.1, 0.1) // call placeModels to initialize the model transform matrix needed by World. eng.placeModels(eng.root(), lin.M4I) // update all transforms. if x, y, z := child.World(); !lin.Aeq(x, 0) || !lin.Aeq(y, -7.96) || !lin.Aeq(z, -3.12) { t.Errorf("Expecting %f %f %f, got %f, %f %f", 0.0, -7.96, -3.12, x, y, z) } if eng != nil { eng.Shutdown() } }
func TestSphereProperties(t *testing.T) { b := newBody(NewSphere(1)).SetMaterial(0.5, 0.8).(*body) if b.movable != true || !lin.Aeq(b.imass, 2) { t.Errorf("Expecting movable body with mass %f", b.imass) } if dumpV3(b.iit) != "{5.0 5.0 5.0}" { t.Errorf("Expecting initial inverse inertia %s", dumpV3(b.iit)) } }
func (d draws) Less(i, j int) bool { di, dj := d[i].(*draw), d[j].(*draw) if di.bucket != dj.bucket { return di.bucket < dj.bucket // First sort into buckets. } if di.bucket == TRANSPARENT { if !lin.Aeq(di.tocam, dj.tocam) { return di.tocam > dj.tocam // Sort transparent by distance to camera. } } return di.tag < dj.tag // Sort by eid. }
// Tests that the narrowphase collision lookup finds the algorithm that flips // the box-sphere to be sphere-box. func TestCollideBoxSphere(t *testing.T) { box, sphere, c, cons := newBody(NewBox(1, 1, 1)), newBody(NewSphere(1)), newCollider(), newManifold() sphere.World().Loc.SetS(0, 2, 0) algorithm := c.algorithms[box.shape.Type()][sphere.shape.Type()] i, j, cs := algorithm(box, sphere, cons) ii, jj := i.(*body), j.(*body) if ii.shape.Type() != SphereShape || jj.shape.Type() != BoxShape { t.Error("Should have flipped the objects into Sphere, Box") } if !lin.Aeq(cs[0].depth, -margin) || dumpV3(cs[0].point) != "{0.0 1.0 0.0}" || dumpV3(cs[0].normal) != "{0.0 1.0 0.0}" { t.Errorf("Contact info should be the same %f %s %s", cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal)) } }
func TestCollideBoxBox1(t *testing.T) { slab := newBody(NewBox(50, 50, 50)).setMaterial(0, 0) slab.World().Loc.SetS(0, -50, 0) box := newBody(NewBox(1, 1, 1)).setMaterial(1, 0) box.World().Loc.SetS(-5.000000, 1.388000, -3.000000) box.World().Rot.SetS(0.182574, 0.365148, 0.547723, 0.730297) wantPoint, wantDepth := lin.NewV3S(-5.2, -0.1, -4.0), -0.108 _, _, cs := collideBoxBox(slab, box, newManifold()) if !lin.Aeq(cs[0].depth, wantDepth) || dumpV3(cs[0].point) != dumpV3(wantPoint) { t.Errorf("Got point %s wanted %s. Got depth %f wanted %f", dumpV3(cs[0].point), dumpV3(wantPoint), cs[0].depth, wantDepth) } }
func TestCollideSphereBox(t *testing.T) { a, b, cons := NewBody(NewSphere(1)), NewBody(NewBox(1, 1, 1)), newManifold() if _, _, cs := collideSphereBox(a, b, cons); cs[0].depth != -2.04 || dumpV3(cs[0].point) != "{1.0 0.0 0.0}" || dumpV3(cs[0].normal) != "{1.0 0.0 0.0}" { t.Errorf("Sphere touching box at point A %f %s %s", cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal)) } a.World().Loc.SetS(0, 2, 0) if _, _, cs := collideSphereBox(a, b, cons); !lin.Aeq(cs[0].depth, -margin) || dumpV3(cs[0].point) != "{0.0 1.0 0.0}" || dumpV3(cs[0].normal) != "{0.0 1.0 0.0}" { t.Errorf("Sphere touching box at point %f %s %s", cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal)) } a.World().Loc.SetS(0, 0, 2.15) if _, _, cs := collideSphereBox(a, b, cons); len(cs) != 0 { t.Errorf("Sphere not touching box %f %s %s", cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal)) } // close enough to be considered in contact. a.World().Loc.SetS(0, 0, 2.1) if _, _, cs := collideSphereBox(a, b, cons); !lin.Aeq(cs[0].depth, 0.06) || dumpV3(cs[0].point) != "{0.0 0.0 1.0}" || dumpV3(cs[0].normal) != "{0.0 0.0 1.0}" { t.Errorf("Sphere close to touching box %f %s %s", cs[0].depth, dumpV3(cs[0].point), dumpV3(cs[0].normal)) } }
func TestSphereVolume(t *testing.T) { sp := Shape(NewSphere(1.25)) if !lin.Aeq(sp.Volume(), 6.13592315) { t.Errorf("Expected sphere mass 6.13592315, got %2.8f", sp.Volume()) } }
func TestRayWithSpin(t *testing.T) { cam, _, _ := initScene() cx, cy, cz := 0.0, -10.0, 14.0 // camera location to cam.SetLocation(cx, cy, cz) // ...point directly at 0, 0, 0 cam.SetPitch(lin.Deg(math.Atan(-cy / cz))) // 35.53768 degrees plane := NewPlane(0, 0, -1) ww, wh := 1280, 800 rx, ry, rz := cam.Ray(0, 0, ww, wh) ray := NewRay(rx, ry, rz) ray.World().SetLoc(cx, cy, cz) hit, hx, hy, hz := Cast(ray, plane) ex, ey, ez := -6.191039, -4.755119, 0.0 if !hit || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) { t.Errorf("Hit %t %f %f %f, expected %f %f %f", hit, hx, hy, hz, ex, ey, ez) } rx, ry, rz = cam.Ray(0, wh, ww, wh) ray = NewRay(rx, ry, rz) ray.World().SetLoc(cx, cy, cz) hit, hx, hy, hz = Cast(ray, plane) ex, ey, ez = -9.121797, 7.006131, 0.0 if !hit || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) { t.Errorf("Hit %t %f %f %f, expected %f %f %f", hit, hx, hy, hz, ex, ey, ez) } rx, ry, rz = cam.Ray(ww, 0, ww, wh) ray = NewRay(rx, ry, rz) ray.World().SetLoc(cx, cy, cz) hit, hx, hy, hz = Cast(ray, plane) ex, ey, ez = 6.191039, -4.755119, 0.0 if !hit || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) { t.Errorf("Hit %t %f %f %f, expected %f %f %f", hit, hx, hy, hz, ex, ey, ez) } rx, ry, rz = cam.Ray(ww, wh, ww, wh) ray = NewRay(rx, ry, rz) ray.World().SetLoc(cx, cy, cz) hit, hx, hy, hz = Cast(ray, plane) ex, ey, ez = 9.121797, 7.006131, 0.0 if !hit || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) || !lin.Aeq(hx, ex) { t.Errorf("Hit %t %f %f %f, expected %f %f %f", hit, hx, hy, hz, ex, ey, ez) } }