@geoblocks/cesium-walk 0.2.2 → 0.3.0
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/README.md +7 -3
- package/cesium-walk.js +83 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -92,10 +92,14 @@ viewer.scene.primitives.add(await Model.fromGltfAsync({url, modelMatrix}));
|
|
|
92
92
|
const mesh = createTriangleMeshFromGltf(await (await fetch(url)).arrayBuffer(), modelMatrix);
|
|
93
93
|
|
|
94
94
|
const walkMode = new CesiumWalk(viewer);
|
|
95
|
-
// stand on the highest floor below the knees, climb steps up to 0.4 m, stop at the walls 0.5 m ahead
|
|
96
|
-
|
|
95
|
+
// stand on the highest floor below the knees, climb steps up to 0.4 m, stop at the walls 0.5 m ahead, keep
|
|
96
|
+
// 0.4 m from the walls beside
|
|
97
|
+
walkOn(walkMode, mesh, {eye: 1.6, step: 0.4, reach: 0.5, radius: 0.4});
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
The walker stands on the highest floor below its knees, up the stairs and under the arches, else on the ground
|
|
100
101
|
it had (the terrain). The walls stop it in every direction, from rays cast at its knees, waist and eyes
|
|
101
|
-
(`rayHeights(eye, step)`); the floors and stairs are left to its ground.
|
|
102
|
+
(`rayHeights(eye, step)`); the floors and stairs are left to its ground. With a `radius`, its body at those
|
|
103
|
+
heights is pushed out of any wall nearer than that, the side or the corner of a piece it walks past, which no ray
|
|
104
|
+
ahead meets, and which the camera's near plane would otherwise cut open; a step's riser under the knees is
|
|
105
|
+
climbed, not kept from. Without, the walker is a point, as on the depth probe.
|
package/cesium-walk.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Cartesian3, Math as CesiumMath} from "@cesium/core";
|
|
1
|
+
import {Cartesian3, Ellipsoid, FixedFrameTransforms, Math as CesiumMath, Matrix4} from "@cesium/core";
|
|
2
2
|
import Controls from "@geoblocks/cesium-input";
|
|
3
3
|
import {ObstacleProbe, blockedStep, meshGroundHeight, meshObstacleAhead} from "@geoblocks/cesium-obstacles";
|
|
4
4
|
|
|
@@ -15,6 +15,8 @@ const SETTLED = 1e-3;
|
|
|
15
15
|
// at a concave corner the slide along one wall heads into the other: a step is checked again along its slide, this
|
|
16
16
|
// many times at most, before it is taken
|
|
17
17
|
const SLIDE_PASSES = 3;
|
|
18
|
+
// in a corner, pushed out of one wall, the body may be in the other: it is pushed again, this many times at most
|
|
19
|
+
const CLEAR_PASSES = 3;
|
|
18
20
|
|
|
19
21
|
// the default bindings: W, A, S, D or the arrows to walk, Shift to sprint and Space to jump; on a gamepad, the
|
|
20
22
|
// left stick, at any speed and in any direction, Cross to sprint and Square to jump, as in GTA 5. Keys by
|
|
@@ -37,6 +39,10 @@ const normalScratch = new Cartesian3();
|
|
|
37
39
|
const forwardScratch = new Cartesian3();
|
|
38
40
|
const stepScratch = new Cartesian3();
|
|
39
41
|
const allowedScratch = new Cartesian3();
|
|
42
|
+
const awayScratch = new Cartesian3();
|
|
43
|
+
const frameScratch = new Matrix4();
|
|
44
|
+
const bodyScratch = new Cartesian3();
|
|
45
|
+
const upScratch = new Cartesian3();
|
|
40
46
|
const targetScratch = new Cartesian3();
|
|
41
47
|
const changeScratch = new Cartesian3();
|
|
42
48
|
|
|
@@ -173,6 +179,21 @@ export default class CesiumWalk {
|
|
|
173
179
|
*/
|
|
174
180
|
this.obstacleAhead = (toward, key) => this.obstacleProbe_.ahead(toward, key);
|
|
175
181
|
|
|
182
|
+
/**
|
|
183
|
+
* The walker's body: how far from the walls it keeps, in meters, pushed out of any it walks past, sideways
|
|
184
|
+
* or at a corner, where no ray ahead meets it; 0 by default, a walker without a body.
|
|
185
|
+
* @type {number}
|
|
186
|
+
*/
|
|
187
|
+
this.radius = 0;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The surface nearest the walker's body, within its radius: a point of it, the surface's normal toward the
|
|
191
|
+
* body, and the point of the body it is nearest, if any; asked after each step while collision is on. None by
|
|
192
|
+
* default, as the obstacles in view tell nothing about what is beside the walker; walkOn gives the mesh's.
|
|
193
|
+
* @type {() => {point: Cartesian3, normal: Cartesian3, origin: Cartesian3} | undefined}
|
|
194
|
+
*/
|
|
195
|
+
this.surfaceNear = () => undefined;
|
|
196
|
+
|
|
176
197
|
/**
|
|
177
198
|
* The height of the ground under a position, in meters above the ellipsoid, or undefined when unknown
|
|
178
199
|
* yet. The terrain by default, none without a globe; a model's floors otherwise, for a walker on several levels.
|
|
@@ -349,6 +370,40 @@ export default class CesiumWalk {
|
|
|
349
370
|
if (length > 0) {
|
|
350
371
|
camera.move(toward, length);
|
|
351
372
|
}
|
|
373
|
+
if (this.collision) {
|
|
374
|
+
this.keepClear_();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Pushes the camera level out of the walls the walker's body is in, each in turn.
|
|
380
|
+
*/
|
|
381
|
+
keepClear_() {
|
|
382
|
+
const camera = this.viewer.camera;
|
|
383
|
+
for (let pass = 0; pass < CLEAR_PASSES; pass++) {
|
|
384
|
+
const surface = this.surfaceNear();
|
|
385
|
+
if (!surface) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const up = this.surfaceNormal_();
|
|
389
|
+
// level, from the surface to the body; a surface straight above or below it is left to the walker's ground
|
|
390
|
+
const away = Cartesian3.subtract(surface.origin, surface.point, awayScratch);
|
|
391
|
+
Cartesian3.subtract(away, Cartesian3.projectVector(away, up, stepScratch), away);
|
|
392
|
+
const distance = Cartesian3.magnitude(away);
|
|
393
|
+
if (distance >= this.radius) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (distance > CesiumMath.EPSILON6) {
|
|
397
|
+
Cartesian3.divideByScalar(away, distance, away);
|
|
398
|
+
} else {
|
|
399
|
+
Cartesian3.subtract(surface.normal, Cartesian3.projectVector(surface.normal, up, stepScratch), away);
|
|
400
|
+
if (Cartesian3.magnitude(away) < CesiumMath.EPSILON6) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
Cartesian3.normalize(away, away);
|
|
404
|
+
}
|
|
405
|
+
camera.move(away, this.radius - distance);
|
|
406
|
+
}
|
|
352
407
|
}
|
|
353
408
|
|
|
354
409
|
clampCameraToTerrain_() {
|
|
@@ -409,17 +464,40 @@ export function rayHeights(eye, step) {
|
|
|
409
464
|
/**
|
|
410
465
|
* Walks a CesiumWalk on a triangle mesh of cesium-obstacles: it stands on the highest floor below its knees, up
|
|
411
466
|
* the stairs, under the arches, else on what it stood on before (the terrain); the walls ahead stop it, from rays
|
|
412
|
-
* cast at the knees, the waist and the eyes, the floors and stairs left to the ground below, in every direction
|
|
467
|
+
* cast at the knees, the waist and the eyes, the floors and stairs left to the ground below, in every direction;
|
|
468
|
+
* with a radius, its body at those heights is kept that far from the walls beside it too, a step's riser under
|
|
469
|
+
* the knees excepted.
|
|
413
470
|
* @param {CesiumWalk} walk
|
|
414
471
|
* @param {import("@geoblocks/cesium-obstacles").TriangleMesh} mesh
|
|
415
|
-
* @param {{eye: number, step: number, reach: number}} options In meters: the eyes above the
|
|
416
|
-
* highest step, how far ahead the walls are looked for.
|
|
472
|
+
* @param {{eye: number, step: number, reach: number, radius?: number}} options In meters: the eyes above the
|
|
473
|
+
* floor, the highest step, how far ahead the walls are looked for, the body's radius (none by default).
|
|
417
474
|
*/
|
|
418
|
-
export function walkOn(walk, mesh, {eye, step, reach}) {
|
|
475
|
+
export function walkOn(walk, mesh, {eye, step, reach, radius = 0}) {
|
|
419
476
|
const camera = walk.viewer.scene.camera;
|
|
420
477
|
const fallback = walk.groundHeight;
|
|
421
478
|
const offsets = rayHeights(eye, step).map(height => new Cartesian3(0, 0, height - eye));
|
|
422
479
|
walk.obstacleAhead = meshObstacleAhead(mesh, () => camera.positionWC, {offsets, reach});
|
|
480
|
+
walk.radius = radius;
|
|
481
|
+
if (radius > 0) {
|
|
482
|
+
walk.surfaceNear = () => {
|
|
483
|
+
const position = camera.positionWC;
|
|
484
|
+
const frame = FixedFrameTransforms.eastNorthUpToFixedFrame(position, Ellipsoid.WGS84, frameScratch);
|
|
485
|
+
const up = Ellipsoid.WGS84.geodeticSurfaceNormal(position, upScratch);
|
|
486
|
+
let nearest;
|
|
487
|
+
for (const [k, offset] of offsets.entries()) {
|
|
488
|
+
const origin = Matrix4.multiplyByPoint(frame, offset, bodyScratch);
|
|
489
|
+
const hit = mesh.nearest(origin, radius, {walls: true});
|
|
490
|
+
// under the knees, a step's riser: climbed, not kept from
|
|
491
|
+
if (!hit || (k === 0 && Cartesian3.dot(Cartesian3.subtract(hit.position, origin, awayScratch), up) < 0)) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (!nearest || hit.distance < nearest.distance) {
|
|
495
|
+
nearest = {point: hit.position, normal: hit.normal, origin: Cartesian3.clone(origin), distance: hit.distance};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return nearest;
|
|
499
|
+
};
|
|
500
|
+
}
|
|
423
501
|
const ground = meshGroundHeight(mesh, {below: eye - step});
|
|
424
502
|
walk.groundHeight = cartographic => ground(cartographic) ?? fallback(cartographic);
|
|
425
503
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geoblocks/cesium-walk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "BSD-3-Clause",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@geoblocks/cesium-input": "^0.1.0",
|
|
25
|
-
"@geoblocks/cesium-obstacles": "^0.
|
|
25
|
+
"@geoblocks/cesium-obstacles": "^0.3.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@cesium/core": ">=0.1.0",
|
|
29
29
|
"@cesium/engine": ">=10"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "18519930ac006985391a708114d9bd746eec4328"
|
|
32
32
|
}
|