Skip to content

Commit 6ab723e

Browse files
committed
use aframe-physics-system constraint api for physics grabs (fix #112). Honor usephysics:only in grab (fix #106)
1 parent 28ef310 commit 6ab723e

File tree

8 files changed

+121
-25
lines changed

8 files changed

+121
-25
lines changed

dist/super-hands.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,17 +1284,23 @@ module.exports = {
12841284
physicsStart: function (evt) {
12851285
// initiate physics constraint if available and not already existing
12861286
if (this.data.usePhysics !== 'never' && this.el.body && evt.detail.hand.body && !this.constraints.has(evt.detail.hand)) {
1287-
let newCon = new window.CANNON.LockConstraint(this.el.body, evt.detail.hand.body);
1288-
this.el.body.world.addConstraint(newCon);
1289-
this.constraints.set(evt.detail.hand, newCon);
1287+
const newConId = Math.random().toString(36).substr(2, 9);
1288+
this.el.setAttribute('constraint__' + newConId, {
1289+
target: evt.detail.hand
1290+
});
1291+
this.constraints.set(evt.detail.hand, newConId);
1292+
return true;
1293+
}
1294+
// Prevent manual grab by returning true
1295+
if (this.data.usePhysics === 'only') {
12901296
return true;
12911297
}
12921298
return false;
12931299
},
12941300
physicsEnd: function (evt) {
1295-
let constraint = this.constraints.get(evt.detail.hand);
1296-
if (constraint) {
1297-
this.el.body.world.removeConstraint(constraint);
1301+
let constraintId = this.constraints.get(evt.detail.hand);
1302+
if (constraintId) {
1303+
this.el.removeAttribute('constraint__' + constraintId);
12981304
this.constraints.delete(evt.detail.hand);
12991305
}
13001306
},

dist/super-hands.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/build.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,17 +2735,23 @@ module.exports = {
27352735
physicsStart: function (evt) {
27362736
// initiate physics constraint if available and not already existing
27372737
if (this.data.usePhysics !== 'never' && this.el.body && evt.detail.hand.body && !this.constraints.has(evt.detail.hand)) {
2738-
let newCon = new window.CANNON.LockConstraint(this.el.body, evt.detail.hand.body);
2739-
this.el.body.world.addConstraint(newCon);
2740-
this.constraints.set(evt.detail.hand, newCon);
2738+
const newConId = Math.random().toString(36).substr(2, 9);
2739+
this.el.setAttribute('constraint__' + newConId, {
2740+
target: evt.detail.hand
2741+
});
2742+
this.constraints.set(evt.detail.hand, newConId);
2743+
return true;
2744+
}
2745+
// Prevent manual grab by returning true
2746+
if (this.data.usePhysics === 'only') {
27412747
return true;
27422748
}
27432749
return false;
27442750
},
27452751
physicsEnd: function (evt) {
2746-
let constraint = this.constraints.get(evt.detail.hand);
2747-
if (constraint) {
2748-
this.el.body.world.removeConstraint(constraint);
2752+
let constraintId = this.constraints.get(evt.detail.hand);
2753+
if (constraintId) {
2754+
this.el.removeAttribute('constraint__' + constraintId);
27492755
this.constraints.delete(evt.detail.hand);
27502756
}
27512757
},
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<html>
2+
<head>
3+
<script src="build.js"></script>
4+
</head>
5+
<body>
6+
<a-scene avatar-recorder="spectatorPlay: true; spectatorPosition: 0 1.6 2"
7+
physics="driver: worker; gravity: 0">
8+
<a-assets>
9+
<!-- <img id="grid" src="grid.png"/> -->
10+
11+
<a-mixin id="controller" super-hands
12+
static-body="shape: sphere; sphereRadius: 0.02;"
13+
sphere-collider="objects: .cube"></a-mixin>
14+
<a-mixin id="cube" geometry="primitive: box; width: 0.5; height: 0.5; depth: 0.5"
15+
hoverable grabbable="usePhysics: only" stretchable draggable droppable
16+
dynamic-body
17+
event-set="_event: drag-drop; geometry.primitive: sphere; geometry.radius: 0.25"></a-mixin>
18+
<a-mixin id="cube-hovered" material="opacity: 0.7; transparent: true">
19+
</a-mixin>
20+
<a-mixin id="cube-dragover" material="wireframe: true;"></a-mixin>
21+
</a-assets>
22+
<a-entity id="lhand" hand-controls="left" mixin="controller"></a-entity>
23+
<a-entity id="rhand" hand-controls="right" mixin="controller"></a-entity>
24+
<a-entity id="redLow" class="cube" mixin="cube" position="0 1 -1" material="color: red"></a-entity>
25+
<a-entity id="redHigh" class="cube" mixin="cube" position="0 1.6 -1" material="color: red"></a-entity>
26+
<a-entity id="blueLow" class="cube" mixin="cube" position="-1 1 -1" material="color: blue"></a-entity>
27+
<a-entity id="blueHigh" class="cube" mixin="cube" position="-1 1.6 -1" material="color: blue"></a-entity>
28+
<a-entity id="greenLow" class="cube" mixin="cube" position="1 1 -1" material="color: green"></a-entity>
29+
<a-entity id="greenHigh" class="cube" mixin="cube" position="1 1.6 -1" material="color: green"></a-entity>
30+
<a-plane rotation="-90 0 0" color="#888"
31+
width="50" height="50"></a-plane>
32+
</a-scene>
33+
</body>
34+
</html>

machinima_tests/super_hands/super-hands-machinima.test.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global assert, process, setup, suite, test, sinon */
1+
/* global assert, process, setup, suite, test, sinon, CANNON */
22

33
const machinima = require('aframe-machinima-testing')
44

@@ -175,6 +175,31 @@ suite('Physics grab', function () {
175175
)
176176
})
177177

178+
suite('Physics worker driver', function () {
179+
setup(function (done) {
180+
/* inject the scene html into the testing docoument */
181+
machinima.setupScene('physics-worker.html')
182+
this.scene = document.querySelector('a-scene')
183+
this.hand1 = document.getElementById('rhand')
184+
this.hand2 = document.getElementById('lhand')
185+
this.target = document.getElementById('greenHigh')
186+
this.scene.addEventListener('loaded', e => {
187+
done()
188+
})
189+
})
190+
machinima.test(
191+
'entity affected by grab',
192+
'base/recordings/handsRecording.json',
193+
function () {
194+
const post = new CANNON.Vec3().copy(this.target.getAttribute('position'))
195+
assert.isFalse(post.almostEquals(this.pre, 0.01))
196+
},
197+
function () {
198+
this.pre = new CANNON.Vec3().copy(this.target.getAttribute('position'))
199+
}
200+
)
201+
})
202+
178203
suite('Locomotion', function () {
179204
setup(function (done) {
180205
/* inject the scene html into the testing docoument */

reaction_components/prototypes/physics-grab-proto.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@ module.exports = {
1818
// initiate physics constraint if available and not already existing
1919
if (this.data.usePhysics !== 'never' && this.el.body &&
2020
evt.detail.hand.body && !this.constraints.has(evt.detail.hand)) {
21-
let newCon = new window.CANNON.LockConstraint(
22-
this.el.body, evt.detail.hand.body
23-
)
24-
this.el.body.world.addConstraint(newCon)
25-
this.constraints.set(evt.detail.hand, newCon)
21+
const newConId = Math.random().toString(36).substr(2, 9)
22+
this.el.setAttribute('constraint__' + newConId, {
23+
target: evt.detail.hand
24+
})
25+
this.constraints.set(evt.detail.hand, newConId)
2626
return true
2727
}
28+
// Prevent manual grab by returning true
29+
if (this.data.usePhysics === 'only') { return true }
2830
return false
2931
},
3032
physicsEnd: function (evt) {
31-
let constraint = this.constraints.get(evt.detail.hand)
32-
if (constraint) {
33-
this.el.body.world.removeConstraint(constraint)
33+
let constraintId = this.constraints.get(evt.detail.hand)
34+
if (constraintId) {
35+
this.el.removeAttribute('constraint__' + constraintId)
3436
this.constraints.delete(evt.detail.hand)
3537
}
3638
},

tests/reaction-components/grabbable.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ suite('grabbable', function () {
126126
})
127127
test('constraint registered on grab', function () {
128128
this.comp.start({ detail: { hand: this.hand } })
129-
let c = this.comp.constraints.get(this.hand)
129+
const cId = this.comp.constraints.get(this.hand)
130+
let c = this.el.components['constraint__' + cId]
130131
assert.isOk(c)
131-
assert.instanceOf(c, window.CANNON.LockConstraint)
132-
assert.notEqual(this.el.body.world.constraints.indexOf(c), -1)
132+
assert.strictEqual(c.data.target, this.hand)
133133
})
134134
test('constraint not registered when usePhysics = never', function () {
135135
this.el.setAttribute('grabbable', 'usePhysics', 'never')

tests/super-hands-component/integration.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,27 @@ suite('super-hands & physics integration', function () {
434434
assert.isTrue(this.target1.body.shapes[1].halfExtents.almostEquals(check, 0.001), 'scale')
435435
assert.isTrue(this.target1.body.shapeOffsets[1].almostEquals(check, 0.001), 'offset')
436436
})
437+
test('net physics stretch correct when throttled', function () {
438+
const stretch = this.target1.components.stretchable
439+
const size2 = new window.CANNON.Vec3(1, 1, 1)
440+
const offset2 = new window.CANNON.Vec3(1, 1, 1)
441+
const shape2 = new window.CANNON.Box(size2)
442+
this.target1.body.addShape(shape2, offset2)
443+
this.hand2.setAttribute('position', '1 1 1')
444+
stretch.start({detail: {hand: this.hand1}})
445+
stretch.start({detail: {hand: this.hand2}})
446+
stretch.tick(1, 1)
447+
this.hand2.setAttribute('position', '1.5 1 1')
448+
stretch.tick(6, 5)
449+
const check = new window.CANNON.Vec3(0.5, 0.5, 0.5)
450+
assert.isTrue(this.target1.body.shapes[0].halfExtents.almostEquals(check, 0.001), 'throttled')
451+
this.hand2.setAttribute('position', '2 1 1')
452+
stretch.tick(2006, 2000)
453+
check.set(0.707, 0.707, 0.707)
454+
assert.isTrue(this.target1.body.shapes[0].halfExtents.almostEquals(check, 0.001))
455+
assert.isTrue(this.target2.body.shapes[0].halfExtents.almostEquals(check, 0.001), 'child')
456+
check.set(1.414, 1.414, 1.414)
457+
assert.isTrue(this.target1.body.shapes[1].halfExtents.almostEquals(check, 0.001), 'scale')
458+
assert.isTrue(this.target1.body.shapeOffsets[1].almostEquals(check, 0.001), 'offset')
459+
})
437460
})

0 commit comments

Comments
 (0)