@genex-ai/multiplayer 0.17.6 → 0.17.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROTOCOL.md +101 -30
- package/README.md +35 -14
- package/WORLD-GLTF.md +109 -0
- package/WORLD-SCENE.md +135 -0
- package/dist/authority/fast-udp.d.ts +4 -2
- package/dist/authority/fast-udp.js +8 -46
- package/dist/authority/fast-webtransport.d.ts +26 -4
- package/dist/authority/fast-webtransport.js +12 -115
- package/dist/authority/index.d.ts +379 -41
- package/dist/authority/index.js +247 -47
- package/dist/authority/module.d.ts +2 -1
- package/dist/authority/module.js +1 -1
- package/dist/bridge-authority.d.ts +3 -2
- package/dist/bridge-authority.global.js +10 -9
- package/dist/bridge-authority.js +11 -6
- package/dist/bridge.global.js +5 -5
- package/dist/bridge.js +3 -3
- package/dist/chunk-2NGXXQDS.js +1916 -0
- package/dist/{chunk-ZSWF4VCN.js → chunk-CSQEEANB.js} +78 -2
- package/dist/{chunk-UX3F6YTV.js → chunk-FPWP22Y7.js} +1 -1
- package/dist/{chunk-5JGRFT2N.js → chunk-NFMGIKFZ.js} +879 -1898
- package/dist/{chunk-K3SF5VDS.js → chunk-OD6NRSX2.js} +3 -1
- package/dist/{chunk-7QVOIHJF.js → chunk-QY3QQNDJ.js} +2 -2
- package/dist/{chunk-Y6XHXDIK.js → chunk-QZOCUQJT.js} +820 -537
- package/dist/chunk-TSYEWPVO.js +44 -0
- package/dist/chunk-TVTVIMIH.js +675 -0
- package/dist/chunk-YWQ3ALYY.js +69 -0
- package/dist/{fast-client-CZ41v-oB.d.ts → fast-client--A88q0kk.d.ts} +85 -4
- package/dist/index.js +3 -3
- package/dist/level-build-CbRqrYJq.d.ts +140 -0
- package/dist/{module-DqtFb5wA.d.ts → module-CSpXrzFc.d.ts} +151 -400
- package/dist/{physics-predict-49udN3a1.d.ts → physics-predict-C7ZB1gDG.d.ts} +18 -3
- package/dist/protocol.d.ts +87 -2
- package/dist/protocol.js +15 -1
- package/dist/shot-world-D22tqOUT.d.ts +485 -0
- package/dist/world-fit/index.d.ts +549 -0
- package/dist/world-fit/index.js +4763 -0
- package/dist/world-fit/three.d.ts +77 -0
- package/dist/world-fit/three.js +140 -0
- package/package.json +15 -3
|
@@ -0,0 +1,1916 @@
|
|
|
1
|
+
// src/authority/numeric.ts
|
|
2
|
+
var MAX_ABS = 1e9;
|
|
3
|
+
var MASK_LIMIT = 2 ** 24;
|
|
4
|
+
var TWO_PI = 2 * Math.PI;
|
|
5
|
+
var F32_PI = Math.fround(Math.PI);
|
|
6
|
+
var unsignedZero = (v) => v === 0 ? 0 : v;
|
|
7
|
+
function axis(v) {
|
|
8
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return 0;
|
|
9
|
+
const clamped = v < -1 ? -1 : v > 1 ? 1 : v;
|
|
10
|
+
return unsignedZero(Math.fround(clamped));
|
|
11
|
+
}
|
|
12
|
+
function wrappedAngle(v) {
|
|
13
|
+
if (typeof v !== "number" || !Number.isFinite(v) || Math.abs(v) > MAX_ABS) return null;
|
|
14
|
+
const wrapped = Math.fround(v - TWO_PI * Math.floor((v + Math.PI) / TWO_PI));
|
|
15
|
+
return wrapped === F32_PI ? -F32_PI : unsignedZero(wrapped);
|
|
16
|
+
}
|
|
17
|
+
function mask(v) {
|
|
18
|
+
return typeof v === "number" && Number.isInteger(v) && v >= 0 && v < MASK_LIMIT ? v : 0;
|
|
19
|
+
}
|
|
20
|
+
var isMask = (v) => typeof v === "number" && mask(v) === v;
|
|
21
|
+
function stepSeconds(v, max) {
|
|
22
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) return 0;
|
|
23
|
+
return v > max ? max : v;
|
|
24
|
+
}
|
|
25
|
+
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
26
|
+
var INVPIO2 = 5734161139222659 / 2 ** 53;
|
|
27
|
+
var PIO2_1 = 1686629713 / 2 ** 30;
|
|
28
|
+
var PIO2_2 = 2242054355 / 2 ** 65;
|
|
29
|
+
var PIO2_2T = 5376105825661043 / 2 ** 121;
|
|
30
|
+
var S1 = -6004799503160649 / 2 ** 55;
|
|
31
|
+
var S2 = 2401919801261139 / 2 ** 58;
|
|
32
|
+
var S3 = -7320136532976085 / 2 ** 65;
|
|
33
|
+
var S4 = 6506786730409597 / 2 ** 71;
|
|
34
|
+
var S5 = -7571127717829867 / 2 ** 78;
|
|
35
|
+
var S6 = 1537454791456095 / 2 ** 83;
|
|
36
|
+
var C1 = 1501199875790163 / 2 ** 55;
|
|
37
|
+
var C2 = -6405119470031223 / 2 ** 62;
|
|
38
|
+
var C3 = 457508533350745 / 2 ** 64;
|
|
39
|
+
var C4 = -5205429506036397 / 2 ** 74;
|
|
40
|
+
var C5 = 1261860039765105 / 2 ** 79;
|
|
41
|
+
var C6 = -1757820317994549 / 2 ** 87;
|
|
42
|
+
var SINCOS_CONSTANTS = Object.freeze({
|
|
43
|
+
invpio2: INVPIO2,
|
|
44
|
+
pio2_1: PIO2_1,
|
|
45
|
+
pio2_2: PIO2_2,
|
|
46
|
+
pio2_2t: PIO2_2T,
|
|
47
|
+
S1,
|
|
48
|
+
S2,
|
|
49
|
+
S3,
|
|
50
|
+
S4,
|
|
51
|
+
S5,
|
|
52
|
+
S6,
|
|
53
|
+
C1,
|
|
54
|
+
C2,
|
|
55
|
+
C3,
|
|
56
|
+
C4,
|
|
57
|
+
C5,
|
|
58
|
+
C6
|
|
59
|
+
});
|
|
60
|
+
function psincos(a) {
|
|
61
|
+
if (!Number.isFinite(a) || Math.abs(a) > MAX_ABS) return [0, 1];
|
|
62
|
+
const n = Math.floor(a * INVPIO2 + 0.5);
|
|
63
|
+
const q = n - 4 * Math.floor(n / 4);
|
|
64
|
+
const r = a - n * PIO2_1 - n * PIO2_2 - n * PIO2_2T;
|
|
65
|
+
const z = r * r;
|
|
66
|
+
const ks = r + z * r * (S1 + z * (S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)))));
|
|
67
|
+
const hz = 0.5 * z;
|
|
68
|
+
const w = 1 - hz;
|
|
69
|
+
const kc = w + (1 - w - hz + z * (z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6)))))));
|
|
70
|
+
if (q === 0) return [unsignedZero(ks), unsignedZero(kc)];
|
|
71
|
+
if (q === 1) return [unsignedZero(kc), unsignedZero(-ks)];
|
|
72
|
+
if (q === 2) return [unsignedZero(-ks), unsignedZero(-kc)];
|
|
73
|
+
return [unsignedZero(-kc), unsignedZero(ks)];
|
|
74
|
+
}
|
|
75
|
+
var psin = (a) => psincos(a)[0];
|
|
76
|
+
var pcos = (a) => psincos(a)[1];
|
|
77
|
+
|
|
78
|
+
// src/authority/presets.ts
|
|
79
|
+
var Preset = {
|
|
80
|
+
/** Planar movement on x/y with an aim angle: the server-authority spike's arena. */
|
|
81
|
+
TOPDOWN: "topdown",
|
|
82
|
+
/** Source-engine character movement through a static box-and-ramp world, z up (§17.7). */
|
|
83
|
+
FIRSTPERSON: "firstperson",
|
|
84
|
+
/** An arcade car (Rocket League's ground and air handling, kept level) through the same world (§17.7). */
|
|
85
|
+
VEHICLE: "vehicle"
|
|
86
|
+
};
|
|
87
|
+
var PRESET_MAX_DT_S = 0.25;
|
|
88
|
+
var TOPDOWN_DEFAULTS = Object.freeze({ speed: 6, arena: 20 });
|
|
89
|
+
var TOPDOWN_RANGE = {
|
|
90
|
+
speed: { min: 0, max: 1e3, positive: false },
|
|
91
|
+
arena: { min: 0, max: 1e6, positive: true }
|
|
92
|
+
};
|
|
93
|
+
function isPresetName(name) {
|
|
94
|
+
return Object.values(Preset).some((known) => known === name);
|
|
95
|
+
}
|
|
96
|
+
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
|
|
97
|
+
function presetParamsValid(name, params) {
|
|
98
|
+
if (params === void 0) return true;
|
|
99
|
+
const ranges = definitionOf(name)?.ranges;
|
|
100
|
+
if (ranges === void 0 || !isPlainObject(params)) return false;
|
|
101
|
+
return Object.keys(params).every((key) => {
|
|
102
|
+
if (!Object.hasOwn(ranges, key)) return false;
|
|
103
|
+
const range = ranges[key];
|
|
104
|
+
const v = params[key];
|
|
105
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < range.min || v > range.max) return false;
|
|
106
|
+
if (range.integer === true && !Number.isInteger(v)) return false;
|
|
107
|
+
return !range.positive || v > 0 && Math.fround(v) > 0;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function resolvePresetParams(name, params) {
|
|
111
|
+
const definition = definitionOf(name);
|
|
112
|
+
if (definition === void 0) return {};
|
|
113
|
+
const out = { ...definition.defaults };
|
|
114
|
+
if (!isPlainObject(params)) return out;
|
|
115
|
+
for (const key of Object.keys(definition.ranges)) {
|
|
116
|
+
if (presetParamsValid(name, { [key]: params[key] })) out[key] = params[key];
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
function resolveTopdownParams(params) {
|
|
121
|
+
return resolvePresetParams(Preset.TOPDOWN, params);
|
|
122
|
+
}
|
|
123
|
+
var finiteOrZero = (v) => Number.isFinite(v) ? v : 0;
|
|
124
|
+
function stepTopdown(body, input, dt, params) {
|
|
125
|
+
const moveX = axis(input.moveX);
|
|
126
|
+
const moveY = axis(input.moveY);
|
|
127
|
+
const step = stepSeconds(dt, PRESET_MAX_DT_S);
|
|
128
|
+
const len = Math.sqrt(moveX * moveX + moveY * moveY);
|
|
129
|
+
const scale = len > 1 ? 1 / len : 1;
|
|
130
|
+
const x = finiteOrZero(body.x) + moveX * scale * params.speed * step;
|
|
131
|
+
const y = finiteOrZero(body.y) + moveY * scale * params.speed * step;
|
|
132
|
+
return {
|
|
133
|
+
x: unsignedZero(Math.fround(clamp(x, -params.arena, params.arena))),
|
|
134
|
+
y: unsignedZero(Math.fround(clamp(y, -params.arena, params.arena))),
|
|
135
|
+
yaw: wrappedAngle(input.yaw) ?? unsignedZero(Math.fround(finiteOrZero(body.yaw)))
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
var EMPTY_WORLD_HASH = 2615243109;
|
|
139
|
+
var FIRSTPERSON_DEFAULTS = Object.freeze({
|
|
140
|
+
maxSpeed: 250,
|
|
141
|
+
walkSpeed: 0.52,
|
|
142
|
+
crouchSpeed: 0.34,
|
|
143
|
+
accelerate: 5.5,
|
|
144
|
+
airAccelerate: 12,
|
|
145
|
+
airMaxWishSpeed: 30,
|
|
146
|
+
friction: 5.2,
|
|
147
|
+
stopSpeed: 80,
|
|
148
|
+
gravity: 800,
|
|
149
|
+
jumpImpulse: 301.993,
|
|
150
|
+
maxVelocity: 3500,
|
|
151
|
+
radius: 16,
|
|
152
|
+
standHeight: 72,
|
|
153
|
+
crouchHeight: 54,
|
|
154
|
+
duckTime: 0.4,
|
|
155
|
+
airDuck: 1,
|
|
156
|
+
stepHeight: 18,
|
|
157
|
+
standable: 0.7,
|
|
158
|
+
groundTrace: 2.5,
|
|
159
|
+
groundSnap: 1,
|
|
160
|
+
skin: 0.03,
|
|
161
|
+
pitchLimit: 1.5533430576324463,
|
|
162
|
+
jumpBit: 1,
|
|
163
|
+
duckBit: 2,
|
|
164
|
+
walkBit: 4,
|
|
165
|
+
substeps: 2,
|
|
166
|
+
capZeroJump: 1,
|
|
167
|
+
runBit: 0,
|
|
168
|
+
jogSpeed: 1,
|
|
169
|
+
world: EMPTY_WORLD_HASH
|
|
170
|
+
});
|
|
171
|
+
var inRange = (min, max) => ({ min, max, positive: false });
|
|
172
|
+
var aboveZero = (max) => ({ min: 0, max, positive: true });
|
|
173
|
+
var integerIn = (min, max) => ({ min, max, positive: false, integer: true });
|
|
174
|
+
var FIRSTPERSON_RANGE = {
|
|
175
|
+
maxSpeed: inRange(0, 1e4),
|
|
176
|
+
walkSpeed: inRange(0, 1),
|
|
177
|
+
crouchSpeed: inRange(0, 1),
|
|
178
|
+
accelerate: inRange(0, 1e3),
|
|
179
|
+
airAccelerate: inRange(0, 1e3),
|
|
180
|
+
airMaxWishSpeed: inRange(0, 1e4),
|
|
181
|
+
friction: inRange(0, 1e3),
|
|
182
|
+
stopSpeed: inRange(0, 1e4),
|
|
183
|
+
gravity: inRange(0, 1e5),
|
|
184
|
+
jumpImpulse: inRange(0, 1e5),
|
|
185
|
+
maxVelocity: inRange(0, 1e5),
|
|
186
|
+
radius: aboveZero(1e4),
|
|
187
|
+
standHeight: aboveZero(1e4),
|
|
188
|
+
crouchHeight: aboveZero(1e4),
|
|
189
|
+
duckTime: aboveZero(100),
|
|
190
|
+
airDuck: integerIn(0, 1),
|
|
191
|
+
stepHeight: inRange(0, 1e4),
|
|
192
|
+
standable: inRange(0, 1),
|
|
193
|
+
groundTrace: inRange(0, 1e4),
|
|
194
|
+
groundSnap: inRange(0, 1e4),
|
|
195
|
+
skin: inRange(0, 100),
|
|
196
|
+
pitchLimit: inRange(0, 1.5707963705062866),
|
|
197
|
+
jumpBit: integerIn(0, MASK_LIMIT - 1),
|
|
198
|
+
duckBit: integerIn(0, MASK_LIMIT - 1),
|
|
199
|
+
walkBit: integerIn(0, MASK_LIMIT - 1),
|
|
200
|
+
substeps: integerIn(1, 8),
|
|
201
|
+
capZeroJump: integerIn(0, 1),
|
|
202
|
+
runBit: integerIn(0, MASK_LIMIT - 1),
|
|
203
|
+
jogSpeed: inRange(0, 1),
|
|
204
|
+
world: integerIn(0, 2 ** 32 - 1)
|
|
205
|
+
};
|
|
206
|
+
function resolveFirstpersonParams(params) {
|
|
207
|
+
return resolvePresetParams(Preset.FIRSTPERSON, params);
|
|
208
|
+
}
|
|
209
|
+
var MoveState = {
|
|
210
|
+
ON_GROUND: 1,
|
|
211
|
+
DUCKING: 2,
|
|
212
|
+
JUMP_HELD: 4
|
|
213
|
+
};
|
|
214
|
+
var MSTATE_BITS = MoveState.ON_GROUND | MoveState.DUCKING | MoveState.JUMP_HELD;
|
|
215
|
+
var WorldLimits = {
|
|
216
|
+
MAX_BOXES: 32768,
|
|
217
|
+
MAX_RAMPS: 4096,
|
|
218
|
+
/** Every world value's magnitude. */
|
|
219
|
+
MAX_ABS
|
|
220
|
+
};
|
|
221
|
+
var WorldError = class extends RangeError {
|
|
222
|
+
path;
|
|
223
|
+
constructor(path, message) {
|
|
224
|
+
super(`firstperson world: ${path} ${message}`);
|
|
225
|
+
this.name = "WorldError";
|
|
226
|
+
this.path = path;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var compiled = /* @__PURE__ */ new WeakMap();
|
|
230
|
+
var worldValue = (v, path) => {
|
|
231
|
+
if (typeof v !== "number" || !Number.isFinite(v)) throw new WorldError(path, "is not a finite number");
|
|
232
|
+
if (Math.fround(v) !== v) throw new WorldError(path, "is not a float32");
|
|
233
|
+
if (Math.abs(v) > MAX_ABS) throw new WorldError(path, `is past \xB1${MAX_ABS}`);
|
|
234
|
+
return unsignedZero(v);
|
|
235
|
+
};
|
|
236
|
+
function solidValues(raw, length, path) {
|
|
237
|
+
if (!Array.isArray(raw) || raw.length !== length) throw new WorldError(path, `is not an array of ${length} numbers`);
|
|
238
|
+
const out = [];
|
|
239
|
+
for (let i = 0; i < length; i++) out.push(worldValue(raw[i], `${path}[${i}]`));
|
|
240
|
+
if (!(out[0] < out[3] && out[1] < out[4] && out[2] < out[5])) throw new WorldError(path, "needs min < max on every axis");
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
function solidList(raw, key, max) {
|
|
244
|
+
if (raw === void 0) return [];
|
|
245
|
+
if (!Array.isArray(raw)) throw new WorldError(key, "is not an array");
|
|
246
|
+
if (raw.length > max) throw new WorldError(key, `holds more than ${max}`);
|
|
247
|
+
return raw;
|
|
248
|
+
}
|
|
249
|
+
function worldHash(boxes, ramps) {
|
|
250
|
+
const bytes = new DataView(new ArrayBuffer(8 + 4 * (6 * boxes.length + 9 * ramps.length)));
|
|
251
|
+
bytes.setUint32(0, boxes.length, true);
|
|
252
|
+
bytes.setUint32(4, ramps.length, true);
|
|
253
|
+
let at = 8;
|
|
254
|
+
for (const solid of [...boxes, ...ramps]) {
|
|
255
|
+
for (const v of solid) {
|
|
256
|
+
bytes.setFloat32(at, v, true);
|
|
257
|
+
at += 4;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
let h = 2166136261;
|
|
261
|
+
for (let i = 0; i < bytes.byteLength; i++) h = Math.imul(h ^ bytes.getUint8(i), 16777619) >>> 0;
|
|
262
|
+
return h;
|
|
263
|
+
}
|
|
264
|
+
function firstpersonWorld(raw, options = {}) {
|
|
265
|
+
const made = compiled.has(raw) ? raw : void 0;
|
|
266
|
+
if (made !== void 0 && (options.broadphase === void 0 || options.broadphase === made.broadphase)) return made;
|
|
267
|
+
if (raw !== void 0 && !isPlainObject(raw)) throw new WorldError("$", "is not an object");
|
|
268
|
+
const spec = raw ?? {};
|
|
269
|
+
const caps = options.caps ?? WorldLimits;
|
|
270
|
+
const boxes = solidList(spec.boxes, "boxes", caps.MAX_BOXES).map(
|
|
271
|
+
(box, i) => Object.freeze(solidValues(box, 6, `boxes[${i}]`))
|
|
272
|
+
);
|
|
273
|
+
const ramps = solidList(spec.ramps, "ramps", caps.MAX_RAMPS).map((ramp, i) => {
|
|
274
|
+
const path = `ramps[${i}]`;
|
|
275
|
+
const v = solidValues(ramp, 9, path);
|
|
276
|
+
if (v[6] !== 0 && v[6] !== 1) throw new WorldError(`${path}[6]`, "is not an axis (0 or 1)");
|
|
277
|
+
const [minZ, maxZ, lo, hi] = [v[2], v[5], v[7], v[8]];
|
|
278
|
+
if (!(minZ <= lo && minZ <= hi)) throw new WorldError(path, "needs minZ \u2264 lo and minZ \u2264 hi");
|
|
279
|
+
if (maxZ !== Math.max(lo, hi)) throw new WorldError(path, "needs maxZ = max(lo, hi)");
|
|
280
|
+
return Object.freeze(v);
|
|
281
|
+
});
|
|
282
|
+
const world = Object.freeze({
|
|
283
|
+
boxes: Object.freeze(boxes),
|
|
284
|
+
ramps: Object.freeze(ramps),
|
|
285
|
+
hash: worldHash(boxes, ramps),
|
|
286
|
+
broadphase: options.broadphase !== false
|
|
287
|
+
});
|
|
288
|
+
compiled.set(world, compile(world));
|
|
289
|
+
return world;
|
|
290
|
+
}
|
|
291
|
+
var EMPTY_WORLD = firstpersonWorld(void 0);
|
|
292
|
+
var PosedKind = { Box: 0, OBox: 1, Disc: 2 };
|
|
293
|
+
var POSED_MAX = 96;
|
|
294
|
+
var POSED_SCRATCH = new Int32Array(POSED_MAX);
|
|
295
|
+
function posedWorld(solids) {
|
|
296
|
+
const n = solids.length;
|
|
297
|
+
if (n > POSED_MAX) throw new RangeError(`posed solids: at most ${POSED_MAX} a tick`);
|
|
298
|
+
const table = () => new Float64Array(n);
|
|
299
|
+
const p = { count: n, kind: new Uint8Array(n), minX: table(), minY: table(), minZ: table(), maxX: table(), maxY: table(), maxZ: table(), cx: table(), cy: table(), hx: table(), hy: table(), sin: table(), cos: table(), radius: table() };
|
|
300
|
+
solids.forEach((s, i) => {
|
|
301
|
+
if (s.kind === "box") {
|
|
302
|
+
p.kind[i] = PosedKind.Box;
|
|
303
|
+
[p.minX[i], p.minY[i], p.minZ[i]] = s.min;
|
|
304
|
+
[p.maxX[i], p.maxY[i], p.maxZ[i]] = s.max;
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
p.cx[i] = s.c[0];
|
|
308
|
+
p.cy[i] = s.c[1];
|
|
309
|
+
p.minZ[i] = s.c[2] - (s.kind === "disc" ? s.half : s.half[2]);
|
|
310
|
+
p.maxZ[i] = s.c[2] + (s.kind === "disc" ? s.half : s.half[2]);
|
|
311
|
+
if (s.kind === "disc") {
|
|
312
|
+
p.kind[i] = PosedKind.Disc;
|
|
313
|
+
p.radius[i] = s.radius;
|
|
314
|
+
p.minX[i] = s.c[0] - s.radius;
|
|
315
|
+
p.maxX[i] = s.c[0] + s.radius;
|
|
316
|
+
p.minY[i] = s.c[1] - s.radius;
|
|
317
|
+
p.maxY[i] = s.c[1] + s.radius;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
p.kind[i] = PosedKind.OBox;
|
|
321
|
+
p.hx[i] = s.half[0];
|
|
322
|
+
p.hy[i] = s.half[1];
|
|
323
|
+
p.sin[i] = s.sin;
|
|
324
|
+
p.cos[i] = s.cos;
|
|
325
|
+
const ac = Math.abs(s.cos);
|
|
326
|
+
const as = Math.abs(s.sin);
|
|
327
|
+
const ex = s.half[0] * ac + s.half[1] * as;
|
|
328
|
+
const ey = s.half[0] * as + s.half[1] * ac;
|
|
329
|
+
p.minX[i] = s.c[0] - ex;
|
|
330
|
+
p.maxX[i] = s.c[0] + ex;
|
|
331
|
+
p.minY[i] = s.c[1] - ey;
|
|
332
|
+
p.maxY[i] = s.c[1] + ey;
|
|
333
|
+
});
|
|
334
|
+
return p;
|
|
335
|
+
}
|
|
336
|
+
var NO_POSED = posedWorld([]);
|
|
337
|
+
function compiledOf(world) {
|
|
338
|
+
const known = compiled.get(world);
|
|
339
|
+
if (known !== void 0) return known;
|
|
340
|
+
const checked = firstpersonWorld(world, { broadphase: world.broadphase !== false });
|
|
341
|
+
const tables = compiled.get(checked);
|
|
342
|
+
compiled.set(world, tables);
|
|
343
|
+
return tables;
|
|
344
|
+
}
|
|
345
|
+
var TREE_FANOUT = 8;
|
|
346
|
+
var TREE_KEY_RADIX = 2 ** 23;
|
|
347
|
+
function compile(world) {
|
|
348
|
+
const solids = [...world.boxes, ...world.ramps];
|
|
349
|
+
const count = solids.length;
|
|
350
|
+
const table = () => new Float64Array(count);
|
|
351
|
+
const t = {
|
|
352
|
+
boxCount: world.boxes.length,
|
|
353
|
+
count,
|
|
354
|
+
minX: table(),
|
|
355
|
+
minY: table(),
|
|
356
|
+
minZ: table(),
|
|
357
|
+
maxX: table(),
|
|
358
|
+
maxY: table(),
|
|
359
|
+
maxZ: table(),
|
|
360
|
+
axis: new Uint8Array(count),
|
|
361
|
+
lo: table(),
|
|
362
|
+
hi: table(),
|
|
363
|
+
m0: table(),
|
|
364
|
+
span: table(),
|
|
365
|
+
slope: table(),
|
|
366
|
+
topZ: table()
|
|
367
|
+
};
|
|
368
|
+
solids.forEach((s, i) => {
|
|
369
|
+
t.minX[i] = s[0];
|
|
370
|
+
t.minY[i] = s[1];
|
|
371
|
+
t.minZ[i] = s[2];
|
|
372
|
+
t.maxX[i] = s[3];
|
|
373
|
+
t.maxY[i] = s[4];
|
|
374
|
+
t.maxZ[i] = s[5];
|
|
375
|
+
if (i < t.boxCount) return;
|
|
376
|
+
const axis2 = s[6];
|
|
377
|
+
const m0 = axis2 === 0 ? s[0] : s[1];
|
|
378
|
+
const m1 = axis2 === 0 ? s[3] : s[4];
|
|
379
|
+
const span = m1 - m0;
|
|
380
|
+
const slope = (s[8] - s[7]) / span;
|
|
381
|
+
t.axis[i] = axis2;
|
|
382
|
+
t.lo[i] = s[7];
|
|
383
|
+
t.hi[i] = s[8];
|
|
384
|
+
t.m0[i] = m0;
|
|
385
|
+
t.span[i] = span;
|
|
386
|
+
t.slope[i] = slope;
|
|
387
|
+
t.topZ[i] = 1 / Math.sqrt(1 + slope * slope);
|
|
388
|
+
});
|
|
389
|
+
return { ...t, tree: world.broadphase && count > 0 ? buildTree(t) : null };
|
|
390
|
+
}
|
|
391
|
+
function spread3(v) {
|
|
392
|
+
let x = v & 1023;
|
|
393
|
+
x = (x | x << 16) & 50331903;
|
|
394
|
+
x = (x | x << 8) & 50393103;
|
|
395
|
+
x = (x | x << 4) & 51130563;
|
|
396
|
+
x = (x | x << 2) & 153391689;
|
|
397
|
+
return x;
|
|
398
|
+
}
|
|
399
|
+
function leafOrder(n, t) {
|
|
400
|
+
let x0 = Infinity, y0 = Infinity, z0 = Infinity, x1 = -Infinity, y1 = -Infinity, z1 = -Infinity;
|
|
401
|
+
for (let i = 0; i < n; i++) {
|
|
402
|
+
x0 = Math.min(x0, t.minX[i]);
|
|
403
|
+
y0 = Math.min(y0, t.minY[i]);
|
|
404
|
+
z0 = Math.min(z0, t.minZ[i]);
|
|
405
|
+
x1 = Math.max(x1, t.maxX[i]);
|
|
406
|
+
y1 = Math.max(y1, t.maxY[i]);
|
|
407
|
+
z1 = Math.max(z1, t.maxZ[i]);
|
|
408
|
+
}
|
|
409
|
+
const cell = (v, lo, hi) => hi > lo ? Math.min(1023, Math.floor((v - lo) / (hi - lo) * 1024)) : 0;
|
|
410
|
+
const keys = new Float64Array(n);
|
|
411
|
+
for (let i = 0; i < n; i++) {
|
|
412
|
+
const cx = cell((t.minX[i] + t.maxX[i]) / 2, x0, x1);
|
|
413
|
+
const cy = cell((t.minY[i] + t.maxY[i]) / 2, y0, y1);
|
|
414
|
+
const cz = cell((t.minZ[i] + t.maxZ[i]) / 2, z0, z1);
|
|
415
|
+
keys[i] = (spread3(cx) | spread3(cy) << 1 | spread3(cz) << 2) * TREE_KEY_RADIX + i;
|
|
416
|
+
}
|
|
417
|
+
keys.sort();
|
|
418
|
+
const order = new Int32Array(n);
|
|
419
|
+
for (let k = 0; k < n; k++) order[k] = keys[k] % TREE_KEY_RADIX;
|
|
420
|
+
return order;
|
|
421
|
+
}
|
|
422
|
+
function worldTreeOrder(solids) {
|
|
423
|
+
const table = (k) => Float64Array.from(solids, (s) => s[k]);
|
|
424
|
+
return leafOrder(solids.length, { minX: table(0), minY: table(1), minZ: table(2), maxX: table(3), maxY: table(4), maxZ: table(5) });
|
|
425
|
+
}
|
|
426
|
+
function buildTree(t) {
|
|
427
|
+
const n = t.count;
|
|
428
|
+
const order = leafOrder(n, t);
|
|
429
|
+
const sizes = [n];
|
|
430
|
+
while (sizes[sizes.length - 1] > 1) sizes.push(Math.ceil(sizes[sizes.length - 1] / TREE_FANOUT));
|
|
431
|
+
const total = sizes.reduce((a, b) => a + b, 0);
|
|
432
|
+
const boxes = new Float64Array(total * 6);
|
|
433
|
+
const ref = new Int32Array(total);
|
|
434
|
+
const levelEnd = new Int32Array(sizes.length);
|
|
435
|
+
for (let k = 0; k < n; k++) {
|
|
436
|
+
const i = order[k];
|
|
437
|
+
const o = k * 6;
|
|
438
|
+
boxes[o] = t.minX[i];
|
|
439
|
+
boxes[o + 1] = t.minY[i];
|
|
440
|
+
boxes[o + 2] = t.minZ[i];
|
|
441
|
+
boxes[o + 3] = t.maxX[i];
|
|
442
|
+
boxes[o + 4] = t.maxY[i];
|
|
443
|
+
boxes[o + 5] = t.maxZ[i];
|
|
444
|
+
ref[k] = i;
|
|
445
|
+
}
|
|
446
|
+
levelEnd[0] = n;
|
|
447
|
+
let from = 0;
|
|
448
|
+
let at = n;
|
|
449
|
+
for (let level = 1; level < sizes.length; level++) {
|
|
450
|
+
const end = levelEnd[level - 1];
|
|
451
|
+
for (let child = from; child < end; child += TREE_FANOUT) {
|
|
452
|
+
const last = Math.min(child + TREE_FANOUT, end);
|
|
453
|
+
let a = Infinity, b = Infinity, c = Infinity, d = -Infinity, e = -Infinity, g = -Infinity;
|
|
454
|
+
for (let k = child; k < last; k++) {
|
|
455
|
+
const o2 = k * 6;
|
|
456
|
+
a = Math.min(a, boxes[o2]);
|
|
457
|
+
b = Math.min(b, boxes[o2 + 1]);
|
|
458
|
+
c = Math.min(c, boxes[o2 + 2]);
|
|
459
|
+
d = Math.max(d, boxes[o2 + 3]);
|
|
460
|
+
e = Math.max(e, boxes[o2 + 4]);
|
|
461
|
+
g = Math.max(g, boxes[o2 + 5]);
|
|
462
|
+
}
|
|
463
|
+
const o = at * 6;
|
|
464
|
+
boxes[o] = a;
|
|
465
|
+
boxes[o + 1] = b;
|
|
466
|
+
boxes[o + 2] = c;
|
|
467
|
+
boxes[o + 3] = d;
|
|
468
|
+
boxes[o + 4] = e;
|
|
469
|
+
boxes[o + 5] = g;
|
|
470
|
+
ref[at] = child;
|
|
471
|
+
at++;
|
|
472
|
+
}
|
|
473
|
+
from = end;
|
|
474
|
+
levelEnd[level] = at;
|
|
475
|
+
}
|
|
476
|
+
return { boxes, ref, levelEnd, root: total - 1 };
|
|
477
|
+
}
|
|
478
|
+
var FIRSTPERSON_RADIUS = 16;
|
|
479
|
+
var VEHICLE_RADIUS = 50;
|
|
480
|
+
var OTHER_UNITS = 0.5;
|
|
481
|
+
var unitOf = (radius, defaultRadius) => radius / defaultRadius < OTHER_UNITS ? radius / defaultRadius : 1;
|
|
482
|
+
var queryMargin = (a, b, c, d, e, g) => 1e-3 + 1e-7 * (Math.abs(a) + Math.abs(b) + Math.abs(c) + Math.abs(d) + Math.abs(e) + Math.abs(g));
|
|
483
|
+
var costMeter = null;
|
|
484
|
+
function withWorldCostMeter(meter, run) {
|
|
485
|
+
costMeter = meter;
|
|
486
|
+
try {
|
|
487
|
+
return run();
|
|
488
|
+
} finally {
|
|
489
|
+
costMeter = null;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function query(m, qx0, qy0, qz0, qx1, qy1, qz1) {
|
|
493
|
+
queryPosed(m, qx0, qy0, qz0, qx1, qy1, qz1);
|
|
494
|
+
const tree = m.w.tree;
|
|
495
|
+
if (tree === null) {
|
|
496
|
+
m.nCand = -1;
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const margin = queryMargin(qx0, qy0, qz0, qx1, qy1, qz1);
|
|
500
|
+
const pad = m.r + margin;
|
|
501
|
+
const x0 = qx0 - pad, x1 = qx1 + pad, y0 = qy0 - pad, y1 = qy1 + pad, z0 = qz0 - margin, z1 = qz1 + margin;
|
|
502
|
+
const { boxes, ref, levelEnd } = tree;
|
|
503
|
+
const count = levelEnd[0];
|
|
504
|
+
const cand = m.cand;
|
|
505
|
+
const stack = m.stack;
|
|
506
|
+
let n = 0;
|
|
507
|
+
let top = 0;
|
|
508
|
+
let walked = 0;
|
|
509
|
+
stack[top++] = tree.root;
|
|
510
|
+
while (top > 0) {
|
|
511
|
+
const node = stack[--top];
|
|
512
|
+
walked += 1;
|
|
513
|
+
const o = node * 6;
|
|
514
|
+
if (boxes[o + 3] < x0 || boxes[o] > x1 || boxes[o + 4] < y0 || boxes[o + 1] > y1 || boxes[o + 5] < z0 || boxes[o + 2] > z1) continue;
|
|
515
|
+
if (node < count) {
|
|
516
|
+
cand[n++] = ref[node];
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const first = ref[node];
|
|
520
|
+
let end = first + TREE_FANOUT;
|
|
521
|
+
for (let l = 0; l < levelEnd.length; l++) {
|
|
522
|
+
if (first < levelEnd[l]) {
|
|
523
|
+
end = Math.min(end, levelEnd[l]);
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
for (let k = first; k < end; k++) stack[top++] = k;
|
|
528
|
+
}
|
|
529
|
+
m.nCand = n;
|
|
530
|
+
if (costMeter !== null) {
|
|
531
|
+
costMeter.nodes += walked;
|
|
532
|
+
costMeter.tests += n;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function queryPosed(m, qx0, qy0, qz0, qx1, qy1, qz1) {
|
|
536
|
+
const p = m.p;
|
|
537
|
+
let n = 0;
|
|
538
|
+
if (p.count > 0) {
|
|
539
|
+
const margin = queryMargin(qx0, qy0, qz0, qx1, qy1, qz1);
|
|
540
|
+
const pad = m.r + margin;
|
|
541
|
+
const x0 = qx0 - pad, x1 = qx1 + pad, y0 = qy0 - pad, y1 = qy1 + pad, z0 = qz0 - margin, z1 = qz1 + margin;
|
|
542
|
+
for (let j = 0; j < p.count; j++) {
|
|
543
|
+
if (p.maxX[j] < x0 || p.minX[j] > x1 || p.maxY[j] < y0 || p.minY[j] > y1 || p.maxZ[j] < z0 || p.minZ[j] > z1) continue;
|
|
544
|
+
m.pc[n++] = j;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
m.nPosed = n;
|
|
548
|
+
}
|
|
549
|
+
var visits = (m) => (m.nCand < 0 ? m.w.count : m.nCand) + m.nPosed;
|
|
550
|
+
var solidAt = (m, k) => {
|
|
551
|
+
const base = m.nCand < 0 ? m.w.count : m.nCand;
|
|
552
|
+
if (k >= base) return m.w.count + m.pc[k - base];
|
|
553
|
+
return m.nCand < 0 ? k : m.cand[k];
|
|
554
|
+
};
|
|
555
|
+
var hullAt = (m, d) => m.standHeight + (m.crouchHeight - m.standHeight) * d;
|
|
556
|
+
function support(m, i, px, py) {
|
|
557
|
+
const w = m.w;
|
|
558
|
+
const slope = w.slope[i];
|
|
559
|
+
const c = w.axis[i] === 0 ? px : py;
|
|
560
|
+
const c2 = slope > 0 ? c + m.r : slope < 0 ? c - m.r : c;
|
|
561
|
+
return w.lo[i] + slope * clamp(c2 - w.m0[i], 0, w.span[i]);
|
|
562
|
+
}
|
|
563
|
+
function topOf(m, i, px, py) {
|
|
564
|
+
const w = m.w;
|
|
565
|
+
if (i < w.boxCount) return w.maxZ[i];
|
|
566
|
+
if (i < w.count) return support(m, i, px, py);
|
|
567
|
+
return m.p.maxZ[i - w.count];
|
|
568
|
+
}
|
|
569
|
+
var minZOf = (m, i) => i < m.w.count ? m.w.minZ[i] : m.p.minZ[i - m.w.count];
|
|
570
|
+
function overlaps(m, i, px, py) {
|
|
571
|
+
const w = m.w;
|
|
572
|
+
const r = m.r;
|
|
573
|
+
if (i >= w.count) return posedOverlaps(m, i - w.count, px, py);
|
|
574
|
+
return px + r > w.minX[i] && px - r < w.maxX[i] && py + r > w.minY[i] && py - r < w.maxY[i];
|
|
575
|
+
}
|
|
576
|
+
function fits(m, px, py, pz, height) {
|
|
577
|
+
const w = m.w;
|
|
578
|
+
query(m, px, py, pz, px, py, pz + height);
|
|
579
|
+
const n = visits(m);
|
|
580
|
+
for (let k = 0; k < n; k++) {
|
|
581
|
+
const i = solidAt(m, k);
|
|
582
|
+
if (!overlaps(m, i, px, py) || !(pz + height > minZOf(m, i))) continue;
|
|
583
|
+
const top = topOf(m, i, px, py);
|
|
584
|
+
if (pz < top - 1e-4) return false;
|
|
585
|
+
}
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
function traceDown(m, px, py, pz, down, up) {
|
|
589
|
+
const w = m.w;
|
|
590
|
+
query(m, px, py, pz - down, px, py, pz + up);
|
|
591
|
+
const n = visits(m);
|
|
592
|
+
let best = Number.NaN;
|
|
593
|
+
let steep = Number.NaN;
|
|
594
|
+
for (let k = 0; k < n; k++) {
|
|
595
|
+
const i = solidAt(m, k);
|
|
596
|
+
if (!overlaps(m, i, px, py)) continue;
|
|
597
|
+
const top = topOf(m, i, px, py);
|
|
598
|
+
if (!(top <= pz + up && top >= pz - down)) continue;
|
|
599
|
+
if (i < w.boxCount || i >= w.count || w.topZ[i] >= m.standable) {
|
|
600
|
+
if (best !== best || top > best) best = top;
|
|
601
|
+
} else if (steep !== steep || top > steep) steep = top;
|
|
602
|
+
}
|
|
603
|
+
return steep > best ? Number.NaN : best;
|
|
604
|
+
}
|
|
605
|
+
function checkGround(m) {
|
|
606
|
+
if (m.vz > 0.1 * m.unit) {
|
|
607
|
+
m.onGround = false;
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const g = traceDown(m, m.x, m.y, m.z, m.groundTrace, m.groundSnap);
|
|
611
|
+
if (g === g) {
|
|
612
|
+
m.z = g;
|
|
613
|
+
m.onGround = true;
|
|
614
|
+
} else m.onGround = false;
|
|
615
|
+
}
|
|
616
|
+
function slabAxis(m, p, d, lo, hi, a) {
|
|
617
|
+
if (Math.abs(d) < 1e-9) return p > lo && p < hi;
|
|
618
|
+
let t1 = (lo - p) / d;
|
|
619
|
+
let t2 = (hi - p) / d;
|
|
620
|
+
let s = -1;
|
|
621
|
+
if (t1 > t2) {
|
|
622
|
+
const swap = t1;
|
|
623
|
+
t1 = t2;
|
|
624
|
+
t2 = swap;
|
|
625
|
+
s = 1;
|
|
626
|
+
}
|
|
627
|
+
if (t1 >= m.tMin) {
|
|
628
|
+
m.tMin = t1;
|
|
629
|
+
m.ax = a;
|
|
630
|
+
m.sg = s;
|
|
631
|
+
}
|
|
632
|
+
if (t2 < m.tMax) m.tMax = t2;
|
|
633
|
+
return !(m.tMin > m.tMax);
|
|
634
|
+
}
|
|
635
|
+
var PLANE = new Float64Array(4);
|
|
636
|
+
function wedgePlane(m, i, plane, height) {
|
|
637
|
+
const w = m.w;
|
|
638
|
+
const r = m.r;
|
|
639
|
+
let nx = 0;
|
|
640
|
+
let ny = 0;
|
|
641
|
+
let nz = 0;
|
|
642
|
+
let e;
|
|
643
|
+
switch (plane) {
|
|
644
|
+
case 0:
|
|
645
|
+
nx = 1;
|
|
646
|
+
e = w.maxX[i] + r;
|
|
647
|
+
break;
|
|
648
|
+
case 1:
|
|
649
|
+
nx = -1;
|
|
650
|
+
e = -w.minX[i] + r;
|
|
651
|
+
break;
|
|
652
|
+
case 2:
|
|
653
|
+
ny = 1;
|
|
654
|
+
e = w.maxY[i] + r;
|
|
655
|
+
break;
|
|
656
|
+
case 3:
|
|
657
|
+
ny = -1;
|
|
658
|
+
e = -w.minY[i] + r;
|
|
659
|
+
break;
|
|
660
|
+
case 4:
|
|
661
|
+
nz = -1;
|
|
662
|
+
e = -w.minZ[i] + height;
|
|
663
|
+
break;
|
|
664
|
+
case 5: {
|
|
665
|
+
const slope = w.slope[i];
|
|
666
|
+
if (w.axis[i] === 0) nx = -slope;
|
|
667
|
+
else ny = -slope;
|
|
668
|
+
nz = 1;
|
|
669
|
+
e = w.lo[i] - slope * w.m0[i] + r * Math.abs(slope);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
default:
|
|
673
|
+
nz = 1;
|
|
674
|
+
e = w.maxZ[i];
|
|
675
|
+
}
|
|
676
|
+
PLANE[0] = nx;
|
|
677
|
+
PLANE[1] = ny;
|
|
678
|
+
PLANE[2] = nz;
|
|
679
|
+
PLANE[3] = e;
|
|
680
|
+
}
|
|
681
|
+
function insideSolid(m, px, py, pz, i, height) {
|
|
682
|
+
const w = m.w;
|
|
683
|
+
const r = m.r;
|
|
684
|
+
const skin = m.skin;
|
|
685
|
+
if (i >= w.count) return posedInside(m, px, py, pz, i - w.count, height);
|
|
686
|
+
if (i < w.boxCount) {
|
|
687
|
+
return px - (w.minX[i] - r) > skin && w.maxX[i] + r - px > skin && py - (w.minY[i] - r) > skin && w.maxY[i] + r - py > skin && pz - (w.minZ[i] - height) > skin && w.maxZ[i] - pz > skin;
|
|
688
|
+
}
|
|
689
|
+
for (let plane = 0; plane < 7; plane++) {
|
|
690
|
+
wedgePlane(m, i, plane, height);
|
|
691
|
+
const nx = PLANE[0];
|
|
692
|
+
const ny = PLANE[1];
|
|
693
|
+
const nz = PLANE[2];
|
|
694
|
+
const depth = -(nx * px + ny * py + nz * pz - PLANE[3]) * (1 / Math.sqrt(nx * nx + ny * ny + nz * nz));
|
|
695
|
+
if (!(depth > skin)) return false;
|
|
696
|
+
}
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
function covered(m, qx, qy, qz, px, py, pz, self, height) {
|
|
700
|
+
const worldCount = m.w.count;
|
|
701
|
+
const tree = m.w.tree;
|
|
702
|
+
let walked = 0;
|
|
703
|
+
let tested = 0;
|
|
704
|
+
const witness = (j) => {
|
|
705
|
+
tested += 1;
|
|
706
|
+
return j !== self && insideSolid(m, qx, qy, qz, j, height) && !insideSolid(m, px, py, pz, j, height);
|
|
707
|
+
};
|
|
708
|
+
let found = false;
|
|
709
|
+
if (tree === null) {
|
|
710
|
+
for (let j = 0; j < worldCount && !found; j++) found = witness(j);
|
|
711
|
+
} else {
|
|
712
|
+
const margin = queryMargin(qx, qy, qz, qx, qy, qz + height);
|
|
713
|
+
const pad = m.r + margin;
|
|
714
|
+
const x0 = qx - pad, x1 = qx + pad, y0 = qy - pad, y1 = qy + pad, z0 = qz - margin, z1 = qz + height + margin;
|
|
715
|
+
const { boxes, ref, levelEnd } = tree;
|
|
716
|
+
const count = levelEnd[0];
|
|
717
|
+
const stack = m.stack;
|
|
718
|
+
let top = 0;
|
|
719
|
+
stack[top++] = tree.root;
|
|
720
|
+
while (top > 0 && !found) {
|
|
721
|
+
const node = stack[--top];
|
|
722
|
+
walked += 1;
|
|
723
|
+
const o = node * 6;
|
|
724
|
+
if (boxes[o + 3] < x0 || boxes[o] > x1 || boxes[o + 4] < y0 || boxes[o + 1] > y1 || boxes[o + 5] < z0 || boxes[o + 2] > z1) continue;
|
|
725
|
+
if (node < count) {
|
|
726
|
+
found = witness(ref[node]);
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
const first = ref[node];
|
|
730
|
+
let end = first + TREE_FANOUT;
|
|
731
|
+
for (let l = 0; l < levelEnd.length; l++) {
|
|
732
|
+
if (first < levelEnd[l]) {
|
|
733
|
+
end = Math.min(end, levelEnd[l]);
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
for (let k = first; k < end; k++) stack[top++] = k;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
for (let j = worldCount; !found && j < worldCount + m.p.count; j++) found = witness(j);
|
|
741
|
+
if (costMeter !== null) {
|
|
742
|
+
costMeter.nodes += walked;
|
|
743
|
+
costMeter.tests += tested;
|
|
744
|
+
}
|
|
745
|
+
return found;
|
|
746
|
+
}
|
|
747
|
+
function lidCovered(m, px, py, pz, ux, uy, uz, depth, i, height) {
|
|
748
|
+
return covered(m, px + ux * depth, py + uy * depth, pz + uz * depth, px, py, pz, i, height);
|
|
749
|
+
}
|
|
750
|
+
function slabInside(m, px, py, pz, b, i, height, self) {
|
|
751
|
+
const r = m.r;
|
|
752
|
+
const skin = m.skin;
|
|
753
|
+
const lx = px - (b.minX[i] - r);
|
|
754
|
+
const hx = b.maxX[i] + r - px;
|
|
755
|
+
const ly = py - (b.minY[i] - r);
|
|
756
|
+
const hy = b.maxY[i] + r - py;
|
|
757
|
+
const lz = pz - (b.minZ[i] - height);
|
|
758
|
+
const hz = b.maxZ[i] - pz;
|
|
759
|
+
if (!(lx > skin && hx > skin && ly > skin && hy > skin && lz > skin && hz > skin)) return false;
|
|
760
|
+
let depth = lx;
|
|
761
|
+
let nx = -1;
|
|
762
|
+
let ny = 0;
|
|
763
|
+
let nz = 0;
|
|
764
|
+
if (hx < depth) [depth, nx, ny, nz] = [hx, 1, 0, 0];
|
|
765
|
+
if (ly < depth) [depth, nx, ny, nz] = [ly, 0, -1, 0];
|
|
766
|
+
if (hy < depth) [depth, nx, ny, nz] = [hy, 0, 1, 0];
|
|
767
|
+
if (hz < depth && !covered(m, px, py, pz + hz, px, py, pz, self, height)) [depth, nx, ny, nz] = [hz, 0, 0, 1];
|
|
768
|
+
m.tMin = 0;
|
|
769
|
+
m.nx = nx;
|
|
770
|
+
m.ny = ny;
|
|
771
|
+
m.nz = nz;
|
|
772
|
+
m.depth = depth;
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
function slab(m, px, py, pz, dx, dy, dz, i, height, b = m.w, self = i) {
|
|
776
|
+
const r = m.r;
|
|
777
|
+
m.depth = 0;
|
|
778
|
+
if (slabInside(m, px, py, pz, b, i, height, self)) return true;
|
|
779
|
+
m.tMin = 0;
|
|
780
|
+
m.tMax = 1;
|
|
781
|
+
m.ax = -1;
|
|
782
|
+
m.sg = 0;
|
|
783
|
+
if (!slabAxis(m, px, dx, b.minX[i] - r, b.maxX[i] + r, 0)) return false;
|
|
784
|
+
if (!slabAxis(m, py, dy, b.minY[i] - r, b.maxY[i] + r, 1)) return false;
|
|
785
|
+
if (!slabAxis(m, pz, dz, b.minZ[i] - height, b.maxZ[i], 2)) return false;
|
|
786
|
+
if (m.tMin >= 1 || m.ax < 0) return false;
|
|
787
|
+
m.nx = m.ax === 0 ? m.sg : 0;
|
|
788
|
+
m.ny = m.ax === 1 ? m.sg : 0;
|
|
789
|
+
m.nz = m.ax === 2 ? m.sg : 0;
|
|
790
|
+
return true;
|
|
791
|
+
}
|
|
792
|
+
function wedge(m, px, py, pz, dx, dy, dz, i, height) {
|
|
793
|
+
const w = m.w;
|
|
794
|
+
const r = m.r;
|
|
795
|
+
const slope = w.slope[i];
|
|
796
|
+
const axisX = w.axis[i] === 0;
|
|
797
|
+
let enter = 0;
|
|
798
|
+
let leave = 1;
|
|
799
|
+
let taken = false;
|
|
800
|
+
let inside = true;
|
|
801
|
+
let least = Number.POSITIVE_INFINITY;
|
|
802
|
+
let lx = 0;
|
|
803
|
+
let ly = 0;
|
|
804
|
+
let lz = 0;
|
|
805
|
+
m.depth = 0;
|
|
806
|
+
for (let plane = 0; plane < 7; plane++) {
|
|
807
|
+
let nx = 0;
|
|
808
|
+
let ny = 0;
|
|
809
|
+
let nz = 0;
|
|
810
|
+
let e;
|
|
811
|
+
switch (plane) {
|
|
812
|
+
case 0:
|
|
813
|
+
nx = 1;
|
|
814
|
+
e = w.maxX[i] + r;
|
|
815
|
+
break;
|
|
816
|
+
case 1:
|
|
817
|
+
nx = -1;
|
|
818
|
+
e = -w.minX[i] + r;
|
|
819
|
+
break;
|
|
820
|
+
case 2:
|
|
821
|
+
ny = 1;
|
|
822
|
+
e = w.maxY[i] + r;
|
|
823
|
+
break;
|
|
824
|
+
case 3:
|
|
825
|
+
ny = -1;
|
|
826
|
+
e = -w.minY[i] + r;
|
|
827
|
+
break;
|
|
828
|
+
case 4:
|
|
829
|
+
nz = -1;
|
|
830
|
+
e = -w.minZ[i] + height;
|
|
831
|
+
break;
|
|
832
|
+
case 5:
|
|
833
|
+
if (axisX) nx = -slope;
|
|
834
|
+
else ny = -slope;
|
|
835
|
+
nz = 1;
|
|
836
|
+
e = w.lo[i] - slope * w.m0[i] + r * Math.abs(slope);
|
|
837
|
+
break;
|
|
838
|
+
default:
|
|
839
|
+
nz = 1;
|
|
840
|
+
e = w.maxZ[i];
|
|
841
|
+
}
|
|
842
|
+
const dist = nx * px + ny * py + nz * pz - e;
|
|
843
|
+
const motion = nx * dx + ny * dy + nz * dz;
|
|
844
|
+
const k = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
845
|
+
const depth = -dist * k;
|
|
846
|
+
if (!(depth > m.skin)) inside = false;
|
|
847
|
+
else if (plane !== 4 && depth < least && !(inside && plane >= 5 && lidCovered(m, px, py, pz, nx * k, ny * k, nz * k, depth, i, height))) {
|
|
848
|
+
least = depth;
|
|
849
|
+
lx = nx * k;
|
|
850
|
+
ly = ny * k;
|
|
851
|
+
lz = nz * k;
|
|
852
|
+
}
|
|
853
|
+
if (Math.abs(motion) < 1e-9) {
|
|
854
|
+
if (dist > 0) return false;
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
const t = motion < 0 && dist <= 0 && dist > -m.skin ? 0 : -dist / motion;
|
|
858
|
+
if (motion < 0) {
|
|
859
|
+
if (t >= enter) {
|
|
860
|
+
enter = t;
|
|
861
|
+
m.nx = nx * k;
|
|
862
|
+
m.ny = ny * k;
|
|
863
|
+
m.nz = nz * k;
|
|
864
|
+
taken = true;
|
|
865
|
+
}
|
|
866
|
+
} else leave = Math.min(leave, t);
|
|
867
|
+
if (enter > leave) return false;
|
|
868
|
+
}
|
|
869
|
+
if (inside) {
|
|
870
|
+
m.tMin = 0;
|
|
871
|
+
m.nx = lx;
|
|
872
|
+
m.ny = ly;
|
|
873
|
+
m.nz = lz;
|
|
874
|
+
m.depth = least;
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
if (!taken || !(enter < 1) || !(leave >= 0)) return false;
|
|
878
|
+
m.tMin = enter;
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
function oboxPlane(m, j, plane, height) {
|
|
882
|
+
const p = m.p;
|
|
883
|
+
const c = p.cos[j];
|
|
884
|
+
const s = p.sin[j];
|
|
885
|
+
const cx = p.cx[j];
|
|
886
|
+
const cy = p.cy[j];
|
|
887
|
+
const hx = p.hx[j];
|
|
888
|
+
const hy = p.hy[j];
|
|
889
|
+
const r = m.r;
|
|
890
|
+
const reach = r * (Math.abs(c) + Math.abs(s));
|
|
891
|
+
let nx = 0;
|
|
892
|
+
let ny = 0;
|
|
893
|
+
let nz = 0;
|
|
894
|
+
let e;
|
|
895
|
+
switch (plane) {
|
|
896
|
+
case 0:
|
|
897
|
+
nx = c;
|
|
898
|
+
ny = s;
|
|
899
|
+
e = c * cx + s * cy + hx + reach;
|
|
900
|
+
break;
|
|
901
|
+
case 1:
|
|
902
|
+
nx = -c;
|
|
903
|
+
ny = -s;
|
|
904
|
+
e = -(c * cx + s * cy) + hx + reach;
|
|
905
|
+
break;
|
|
906
|
+
case 2:
|
|
907
|
+
nx = -s;
|
|
908
|
+
ny = c;
|
|
909
|
+
e = -s * cx + c * cy + hy + reach;
|
|
910
|
+
break;
|
|
911
|
+
case 3:
|
|
912
|
+
nx = s;
|
|
913
|
+
ny = -c;
|
|
914
|
+
e = s * cx - c * cy + hy + reach;
|
|
915
|
+
break;
|
|
916
|
+
case 4:
|
|
917
|
+
nx = 1;
|
|
918
|
+
e = cx + r + hx * Math.abs(c) + hy * Math.abs(s);
|
|
919
|
+
break;
|
|
920
|
+
case 5:
|
|
921
|
+
nx = -1;
|
|
922
|
+
e = -cx + r + hx * Math.abs(c) + hy * Math.abs(s);
|
|
923
|
+
break;
|
|
924
|
+
case 6:
|
|
925
|
+
ny = 1;
|
|
926
|
+
e = cy + r + hx * Math.abs(s) + hy * Math.abs(c);
|
|
927
|
+
break;
|
|
928
|
+
case 7:
|
|
929
|
+
ny = -1;
|
|
930
|
+
e = -cy + r + hx * Math.abs(s) + hy * Math.abs(c);
|
|
931
|
+
break;
|
|
932
|
+
case 8:
|
|
933
|
+
nz = -1;
|
|
934
|
+
e = -p.minZ[j] + height;
|
|
935
|
+
break;
|
|
936
|
+
default:
|
|
937
|
+
nz = 1;
|
|
938
|
+
e = p.maxZ[j];
|
|
939
|
+
}
|
|
940
|
+
PLANE[0] = nx;
|
|
941
|
+
PLANE[1] = ny;
|
|
942
|
+
PLANE[2] = nz;
|
|
943
|
+
PLANE[3] = e;
|
|
944
|
+
}
|
|
945
|
+
var OBOX_PLANES = 10;
|
|
946
|
+
var OBOX_BOTTOM = 8;
|
|
947
|
+
var OBOX_TOP = 9;
|
|
948
|
+
function posedOverlaps(m, j, px, py) {
|
|
949
|
+
const p = m.p;
|
|
950
|
+
const r = m.r;
|
|
951
|
+
if (!(px + r > p.minX[j] && px - r < p.maxX[j] && py + r > p.minY[j] && py - r < p.maxY[j])) return false;
|
|
952
|
+
const kind = p.kind[j];
|
|
953
|
+
if (kind === PosedKind.Box) return true;
|
|
954
|
+
const dx = px - p.cx[j];
|
|
955
|
+
const dy = py - p.cy[j];
|
|
956
|
+
if (kind === PosedKind.Disc) {
|
|
957
|
+
const ax = Math.max(Math.abs(dx) - r, 0);
|
|
958
|
+
const ay = Math.max(Math.abs(dy) - r, 0);
|
|
959
|
+
return ax * ax + ay * ay < p.radius[j] * p.radius[j];
|
|
960
|
+
}
|
|
961
|
+
const c = p.cos[j];
|
|
962
|
+
const s = p.sin[j];
|
|
963
|
+
const reach = r * (Math.abs(c) + Math.abs(s));
|
|
964
|
+
return Math.abs(c * dx + s * dy) < p.hx[j] + reach && Math.abs(-s * dx + c * dy) < p.hy[j] + reach;
|
|
965
|
+
}
|
|
966
|
+
function discDepth(m, j, px, py) {
|
|
967
|
+
const p = m.p;
|
|
968
|
+
const r = m.r;
|
|
969
|
+
const dx = px - p.cx[j];
|
|
970
|
+
const dy = py - p.cy[j];
|
|
971
|
+
const adx = Math.abs(dx);
|
|
972
|
+
const ady = Math.abs(dy);
|
|
973
|
+
const R = p.radius[j];
|
|
974
|
+
if (adx <= r && ady <= r) {
|
|
975
|
+
const alongX = adx >= ady;
|
|
976
|
+
PLANE[0] = alongX ? dx < 0 ? -1 : 1 : 0;
|
|
977
|
+
PLANE[1] = alongX ? 0 : dy < 0 ? -1 : 1;
|
|
978
|
+
return R + r - (alongX ? adx : ady);
|
|
979
|
+
}
|
|
980
|
+
const qx = adx > r ? dx < 0 ? -(adx - r) : adx - r : 0;
|
|
981
|
+
const qy = ady > r ? dy < 0 ? -(ady - r) : ady - r : 0;
|
|
982
|
+
const len = Math.sqrt(qx * qx + qy * qy);
|
|
983
|
+
PLANE[0] = qx / len;
|
|
984
|
+
PLANE[1] = qy / len;
|
|
985
|
+
return R - len;
|
|
986
|
+
}
|
|
987
|
+
function posedInside(m, px, py, pz, j, height) {
|
|
988
|
+
const p = m.p;
|
|
989
|
+
const skin = m.skin;
|
|
990
|
+
const kind = p.kind[j];
|
|
991
|
+
if (kind === PosedKind.Box) {
|
|
992
|
+
const r = m.r;
|
|
993
|
+
return px - (p.minX[j] - r) > skin && p.maxX[j] + r - px > skin && py - (p.minY[j] - r) > skin && p.maxY[j] + r - py > skin && pz - (p.minZ[j] - height) > skin && p.maxZ[j] - pz > skin;
|
|
994
|
+
}
|
|
995
|
+
if (!(pz - (p.minZ[j] - height) > skin && p.maxZ[j] - pz > skin)) return false;
|
|
996
|
+
if (kind === PosedKind.Disc) return discDepth(m, j, px, py) > skin;
|
|
997
|
+
for (let plane = 0; plane < OBOX_BOTTOM; plane++) {
|
|
998
|
+
oboxPlane(m, j, plane, height);
|
|
999
|
+
const nx = PLANE[0];
|
|
1000
|
+
const ny = PLANE[1];
|
|
1001
|
+
if (!(-(nx * px + ny * py - PLANE[3]) * (1 / Math.sqrt(nx * nx + ny * ny)) > skin)) return false;
|
|
1002
|
+
}
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
function oboxSweep(m, px, py, pz, dx, dy, dz, j, i, height) {
|
|
1006
|
+
let enter = 0;
|
|
1007
|
+
let leave = 1;
|
|
1008
|
+
let taken = false;
|
|
1009
|
+
let inside = true;
|
|
1010
|
+
let least = Number.POSITIVE_INFINITY;
|
|
1011
|
+
let lx = 0;
|
|
1012
|
+
let ly = 0;
|
|
1013
|
+
let lz = 0;
|
|
1014
|
+
m.depth = 0;
|
|
1015
|
+
for (let plane = 0; plane < OBOX_PLANES; plane++) {
|
|
1016
|
+
oboxPlane(m, j, plane, height);
|
|
1017
|
+
const nx = PLANE[0];
|
|
1018
|
+
const ny = PLANE[1];
|
|
1019
|
+
const nz = PLANE[2];
|
|
1020
|
+
const dist = nx * px + ny * py + nz * pz - PLANE[3];
|
|
1021
|
+
const motion = nx * dx + ny * dy + nz * dz;
|
|
1022
|
+
const k = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
1023
|
+
const depth = -dist * k;
|
|
1024
|
+
if (!(depth > m.skin)) inside = false;
|
|
1025
|
+
else if (plane !== OBOX_BOTTOM && depth < least && !(inside && plane === OBOX_TOP && lidCovered(m, px, py, pz, nx * k, ny * k, nz * k, depth, i, height))) {
|
|
1026
|
+
least = depth;
|
|
1027
|
+
lx = nx * k;
|
|
1028
|
+
ly = ny * k;
|
|
1029
|
+
lz = nz * k;
|
|
1030
|
+
}
|
|
1031
|
+
if (Math.abs(motion) < 1e-9) {
|
|
1032
|
+
if (dist > 0) return false;
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
const t = motion < 0 && dist <= 0 && dist > -m.skin ? 0 : -dist / motion;
|
|
1036
|
+
if (motion < 0) {
|
|
1037
|
+
if (t >= enter) {
|
|
1038
|
+
enter = t;
|
|
1039
|
+
m.nx = nx * k;
|
|
1040
|
+
m.ny = ny * k;
|
|
1041
|
+
m.nz = nz * k;
|
|
1042
|
+
taken = true;
|
|
1043
|
+
}
|
|
1044
|
+
} else leave = Math.min(leave, t);
|
|
1045
|
+
if (enter > leave) return false;
|
|
1046
|
+
}
|
|
1047
|
+
if (inside) {
|
|
1048
|
+
m.tMin = 0;
|
|
1049
|
+
m.nx = lx;
|
|
1050
|
+
m.ny = ly;
|
|
1051
|
+
m.nz = lz;
|
|
1052
|
+
m.depth = least;
|
|
1053
|
+
return true;
|
|
1054
|
+
}
|
|
1055
|
+
if (!taken || !(enter < 1) || !(leave >= 0)) return false;
|
|
1056
|
+
m.tMin = enter;
|
|
1057
|
+
return true;
|
|
1058
|
+
}
|
|
1059
|
+
function cylinder(m, px, py, pz, dx, dy, dz, ox, oy, R, z0, z1) {
|
|
1060
|
+
m.tMin = 0;
|
|
1061
|
+
m.tMax = 1;
|
|
1062
|
+
m.ax = -1;
|
|
1063
|
+
m.sg = 0;
|
|
1064
|
+
const ex = px - ox;
|
|
1065
|
+
const ey = py - oy;
|
|
1066
|
+
const a = dx * dx + dy * dy;
|
|
1067
|
+
const c = ex * ex + ey * ey - R * R;
|
|
1068
|
+
if (a < 1e-18) {
|
|
1069
|
+
if (!(c < 0)) return false;
|
|
1070
|
+
} else {
|
|
1071
|
+
const b = ex * dx + ey * dy;
|
|
1072
|
+
const disc = b * b - a * c;
|
|
1073
|
+
if (disc < 0) return false;
|
|
1074
|
+
const sq = Math.sqrt(disc);
|
|
1075
|
+
const t1 = (-b - sq) / a;
|
|
1076
|
+
const t2 = (-b + sq) / a;
|
|
1077
|
+
if (t1 >= m.tMin) {
|
|
1078
|
+
m.tMin = t1;
|
|
1079
|
+
m.ax = 3;
|
|
1080
|
+
}
|
|
1081
|
+
if (t2 < m.tMax) m.tMax = t2;
|
|
1082
|
+
if (m.tMin > m.tMax) return false;
|
|
1083
|
+
}
|
|
1084
|
+
if (!slabAxis(m, pz, dz, z0, z1, 2)) return false;
|
|
1085
|
+
if (m.tMin >= 1 || m.ax < 0) return false;
|
|
1086
|
+
if (m.ax === 3) {
|
|
1087
|
+
const hx = ex + dx * m.tMin;
|
|
1088
|
+
const hy = ey + dy * m.tMin;
|
|
1089
|
+
const len = Math.sqrt(hx * hx + hy * hy);
|
|
1090
|
+
m.nx = hx / len;
|
|
1091
|
+
m.ny = hy / len;
|
|
1092
|
+
m.nz = 0;
|
|
1093
|
+
} else {
|
|
1094
|
+
m.nx = 0;
|
|
1095
|
+
m.ny = 0;
|
|
1096
|
+
m.nz = m.sg;
|
|
1097
|
+
}
|
|
1098
|
+
return true;
|
|
1099
|
+
}
|
|
1100
|
+
var CROSS = { minX: new Float64Array(2), minY: new Float64Array(2), minZ: new Float64Array(2), maxX: new Float64Array(2), maxY: new Float64Array(2), maxZ: new Float64Array(2) };
|
|
1101
|
+
function discSweep(m, px, py, pz, dx, dy, dz, j, i, height) {
|
|
1102
|
+
const p = m.p;
|
|
1103
|
+
const r = m.r;
|
|
1104
|
+
const R = p.radius[j];
|
|
1105
|
+
const cx = p.cx[j];
|
|
1106
|
+
const cy = p.cy[j];
|
|
1107
|
+
const z0 = p.minZ[j] - height;
|
|
1108
|
+
const z1 = p.maxZ[j];
|
|
1109
|
+
m.depth = 0;
|
|
1110
|
+
const side = discDepth(m, j, px, py);
|
|
1111
|
+
const lz = pz - z0;
|
|
1112
|
+
const hz = z1 - pz;
|
|
1113
|
+
if (side > m.skin && lz > m.skin && hz > m.skin) {
|
|
1114
|
+
m.tMin = 0;
|
|
1115
|
+
if (hz < side && !covered(m, px, py, pz + hz, px, py, pz, i, height)) [m.nx, m.ny, m.nz, m.depth] = [0, 0, 1, hz];
|
|
1116
|
+
else [m.nx, m.ny, m.nz, m.depth] = [PLANE[0], PLANE[1], 0, side];
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
let found = false;
|
|
1120
|
+
let bt = 0;
|
|
1121
|
+
let bx = 0;
|
|
1122
|
+
let by = 0;
|
|
1123
|
+
let bz = 0;
|
|
1124
|
+
CROSS.minX[0] = cx - R;
|
|
1125
|
+
CROSS.maxX[0] = cx + R;
|
|
1126
|
+
CROSS.minY[0] = cy;
|
|
1127
|
+
CROSS.maxY[0] = cy;
|
|
1128
|
+
CROSS.minX[1] = cx;
|
|
1129
|
+
CROSS.maxX[1] = cx;
|
|
1130
|
+
CROSS.minY[1] = cy - R;
|
|
1131
|
+
CROSS.maxY[1] = cy + R;
|
|
1132
|
+
for (let k = 0; k < 2; k++) {
|
|
1133
|
+
CROSS.minZ[k] = p.minZ[j];
|
|
1134
|
+
CROSS.maxZ[k] = z1;
|
|
1135
|
+
if (slab(m, px, py, pz, dx, dy, dz, k, height, CROSS, i) && (!found || m.tMin < bt)) {
|
|
1136
|
+
found = true;
|
|
1137
|
+
bt = m.tMin;
|
|
1138
|
+
bx = m.nx;
|
|
1139
|
+
by = m.ny;
|
|
1140
|
+
bz = m.nz;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
for (let k = 0; k < 4; k++) {
|
|
1144
|
+
const ox = k % 2 === 0 ? cx - r : cx + r;
|
|
1145
|
+
const oy = k < 2 ? cy - r : cy + r;
|
|
1146
|
+
if (cylinder(m, px, py, pz, dx, dy, dz, ox, oy, R, z0, z1) && (!found || m.tMin < bt)) {
|
|
1147
|
+
found = true;
|
|
1148
|
+
bt = m.tMin;
|
|
1149
|
+
bx = m.nx;
|
|
1150
|
+
by = m.ny;
|
|
1151
|
+
bz = m.nz;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
if (!found) return false;
|
|
1155
|
+
m.tMin = bt;
|
|
1156
|
+
m.nx = bx;
|
|
1157
|
+
m.ny = by;
|
|
1158
|
+
m.nz = bz;
|
|
1159
|
+
m.depth = 0;
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
function posedSweep(m, px, py, pz, dx, dy, dz, i, height) {
|
|
1163
|
+
const j = i - m.w.count;
|
|
1164
|
+
const kind = m.p.kind[j];
|
|
1165
|
+
if (kind === PosedKind.Box) return slab(m, px, py, pz, dx, dy, dz, j, height, m.p, i);
|
|
1166
|
+
if (kind === PosedKind.OBox) return oboxSweep(m, px, py, pz, dx, dy, dz, j, i, height);
|
|
1167
|
+
return discSweep(m, px, py, pz, dx, dy, dz, j, i, height);
|
|
1168
|
+
}
|
|
1169
|
+
function sweep(m, px, py, pz, dx, dy, dz) {
|
|
1170
|
+
const w = m.w;
|
|
1171
|
+
const height = hullAt(m, m.duck);
|
|
1172
|
+
const ex = px + dx;
|
|
1173
|
+
const ey = py + dy;
|
|
1174
|
+
const ez = pz + dz;
|
|
1175
|
+
query(m, px < ex ? px : ex, py < ey ? py : ey, pz < ez ? pz : ez, px < ex ? ex : px, py < ey ? ey : py, (pz < ez ? ez : pz) + height);
|
|
1176
|
+
const n = visits(m);
|
|
1177
|
+
let found = false;
|
|
1178
|
+
let bestT = 0;
|
|
1179
|
+
let bestI = 0;
|
|
1180
|
+
let bnx = 0;
|
|
1181
|
+
let bny = 0;
|
|
1182
|
+
let bnz = 0;
|
|
1183
|
+
let bd = 0;
|
|
1184
|
+
if (costMeter !== null) costMeter.sweeps += n;
|
|
1185
|
+
for (let k = 0; k < n; k++) {
|
|
1186
|
+
const i = solidAt(m, k);
|
|
1187
|
+
if (costMeter !== null && i >= w.boxCount && i < w.count) costMeter.wedges += 1;
|
|
1188
|
+
const hit = i < w.boxCount ? slab(m, px, py, pz, dx, dy, dz, i, height) : i < w.count ? wedge(m, px, py, pz, dx, dy, dz, i, height) : posedSweep(m, px, py, pz, dx, dy, dz, i, height);
|
|
1189
|
+
if (hit && (!found || m.tMin < bestT || m.tMin === bestT && i < bestI)) {
|
|
1190
|
+
found = true;
|
|
1191
|
+
bestT = m.tMin;
|
|
1192
|
+
bestI = i;
|
|
1193
|
+
bnx = m.nx;
|
|
1194
|
+
bny = m.ny;
|
|
1195
|
+
bnz = m.nz;
|
|
1196
|
+
bd = m.depth;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
m.hit = found;
|
|
1200
|
+
if (found) {
|
|
1201
|
+
m.tMin = Math.max(0, bestT - 1e-4);
|
|
1202
|
+
m.nx = bnx;
|
|
1203
|
+
m.ny = bny;
|
|
1204
|
+
m.nz = bnz;
|
|
1205
|
+
m.depth = bd;
|
|
1206
|
+
} else {
|
|
1207
|
+
m.tMin = 1;
|
|
1208
|
+
m.depth = 0;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function slideMove(m, T, car = false) {
|
|
1212
|
+
let remaining = T;
|
|
1213
|
+
let hasPrev = false;
|
|
1214
|
+
let qx = 0;
|
|
1215
|
+
let qy = 0;
|
|
1216
|
+
let qz = 0;
|
|
1217
|
+
for (let bump = 0; bump < 4 && remaining > 1e-6; bump++) {
|
|
1218
|
+
if (m.vx * m.vx + m.vy * m.vy + m.vz * m.vz < 1e-8) return;
|
|
1219
|
+
sweep(m, m.x, m.y, m.z, m.vx * remaining, m.vy * remaining, m.vz * remaining);
|
|
1220
|
+
const t = m.tMin;
|
|
1221
|
+
if (t > 0) {
|
|
1222
|
+
const s = t * remaining;
|
|
1223
|
+
m.x = m.x + m.vx * s;
|
|
1224
|
+
m.y = m.y + m.vy * s;
|
|
1225
|
+
m.z = m.z + m.vz * s;
|
|
1226
|
+
remaining = remaining - s;
|
|
1227
|
+
}
|
|
1228
|
+
if (!m.hit) return;
|
|
1229
|
+
if (m.depth > 0) {
|
|
1230
|
+
m.x = m.x + m.nx * m.depth;
|
|
1231
|
+
m.y = m.y + m.ny * m.depth;
|
|
1232
|
+
m.z = m.z + m.nz * m.depth;
|
|
1233
|
+
}
|
|
1234
|
+
let nx = m.nx;
|
|
1235
|
+
let ny = m.ny;
|
|
1236
|
+
let nz = m.nz;
|
|
1237
|
+
let lift = false;
|
|
1238
|
+
if (car && nz > 0) {
|
|
1239
|
+
if (nz >= m.standable) lift = true;
|
|
1240
|
+
else {
|
|
1241
|
+
const k = 1 / Math.sqrt(nx * nx + ny * ny);
|
|
1242
|
+
nx = nx * k;
|
|
1243
|
+
ny = ny * k;
|
|
1244
|
+
nz = 0;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
m.x = m.x + nx * m.skin;
|
|
1248
|
+
m.y = m.y + ny * m.skin;
|
|
1249
|
+
m.z = m.z + nz * m.skin;
|
|
1250
|
+
const b = m.vx * nx + m.vy * ny + m.vz * nz;
|
|
1251
|
+
if (b < 0) {
|
|
1252
|
+
if (lift) m.vz = m.vz - b / nz;
|
|
1253
|
+
else {
|
|
1254
|
+
m.vx = m.vx + nx * -b;
|
|
1255
|
+
m.vy = m.vy + ny * -b;
|
|
1256
|
+
m.vz = m.vz + nz * -b;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
if (hasPrev) {
|
|
1260
|
+
let cx = qy * nz - qz * ny;
|
|
1261
|
+
let cy = qz * nx - qx * nz;
|
|
1262
|
+
let cz = qx * ny - qy * nx;
|
|
1263
|
+
const L = cx * cx + cy * cy + cz * cz;
|
|
1264
|
+
if (L > 1e-6) {
|
|
1265
|
+
const k = 1 / Math.sqrt(L);
|
|
1266
|
+
cx = cx * k;
|
|
1267
|
+
cy = cy * k;
|
|
1268
|
+
cz = cz * k;
|
|
1269
|
+
const d = m.vx * cx + m.vy * cy + m.vz * cz;
|
|
1270
|
+
m.vx = cx * d;
|
|
1271
|
+
m.vy = cy * d;
|
|
1272
|
+
m.vz = cz * d;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
hasPrev = true;
|
|
1276
|
+
qx = nx;
|
|
1277
|
+
qy = ny;
|
|
1278
|
+
qz = nz;
|
|
1279
|
+
if (m.vx * m.vx + m.vy * m.vy + m.vz * m.vz < 1e-8) return;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
function groundMove(m, car = false) {
|
|
1283
|
+
const x0 = m.x, y0 = m.y, z0 = m.z;
|
|
1284
|
+
const vx0 = m.vx, vy0 = m.vy, vz0 = m.vz;
|
|
1285
|
+
slideMove(m, m.h, car);
|
|
1286
|
+
const x1 = m.x, y1 = m.y, z1 = m.z;
|
|
1287
|
+
const vx1 = m.vx, vy1 = m.vy, vz1 = m.vz;
|
|
1288
|
+
const ax = x1 - x0;
|
|
1289
|
+
const ay = y1 - y0;
|
|
1290
|
+
const flat = ax * ax + ay * ay;
|
|
1291
|
+
m.x = x0;
|
|
1292
|
+
m.y = y0;
|
|
1293
|
+
m.z = z0;
|
|
1294
|
+
m.vx = vx0;
|
|
1295
|
+
m.vy = vy0;
|
|
1296
|
+
m.vz = vz0;
|
|
1297
|
+
const lift = m.stepHeight + m.skin;
|
|
1298
|
+
sweep(m, x0, y0, z0, 0, 0, lift);
|
|
1299
|
+
const t = m.tMin;
|
|
1300
|
+
const zl = z0 + lift * t;
|
|
1301
|
+
if (t > 0 && fits(m, x0, y0, zl, hullAt(m, m.duck))) {
|
|
1302
|
+
m.z = zl;
|
|
1303
|
+
slideMove(m, m.h, car);
|
|
1304
|
+
const g = traceDown(m, m.x, m.y, m.z, m.stepHeight * 2, 1e-3);
|
|
1305
|
+
if (g === g) m.z = g;
|
|
1306
|
+
const bx = m.x - x0;
|
|
1307
|
+
const by = m.y - y0;
|
|
1308
|
+
const stepped = bx * bx + by * by;
|
|
1309
|
+
if (g === g && stepped > flat + 1e-3 * m.unit * m.unit) {
|
|
1310
|
+
m.vz = 0;
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
m.x = x1;
|
|
1315
|
+
m.y = y1;
|
|
1316
|
+
m.z = z1;
|
|
1317
|
+
m.vx = vx1;
|
|
1318
|
+
m.vy = vy1;
|
|
1319
|
+
m.vz = vz1;
|
|
1320
|
+
}
|
|
1321
|
+
function stayOnGround(m) {
|
|
1322
|
+
const g = traceDown(m, m.x, m.y, m.z, m.stepHeight, m.groundSnap);
|
|
1323
|
+
if (g === g) {
|
|
1324
|
+
m.z = g;
|
|
1325
|
+
m.vz = 0;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
function applyFriction(m) {
|
|
1329
|
+
const speed = Math.sqrt(m.vx * m.vx + m.vy * m.vy);
|
|
1330
|
+
if (speed < 0.1 * m.unit) {
|
|
1331
|
+
m.vx = 0;
|
|
1332
|
+
m.vy = 0;
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
const control = speed < m.stopSpeed ? m.stopSpeed : speed;
|
|
1336
|
+
const drop = control * m.friction * m.h;
|
|
1337
|
+
const k = Math.max(0, speed - drop) / speed;
|
|
1338
|
+
m.vx = m.vx * k;
|
|
1339
|
+
m.vy = m.vy * k;
|
|
1340
|
+
}
|
|
1341
|
+
function groundAccelerate(m) {
|
|
1342
|
+
const add = m.wishSpeed - (m.vx * m.dirX + m.vy * m.dirY);
|
|
1343
|
+
if (add <= 0) return;
|
|
1344
|
+
let a = m.accelerate * m.h * Math.max(m.wishSpeed, m.maxSpeed);
|
|
1345
|
+
if (a > add) a = add;
|
|
1346
|
+
m.vx = m.vx + a * m.dirX;
|
|
1347
|
+
m.vy = m.vy + a * m.dirY;
|
|
1348
|
+
}
|
|
1349
|
+
function airAccelerate(m) {
|
|
1350
|
+
const add = Math.min(m.wishSpeed, m.airMaxWishSpeed) - (m.vx * m.dirX + m.vy * m.dirY);
|
|
1351
|
+
if (add <= 0) return;
|
|
1352
|
+
let a = m.airAccelerate * m.wishSpeed * m.h;
|
|
1353
|
+
if (a > add) a = add;
|
|
1354
|
+
m.vx = m.vx + a * m.dirX;
|
|
1355
|
+
m.vy = m.vy + a * m.dirY;
|
|
1356
|
+
}
|
|
1357
|
+
var stored = (v) => unsignedZero(Math.fround(clamp(v, -MAX_ABS, MAX_ABS)));
|
|
1358
|
+
function subStep(m, duckDown, walk) {
|
|
1359
|
+
const rate = m.h / m.duckTime;
|
|
1360
|
+
if (duckDown && !m.onGround && !m.airDuck) {
|
|
1361
|
+
} else if (duckDown) {
|
|
1362
|
+
const next = Math.min(1, m.duck + rate);
|
|
1363
|
+
if (!m.onGround) m.z = m.z + (hullAt(m, m.duck) - hullAt(m, next));
|
|
1364
|
+
m.duck = next;
|
|
1365
|
+
m.ducking = true;
|
|
1366
|
+
} else if (m.duck > 0) {
|
|
1367
|
+
const next = Math.max(0, m.duck - rate);
|
|
1368
|
+
const zn = m.onGround ? m.z : m.z - (hullAt(m, next) - hullAt(m, m.duck));
|
|
1369
|
+
if (fits(m, m.x, m.y, zn, hullAt(m, next))) {
|
|
1370
|
+
m.duck = next;
|
|
1371
|
+
m.z = zn;
|
|
1372
|
+
if (m.duck === 0) m.ducking = false;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
const was = m.onGround;
|
|
1376
|
+
checkGround(m);
|
|
1377
|
+
if (m.onGround && !was) m.vz = 0;
|
|
1378
|
+
m.base = m.maxSpeed * m.cap;
|
|
1379
|
+
const target = m.ducking ? m.base * m.crouchSpeed : walk ? m.base * m.walkSpeed : m.jogging ? m.base * m.jogSpeed : m.base;
|
|
1380
|
+
m.wishSpeed = m.amount * target;
|
|
1381
|
+
if (m.onGround) {
|
|
1382
|
+
if (m.pressed) {
|
|
1383
|
+
m.vz = m.jumpImpulse;
|
|
1384
|
+
m.onGround = false;
|
|
1385
|
+
m.pressed = false;
|
|
1386
|
+
airAccelerate(m);
|
|
1387
|
+
} else {
|
|
1388
|
+
applyFriction(m);
|
|
1389
|
+
groundAccelerate(m);
|
|
1390
|
+
m.vz = 0;
|
|
1391
|
+
}
|
|
1392
|
+
} else airAccelerate(m);
|
|
1393
|
+
const falling = !m.onGround;
|
|
1394
|
+
if (falling) m.vz = m.vz - m.gravity * m.h * 0.5;
|
|
1395
|
+
m.vx = clamp(m.vx, -m.maxVelocity, m.maxVelocity);
|
|
1396
|
+
m.vy = clamp(m.vy, -m.maxVelocity, m.maxVelocity);
|
|
1397
|
+
m.vz = clamp(m.vz, -m.maxVelocity, m.maxVelocity);
|
|
1398
|
+
if (m.onGround) {
|
|
1399
|
+
groundMove(m);
|
|
1400
|
+
stayOnGround(m);
|
|
1401
|
+
} else slideMove(m, m.h);
|
|
1402
|
+
checkGround(m);
|
|
1403
|
+
if (falling && !m.onGround) m.vz = m.vz - m.gravity * m.h * 0.5;
|
|
1404
|
+
else if (m.onGround) m.vz = 0;
|
|
1405
|
+
m.x = stored(m.x);
|
|
1406
|
+
m.y = stored(m.y);
|
|
1407
|
+
m.z = stored(m.z);
|
|
1408
|
+
m.vx = stored(m.vx);
|
|
1409
|
+
m.vy = stored(m.vy);
|
|
1410
|
+
m.vz = stored(m.vz);
|
|
1411
|
+
m.duck = unsignedZero(Math.fround(m.duck));
|
|
1412
|
+
}
|
|
1413
|
+
var bodyValue = (v, fallback, lo, hi) => unsignedZero(Math.fround(clamp(typeof v === "number" && Number.isFinite(v) ? v : fallback, lo, hi)));
|
|
1414
|
+
var bodyAngle = (v) => unsignedZero(Math.fround(typeof v === "number" && Number.isFinite(v) ? v : 0));
|
|
1415
|
+
var scratch = { cand: new Int32Array(0), stack: new Int32Array(TREE_FANOUT * 16) };
|
|
1416
|
+
function scratchFor(w) {
|
|
1417
|
+
if (scratch.cand.length < w.count) scratch = { cand: new Int32Array(w.count), stack: scratch.stack };
|
|
1418
|
+
return scratch;
|
|
1419
|
+
}
|
|
1420
|
+
function hullOps(preset, params, world = EMPTY_WORLD) {
|
|
1421
|
+
const w = compiledOf(world);
|
|
1422
|
+
const f = Math.fround;
|
|
1423
|
+
const height = (k) => f(preset === "vehicle" ? params.height : params[k]);
|
|
1424
|
+
const hullOf = (body, solids) => {
|
|
1425
|
+
const room = scratchFor(w);
|
|
1426
|
+
return {
|
|
1427
|
+
w,
|
|
1428
|
+
p: posedWorld(solids),
|
|
1429
|
+
r: f(params.radius),
|
|
1430
|
+
unit: unitOf(f(params.radius), preset === "vehicle" ? VEHICLE_RADIUS : FIRSTPERSON_RADIUS),
|
|
1431
|
+
standHeight: height("standHeight"),
|
|
1432
|
+
crouchHeight: height("crouchHeight"),
|
|
1433
|
+
stepHeight: f(params.stepHeight),
|
|
1434
|
+
standable: f(params.standable),
|
|
1435
|
+
groundTrace: f(params.groundTrace),
|
|
1436
|
+
groundSnap: f(params.groundSnap),
|
|
1437
|
+
skin: f(params.skin),
|
|
1438
|
+
x: body.x,
|
|
1439
|
+
y: body.y,
|
|
1440
|
+
z: body.z,
|
|
1441
|
+
vx: 0,
|
|
1442
|
+
vy: 0,
|
|
1443
|
+
vz: body.vz ?? 0,
|
|
1444
|
+
duck: preset === "vehicle" ? 0 : body.duck ?? 0,
|
|
1445
|
+
onGround: false,
|
|
1446
|
+
h: 0,
|
|
1447
|
+
tMin: 0,
|
|
1448
|
+
tMax: 0,
|
|
1449
|
+
ax: -1,
|
|
1450
|
+
sg: 0,
|
|
1451
|
+
nx: 0,
|
|
1452
|
+
ny: 0,
|
|
1453
|
+
nz: 0,
|
|
1454
|
+
hit: false,
|
|
1455
|
+
depth: 0,
|
|
1456
|
+
cand: room.cand,
|
|
1457
|
+
nCand: -1,
|
|
1458
|
+
pc: POSED_SCRATCH,
|
|
1459
|
+
nPosed: 0,
|
|
1460
|
+
stack: room.stack
|
|
1461
|
+
};
|
|
1462
|
+
};
|
|
1463
|
+
return {
|
|
1464
|
+
support(body, solids) {
|
|
1465
|
+
const m = hullOf(body, solids);
|
|
1466
|
+
if (m.vz > 0.1 * m.unit) return -1;
|
|
1467
|
+
const g = traceDown(m, m.x, m.y, m.z, m.groundTrace, m.groundSnap);
|
|
1468
|
+
if (g !== g) return -1;
|
|
1469
|
+
for (let j = 0; j < m.p.count; j++) if (m.p.maxZ[j] === g && posedOverlaps(m, j, m.x, m.y)) return j;
|
|
1470
|
+
return -1;
|
|
1471
|
+
},
|
|
1472
|
+
inside(body, solids, k) {
|
|
1473
|
+
const m = hullOf(body, solids);
|
|
1474
|
+
return insideSolid(m, m.x, m.y, m.z, w.count + k, hullAt(m, m.duck));
|
|
1475
|
+
},
|
|
1476
|
+
pushOut(body, solids) {
|
|
1477
|
+
const m = hullOf(body, solids);
|
|
1478
|
+
for (let k = 0; k < 4; k++) {
|
|
1479
|
+
sweep(m, m.x, m.y, m.z, 0, 0, 0);
|
|
1480
|
+
if (!m.hit) break;
|
|
1481
|
+
m.x = m.x + m.nx * m.depth;
|
|
1482
|
+
m.y = m.y + m.ny * m.depth;
|
|
1483
|
+
m.z = m.z + m.nz * m.depth;
|
|
1484
|
+
m.x = m.x + m.nx * m.skin;
|
|
1485
|
+
m.y = m.y + m.ny * m.skin;
|
|
1486
|
+
m.z = m.z + m.nz * m.skin;
|
|
1487
|
+
}
|
|
1488
|
+
const height2 = hullAt(m, m.duck);
|
|
1489
|
+
let inside = false;
|
|
1490
|
+
for (let i = 0; i < w.count + m.p.count && !inside; i++) inside = insideSolid(m, m.x, m.y, m.z, i, height2);
|
|
1491
|
+
return { x: m.x, y: m.y, z: m.z, inside };
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
function stepFirstperson(body, input, dt, params, world = EMPTY_WORLD, posed = NO_POSED) {
|
|
1496
|
+
const w = compiledOf(world);
|
|
1497
|
+
const room = scratchFor(w);
|
|
1498
|
+
const f = Math.fround;
|
|
1499
|
+
const mstate = mask(body.mstate) & MSTATE_BITS;
|
|
1500
|
+
const m = {
|
|
1501
|
+
w,
|
|
1502
|
+
p: posed,
|
|
1503
|
+
maxSpeed: f(params.maxSpeed),
|
|
1504
|
+
walkSpeed: f(params.walkSpeed),
|
|
1505
|
+
jogSpeed: f(params.jogSpeed),
|
|
1506
|
+
jogging: false,
|
|
1507
|
+
crouchSpeed: f(params.crouchSpeed),
|
|
1508
|
+
accelerate: f(params.accelerate),
|
|
1509
|
+
airAccelerate: f(params.airAccelerate),
|
|
1510
|
+
airMaxWishSpeed: f(params.airMaxWishSpeed),
|
|
1511
|
+
friction: f(params.friction),
|
|
1512
|
+
stopSpeed: f(params.stopSpeed),
|
|
1513
|
+
gravity: f(params.gravity),
|
|
1514
|
+
jumpImpulse: f(params.jumpImpulse),
|
|
1515
|
+
maxVelocity: f(params.maxVelocity),
|
|
1516
|
+
r: f(params.radius),
|
|
1517
|
+
unit: unitOf(f(params.radius), FIRSTPERSON_RADIUS),
|
|
1518
|
+
standHeight: f(params.standHeight),
|
|
1519
|
+
crouchHeight: f(params.crouchHeight),
|
|
1520
|
+
duckTime: f(params.duckTime),
|
|
1521
|
+
airDuck: params.airDuck !== 0,
|
|
1522
|
+
stepHeight: f(params.stepHeight),
|
|
1523
|
+
standable: f(params.standable),
|
|
1524
|
+
groundTrace: f(params.groundTrace),
|
|
1525
|
+
groundSnap: f(params.groundSnap),
|
|
1526
|
+
skin: f(params.skin),
|
|
1527
|
+
x: bodyValue(body.x, 0, -MAX_ABS, MAX_ABS),
|
|
1528
|
+
y: bodyValue(body.y, 0, -MAX_ABS, MAX_ABS),
|
|
1529
|
+
z: bodyValue(body.z, 0, -MAX_ABS, MAX_ABS),
|
|
1530
|
+
vx: bodyValue(body.vx, 0, -MAX_ABS, MAX_ABS),
|
|
1531
|
+
vy: bodyValue(body.vy, 0, -MAX_ABS, MAX_ABS),
|
|
1532
|
+
vz: bodyValue(body.vz, 0, -MAX_ABS, MAX_ABS),
|
|
1533
|
+
duck: bodyValue(body.duck, 0, 0, 1),
|
|
1534
|
+
cap: bodyValue(body.cap, 1, 0, 4),
|
|
1535
|
+
onGround: (mstate & MoveState.ON_GROUND) !== 0,
|
|
1536
|
+
ducking: (mstate & MoveState.DUCKING) !== 0,
|
|
1537
|
+
jumpHeld: (mstate & MoveState.JUMP_HELD) !== 0,
|
|
1538
|
+
pressed: false,
|
|
1539
|
+
h: 0,
|
|
1540
|
+
dirX: 0,
|
|
1541
|
+
dirY: 0,
|
|
1542
|
+
amount: 0,
|
|
1543
|
+
base: 0,
|
|
1544
|
+
wishSpeed: 0,
|
|
1545
|
+
tMin: 0,
|
|
1546
|
+
tMax: 0,
|
|
1547
|
+
ax: -1,
|
|
1548
|
+
sg: 0,
|
|
1549
|
+
nx: 0,
|
|
1550
|
+
ny: 0,
|
|
1551
|
+
nz: 0,
|
|
1552
|
+
hit: false,
|
|
1553
|
+
depth: 0,
|
|
1554
|
+
cand: room.cand,
|
|
1555
|
+
nCand: -1,
|
|
1556
|
+
pc: POSED_SCRATCH,
|
|
1557
|
+
nPosed: 0,
|
|
1558
|
+
stack: room.stack
|
|
1559
|
+
};
|
|
1560
|
+
const mx = axis(input.moveX);
|
|
1561
|
+
const my = axis(input.moveY);
|
|
1562
|
+
const len = Math.sqrt(mx * mx + my * my);
|
|
1563
|
+
if (len > 1e-4) {
|
|
1564
|
+
m.dirX = mx / len;
|
|
1565
|
+
m.dirY = my / len;
|
|
1566
|
+
m.amount = Math.min(len, 1);
|
|
1567
|
+
}
|
|
1568
|
+
const buttons = mask(input.buttons);
|
|
1569
|
+
const jump = (buttons & params.jumpBit) !== 0;
|
|
1570
|
+
const duckDown = (buttons & params.duckBit) !== 0;
|
|
1571
|
+
const walk = (buttons & params.walkBit) !== 0;
|
|
1572
|
+
m.jogging = params.runBit !== 0 && (buttons & params.runBit) === 0;
|
|
1573
|
+
const step = stepSeconds(dt, PRESET_MAX_DT_S);
|
|
1574
|
+
m.h = step / params.substeps;
|
|
1575
|
+
const yaw = wrappedAngle(input.yaw) ?? bodyAngle(body.yaw);
|
|
1576
|
+
const pitchLimit = f(params.pitchLimit);
|
|
1577
|
+
const pitch = unsignedZero(clamp(wrappedAngle(input.pitch) ?? bodyAngle(body.pitch), -pitchLimit, pitchLimit));
|
|
1578
|
+
if (step > 0) {
|
|
1579
|
+
m.pressed = jump && !m.jumpHeld && (params.capZeroJump !== 0 || m.cap !== 0);
|
|
1580
|
+
m.jumpHeld = jump;
|
|
1581
|
+
for (let i = 0; i < params.substeps; i++) subStep(m, duckDown, walk);
|
|
1582
|
+
}
|
|
1583
|
+
return {
|
|
1584
|
+
x: m.x,
|
|
1585
|
+
y: m.y,
|
|
1586
|
+
z: m.z,
|
|
1587
|
+
vx: m.vx,
|
|
1588
|
+
vy: m.vy,
|
|
1589
|
+
vz: m.vz,
|
|
1590
|
+
yaw,
|
|
1591
|
+
pitch,
|
|
1592
|
+
duck: m.duck,
|
|
1593
|
+
mstate: (m.onGround ? MoveState.ON_GROUND : 0) + (m.ducking ? MoveState.DUCKING : 0) + (m.jumpHeld ? MoveState.JUMP_HELD : 0)
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
var VEHICLE_DEFAULTS = Object.freeze({
|
|
1597
|
+
maxSpeed: 1410,
|
|
1598
|
+
accel: 1e3,
|
|
1599
|
+
brake: 3500,
|
|
1600
|
+
coast: 525,
|
|
1601
|
+
boostAccel: 1e3,
|
|
1602
|
+
boostMaxSpeed: 2300,
|
|
1603
|
+
boostBurn: 0.3333333432674408,
|
|
1604
|
+
grip: 6e3,
|
|
1605
|
+
driftGrip: 1500,
|
|
1606
|
+
turnRate: 2.2,
|
|
1607
|
+
turnSpeed: 400,
|
|
1608
|
+
spinAccel: 30,
|
|
1609
|
+
airTurnRate: 5.5,
|
|
1610
|
+
airSpinAccel: 9,
|
|
1611
|
+
gravity: 650,
|
|
1612
|
+
jumpImpulse: 450,
|
|
1613
|
+
maxVelocity: 6e3,
|
|
1614
|
+
radius: 50,
|
|
1615
|
+
height: 36,
|
|
1616
|
+
stepHeight: 8,
|
|
1617
|
+
standable: 0.7,
|
|
1618
|
+
groundTrace: 4,
|
|
1619
|
+
groundSnap: 2,
|
|
1620
|
+
skin: 0.03,
|
|
1621
|
+
jumpBit: 1,
|
|
1622
|
+
boostBit: 2,
|
|
1623
|
+
driftBit: 4,
|
|
1624
|
+
substeps: 2,
|
|
1625
|
+
capZeroJump: 1,
|
|
1626
|
+
world: EMPTY_WORLD_HASH
|
|
1627
|
+
});
|
|
1628
|
+
var VEHICLE_RANGE = {
|
|
1629
|
+
maxSpeed: inRange(0, 1e5),
|
|
1630
|
+
accel: inRange(0, 1e5),
|
|
1631
|
+
brake: inRange(0, 1e5),
|
|
1632
|
+
coast: inRange(0, 1e5),
|
|
1633
|
+
boostAccel: inRange(0, 1e5),
|
|
1634
|
+
boostMaxSpeed: inRange(0, 1e5),
|
|
1635
|
+
boostBurn: inRange(0, 1e3),
|
|
1636
|
+
grip: inRange(0, 1e5),
|
|
1637
|
+
driftGrip: inRange(0, 1e5),
|
|
1638
|
+
turnRate: inRange(0, 100),
|
|
1639
|
+
turnSpeed: aboveZero(1e5),
|
|
1640
|
+
spinAccel: inRange(0, 1e4),
|
|
1641
|
+
airTurnRate: inRange(0, 100),
|
|
1642
|
+
airSpinAccel: inRange(0, 1e4),
|
|
1643
|
+
gravity: inRange(0, 1e5),
|
|
1644
|
+
jumpImpulse: inRange(0, 1e5),
|
|
1645
|
+
maxVelocity: inRange(0, 1e5),
|
|
1646
|
+
radius: aboveZero(1e4),
|
|
1647
|
+
height: aboveZero(1e4),
|
|
1648
|
+
stepHeight: inRange(0, 1e4),
|
|
1649
|
+
standable: inRange(0, 1),
|
|
1650
|
+
groundTrace: inRange(0, 1e4),
|
|
1651
|
+
groundSnap: inRange(0, 1e4),
|
|
1652
|
+
skin: inRange(0, 100),
|
|
1653
|
+
jumpBit: integerIn(0, MASK_LIMIT - 1),
|
|
1654
|
+
boostBit: integerIn(0, MASK_LIMIT - 1),
|
|
1655
|
+
driftBit: integerIn(0, MASK_LIMIT - 1),
|
|
1656
|
+
substeps: integerIn(1, 8),
|
|
1657
|
+
capZeroJump: integerIn(0, 1),
|
|
1658
|
+
world: integerIn(0, 2 ** 32 - 1)
|
|
1659
|
+
};
|
|
1660
|
+
function resolveVehicleParams(params) {
|
|
1661
|
+
return resolvePresetParams(Preset.VEHICLE, params);
|
|
1662
|
+
}
|
|
1663
|
+
var VEHICLE_MSTATE_BITS = MoveState.ON_GROUND | MoveState.JUMP_HELD;
|
|
1664
|
+
var toward = (v, target, d) => v < target ? Math.min(target, v + d) : v > target ? Math.max(target, v - d) : v;
|
|
1665
|
+
var wrap = (v) => wrappedAngle(v) ?? 0;
|
|
1666
|
+
function drive(m) {
|
|
1667
|
+
const top = m.maxSpeed * m.cap;
|
|
1668
|
+
const t = m.boosting ? 1 : m.throttle;
|
|
1669
|
+
const target = t * top;
|
|
1670
|
+
let f = m.f;
|
|
1671
|
+
if (t === 0) f = toward(f, 0, m.coast * m.h);
|
|
1672
|
+
else if (t > 0 && f < 0 || t < 0 && f > 0) f = toward(f, 0, m.brake * m.h);
|
|
1673
|
+
else if (Math.abs(f) < Math.abs(target)) f = toward(f, target, m.accel * m.h);
|
|
1674
|
+
else if (!m.boosting) f = toward(f, target, m.coast * m.h);
|
|
1675
|
+
if (m.boosting) {
|
|
1676
|
+
const boostTop = m.boostMaxSpeed * m.cap;
|
|
1677
|
+
if (f < boostTop) f = Math.min(boostTop, f + m.boostAccel * m.h);
|
|
1678
|
+
}
|
|
1679
|
+
const l = toward(m.l, 0, (m.drift ? m.driftGrip : m.grip) * m.h);
|
|
1680
|
+
let want = m.steer * m.turnRate * Math.min(1, Math.abs(f) / m.turnSpeed);
|
|
1681
|
+
if (f < 0) want = -want;
|
|
1682
|
+
m.spin = toward(m.spin, want, m.spinAccel * m.h);
|
|
1683
|
+
m.vx = f * m.c - l * m.s;
|
|
1684
|
+
m.vy = f * m.s + l * m.c;
|
|
1685
|
+
m.vz = 0;
|
|
1686
|
+
m.f = f;
|
|
1687
|
+
m.l = l;
|
|
1688
|
+
}
|
|
1689
|
+
function air(m) {
|
|
1690
|
+
m.spin = toward(m.spin, m.steer * m.airTurnRate, m.airSpinAccel * m.h);
|
|
1691
|
+
if (m.boosting) {
|
|
1692
|
+
const boostTop = m.boostMaxSpeed * m.cap;
|
|
1693
|
+
if (m.f < boostTop) {
|
|
1694
|
+
const a = Math.min(m.boostAccel * m.h, boostTop - m.f);
|
|
1695
|
+
m.vx = m.vx + a * m.c;
|
|
1696
|
+
m.vy = m.vy + a * m.s;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
function carSubStep(m) {
|
|
1701
|
+
const was = m.onGround;
|
|
1702
|
+
checkGround(m);
|
|
1703
|
+
if (m.onGround && !was) m.vz = 0;
|
|
1704
|
+
m.c = pcos(m.yaw);
|
|
1705
|
+
m.s = psin(m.yaw);
|
|
1706
|
+
m.f = m.vx * m.c + m.vy * m.s;
|
|
1707
|
+
m.l = m.vy * m.c - m.vx * m.s;
|
|
1708
|
+
m.boosting = m.boost && m.fuel > 0;
|
|
1709
|
+
if (m.boosting) m.fuel = Math.max(0, m.fuel - m.boostBurn * m.h);
|
|
1710
|
+
if (m.onGround && m.pressed) {
|
|
1711
|
+
m.vz = m.jumpImpulse;
|
|
1712
|
+
m.onGround = false;
|
|
1713
|
+
m.pressed = false;
|
|
1714
|
+
}
|
|
1715
|
+
if (m.onGround) drive(m);
|
|
1716
|
+
else air(m);
|
|
1717
|
+
const falling = !m.onGround;
|
|
1718
|
+
if (falling) m.vz -= m.gravity * m.h * 0.5;
|
|
1719
|
+
m.vx = clamp(m.vx, -m.maxVelocity, m.maxVelocity);
|
|
1720
|
+
m.vy = clamp(m.vy, -m.maxVelocity, m.maxVelocity);
|
|
1721
|
+
m.vz = clamp(m.vz, -m.maxVelocity, m.maxVelocity);
|
|
1722
|
+
m.yaw = wrap(m.yaw + m.spin * m.h);
|
|
1723
|
+
if (m.onGround) {
|
|
1724
|
+
groundMove(m, true);
|
|
1725
|
+
stayOnGround(m);
|
|
1726
|
+
} else slideMove(m, m.h);
|
|
1727
|
+
checkGround(m);
|
|
1728
|
+
if (falling && !m.onGround) m.vz -= m.gravity * m.h * 0.5;
|
|
1729
|
+
else if (m.onGround) m.vz = 0;
|
|
1730
|
+
m.x = stored(m.x);
|
|
1731
|
+
m.y = stored(m.y);
|
|
1732
|
+
m.z = stored(m.z);
|
|
1733
|
+
m.vx = stored(m.vx);
|
|
1734
|
+
m.vy = stored(m.vy);
|
|
1735
|
+
m.vz = stored(m.vz);
|
|
1736
|
+
m.spin = unsignedZero(Math.fround(m.spin));
|
|
1737
|
+
m.fuel = unsignedZero(Math.fround(m.fuel));
|
|
1738
|
+
}
|
|
1739
|
+
function stepVehicle(body, input, dt, params, world = EMPTY_WORLD, posed = NO_POSED) {
|
|
1740
|
+
const w = compiledOf(world);
|
|
1741
|
+
const room = scratchFor(w);
|
|
1742
|
+
const f = Math.fround;
|
|
1743
|
+
const mstate = mask(body.mstate) & VEHICLE_MSTATE_BITS;
|
|
1744
|
+
const height = f(params.height);
|
|
1745
|
+
const steer = unsignedZero(-axis(input.moveX));
|
|
1746
|
+
const throttle = axis(input.moveY);
|
|
1747
|
+
const buttons = mask(input.buttons);
|
|
1748
|
+
const jump = (buttons & params.jumpBit) !== 0;
|
|
1749
|
+
const step = stepSeconds(dt, PRESET_MAX_DT_S);
|
|
1750
|
+
const readYaw = bodyAngle(body.yaw);
|
|
1751
|
+
const m = {
|
|
1752
|
+
w,
|
|
1753
|
+
p: posed,
|
|
1754
|
+
r: f(params.radius),
|
|
1755
|
+
unit: unitOf(f(params.radius), VEHICLE_RADIUS),
|
|
1756
|
+
standHeight: height,
|
|
1757
|
+
crouchHeight: height,
|
|
1758
|
+
stepHeight: f(params.stepHeight),
|
|
1759
|
+
standable: f(params.standable),
|
|
1760
|
+
groundTrace: f(params.groundTrace),
|
|
1761
|
+
groundSnap: f(params.groundSnap),
|
|
1762
|
+
skin: f(params.skin),
|
|
1763
|
+
maxSpeed: f(params.maxSpeed),
|
|
1764
|
+
accel: f(params.accel),
|
|
1765
|
+
brake: f(params.brake),
|
|
1766
|
+
coast: f(params.coast),
|
|
1767
|
+
boostAccel: f(params.boostAccel),
|
|
1768
|
+
boostMaxSpeed: f(params.boostMaxSpeed),
|
|
1769
|
+
boostBurn: f(params.boostBurn),
|
|
1770
|
+
grip: f(params.grip),
|
|
1771
|
+
driftGrip: f(params.driftGrip),
|
|
1772
|
+
turnRate: f(params.turnRate),
|
|
1773
|
+
turnSpeed: f(params.turnSpeed),
|
|
1774
|
+
spinAccel: f(params.spinAccel),
|
|
1775
|
+
airTurnRate: f(params.airTurnRate),
|
|
1776
|
+
airSpinAccel: f(params.airSpinAccel),
|
|
1777
|
+
gravity: f(params.gravity),
|
|
1778
|
+
jumpImpulse: f(params.jumpImpulse),
|
|
1779
|
+
maxVelocity: f(params.maxVelocity),
|
|
1780
|
+
x: bodyValue(body.x, 0, -MAX_ABS, MAX_ABS),
|
|
1781
|
+
y: bodyValue(body.y, 0, -MAX_ABS, MAX_ABS),
|
|
1782
|
+
z: bodyValue(body.z, 0, -MAX_ABS, MAX_ABS),
|
|
1783
|
+
vx: bodyValue(body.vx, 0, -MAX_ABS, MAX_ABS),
|
|
1784
|
+
vy: bodyValue(body.vy, 0, -MAX_ABS, MAX_ABS),
|
|
1785
|
+
vz: bodyValue(body.vz, 0, -MAX_ABS, MAX_ABS),
|
|
1786
|
+
yaw: Math.abs(readYaw) <= MAX_ABS ? wrap(readYaw) : 0,
|
|
1787
|
+
spin: bodyValue(body.spin, 0, -1e3, 1e3),
|
|
1788
|
+
fuel: bodyValue(body.fuel, 0, 0, 1),
|
|
1789
|
+
cap: bodyValue(body.cap, 1, 0, 4),
|
|
1790
|
+
duck: 0,
|
|
1791
|
+
onGround: (mstate & MoveState.ON_GROUND) !== 0,
|
|
1792
|
+
jumpHeld: (mstate & MoveState.JUMP_HELD) !== 0,
|
|
1793
|
+
pressed: false,
|
|
1794
|
+
boosting: false,
|
|
1795
|
+
steer,
|
|
1796
|
+
throttle,
|
|
1797
|
+
boost: (buttons & params.boostBit) !== 0,
|
|
1798
|
+
drift: (buttons & params.driftBit) !== 0,
|
|
1799
|
+
h: step / params.substeps,
|
|
1800
|
+
c: 1,
|
|
1801
|
+
s: 0,
|
|
1802
|
+
f: 0,
|
|
1803
|
+
l: 0,
|
|
1804
|
+
tMin: 0,
|
|
1805
|
+
tMax: 0,
|
|
1806
|
+
ax: -1,
|
|
1807
|
+
sg: 0,
|
|
1808
|
+
nx: 0,
|
|
1809
|
+
ny: 0,
|
|
1810
|
+
nz: 0,
|
|
1811
|
+
hit: false,
|
|
1812
|
+
depth: 0,
|
|
1813
|
+
cand: room.cand,
|
|
1814
|
+
nCand: -1,
|
|
1815
|
+
pc: POSED_SCRATCH,
|
|
1816
|
+
nPosed: 0,
|
|
1817
|
+
stack: room.stack
|
|
1818
|
+
};
|
|
1819
|
+
if (step > 0) {
|
|
1820
|
+
m.pressed = jump && !m.jumpHeld && (params.capZeroJump !== 0 || m.cap !== 0);
|
|
1821
|
+
m.jumpHeld = jump;
|
|
1822
|
+
for (let i = 0; i < params.substeps; i++) carSubStep(m);
|
|
1823
|
+
}
|
|
1824
|
+
return {
|
|
1825
|
+
x: m.x,
|
|
1826
|
+
y: m.y,
|
|
1827
|
+
z: m.z,
|
|
1828
|
+
vx: m.vx,
|
|
1829
|
+
vy: m.vy,
|
|
1830
|
+
vz: m.vz,
|
|
1831
|
+
yaw: m.yaw,
|
|
1832
|
+
spin: m.spin,
|
|
1833
|
+
fuel: m.fuel,
|
|
1834
|
+
mstate: (m.onGround ? MoveState.ON_GROUND : 0) + (m.jumpHeld ? MoveState.JUMP_HELD : 0)
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
var TOPDOWN_PRESET = {
|
|
1838
|
+
defaults: TOPDOWN_DEFAULTS,
|
|
1839
|
+
ranges: TOPDOWN_RANGE,
|
|
1840
|
+
fields: ["x", "y", "yaw"],
|
|
1841
|
+
reads: [],
|
|
1842
|
+
world: false,
|
|
1843
|
+
step: (body, input, dt, params) => stepTopdown(body, input, dt, params)
|
|
1844
|
+
};
|
|
1845
|
+
var FIRSTPERSON_PRESET = {
|
|
1846
|
+
defaults: FIRSTPERSON_DEFAULTS,
|
|
1847
|
+
ranges: FIRSTPERSON_RANGE,
|
|
1848
|
+
fields: ["x", "y", "z", "vx", "vy", "vz", "yaw", "pitch", "duck", "mstate"],
|
|
1849
|
+
reads: ["cap"],
|
|
1850
|
+
world: true,
|
|
1851
|
+
step: (body, input, dt, params, world, posed) => stepFirstperson(body, input, dt, params, world, posed)
|
|
1852
|
+
};
|
|
1853
|
+
var VEHICLE_PRESET = {
|
|
1854
|
+
defaults: VEHICLE_DEFAULTS,
|
|
1855
|
+
ranges: VEHICLE_RANGE,
|
|
1856
|
+
fields: ["x", "y", "z", "vx", "vy", "vz", "yaw", "spin", "fuel", "mstate"],
|
|
1857
|
+
reads: ["cap"],
|
|
1858
|
+
world: true,
|
|
1859
|
+
step: (body, input, dt, params, world, posed) => stepVehicle(body, input, dt, params, world, posed)
|
|
1860
|
+
};
|
|
1861
|
+
var PRESETS = Object.freeze({
|
|
1862
|
+
[Preset.TOPDOWN]: Object.freeze(TOPDOWN_PRESET),
|
|
1863
|
+
[Preset.FIRSTPERSON]: Object.freeze(FIRSTPERSON_PRESET),
|
|
1864
|
+
[Preset.VEHICLE]: Object.freeze(VEHICLE_PRESET)
|
|
1865
|
+
});
|
|
1866
|
+
function definitionOf(name) {
|
|
1867
|
+
return typeof name === "string" && Object.hasOwn(PRESETS, name) ? PRESETS[name] : void 0;
|
|
1868
|
+
}
|
|
1869
|
+
function stepPreset(name, body, input, dt, params, world, posed) {
|
|
1870
|
+
const definition = definitionOf(name);
|
|
1871
|
+
if (definition === void 0) throw new RangeError(`unknown preset ${String(name)}`);
|
|
1872
|
+
return definition.step(body, input, dt, params, world, posed);
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
export {
|
|
1876
|
+
MAX_ABS,
|
|
1877
|
+
MASK_LIMIT,
|
|
1878
|
+
unsignedZero,
|
|
1879
|
+
axis,
|
|
1880
|
+
wrappedAngle,
|
|
1881
|
+
mask,
|
|
1882
|
+
isMask,
|
|
1883
|
+
psincos,
|
|
1884
|
+
psin,
|
|
1885
|
+
pcos,
|
|
1886
|
+
Preset,
|
|
1887
|
+
PRESET_MAX_DT_S,
|
|
1888
|
+
TOPDOWN_DEFAULTS,
|
|
1889
|
+
isPresetName,
|
|
1890
|
+
presetParamsValid,
|
|
1891
|
+
resolvePresetParams,
|
|
1892
|
+
resolveTopdownParams,
|
|
1893
|
+
stepTopdown,
|
|
1894
|
+
EMPTY_WORLD_HASH,
|
|
1895
|
+
FIRSTPERSON_DEFAULTS,
|
|
1896
|
+
resolveFirstpersonParams,
|
|
1897
|
+
MoveState,
|
|
1898
|
+
WorldLimits,
|
|
1899
|
+
WorldError,
|
|
1900
|
+
firstpersonWorld,
|
|
1901
|
+
EMPTY_WORLD,
|
|
1902
|
+
POSED_MAX,
|
|
1903
|
+
posedWorld,
|
|
1904
|
+
NO_POSED,
|
|
1905
|
+
TREE_KEY_RADIX,
|
|
1906
|
+
worldTreeOrder,
|
|
1907
|
+
withWorldCostMeter,
|
|
1908
|
+
hullOps,
|
|
1909
|
+
stepFirstperson,
|
|
1910
|
+
VEHICLE_DEFAULTS,
|
|
1911
|
+
resolveVehicleParams,
|
|
1912
|
+
stepVehicle,
|
|
1913
|
+
PRESETS,
|
|
1914
|
+
definitionOf,
|
|
1915
|
+
stepPreset
|
|
1916
|
+
};
|