forked from AdaRoseCannon/aframe-xr-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimple-navmesh-constraint.js
132 lines (120 loc) · 4.4 KB
/
simple-navmesh-constraint.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* global AFRAME, THREE */
/* Constrain an object to a navmesh, for example place this element after wasd-controls like so:
`wasd-controls navmesh-physics="#navmesh-el"`
*/
AFRAME.registerComponent('simple-navmesh-constraint', {
schema: {
enabled: {
default: true
},
navmesh: {
default: ''
},
fall: {
default: 0.5
},
height: {
default: 1.6
},
exclude: {
default: ''
},
xzOrigin: {
default: ''
}
},
update: function () {
this.lastPosition = null;
this.excludes = this.data.exclude ? Array.from(document.querySelectorAll(this.data.exclude)):[];
const els = Array.from(document.querySelectorAll(this.data.navmesh));
if (els === null) {
console.warn('navmesh-physics: Did not match any elements');
this.objects = [];
} else {
this.objects = els.map(el => el.object3D).concat(this.excludes.map(el => el.object3D));
}
this.xzOrigin = this.data.xzOrigin ? this.el.querySelector(this.data.xzOrigin) : this.el;
},
tick: (function () {
const nextPosition = new THREE.Vector3();
const tempVec = new THREE.Vector3();
const scanPattern = [
[0,1], // Default the next location
[0,0.5], // Check that the path to that location was fine
[30,0.4], // A little to the side shorter range
[-30,0.4], // A little to the side shorter range
[60,0.2], // Moderately to the side short range
[-60,0.2], // Moderately to the side short range
[80,0.06], // Perpendicular very short range
[-80,0.06], // Perpendicular very short range
];
const down = new THREE.Vector3(0,-1,0);
const raycaster = new THREE.Raycaster();
const gravity = -1;
const maxYVelocity = 0.5;
const results = [];
let yVel = 0;
let firstTry = true;
return function tick(time, delta) {
if (this.data.enabled === false) return;
if (this.lastPosition === null) {
firstTry = true;
this.lastPosition = new THREE.Vector3();
this.xzOrigin.object3D.getWorldPosition(this.lastPosition);
if (this.data.xzOrigin) this.lastPosition.y -= this.xzOrigin.object3D.position.y;
}
const el = this.el;
if (this.objects.length === 0) return;
this.xzOrigin.object3D.getWorldPosition(nextPosition);
if (this.data.xzOrigin) nextPosition.y -= this.xzOrigin.object3D.position.y;
if (nextPosition.distanceTo(this.lastPosition) <= 0.01) return;
let didHit = false;
// So that it does not get stuck it takes as few samples around the user and finds the most appropriate
scanPatternLoop:
for (const [angle, distance] of scanPattern) {
tempVec.subVectors(nextPosition, this.lastPosition);
tempVec.applyAxisAngle(down, angle*Math.PI/180);
tempVec.multiplyScalar(distance);
tempVec.add(this.lastPosition);
tempVec.y += maxYVelocity;
tempVec.y -= this.data.height;
raycaster.set(tempVec, down);
raycaster.far = this.data.fall > 0 ? this.data.fall + maxYVelocity : Infinity;
raycaster.intersectObjects(this.objects, true, results);
if (results.length) {
// If it hit something we want to avoid then ignore it and stop looking
for (const result of results) {
if(this.excludes.includes(result.object.el)) {
results.splice(0);
continue scanPatternLoop;
}
}
const hitPos = results[0].point;
results.splice(0);
hitPos.y += this.data.height;
if (nextPosition.y - (hitPos.y - yVel*2) > 0.01) {
yVel += Math.max(gravity * delta * 0.001, -maxYVelocity);
hitPos.y = nextPosition.y + yVel;
} else {
yVel = 0;
}
tempVec.copy(hitPos);
this.xzOrigin.object3D.parent.worldToLocal(tempVec);
tempVec.sub(this.xzOrigin.object3D.position);
if (this.data.xzOrigin) tempVec.y += this.xzOrigin.object3D.position.y;
this.el.object3D.position.add(tempVec);
this.lastPosition.copy(hitPos);
didHit = true;
break;
}
}
if (didHit) {
firstTry = false;
}
if (!firstTry && !didHit) {
this.el.object3D.position.copy(this.lastPosition);
this.el.object3D.parent.worldToLocal(this.el.object3D.position);
}
}
}())
});