-
Notifications
You must be signed in to change notification settings - Fork 0
/
aabb-collider.js
102 lines (94 loc) · 3.37 KB
/
aabb-collider.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* global AFRAME, THREE */
/**
* Implement AABB collision detection for entities with a mesh.
* (https://en.wikipedia.org/wiki/Minimum_bounding_box#Axis-aligned_minimum_bounding_box)
* It sets the specified state on the intersected entities.
*
* @property {string} objects - Selector of the entities to test for collision.
* @property {string} state - State to set on collided entities.
*
*/
AFRAME.registerComponent('aabb-collider', {
schema: {
objects: {default: ''},
state: {default: 'collided'}
},
init: function () {
this.els = [];
this.collisions = [];
this.elMax = new THREE.Vector3();
this.elMin = new THREE.Vector3();
},
/**
* Update list of entities to test for collision.
*/
update: function () {
var data = this.data;
var objectEls;
// Push entities into list of els to intersect.
if (data.objects) {
objectEls = this.el.sceneEl.querySelectorAll(data.objects);
} else {
// If objects not defined, intersect with everything.
objectEls = this.el.sceneEl.children;
}
// Convert from NodeList to Array
this.els = Array.prototype.slice.call(objectEls);
},
tick: (function () {
var boundingBox = new THREE.Box3();
return function () {
var collisions = [];
var el = this.el;
var mesh = el.getObject3D('mesh');
var self = this;
// No mesh, no collisions
if (!mesh) { return; }
// Update the bounding box to account for rotations and
// position changes.
updateBoundingBox();
// Update collisions.
this.els.forEach(intersect);
// Emit events.
collisions.forEach(handleHit);
// No collisions.
if (collisions.length === 0) { self.el.emit('hit', {el: null}); }
// Updated the state of the elements that are not intersected anymore.
this.collisions.filter(function (el) {
return collisions.indexOf(el) === -1;
}).forEach(function removeState (el) {
el.removeState(self.data.state);
});
// Store new collisions
this.collisions = collisions;
// AABB collision detection
function intersect (el) {
var intersected;
var mesh = el.getObject3D('mesh');
var elMin;
var elMax;
if (!mesh) { return; }
boundingBox.setFromObject(mesh);
elMin = boundingBox.min;
elMax = boundingBox.max;
// Bounding boxes are always aligned with the world coordinate system.
// The collision test checks for the conditions where cubes intersect.
// It's an extension to 3 dimensions of this approach (with the condition negated)
// https://www.youtube.com/watch?v=ghqD3e37R7E
intersected = (self.elMin.x <= elMax.x && self.elMax.x >= elMin.x) &&
(self.elMin.y <= elMax.y && self.elMax.y >= elMin.y) &&
(self.elMin.z <= elMax.z && self.elMax.z >= elMin.z);
if (!intersected) { return; }
collisions.push(el);
}
function handleHit (hitEl) {
hitEl.emit('hit');
hitEl.addState(self.data.state);
self.el.emit('hit', {el: hitEl});
}
function updateBoundingBox () {
boundingBox.setFromObject(mesh);
self.elMin.copy(boundingBox.min);
self.elMax.copy(boundingBox.max);
}
};