@babylonjs/core 7.38.0 → 7.39.1

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.
Files changed (61) hide show
  1. package/Cameras/targetCamera.js +6 -0
  2. package/Cameras/targetCamera.js.map +1 -1
  3. package/Engines/abstractEngine.js +2 -2
  4. package/Engines/abstractEngine.js.map +1 -1
  5. package/Lights/spotLight.d.ts +7 -0
  6. package/Lights/spotLight.js +27 -0
  7. package/Lights/spotLight.js.map +1 -1
  8. package/Materials/Node/Blocks/Dual/lightBlock.js +1 -1
  9. package/Materials/Node/Blocks/Dual/lightBlock.js.map +1 -1
  10. package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.js +1 -1
  11. package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.js.map +1 -1
  12. package/Materials/Textures/Loaders/exrTextureLoader.d.ts +1 -0
  13. package/Materials/Textures/Loaders/exrTextureLoader.js +1 -0
  14. package/Materials/Textures/Loaders/exrTextureLoader.js.map +1 -1
  15. package/Materials/Textures/Loaders/hdrTextureLoader.d.ts +1 -0
  16. package/Materials/Textures/Loaders/hdrTextureLoader.js +2 -1
  17. package/Materials/Textures/Loaders/hdrTextureLoader.js.map +1 -1
  18. package/Materials/Textures/Loaders/iesTextureLoader.js +1 -1
  19. package/Materials/Textures/Loaders/iesTextureLoader.js.map +1 -1
  20. package/Materials/material.d.ts +1 -4
  21. package/Materials/material.js +26 -23
  22. package/Materials/material.js.map +1 -1
  23. package/Materials/materialHelper.functions.d.ts +2 -1
  24. package/Materials/materialHelper.functions.js +6 -2
  25. package/Materials/materialHelper.functions.js.map +1 -1
  26. package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +5 -3
  27. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +13 -14
  28. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
  29. package/Meshes/abstractMesh.js +7 -0
  30. package/Meshes/abstractMesh.js.map +1 -1
  31. package/Physics/v2/characterController.d.ts +327 -0
  32. package/Physics/v2/characterController.js +1236 -0
  33. package/Physics/v2/characterController.js.map +1 -0
  34. package/Physics/v2/index.d.ts +1 -0
  35. package/Physics/v2/index.js +1 -0
  36. package/Physics/v2/index.js.map +1 -1
  37. package/Rendering/objectRenderer.js +8 -0
  38. package/Rendering/objectRenderer.js.map +1 -1
  39. package/Shaders/ShadersInclude/lightFragment.js +28 -4
  40. package/Shaders/ShadersInclude/lightFragment.js.map +1 -1
  41. package/Shaders/ShadersInclude/lightFragmentDeclaration.js +3 -0
  42. package/Shaders/ShadersInclude/lightFragmentDeclaration.js.map +1 -1
  43. package/Shaders/ShadersInclude/lightUboDeclaration.js +3 -0
  44. package/Shaders/ShadersInclude/lightUboDeclaration.js.map +1 -1
  45. package/Shaders/ShadersInclude/lightsFragmentFunctions.js +20 -3
  46. package/Shaders/ShadersInclude/lightsFragmentFunctions.js.map +1 -1
  47. package/Shaders/ShadersInclude/pbrDirectLightingFalloffFunctions.js +2 -0
  48. package/Shaders/ShadersInclude/pbrDirectLightingFalloffFunctions.js.map +1 -1
  49. package/ShadersWGSL/ShadersInclude/fogVertex.js +4 -0
  50. package/ShadersWGSL/ShadersInclude/fogVertex.js.map +1 -1
  51. package/ShadersWGSL/ShadersInclude/lightFragment.js +28 -4
  52. package/ShadersWGSL/ShadersInclude/lightFragment.js.map +1 -1
  53. package/ShadersWGSL/ShadersInclude/lightUboDeclaration.js +3 -0
  54. package/ShadersWGSL/ShadersInclude/lightUboDeclaration.js.map +1 -1
  55. package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.js +16 -3
  56. package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.js.map +1 -1
  57. package/ShadersWGSL/ShadersInclude/pbrDirectLightingFalloffFunctions.js +2 -0
  58. package/ShadersWGSL/ShadersInclude/pbrDirectLightingFalloffFunctions.js.map +1 -1
  59. package/ShadersWGSL/ShadersInclude/sceneUboDeclaration.js +3 -1
  60. package/ShadersWGSL/ShadersInclude/sceneUboDeclaration.js.map +1 -1
  61. package/package.json +1 -1
@@ -0,0 +1,1236 @@
1
+ import { Vector3, Quaternion, Matrix, TmpVectors } from "../../Maths/math.vector.js";
2
+ import { PhysicsShapeCapsule } from "./physicsShape.js";
3
+ import { BuildArray } from "../../Misc/arrayTools.js";
4
+ /**
5
+ * State of the character on the surface
6
+ */
7
+ export var CharacterSupportedState;
8
+ (function (CharacterSupportedState) {
9
+ CharacterSupportedState[CharacterSupportedState["UNSUPPORTED"] = 0] = "UNSUPPORTED";
10
+ CharacterSupportedState[CharacterSupportedState["SLIDING"] = 1] = "SLIDING";
11
+ CharacterSupportedState[CharacterSupportedState["SUPPORTED"] = 2] = "SUPPORTED";
12
+ })(CharacterSupportedState || (CharacterSupportedState = {}));
13
+ /** @internal */
14
+ var SurfaceConstraintInteractionStatus;
15
+ (function (SurfaceConstraintInteractionStatus) {
16
+ SurfaceConstraintInteractionStatus[SurfaceConstraintInteractionStatus["OK"] = 0] = "OK";
17
+ SurfaceConstraintInteractionStatus[SurfaceConstraintInteractionStatus["FAILURE_3D"] = 1] = "FAILURE_3D";
18
+ SurfaceConstraintInteractionStatus[SurfaceConstraintInteractionStatus["FAILURE_2D"] = 2] = "FAILURE_2D";
19
+ })(SurfaceConstraintInteractionStatus || (SurfaceConstraintInteractionStatus = {}));
20
+ /** @internal */
21
+ class SimplexSolverOutput {
22
+ }
23
+ /** @internal */
24
+ class SimplexSolverActivePlanes {
25
+ /** @internal */
26
+ copyFrom(other) {
27
+ this.index = other.index;
28
+ this.constraint = other.constraint;
29
+ this.interaction = other.interaction;
30
+ }
31
+ }
32
+ /** @internal */
33
+ class SimplexSolverInfo {
34
+ constructor() {
35
+ /** @internal */
36
+ this.supportPlanes = new Array(4);
37
+ /** @internal */
38
+ this.numSupportPlanes = 0;
39
+ /** @internal */
40
+ this.currentTime = 0;
41
+ }
42
+ /** @internal */
43
+ getOutput(constraint) {
44
+ return this.outputInteractions[this.inputConstraints.indexOf(constraint)]; //<todo.eoin This is O(1) in C++! Equivalent in TS?
45
+ }
46
+ }
47
+ /** @internal */
48
+ function contactFromCast(hp, cp /*ContactPoint*/, castPath, hitFraction, keepDistance) {
49
+ //@ts-ignore
50
+ const bodyMap = hp._bodies;
51
+ const normal = Vector3.FromArray(cp[4]);
52
+ const dist = -hitFraction * castPath.dot(normal);
53
+ return {
54
+ position: Vector3.FromArray(cp[3]),
55
+ normal: normal,
56
+ distance: dist,
57
+ fraction: hitFraction,
58
+ bodyB: bodyMap.get(cp[0][0]),
59
+ allowedPenetration: Math.min(Math.max(keepDistance - dist, 0.0), keepDistance),
60
+ };
61
+ }
62
+ /**
63
+ * Character controller using physics
64
+ */
65
+ export class PhysicsCharacterController {
66
+ /**
67
+ * instanciate a new characterController
68
+ * @param position Initial position
69
+ * @param characterShapeOptions character physics shape options
70
+ * @param scene Scene
71
+ */
72
+ constructor(position, characterShapeOptions, scene) {
73
+ this._orientation = Quaternion.Identity();
74
+ this._manifold = [];
75
+ this._contactAngleSensitivity = 10.0;
76
+ this._tmpMatrix = new Matrix();
77
+ this._tmpVecs = BuildArray(31, Vector3.Zero);
78
+ /**
79
+ * minimum distance to make contact
80
+ * default 0.05
81
+ */
82
+ this.keepDistance = 0.05;
83
+ /**
84
+ * maximum distance to keep contact
85
+ * default 0.1
86
+ */
87
+ this.keepContactTolerance = 0.1;
88
+ /**
89
+ * maximum number of raycast per integration starp
90
+ * default 10
91
+ */
92
+ this.maxCastIterations = 10;
93
+ /**
94
+ * speed when recovery from penetration
95
+ * default 1.0
96
+ */
97
+ this.penetrationRecoverySpeed = 1.0;
98
+ /**
99
+ * friction with static surfaces
100
+ * default 0
101
+ */
102
+ this.staticFriction = 0;
103
+ /**
104
+ * friction with dynamic surfaces
105
+ * default 1
106
+ */
107
+ this.dynamicFriction = 1;
108
+ /**
109
+ * cosine value of slop angle that can be climbed
110
+ * computed as `Math.cos(Math.PI * (angleInDegree / 180.0));`
111
+ * default 0.5 (value for a 60deg angle)
112
+ */
113
+ this.maxSlopeCosine = 0.5;
114
+ /**
115
+ * character maximum speed
116
+ * default 10
117
+ */
118
+ this.maxCharacterSpeedForSolver = 10.0;
119
+ /**
120
+ * up vector
121
+ */
122
+ this.up = new Vector3(0, 1, 0);
123
+ /**
124
+ * Strength when pushing other bodies
125
+ * default 1e38
126
+ */
127
+ this.characterStrength = 1e38;
128
+ /**
129
+ * character mass
130
+ * default 0
131
+ */
132
+ this.characterMass = 0;
133
+ this._position = position.clone();
134
+ this._velocity = Vector3.Zero();
135
+ this._lastVelocity = Vector3.Zero();
136
+ const r = characterShapeOptions.capsuleRadius ?? 0.6;
137
+ const h = characterShapeOptions.capsuleHeight ?? 1.8;
138
+ this._tmpVecs[0].set(0, h * 0.5 - r, 0);
139
+ this._tmpVecs[1].set(0, -h * 0.5 + r, 0);
140
+ this._shape = characterShapeOptions.shape ?? new PhysicsShapeCapsule(this._tmpVecs[0], this._tmpVecs[1], r, scene);
141
+ this._lastInvDeltaTime = 1.0 / 60.0;
142
+ this._lastDisplacement = Vector3.Zero();
143
+ this._scene = scene;
144
+ const hk = this._scene.getPhysicsEngine().getPhysicsPlugin();
145
+ const hknp = hk._hknp;
146
+ this._startCollector = hknp.HP_QueryCollector_Create(16)[1];
147
+ this._castCollector = hknp.HP_QueryCollector_Create(16)[1];
148
+ }
149
+ /**
150
+ * Character position
151
+ * @returns Character position
152
+ */
153
+ getPosition() {
154
+ return this._position;
155
+ }
156
+ /**
157
+ * Character velocity
158
+ * @returns Character velocity vector
159
+ */
160
+ getVelocity() {
161
+ return this._velocity;
162
+ }
163
+ /**
164
+ * Set velocity vector
165
+ * @param velocity vector
166
+ */
167
+ setVelocity(velocity) {
168
+ this._velocity.copyFrom(velocity);
169
+ }
170
+ _validateManifold() {
171
+ const newManifold = [];
172
+ for (let i = 0; i < this._manifold.length; i++) {
173
+ if (!this._manifold[i].bodyB.body.isDisposed) {
174
+ newManifold.push(this._manifold[i]);
175
+ }
176
+ }
177
+ this._manifold = newManifold;
178
+ }
179
+ _getPointVelocityToRef(body, pointWorld, result) {
180
+ //<todo does this really not exist in body interface?
181
+ const comWorld = this._tmpVecs[10];
182
+ this._getComWorldToRef(body, comWorld);
183
+ const relPos = this._tmpVecs[11];
184
+ pointWorld.subtractToRef(comWorld, relPos);
185
+ const av = this._tmpVecs[12];
186
+ body.body.getAngularVelocityToRef(av, body.index);
187
+ const arm = this._tmpVecs[13];
188
+ Vector3.CrossToRef(av, relPos, arm);
189
+ arm.addToRef(body.body.getLinearVelocity(body.index), result);
190
+ }
191
+ _compareContacts(contactA, contactB) {
192
+ const angSquared = (1.0 - contactA.normal.dot(contactB.normal)) * this._contactAngleSensitivity * this._contactAngleSensitivity;
193
+ const planeDistSquared = (contactA.distance - contactB.distance) * (contactA.distance * contactB.distance);
194
+ const p1Vel = this._tmpVecs[7];
195
+ this._getPointVelocityToRef(contactA.bodyB, contactA.position, p1Vel);
196
+ const p2Vel = this._tmpVecs[8];
197
+ this._getPointVelocityToRef(contactB.bodyB, contactB.position, p2Vel);
198
+ const velocityDiff = this._tmpVecs[9];
199
+ p1Vel.subtractToRef(p2Vel, velocityDiff);
200
+ const velocityDiffSquared = velocityDiff.lengthSquared();
201
+ const fitness = angSquared * 10.0 + velocityDiffSquared * 0.1 + planeDistSquared;
202
+ return fitness;
203
+ }
204
+ _findContact(referenceContact, contactList, threshold) {
205
+ let bestIdx = -1;
206
+ let bestFitness = threshold;
207
+ for (let i = 0; i < contactList.length; i++) {
208
+ const fitness = this._compareContacts(referenceContact, contactList[i]);
209
+ if (fitness < bestFitness) {
210
+ bestFitness = fitness;
211
+ bestIdx = i;
212
+ }
213
+ }
214
+ return bestIdx;
215
+ }
216
+ _updateManifold(startCollector /*HP_CollectorId*/, castCollector /*HP_CollectorId*/, castPath) {
217
+ const hk = this._scene.getPhysicsEngine().getPhysicsPlugin();
218
+ const hknp = hk._hknp;
219
+ const numProximityHits = hknp.HP_QueryCollector_GetNumHits(startCollector)[1];
220
+ if (numProximityHits > 0) {
221
+ const newContacts = [];
222
+ let minDistance = 1e38;
223
+ const bodyMap = hk._bodies;
224
+ for (let i = 0; i < numProximityHits; i++) {
225
+ const [distance, , contactWorld] = hknp.HP_QueryCollector_GetShapeProximityResult(startCollector, i)[1];
226
+ minDistance = Math.min(minDistance, distance);
227
+ newContacts.push({
228
+ position: Vector3.FromArray(contactWorld[3]),
229
+ normal: Vector3.FromArray(contactWorld[4]),
230
+ distance: distance,
231
+ fraction: 0,
232
+ bodyB: bodyMap.get(contactWorld[0][0]),
233
+ allowedPenetration: Math.min(Math.max(this.keepDistance - distance, 0.0), this.keepDistance),
234
+ });
235
+ }
236
+ for (let i = this._manifold.length - 1; i >= 0; i--) {
237
+ const currentContact = this._manifold[i];
238
+ const bestMatch = this._findContact(currentContact, newContacts, 1.1);
239
+ if (bestMatch >= 0) {
240
+ const newAllowedPenetration = Math.min(Math.max(this.keepDistance - newContacts[bestMatch].distance, 0.0), currentContact.allowedPenetration);
241
+ this._manifold[i] = newContacts[bestMatch];
242
+ this._manifold[i].allowedPenetration = newAllowedPenetration;
243
+ newContacts.splice(bestMatch, 1);
244
+ }
245
+ else {
246
+ this._manifold.splice(i, 1);
247
+ }
248
+ }
249
+ const closestContactIndex = newContacts.findIndex((c) => c.distance == minDistance);
250
+ if (closestContactIndex >= 0) {
251
+ const bestMatch = this._findContact(newContacts[closestContactIndex], this._manifold, 0.1);
252
+ if (bestMatch >= 0) {
253
+ const newAllowedPenetration = Math.min(Math.max(this.keepDistance - newContacts[closestContactIndex].distance, 0.0), this._manifold[bestMatch].allowedPenetration);
254
+ this._manifold[bestMatch] = newContacts[closestContactIndex];
255
+ this._manifold[bestMatch].allowedPenetration = newAllowedPenetration;
256
+ }
257
+ else {
258
+ this._manifold.push(newContacts[closestContactIndex]);
259
+ }
260
+ }
261
+ }
262
+ else {
263
+ // No start hits; clear manifold completely
264
+ this._manifold.length = 0;
265
+ }
266
+ let numHitBodies = 0; //< == CASTCOLLECTOR_HIT_SINGLE_BODY
267
+ // Process shape cast results if any
268
+ const numCastHits = hknp.HP_QueryCollector_GetNumHits(castCollector)[1];
269
+ if (numCastHits > 0) {
270
+ let closestHitBody = null;
271
+ for (let i = 0; i < numCastHits; i++) {
272
+ const [fraction, , hitWorld] = hknp.HP_QueryCollector_GetShapeCastResult(castCollector, i)[1];
273
+ if (closestHitBody == null) {
274
+ const contact = contactFromCast(hk, hitWorld, castPath, fraction, this.keepDistance);
275
+ closestHitBody = hitWorld[0][0];
276
+ const bestMatch = this._findContact(contact, this._manifold, 0.1);
277
+ if (bestMatch == -1) {
278
+ this._manifold.push(contact);
279
+ }
280
+ if (contact.bodyB.body.getMotionType(contact.bodyB.index) == 0 /* PhysicsMotionType.STATIC */) {
281
+ // The closest body is static, so it cannot move away from CC and we don't need to look any further.
282
+ break;
283
+ }
284
+ }
285
+ else if (closestHitBody._pluginData && hitWorld[0] != closestHitBody._pluginData.hpBodyId) {
286
+ numHitBodies++;
287
+ break;
288
+ }
289
+ }
290
+ }
291
+ // Remove from the manifold contacts that are too similar as the simplex does not handle parallel planes
292
+ for (let e1 = this._manifold.length - 1; e1 >= 0; e1--) {
293
+ let e2 = e1 - 1;
294
+ for (; e2 >= 0; e2--) {
295
+ const fitness = this._compareContacts(this._manifold[e1], this._manifold[e2]);
296
+ if (fitness < 0.1)
297
+ break;
298
+ }
299
+ if (e2 >= 0) {
300
+ this._manifold.slice(e1, 1);
301
+ }
302
+ }
303
+ return numHitBodies;
304
+ }
305
+ _createSurfaceConstraint(contact, timeTravelled) {
306
+ const constraint = {
307
+ //let distance = contact.distance - this.keepDistance;
308
+ planeNormal: contact.normal.clone(),
309
+ planeDistance: contact.distance,
310
+ staticFriction: this.staticFriction,
311
+ dynamicFriction: this.dynamicFriction,
312
+ extraUpStaticFriction: 0,
313
+ extraDownStaticFriction: 0,
314
+ velocity: Vector3.Zero(),
315
+ angularVelocity: Vector3.Zero(),
316
+ priority: 0,
317
+ };
318
+ const maxSlopeCosEps = 0.1;
319
+ const maxSlopeCosine = Math.max(this.maxSlopeCosine, maxSlopeCosEps);
320
+ const normalDotUp = contact.normal.dot(this.up);
321
+ const contactPosition = contact.position.clone();
322
+ if (normalDotUp > maxSlopeCosine) {
323
+ const com = this.getPosition();
324
+ const contactArm = this._tmpVecs[20];
325
+ contact.position.subtractToRef(com, contactArm);
326
+ const scale = contact.normal.dot(contactArm);
327
+ contactPosition.x = com.x + this.up.x * scale;
328
+ contactPosition.y = com.y + this.up.y * scale;
329
+ contactPosition.z = com.z + this.up.z * scale;
330
+ }
331
+ const motionType = contact.bodyB.body.getMotionType(contact.bodyB.index);
332
+ if (motionType != 0 /* PhysicsMotionType.STATIC */) {
333
+ //<todo Need hknpMotionUtil::predictPontVelocity
334
+ }
335
+ const shift = constraint.velocity.dot(constraint.planeNormal) * timeTravelled;
336
+ constraint.planeDistance -= shift;
337
+ if (motionType == 0 /* PhysicsMotionType.STATIC */) {
338
+ constraint.priority = 2;
339
+ }
340
+ else if (motionType == 1 /* PhysicsMotionType.ANIMATED */) {
341
+ constraint.priority = 1;
342
+ }
343
+ return constraint;
344
+ }
345
+ _addMaxSlopePlane(maxSlopeCos, up, index, constraints, allowedPenetration) {
346
+ const verticalComponent = constraints[index].planeNormal.dot(up);
347
+ if (verticalComponent > 0.01 && verticalComponent < maxSlopeCos) {
348
+ const newConstraint = {
349
+ planeNormal: constraints[index].planeNormal.clone(),
350
+ planeDistance: constraints[index].planeDistance,
351
+ velocity: constraints[index].velocity.clone(),
352
+ angularVelocity: constraints[index].angularVelocity.clone(),
353
+ priority: constraints[index].priority,
354
+ dynamicFriction: constraints[index].dynamicFriction,
355
+ staticFriction: constraints[index].staticFriction,
356
+ extraDownStaticFriction: constraints[index].extraDownStaticFriction,
357
+ extraUpStaticFriction: constraints[index].extraUpStaticFriction,
358
+ };
359
+ const distance = newConstraint.planeDistance;
360
+ newConstraint.planeNormal.subtractInPlace(up.scale(verticalComponent));
361
+ newConstraint.planeNormal.normalize();
362
+ if (distance >= 0) {
363
+ newConstraint.planeDistance = distance * newConstraint.planeNormal.dot(constraints[index].planeNormal);
364
+ }
365
+ else {
366
+ const penetrationToResolve = Math.min(0, distance + allowedPenetration);
367
+ newConstraint.planeDistance = penetrationToResolve / newConstraint.planeNormal.dot(constraints[index].planeNormal);
368
+ constraints[index].planeDistance = 0;
369
+ this._resolveConstraintPenetration(newConstraint, this.penetrationRecoverySpeed);
370
+ }
371
+ constraints.push(newConstraint);
372
+ return true;
373
+ }
374
+ return false;
375
+ }
376
+ _resolveConstraintPenetration(constraint, penetrationRecoverySpeed) {
377
+ // If penetrating we add extra velocity to push the character back out
378
+ const eps = 1e-6;
379
+ if (constraint.planeDistance < -eps) {
380
+ constraint.planeNormal.scaleToRef(constraint.planeDistance * penetrationRecoverySpeed, this._tmpVecs[6]);
381
+ constraint.velocity.subtractInPlace(this._tmpVecs[6]);
382
+ }
383
+ }
384
+ _createConstraintsFromManifold(dt, timeTravelled) {
385
+ const constraints = [];
386
+ for (let i = 0; i < this._manifold.length; i++) {
387
+ const surfaceConstraint = this._createSurfaceConstraint(this._manifold[i], timeTravelled);
388
+ constraints.push(surfaceConstraint);
389
+ this._addMaxSlopePlane(this.maxSlopeCosine, this.up, i, constraints, this._manifold[i].allowedPenetration);
390
+ this._resolveConstraintPenetration(surfaceConstraint, this.penetrationRecoverySpeed);
391
+ }
392
+ return constraints;
393
+ }
394
+ _simplexSolverSortInfo(info) {
395
+ // simple bubble sort by (priority,velocity)
396
+ for (let i = 0; i < info.numSupportPlanes - 1; i++) {
397
+ for (let j = i + 1; j < info.numSupportPlanes; j++) {
398
+ const p0 = info.supportPlanes[i];
399
+ const p1 = info.supportPlanes[j];
400
+ if (p0.constraint.priority < p1.constraint.priority) {
401
+ continue;
402
+ }
403
+ if (p0.constraint.priority == p1.constraint.priority) {
404
+ const vel0 = p0.constraint.velocity.lengthSquared();
405
+ const vel1 = p1.constraint.velocity.lengthSquared();
406
+ if (vel0 < vel1) {
407
+ continue;
408
+ }
409
+ }
410
+ info.supportPlanes[i] = p1;
411
+ info.supportPlanes[j] = p0;
412
+ }
413
+ }
414
+ }
415
+ _simplexSolverSolve1d(info, sci, velocityIn, velocityOut) {
416
+ const eps = 1e-5;
417
+ const groundVelocity = sci.velocity;
418
+ const relativeVelocity = this._tmpVecs[22];
419
+ velocityIn.subtractToRef(groundVelocity, relativeVelocity);
420
+ const planeVel = relativeVelocity.dot(sci.planeNormal);
421
+ const origVelocity2 = relativeVelocity.lengthSquared();
422
+ relativeVelocity.subtractInPlace(sci.planeNormal.scale(planeVel));
423
+ {
424
+ const vp2 = planeVel * planeVel;
425
+ // static friction is active if
426
+ // velProjPlane * friction > |(velParallel)|
427
+ // vplane * f > vpar
428
+ // vp * f > vpar
429
+ // vp2 * f2 > vpar2
430
+ const extraStaticFriction = relativeVelocity.dot(this.up) > 0 ? sci.extraUpStaticFriction : sci.extraDownStaticFriction;
431
+ if (extraStaticFriction > 0) {
432
+ const horizontal = this.up.cross(sci.planeNormal);
433
+ const hor2 = horizontal.lengthSquared();
434
+ let horVel = 0.0;
435
+ if (hor2 > eps) {
436
+ horizontal.scaleInPlace(1 / Math.sqrt(hor2));
437
+ horVel = relativeVelocity.dot(horizontal);
438
+ // horizontal component
439
+ {
440
+ const horVel2 = horVel * horVel;
441
+ const f2 = sci.staticFriction * sci.staticFriction;
442
+ if (vp2 * f2 >= horVel2) {
443
+ relativeVelocity.subtractInPlace(horizontal.scale(horVel));
444
+ horVel = 0;
445
+ }
446
+ }
447
+ }
448
+ // vert component
449
+ {
450
+ const vertVel2 = origVelocity2 - horVel * horVel - vp2;
451
+ const f2 = (sci.staticFriction + extraStaticFriction) * (sci.staticFriction + extraStaticFriction);
452
+ if (vp2 * f2 >= vertVel2) {
453
+ if (horVel == 0.0) {
454
+ velocityOut.copyFrom(groundVelocity);
455
+ return;
456
+ }
457
+ }
458
+ }
459
+ }
460
+ else {
461
+ // static friction is active if
462
+ // velProjPlane * friction > |(vel-velProjPlane)|
463
+ // vp * f > rvProj
464
+ //
465
+ // -> vp * f >= rvProj
466
+ // -> vp * f >= sqrt( vel^2 - vp^2 )
467
+ // -> vp^2 ( f^2 + 1 ) >= vel^2
468
+ const f2 = sci.staticFriction * sci.staticFriction;
469
+ if (vp2 * (1 + f2) >= origVelocity2) {
470
+ velocityOut.copyFrom(groundVelocity);
471
+ return;
472
+ }
473
+ }
474
+ }
475
+ if (sci.dynamicFriction < 1) {
476
+ // apply dynamic friction 0 = conserve input velocity 1 = clip against normal
477
+ const velOut2 = relativeVelocity.lengthSquared();
478
+ if (velOut2 >= eps) {
479
+ if (velOut2 > 1e-4 * origVelocity2) {
480
+ let f = Math.sqrt(origVelocity2 / velOut2);
481
+ f = sci.dynamicFriction + (1 - sci.dynamicFriction) * f;
482
+ relativeVelocity.scaleInPlace(f);
483
+ const p = sci.planeNormal.dot(relativeVelocity);
484
+ relativeVelocity.subtractInPlace(sci.planeNormal.scale(p));
485
+ }
486
+ }
487
+ }
488
+ velocityOut.copyFrom(relativeVelocity);
489
+ velocityOut.addInPlace(groundVelocity);
490
+ }
491
+ _simplexSolverSolveTest1d(sci, velocityIn) {
492
+ const eps = 1e-3;
493
+ const relativeVelocity = this._tmpVecs[23];
494
+ velocityIn.subtractToRef(sci.velocity, relativeVelocity);
495
+ return relativeVelocity.dot(sci.planeNormal) < -eps;
496
+ }
497
+ _simplexSolverSolve2d(info, maxSurfaceVelocity, sci0, sci1, velocityIn, velocityOut) {
498
+ const eps = 1e-5;
499
+ const axis = sci0.planeNormal.cross(sci1.planeNormal);
500
+ const axisLen2 = axis.lengthSquared();
501
+ let solveSequentially = false;
502
+ let axisVel = null;
503
+ // eslint-disable-next-line no-constant-condition
504
+ while (true) {
505
+ // Check for parallel planes
506
+ if (axisLen2 <= eps || solveSequentially) {
507
+ info.getOutput(sci0).status = 2 /* SurfaceConstraintInteractionStatus.FAILURE_2D */;
508
+ info.getOutput(sci1).status = 2 /* SurfaceConstraintInteractionStatus.FAILURE_2D */;
509
+ if (sci0.priority > sci1.priority) {
510
+ this._simplexSolverSolve1d(info, sci1, velocityIn, velocityOut);
511
+ this._simplexSolverSolve1d(info, sci0, velocityIn, velocityOut);
512
+ }
513
+ else {
514
+ this._simplexSolverSolve1d(info, sci0, velocityIn, velocityOut);
515
+ this._simplexSolverSolve1d(info, sci1, velocityIn, velocityOut);
516
+ }
517
+ return;
518
+ }
519
+ const invAxisLen = 1.0 / Math.sqrt(axisLen2);
520
+ axis.scaleInPlace(invAxisLen);
521
+ // Calculate the velocity of the free axis
522
+ {
523
+ const r0 = sci0.planeNormal.cross(sci1.planeNormal);
524
+ const r1 = sci1.planeNormal.cross(axis);
525
+ const r2 = axis.cross(sci0.planeNormal);
526
+ const sVel = sci0.velocity.add(sci1.velocity);
527
+ const t = this._tmpVecs[2];
528
+ t.set(0.5 * axis.dot(sVel), sci0.planeNormal.dot(sci0.velocity), sci1.planeNormal.dot(sci1.velocity));
529
+ const m = Matrix.FromValues(r0.x, r1.x, r2.x, 0, r0.y, r1.y, r2.y, 0, r0.z, r1.z, r2.z, 0, 0, 0, 0, 1);
530
+ axisVel = Vector3.TransformNormal(t, m);
531
+ axisVel.scaleInPlace(invAxisLen);
532
+ if (Math.abs(axisVel.x) > maxSurfaceVelocity.x || Math.abs(axisVel.y) > maxSurfaceVelocity.y || Math.abs(axisVel.z) > maxSurfaceVelocity.z) {
533
+ solveSequentially = true;
534
+ }
535
+ else {
536
+ break;
537
+ }
538
+ }
539
+ }
540
+ const groundVelocity = axisVel;
541
+ const relativeVelocity = this._tmpVecs[24];
542
+ velocityIn.subtractToRef(groundVelocity, relativeVelocity);
543
+ const vel2 = relativeVelocity.lengthSquared();
544
+ const axisVert = this.up.dot(axis);
545
+ let axisProjVelocity = relativeVelocity.dot(axis);
546
+ let staticFriction = sci0.staticFriction + sci1.staticFriction;
547
+ if (axisVert * axisProjVelocity > 0) {
548
+ staticFriction += (sci0.extraUpStaticFriction + sci1.extraUpStaticFriction) * axisVert;
549
+ }
550
+ else {
551
+ staticFriction += (sci0.extraDownStaticFriction + sci1.extraDownStaticFriction) * axisVert;
552
+ }
553
+ staticFriction *= 0.5;
554
+ const dynamicFriction = (sci0.dynamicFriction + sci1.dynamicFriction) * 0.5;
555
+ // static friction is active if
556
+ // |vel-axisProjVelocity|(rv) * friction(f) > axisProjVelocity(av)
557
+ // -> sqrt( vel2 - av2 ) * f > av
558
+ // -> (vel2 - av2) * f2 > av2
559
+ const f2 = staticFriction * staticFriction;
560
+ const av2 = axisProjVelocity * axisProjVelocity;
561
+ if ((vel2 - av2) * f2 >= av2) {
562
+ // static friction kicks in
563
+ velocityOut.copyFrom(groundVelocity);
564
+ return;
565
+ }
566
+ if (dynamicFriction < 1) {
567
+ // apply dynamic friction
568
+ if (axisProjVelocity * axisProjVelocity > 1e-4 * vel2) {
569
+ const tmp = 1.0 / axisProjVelocity;
570
+ const f = Math.abs(tmp) * Math.sqrt(vel2) * (1.0 - dynamicFriction) + dynamicFriction;
571
+ axisProjVelocity *= f;
572
+ }
573
+ }
574
+ velocityOut.copyFrom(groundVelocity);
575
+ velocityOut.addInPlace(axis.scale(axisProjVelocity));
576
+ }
577
+ _simplexSolverSolve3d(info, maxSurfaceVelocity, sci0, sci1, sci2, allowResort, velocityIn, velocityOut) {
578
+ const eps = 1e-5;
579
+ // Calculate the velocity of the point axis
580
+ let pointVel = null;
581
+ {
582
+ const r0 = sci1.planeNormal.cross(sci2.planeNormal);
583
+ const r1 = sci2.planeNormal.cross(sci0.planeNormal);
584
+ const r2 = sci0.planeNormal.cross(sci1.planeNormal);
585
+ const det = r0.dot(sci0.planeNormal);
586
+ let solveSequentially = false;
587
+ // eslint-disable-next-line no-constant-condition
588
+ while (true) {
589
+ if (Math.abs(det) < eps || solveSequentially) {
590
+ if (allowResort) {
591
+ this._simplexSolverSortInfo(info);
592
+ sci0 = info.supportPlanes[0].constraint;
593
+ sci1 = info.supportPlanes[1].constraint;
594
+ sci2 = info.supportPlanes[2].constraint;
595
+ }
596
+ info.getOutput(sci0).status = 1 /* SurfaceConstraintInteractionStatus.FAILURE_3D */;
597
+ info.getOutput(sci1).status = 1 /* SurfaceConstraintInteractionStatus.FAILURE_3D */;
598
+ info.getOutput(sci2).status = 1 /* SurfaceConstraintInteractionStatus.FAILURE_3D */;
599
+ const oldNum = info.numSupportPlanes;
600
+ this._simplexSolverSolve2d(info, maxSurfaceVelocity, sci0, sci1, velocityIn, velocityOut);
601
+ if (oldNum == info.numSupportPlanes) {
602
+ this._simplexSolverSolve2d(info, maxSurfaceVelocity, sci0, sci2, velocityIn, velocityOut);
603
+ }
604
+ if (oldNum == info.numSupportPlanes) {
605
+ this._simplexSolverSolve2d(info, maxSurfaceVelocity, sci1, sci2, velocityIn, velocityOut);
606
+ }
607
+ return;
608
+ }
609
+ const t = this._tmpVecs[2];
610
+ t.set(sci0.planeNormal.dot(sci0.velocity), sci1.planeNormal.dot(sci1.velocity), sci2.planeNormal.dot(sci2.velocity));
611
+ const m = Matrix.FromValues(r0.x, r0.y, r0.z, 0, r1.x, r1.y, r1.z, 0, r2.x, r2.y, r2.z, 0, 0, 0, 0, 1);
612
+ pointVel = Vector3.TransformNormal(t, m);
613
+ pointVel.scaleInPlace(1 / det);
614
+ if (Math.abs(pointVel.x) > maxSurfaceVelocity.x || Math.abs(pointVel.y) > maxSurfaceVelocity.y || Math.abs(pointVel.z) > maxSurfaceVelocity.z) {
615
+ solveSequentially = true;
616
+ }
617
+ else {
618
+ break;
619
+ }
620
+ }
621
+ }
622
+ velocityOut.copyFrom(pointVel);
623
+ }
624
+ _simplexSolverExamineActivePlanes(info, maxSurfaceVelocity, velocityIn, velocityOut) {
625
+ // eslint-disable-next-line no-constant-condition
626
+ while (true) {
627
+ switch (info.numSupportPlanes) {
628
+ case 1: {
629
+ const sci = info.supportPlanes[0].constraint;
630
+ this._simplexSolverSolve1d(info, sci, velocityIn, velocityOut);
631
+ return;
632
+ }
633
+ case 2: {
634
+ const velocity = Vector3.Zero();
635
+ this._simplexSolverSolve1d(info, info.supportPlanes[1].constraint, velocityIn, velocity);
636
+ const plane0Used = this._simplexSolverSolveTest1d(info.supportPlanes[0].constraint, velocity);
637
+ if (!plane0Used) {
638
+ // Only need plane 1, so remove plane 0
639
+ info.supportPlanes[0].copyFrom(info.supportPlanes[1]);
640
+ info.numSupportPlanes = 1;
641
+ velocityOut.copyFrom(velocity);
642
+ }
643
+ else {
644
+ this._simplexSolverSolve2d(info, maxSurfaceVelocity, info.supportPlanes[0].constraint, info.supportPlanes[1].constraint, velocityIn, velocityOut);
645
+ }
646
+ return;
647
+ }
648
+ case 3: {
649
+ // Try to drop both planes
650
+ {
651
+ const velocity = Vector3.Zero();
652
+ this._simplexSolverSolve1d(info, info.supportPlanes[2].constraint, velocityIn, velocityOut);
653
+ const plane0Used = this._simplexSolverSolveTest1d(info.supportPlanes[0].constraint, velocity);
654
+ if (!plane0Used) {
655
+ const plane1Used = this._simplexSolverSolveTest1d(info.supportPlanes[1].constraint, velocity);
656
+ if (!plane1Used) {
657
+ velocityOut.copyFrom(velocity);
658
+ info.supportPlanes[0].copyFrom(info.supportPlanes[2]);
659
+ info.numSupportPlanes = 1;
660
+ continue;
661
+ }
662
+ }
663
+ }
664
+ // Try to drop plane 0 or 1
665
+ {
666
+ let droppedAPlane = false;
667
+ for (let testPlane = 0; testPlane < 2; testPlane++) {
668
+ const velocity = Vector3.Zero();
669
+ this._simplexSolverSolve2d(info, maxSurfaceVelocity, info.supportPlanes[testPlane].constraint, info.supportPlanes[2].constraint, velocityIn, velocityOut);
670
+ const planeUsed = this._simplexSolverSolveTest1d(info.supportPlanes[1 - testPlane].constraint, velocity);
671
+ if (!planeUsed) {
672
+ info.supportPlanes[0].copyFrom(info.supportPlanes[testPlane]);
673
+ info.supportPlanes[1].copyFrom(info.supportPlanes[2]);
674
+ info.numSupportPlanes--;
675
+ droppedAPlane = true;
676
+ break;
677
+ }
678
+ }
679
+ if (droppedAPlane) {
680
+ continue;
681
+ }
682
+ }
683
+ // Otherwise, try and solve all three planes:
684
+ this._simplexSolverSolve3d(info, maxSurfaceVelocity, info.supportPlanes[0].constraint, info.supportPlanes[1].constraint, info.supportPlanes[2].constraint, true, velocityIn, velocityOut);
685
+ return;
686
+ }
687
+ case 4: {
688
+ this._simplexSolverSortInfo(info);
689
+ let droppedAPlane = false;
690
+ for (let i = 0; i < 3; i++) {
691
+ const velocity = Vector3.Zero();
692
+ this._simplexSolverSolve3d(info, maxSurfaceVelocity, info.supportPlanes[(i + 1) % 3].constraint, info.supportPlanes[(i + 2) % 3].constraint, info.supportPlanes[3].constraint, false, velocityIn, velocity);
693
+ const planeUsed = this._simplexSolverSolveTest1d(info.supportPlanes[i].constraint, velocity);
694
+ if (!planeUsed) {
695
+ info.supportPlanes[i].copyFrom(info.supportPlanes[2]);
696
+ info.supportPlanes[2].copyFrom(info.supportPlanes[3]);
697
+ info.numSupportPlanes = 3;
698
+ droppedAPlane = true;
699
+ break;
700
+ }
701
+ }
702
+ if (droppedAPlane) {
703
+ continue;
704
+ }
705
+ // Nothing can be dropped so we've failed to solve
706
+ // Now we try all 3d combinations
707
+ {
708
+ const velocity = velocityIn.clone();
709
+ const sci0 = info.supportPlanes[0].constraint;
710
+ const sci1 = info.supportPlanes[1].constraint;
711
+ const sci2 = info.supportPlanes[2].constraint;
712
+ const sci3 = info.supportPlanes[3].constraint;
713
+ const oldNum = info.numSupportPlanes;
714
+ if (oldNum == info.numSupportPlanes) {
715
+ this._simplexSolverSolve3d(info, maxSurfaceVelocity, sci0, sci1, sci2, false, velocity, velocity);
716
+ // eslint-disable-next-line no-dupe-else-if
717
+ }
718
+ else if (oldNum == info.numSupportPlanes) {
719
+ this._simplexSolverSolve3d(info, maxSurfaceVelocity, sci0, sci1, sci3, false, velocity, velocity);
720
+ // eslint-disable-next-line no-dupe-else-if
721
+ }
722
+ else if (oldNum == info.numSupportPlanes) {
723
+ this._simplexSolverSolve3d(info, maxSurfaceVelocity, sci0, sci2, sci3, false, velocity, velocity);
724
+ // eslint-disable-next-line no-dupe-else-if
725
+ }
726
+ else if (oldNum == info.numSupportPlanes) {
727
+ this._simplexSolverSolve3d(info, maxSurfaceVelocity, sci1, sci2, sci3, false, velocity, velocity);
728
+ }
729
+ velocityOut.copyFrom(velocity);
730
+ }
731
+ // Search a plane to drop
732
+ {
733
+ // Search the highest penalty value
734
+ let maxStatus = 0 /* SurfaceConstraintInteractionStatus.OK */;
735
+ for (let i = 0; i < 4; i++) {
736
+ maxStatus = Math.max(maxStatus, info.supportPlanes[i].interaction.status);
737
+ }
738
+ // Remove the place with the lowest priority and the highest penalty
739
+ let i = 0;
740
+ for (; i < 4; i++) {
741
+ if (maxStatus == info.supportPlanes[i].interaction.status) {
742
+ info.supportPlanes[i].copyFrom(info.supportPlanes[3]);
743
+ break;
744
+ }
745
+ info.numSupportPlanes--;
746
+ }
747
+ }
748
+ // Clear penalty flags for the other planes
749
+ for (let i = 0; i < 3; i++) {
750
+ info.supportPlanes[i].interaction.status = 0 /* SurfaceConstraintInteractionStatus.OK */;
751
+ }
752
+ continue;
753
+ }
754
+ }
755
+ }
756
+ }
757
+ _simplexSolverSolve(constraints, velocity, deltaTime, minDeltaTime, up, maxSurfaceVelocity) {
758
+ const eps = 1e-6;
759
+ const output = new SimplexSolverOutput();
760
+ output.position = Vector3.Zero();
761
+ output.velocity = velocity.clone();
762
+ output.planeInteractions = [];
763
+ let remainingTime = deltaTime;
764
+ for (let i = 0; i < constraints.length; i++) {
765
+ output.planeInteractions.push({
766
+ touched: false,
767
+ stopped: false,
768
+ surfaceTime: 0,
769
+ penaltyDistance: 0,
770
+ status: 0 /* SurfaceConstraintInteractionStatus.OK */,
771
+ });
772
+ }
773
+ const info = new SimplexSolverInfo();
774
+ info.inputConstraints = constraints;
775
+ info.outputInteractions = output.planeInteractions;
776
+ info.supportPlanes[0] = new SimplexSolverActivePlanes();
777
+ info.supportPlanes[1] = new SimplexSolverActivePlanes();
778
+ info.supportPlanes[2] = new SimplexSolverActivePlanes();
779
+ info.supportPlanes[3] = new SimplexSolverActivePlanes();
780
+ while (remainingTime > 0) {
781
+ // search for a plane which collides our current direction
782
+ let hitIndex = -1;
783
+ let minCollisionTime = remainingTime;
784
+ for (let i = 0; i < constraints.length; i++) {
785
+ // Do not search existing active planes
786
+ if (info.numSupportPlanes >= 1 && info.supportPlanes[0].index == i)
787
+ continue;
788
+ if (info.numSupportPlanes >= 2 && info.supportPlanes[1].index == i)
789
+ continue;
790
+ if (info.numSupportPlanes >= 3 && info.supportPlanes[2].index == i)
791
+ continue;
792
+ if (output.planeInteractions[i].status != 0 /* SurfaceConstraintInteractionStatus.OK */) {
793
+ continue;
794
+ }
795
+ // Try to find the plane with the shortest time to move
796
+ const sci = constraints[i];
797
+ const relativeVel = this._tmpVecs[25];
798
+ output.velocity.subtractToRef(sci.velocity, relativeVel);
799
+ const relativeProjectedVel = -relativeVel.dot(sci.planeNormal);
800
+ // if projected velocity is pointing away skip it
801
+ if (relativeProjectedVel <= 0) {
802
+ continue;
803
+ }
804
+ // Calculate the time of impact
805
+ const relativePos = this._tmpVecs[26];
806
+ sci.velocity.scaleToRef(info.currentTime, this._tmpVecs[27]);
807
+ output.position.subtractToRef(this._tmpVecs[27], relativePos);
808
+ let projectedPos = sci.planeNormal.dot(relativePos);
809
+ // treat penetrations
810
+ const penaltyDist = output.planeInteractions[i].penaltyDistance;
811
+ if (penaltyDist < eps) {
812
+ projectedPos = 0;
813
+ }
814
+ projectedPos += penaltyDist;
815
+ // check for new hit
816
+ if (projectedPos < minCollisionTime * relativeProjectedVel) {
817
+ minCollisionTime = projectedPos / relativeProjectedVel;
818
+ hitIndex = i;
819
+ }
820
+ }
821
+ // integrate: Walk to our hitPosition we must walk more than 10 microseconds into the future to consider it valid.
822
+ const minAcceptableCollisionTime = 1e-4;
823
+ if (minCollisionTime > minAcceptableCollisionTime) {
824
+ info.currentTime += minCollisionTime;
825
+ remainingTime -= minCollisionTime;
826
+ output.position.addInPlace(output.velocity.scale(minCollisionTime));
827
+ for (let i = 0; i < info.numSupportPlanes; i++) {
828
+ info.supportPlanes[i].interaction.surfaceTime += minCollisionTime;
829
+ info.supportPlanes[i].interaction.touched = true;
830
+ }
831
+ output.deltaTime = info.currentTime;
832
+ if (info.currentTime > minDeltaTime) {
833
+ return output;
834
+ }
835
+ }
836
+ // If we have no hit than we are done
837
+ if (hitIndex < 0) {
838
+ output.deltaTime = deltaTime;
839
+ break;
840
+ }
841
+ // Add our hit to our current list of active planes
842
+ const supportPlane = info.supportPlanes[info.numSupportPlanes++];
843
+ supportPlane.constraint = constraints[hitIndex];
844
+ supportPlane.interaction = output.planeInteractions[hitIndex];
845
+ supportPlane.interaction.penaltyDistance = (supportPlane.interaction.penaltyDistance + eps) * 2.0;
846
+ supportPlane.index = hitIndex;
847
+ // Move our character along the current set of active planes
848
+ this._simplexSolverExamineActivePlanes(info, maxSurfaceVelocity, velocity, output.velocity);
849
+ }
850
+ return output;
851
+ }
852
+ /**
853
+ * Compute a CharacterSurfaceInfo from current state and a direction
854
+ * @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
855
+ * @param direction direction to check, usually gravity direction
856
+ * @returns a CharacterSurfaceInfo object
857
+ */
858
+ checkSupport(deltaTime, direction) {
859
+ const surfaceInfo = {
860
+ isSurfaceDynamic: false,
861
+ supportedState: 0 /* CharacterSupportedState.UNSUPPORTED */,
862
+ averageSurfaceNormal: Vector3.Zero(),
863
+ averageSurfaceVelocity: Vector3.Zero(),
864
+ averageAngularSurfaceVelocity: Vector3.Zero(),
865
+ };
866
+ this.checkSupportToRef(deltaTime, direction, surfaceInfo);
867
+ return surfaceInfo;
868
+ }
869
+ /**
870
+ * Compute a CharacterSurfaceInfo from current state and a direction
871
+ * @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
872
+ * @param direction direction to check, usually gravity direction
873
+ * @param surfaceInfo output for surface info
874
+ */
875
+ checkSupportToRef(deltaTime, direction, surfaceInfo) {
876
+ const eps = 1e-4;
877
+ this._validateManifold();
878
+ const constraints = this._createConstraintsFromManifold(deltaTime, 0.0);
879
+ const storedVelocities = [];
880
+ // Remove velocities and friction to make this a query of the static geometry
881
+ for (let i = 0; i < constraints.length; i++) {
882
+ storedVelocities.push(constraints[i].velocity.clone());
883
+ constraints[i].velocity.setAll(0);
884
+ }
885
+ const maxSurfaceVelocity = this._tmpVecs[3];
886
+ maxSurfaceVelocity.set(this.maxCharacterSpeedForSolver, this.maxCharacterSpeedForSolver, this.maxCharacterSpeedForSolver);
887
+ const output = this._simplexSolverSolve(constraints, direction, deltaTime, deltaTime, this.up, maxSurfaceVelocity);
888
+ surfaceInfo.averageSurfaceVelocity.setAll(0);
889
+ surfaceInfo.averageAngularSurfaceVelocity.setAll(0);
890
+ surfaceInfo.averageSurfaceNormal.setAll(0);
891
+ // If the constraints did not affect the character movement then it is unsupported and we can finish
892
+ if (output.velocity.equalsWithEpsilon(direction, eps)) {
893
+ surfaceInfo.supportedState = 0 /* CharacterSupportedState.UNSUPPORTED */;
894
+ return;
895
+ }
896
+ // Check how was the input velocity modified to determine if the character is supported or sliding
897
+ if (output.velocity.lengthSquared() < eps) {
898
+ surfaceInfo.supportedState = 2 /* CharacterSupportedState.SUPPORTED */;
899
+ }
900
+ else {
901
+ output.velocity.normalize();
902
+ const angleSin = output.velocity.dot(direction);
903
+ const cosSqr = 1 - angleSin * angleSin;
904
+ if (cosSqr < this.maxSlopeCosine * this.maxSlopeCosine) {
905
+ surfaceInfo.supportedState = 1 /* CharacterSupportedState.SLIDING */;
906
+ }
907
+ else {
908
+ surfaceInfo.supportedState = 2 /* CharacterSupportedState.SUPPORTED */;
909
+ }
910
+ }
911
+ // Add all supporting constraints to the ground information
912
+ let numTouching = 0;
913
+ for (let i = -0; i < constraints.length; i++) {
914
+ if (output.planeInteractions[i].touched && constraints[i].planeNormal.dot(direction) < -0.08) {
915
+ surfaceInfo.averageSurfaceNormal.addInPlace(constraints[i].planeNormal);
916
+ surfaceInfo.averageSurfaceVelocity.addInPlace(storedVelocities[i]);
917
+ surfaceInfo.averageAngularSurfaceVelocity.addInPlace(constraints[i].angularVelocity);
918
+ numTouching++;
919
+ }
920
+ }
921
+ if (numTouching > 0) {
922
+ surfaceInfo.averageSurfaceNormal.normalize();
923
+ surfaceInfo.averageSurfaceVelocity.scaleInPlace(1 / numTouching);
924
+ surfaceInfo.averageAngularSurfaceVelocity.scaleInPlace(1 / numTouching);
925
+ }
926
+ }
927
+ _castWithCollectors(startPos, endPos, castCollector /*HP_CollectorId*/, startCollector /*HP_CollectorId*/) {
928
+ const hk = this._scene.getPhysicsEngine().getPhysicsPlugin();
929
+ const hknp = hk._hknp;
930
+ const startNative = [startPos.x, startPos.y, startPos.z];
931
+ const orientation = [this._orientation.x, this._orientation.y, this._orientation.z, this._orientation.w];
932
+ if (startCollector != null) {
933
+ const query /*: ShapeProximityInput*/ = [
934
+ this._shape._pluginData,
935
+ //@ts-ignore
936
+ startNative,
937
+ //@ts-ignore
938
+ orientation,
939
+ this.keepDistance + this.keepContactTolerance, // max distance
940
+ false, // should hit triggers
941
+ [BigInt(0)], // body to ignore //<todo allow for a proxy body!
942
+ ];
943
+ hknp.HP_World_ShapeProximityWithCollector(hk.world, startCollector, query);
944
+ }
945
+ {
946
+ const query /*: ShapeCastInput*/ = [
947
+ this._shape._pluginData,
948
+ //@ts-ignore
949
+ orientation,
950
+ //@ts-ignore
951
+ startNative,
952
+ [endPos.x, endPos.y, endPos.z],
953
+ false, // should hit triggers
954
+ [BigInt(0)], // body to ignore //<todo allow for proxy body
955
+ ];
956
+ hknp.HP_World_ShapeCastWithCollector(hk.world, castCollector, query);
957
+ }
958
+ }
959
+ _resolveContacts(deltaTime, gravity) {
960
+ const eps = 1e-12;
961
+ //<todo object interactions out
962
+ for (let i = 0; i < this._manifold.length; i++) {
963
+ const contact = this._manifold[i];
964
+ const bodyB = this._manifold[i].bodyB;
965
+ //<todo test if bodyB is another character with a proxy body
966
+ // Skip fixed or keyframed bodies as we won't apply impulses to them
967
+ if (bodyB.body.getMotionType(bodyB.index) != 2 /* PhysicsMotionType.DYNAMIC */) {
968
+ continue;
969
+ }
970
+ // Calculate and apply impulse on contacted body
971
+ {
972
+ //<todo input/output for callbacks
973
+ let inputObjectMassInv = 0;
974
+ let inputObjectImpulse = 0;
975
+ let outputObjectImpulse = Vector3.Zero();
976
+ const outputImpulsePosition = contact.position;
977
+ // Calculate relative normal velocity of the contact point in the contacted body
978
+ const pointRelVel = this._tmpVecs[19];
979
+ this._getPointVelocityToRef(bodyB, contact.position, pointRelVel);
980
+ pointRelVel.subtractInPlace(this._velocity);
981
+ const inputProjectedVelocity = pointRelVel.dot(contact.normal);
982
+ const dampFactor = 0.9;
983
+ // Change velocity
984
+ let deltaVelocity = -inputProjectedVelocity * dampFactor;
985
+ // Apply an extra impulse if the collision is actually penetrating
986
+ if (contact.distance < 0) {
987
+ const recoveryTau = 0.4;
988
+ deltaVelocity += (contact.distance * recoveryTau) / deltaTime;
989
+ }
990
+ // Apply impulse if required to keep bodies apart
991
+ if (deltaVelocity < 0) {
992
+ // Calculate the impulse magnitude
993
+ const invInertia = this._getInverseInertiaWorld(bodyB);
994
+ const comWorld = this._tmpVecs[15];
995
+ this._getComWorldToRef(bodyB, comWorld);
996
+ const r = this._tmpVecs[16];
997
+ contact.position.subtractToRef(comWorld, r);
998
+ const jacAng = this._tmpVecs[17];
999
+ Vector3.CrossToRef(r, contact.normal, jacAng);
1000
+ const rc = this._tmpVecs[18];
1001
+ Vector3.TransformNormalToRef(jacAng, invInertia, rc);
1002
+ inputObjectMassInv = rc.dot(jacAng) + this._getInvMass(bodyB);
1003
+ inputObjectImpulse = deltaVelocity / inputObjectMassInv;
1004
+ // Clamp impulse magnitude if required and apply it to the normal direction
1005
+ const maxPushImpulse = -this.characterStrength * deltaTime;
1006
+ if (inputObjectImpulse < maxPushImpulse) {
1007
+ inputObjectImpulse = maxPushImpulse;
1008
+ }
1009
+ outputObjectImpulse = contact.normal.scale(inputObjectImpulse);
1010
+ }
1011
+ else {
1012
+ inputObjectImpulse = 0;
1013
+ inputObjectMassInv = this._getInvMass(bodyB);
1014
+ }
1015
+ // Add gravity
1016
+ {
1017
+ // Calculate effect of gravity on the velocity of the character in the contact normal direction
1018
+ let relVelN = contact.normal.dot(gravity.scale(deltaTime));
1019
+ // If it is a separating contact subtract the separation velocity
1020
+ if (inputProjectedVelocity < 0) {
1021
+ relVelN -= inputProjectedVelocity;
1022
+ }
1023
+ // If the resulting velocity is negative an impulse is applied to stop the character from falling into
1024
+ // the contacted body
1025
+ if (relVelN < -eps) {
1026
+ outputObjectImpulse.addInPlace(contact.normal.scale(this.characterMass * relVelN));
1027
+ }
1028
+ }
1029
+ //<todo Fire callback to allow user to change impulse + use the info / play sounds
1030
+ bodyB.body.applyImpulse(outputObjectImpulse, outputImpulsePosition, bodyB.index);
1031
+ }
1032
+ }
1033
+ }
1034
+ _getInverseInertiaWorld(body) {
1035
+ const mp = body.body.getMassProperties(body.index);
1036
+ if (!mp.inertia || !mp.inertiaOrientation) {
1037
+ return Matrix.IdentityReadOnly;
1038
+ }
1039
+ const invOrientation = Matrix.FromQuaternionToRef(mp.inertiaOrientation, TmpVectors.Matrix[0]).invert();
1040
+ const it = TmpVectors.Matrix[1];
1041
+ const ir = invOrientation.getRowToRef(0, TmpVectors.Vector4[0]);
1042
+ it.setRowFromFloats(0, mp.inertia.x * ir.x, mp.inertia.x * ir.y, mp.inertia.x * ir.z, 0);
1043
+ invOrientation.getRowToRef(1, ir);
1044
+ it.setRowFromFloats(0, mp.inertia.y * ir.x, mp.inertia.y * ir.y, mp.inertia.y * ir.z, 0);
1045
+ invOrientation.getRowToRef(2, ir);
1046
+ it.setRowFromFloats(0, mp.inertia.z * ir.x, mp.inertia.z * ir.y, mp.inertia.z * ir.z, 0);
1047
+ invOrientation.multiplyToRef(it, this._tmpMatrix);
1048
+ return this._tmpMatrix;
1049
+ }
1050
+ _getComWorldToRef(body, result) {
1051
+ const mp = body.body.getMassProperties(body.index);
1052
+ Vector3.TransformCoordinatesToRef(mp.centerOfMass, body.body.transformNode.getWorldMatrix(), result);
1053
+ }
1054
+ _getInvMass(body) {
1055
+ return 1 / body.body.getMassProperties(body.index).mass;
1056
+ }
1057
+ /**
1058
+ * Update internal state. Must be called once per frame
1059
+ * @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
1060
+ * @param surfaceInfo surface information returned by checkSupport
1061
+ * @param gravity gravity applied to the character. Can be different that world gravity
1062
+ */
1063
+ integrate(deltaTime, surfaceInfo, gravity) {
1064
+ const hk = this._scene.getPhysicsEngine().getPhysicsPlugin();
1065
+ const invDeltaTime = 1 / deltaTime;
1066
+ let remainingTime = deltaTime;
1067
+ let newVelocity = Vector3.Zero();
1068
+ // If the difference between the cast displacement and the simplex solver output position is less than this
1069
+ // value (per component), do not do a second cast to check if it's possible to reach the output position.
1070
+ const displacementEps = 1e-4;
1071
+ const epsSqrd = 1e-8;
1072
+ // Choose the first cast direction. If velocity hasn't changed from the previous integrate, guess that the
1073
+ // displacement will be the same as last integrate, scaled by relative step length. Otherwise, guess based
1074
+ // on current velocity.
1075
+ {
1076
+ const tolerance = displacementEps * invDeltaTime;
1077
+ if (this._velocity.equalsWithEpsilon(this._lastVelocity, tolerance)) {
1078
+ this._lastDisplacement.scaleInPlace(remainingTime * this._lastInvDeltaTime);
1079
+ }
1080
+ else {
1081
+ const displacementVelocity = this._velocity;
1082
+ if (surfaceInfo.supportedState == 2 /* CharacterSupportedState.SUPPORTED */) {
1083
+ const relativeVelocity = this._tmpVecs[28];
1084
+ this._velocity.subtractToRef(surfaceInfo.averageSurfaceVelocity, relativeVelocity);
1085
+ const normalDotVelocity = surfaceInfo.averageSurfaceNormal.dot(relativeVelocity);
1086
+ if (normalDotVelocity < 0) {
1087
+ relativeVelocity.subtractInPlace(surfaceInfo.averageSurfaceNormal.scale(normalDotVelocity));
1088
+ displacementVelocity.copyFrom(relativeVelocity);
1089
+ displacementVelocity.addInPlace(surfaceInfo.averageSurfaceVelocity);
1090
+ }
1091
+ }
1092
+ this._lastDisplacement.copyFrom(displacementVelocity);
1093
+ this._lastDisplacement.scaleInPlace(remainingTime);
1094
+ }
1095
+ this._lastVelocity.copyFrom(this._velocity);
1096
+ this._lastInvDeltaTime = invDeltaTime;
1097
+ }
1098
+ // Make sure that contact with bodies that have been removed since the call to checkSupport() are removed from the
1099
+ // manifold
1100
+ this._validateManifold();
1101
+ for (let iter = 0; iter < this.maxCastIterations && remainingTime > 1e-5; iter++) {
1102
+ this._castWithCollectors(this._position, this._position.add(this._lastDisplacement), this._castCollector, this._startCollector);
1103
+ const updateResult = this._updateManifold(this._startCollector, this._castCollector, this._lastDisplacement);
1104
+ // Create surface constraints from the manifold contacts.
1105
+ const constraints = this._createConstraintsFromManifold(deltaTime, deltaTime - remainingTime);
1106
+ const maxSurfaceVelocity = this._tmpVecs[3];
1107
+ maxSurfaceVelocity.set(this.maxCharacterSpeedForSolver, this.maxCharacterSpeedForSolver, this.maxCharacterSpeedForSolver);
1108
+ const minDeltaTime = this._velocity.lengthSquared() == 0 ? 0.0 : (0.5 * this.keepDistance) / this._velocity.length();
1109
+ const solveResults = this._simplexSolverSolve(constraints, this._velocity, remainingTime, minDeltaTime, this.up, maxSurfaceVelocity);
1110
+ const newDisplacement = solveResults.position;
1111
+ const solverDeltaTime = solveResults.deltaTime;
1112
+ newVelocity = solveResults.velocity;
1113
+ this._resolveContacts(deltaTime, gravity);
1114
+ let newContactIndex = -1;
1115
+ // todo if (updateResult == hit multiple bodies) ... cast again
1116
+ // If castCollector had hits on different bodies (so we're not sure if some non-closest body could be in our way) OR
1117
+ // the simplex has given an output direction different from the cast guess
1118
+ // we re-cast to check we can move there. There is no need to get the start points again.
1119
+ if (updateResult != 0 || (newDisplacement.lengthSquared() > epsSqrd && !this._lastDisplacement.equalsWithEpsilon(newDisplacement, displacementEps))) {
1120
+ this._castWithCollectors(this._position, this._position.add(newDisplacement), this._castCollector, this._startCollector);
1121
+ const hknp = hk._hknp;
1122
+ const numCastHits = hknp.HP_QueryCollector_GetNumHits(this._castCollector)[1];
1123
+ // Find the first contact that isn't already in the manifold
1124
+ if (numCastHits > 0) {
1125
+ //<todo sortHits()
1126
+ for (let i = 0; i < numCastHits; i++) {
1127
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1128
+ const [fraction, _hitLocal, hitWorld] = hknp.HP_QueryCollector_GetShapeCastResult(this._castCollector, i)[1];
1129
+ const newContact = contactFromCast(hk, hitWorld, newDisplacement, fraction, this.keepDistance);
1130
+ if (this._findContact(newContact, this._manifold, 0.1) == -1) {
1131
+ //<todo fireContactAdded
1132
+ newContactIndex = this._manifold.length;
1133
+ this._manifold.push(newContact);
1134
+ //<todo updateTriggersSeen()
1135
+ break;
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ if (newContactIndex >= 0) {
1141
+ const newContact = this._manifold[newContactIndex];
1142
+ const displacementLengthInv = 1.0 / newDisplacement.length();
1143
+ const angleBetweenMovementAndSurface = newDisplacement.dot(newContact.normal) * displacementLengthInv;
1144
+ const keepDistanceAlongMovement = this.keepDistance / -angleBetweenMovementAndSurface;
1145
+ const distance = newContact.fraction;
1146
+ let fraction = distance - keepDistanceAlongMovement * displacementLengthInv;
1147
+ fraction = Math.min(Math.max(fraction, 0.0), 1.0);
1148
+ const displacement = newDisplacement.scale(fraction);
1149
+ this._position.addInPlace(displacement);
1150
+ remainingTime -= solverDeltaTime * fraction;
1151
+ }
1152
+ else {
1153
+ this._position.addInPlace(newDisplacement);
1154
+ remainingTime -= solverDeltaTime;
1155
+ }
1156
+ this._lastDisplacement.copyFrom(newDisplacement);
1157
+ }
1158
+ this._velocity.copyFrom(newVelocity);
1159
+ }
1160
+ /**
1161
+ * Helper function to calculate velocity based on surface informations and current velocity state and target
1162
+ * @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
1163
+ * @param forwardWorld character forward in world coordinates
1164
+ * @param surfaceNormal surface normal direction
1165
+ * @param currentVelocity current velocity
1166
+ * @param surfaceVelocity velocity induced by the surface
1167
+ * @param desiredVelocity desired character velocity
1168
+ * @param upWorld up vector in world space
1169
+ * @param result resulting velocity vector
1170
+ * @returns boolean true if result has been computed
1171
+ */
1172
+ calculateMovementToRef(deltaTime, forwardWorld, surfaceNormal, currentVelocity, surfaceVelocity, desiredVelocity, upWorld, result) {
1173
+ const eps = 1e-5;
1174
+ let binorm = forwardWorld.cross(upWorld);
1175
+ if (binorm.lengthSquared() < eps) {
1176
+ return false;
1177
+ }
1178
+ binorm.normalize();
1179
+ const tangent = binorm.cross(surfaceNormal);
1180
+ tangent.normalize();
1181
+ binorm = tangent.cross(surfaceNormal);
1182
+ binorm.normalize();
1183
+ const surfaceFrame = Matrix.FromValues(tangent.x, tangent.y, tangent.z, 0, binorm.x, binorm.y, binorm.z, 0, surfaceNormal.x, surfaceNormal.y, surfaceNormal.z, 0, 0, 0, 0, 1);
1184
+ const invSurfaceFrame = surfaceFrame.clone().invert();
1185
+ currentVelocity.subtractToRef(surfaceVelocity, this._tmpVecs[29]);
1186
+ const relative = this._tmpVecs[30];
1187
+ Vector3.TransformNormalToRef(this._tmpVecs[29], invSurfaceFrame, relative);
1188
+ const sideVec = upWorld.cross(forwardWorld);
1189
+ const fwd = desiredVelocity.dot(forwardWorld);
1190
+ const side = desiredVelocity.dot(sideVec);
1191
+ const len = desiredVelocity.length();
1192
+ const desiredVelocitySF = this._tmpVecs[4];
1193
+ desiredVelocitySF.set(-fwd, side, 0);
1194
+ desiredVelocitySF.normalize();
1195
+ desiredVelocitySF.scaleInPlace(len);
1196
+ const diff = this._tmpVecs[5];
1197
+ desiredVelocitySF.subtractToRef(relative, diff);
1198
+ // Clamp it by maxVelocityDelta and limit it by gain.
1199
+ {
1200
+ const lenSq = diff.lengthSquared();
1201
+ const gain = 0.05;
1202
+ const maxVelocityDelta = 50.0 * deltaTime;
1203
+ let tmp;
1204
+ if (lenSq * gain * gain > maxVelocityDelta * maxVelocityDelta) {
1205
+ tmp = maxVelocityDelta / Math.sqrt(lenSq);
1206
+ }
1207
+ else {
1208
+ tmp = gain;
1209
+ }
1210
+ diff.scaleInPlace(tmp);
1211
+ }
1212
+ relative.addInPlace(diff);
1213
+ // Transform back to world space and apply
1214
+ Vector3.TransformNormalToRef(relative, surfaceFrame, result);
1215
+ // Add back in the surface velocity
1216
+ result.addInPlace(surfaceVelocity);
1217
+ return true;
1218
+ }
1219
+ /**
1220
+ * Helper function to calculate velocity based on surface informations and current velocity state and target
1221
+ * @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
1222
+ * @param forwardWorld character forward in world coordinates
1223
+ * @param surfaceNormal surface normal direction
1224
+ * @param currentVelocity current velocity
1225
+ * @param surfaceVelocity velocity induced by the surface
1226
+ * @param desiredVelocity desired character velocity
1227
+ * @param upWorld up vector in world space
1228
+ * @returns a new velocity vector
1229
+ */
1230
+ calculateMovement(deltaTime, forwardWorld, surfaceNormal, currentVelocity, surfaceVelocity, desiredVelocity, upWorld) {
1231
+ const result = new Vector3(0, 0, 0);
1232
+ this.calculateMovementToRef(deltaTime, forwardWorld, surfaceNormal, currentVelocity, surfaceVelocity, desiredVelocity, upWorld, result);
1233
+ return result;
1234
+ }
1235
+ }
1236
+ //# sourceMappingURL=characterController.js.map