@woosh/meep-engine 2.39.4 → 2.39.7

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 (50) hide show
  1. package/core/binary/BinaryBuffer.js +1 -0
  2. package/core/geom/3d/topology/bounds/{computeTraingleClusterNormalBoundingCone.js → computeTriangleClusterNormalBoundingCone.js} +8 -1
  3. package/core/math/FLT_EPSILON_32.js +7 -0
  4. package/core/math/random/seededRandom_Mulberry32.js +1 -1
  5. package/core/model/stat/LinearModifier.js +7 -0
  6. package/core/process/task/RemainingTimeEstimator.js +49 -0
  7. package/core/process/task/Task.js +3 -1
  8. package/engine/Engine.js +0 -79
  9. package/engine/asset/AssetManager.d.ts +3 -0
  10. package/engine/asset/AssetManager.js +5 -4
  11. package/engine/ecs/terrain/ecs/Terrain.js +33 -0
  12. package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +13 -0
  13. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +44 -2
  14. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +12 -4
  15. package/engine/graphics/ecs/path/testPathDisplaySystem.js +58 -53
  16. package/engine/graphics/ecs/path/tube/PathNormalType.d.ts +4 -0
  17. package/engine/graphics/ecs/path/tube/PathNormalType.js +11 -0
  18. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +4 -0
  19. package/engine/graphics/ecs/path/tube/TubePathStyle.js +21 -3
  20. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +4 -1
  21. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +4 -1
  22. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +41 -24
  23. package/engine/graphics/geometry/VertexDataSpec.js +24 -0
  24. package/engine/graphics/micron/build/PatchRepresentation.js +3 -3
  25. package/engine/graphics/shaders/TerrainShader.js +3 -7
  26. package/engine/knowledge/database/StaticKnowledgeDataTable.js +66 -28
  27. package/engine/navigation/ecs/components/Path.js +58 -232
  28. package/engine/notify/Notification.js +3 -0
  29. package/engine/scene/transitionToScene.js +2 -1
  30. package/engine/ui/notification/AnimatedObjectEmitter.js +3 -2
  31. package/engine/ui/notification/ViewEmitter.js +8 -0
  32. package/engine/ui/scene/initializeNotifications.js +3 -0
  33. package/generation/{GridGenerator.js → GridTaskGroup.js} +21 -5
  34. package/generation/example/SampleGenerator0.js +2 -2
  35. package/generation/example/main.js +2 -1
  36. package/generation/filtering/core/CellFilterUnaryOperation.js +5 -8
  37. package/generation/filtering/numeric/util/CellFilterDisplaced.js +7 -7
  38. package/generation/grid/generation/GridTaskSequence.js +5 -3
  39. package/generation/markers/actions/MarkerNodeActionImaginary.js +86 -0
  40. package/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +265 -0
  41. package/generation/theme/TerrainLayerDescription.js +11 -0
  42. package/generation/theme/ThemeEngine.js +5 -6
  43. package/package.json +1 -1
  44. package/samples/terrain/from_image.js +2 -1
  45. package/view/common/ListView.js +7 -1
  46. package/view/elements/windrose/WindRoseDiagram.js +13 -0
  47. package/view/task/TaskLoadingScreen.js +172 -0
  48. package/view/{common → task}/TaskProgressView.js +4 -36
  49. package/view/tooltip/gml/GMLEngine.js +13 -0
  50. package/generation/markers/actions/MarkerNodeActionSelectRandom.js +0 -65
@@ -627,6 +627,7 @@ const MAX_SAFE_UINT_VAR = 2147483647;
627
627
  * @param {number} value
628
628
  */
629
629
  BinaryBuffer.prototype.writeUintVar = function (value) {
630
+ assert.isNonNegativeInteger(value, 'value');
630
631
  assert.ok(value <= MAX_SAFE_UINT_VAR, `value=[${value}] exceeds maximum safe limit[=${MAX_SAFE_UINT_VAR}]`);
631
632
 
632
633
  let first = true;
@@ -11,7 +11,7 @@ import { v3_angle_between } from "../../../v3_angle_between.js";
11
11
  * @param {ConicRay} result
12
12
  * @param {Iterable<TopoTriangle>} faces
13
13
  */
14
- export function computeTraingleClusterNormalBoundingCone(result, faces) {
14
+ export function computeTriangleClusterNormalBoundingCone(result, faces) {
15
15
  const normal_data = [];
16
16
  let normal_count = 0;
17
17
 
@@ -45,6 +45,13 @@ export function computeTraingleClusterNormalBoundingCone(result, faces) {
45
45
 
46
46
  const miniball_center = miniball.center();
47
47
 
48
+ if (Number.isNaN(miniball_center[0]) || Number.isNaN(miniball_center[1]) || Number.isNaN(miniball_center[2])) {
49
+ // something went wrong, center is undefined, fall-back to an open cone
50
+ result_direction.set(1, 0, 0);
51
+ result.angle = Math.PI;
52
+ return;
53
+ }
54
+
48
55
  result_direction.readFromArray(miniball_center, 0);
49
56
  const center_vector_magnitude = result_direction.length();
50
57
 
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Smallest safe increment for a Float32
3
+ * @see https://www.cplusplus.com/reference/cfloat/
4
+ * @see https://bitbashing.io/comparing-floats.html
5
+ * @type {number}
6
+ */
7
+ export const FLT_EPSILON_32 = 1.192092896E-7;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  *
3
3
  * @param {number} [seed]
4
- * @returns {function():number|{setCurrentSeed(v:number):void,getCurrentSeed():number}}
4
+ * @returns {(function():number)|{setCurrentSeed:( function(v:number):void),getCurrentSeed:(function():number)}}
5
5
  */
6
6
  export function seededRandom_Mulberry32(seed = 0) {
7
7
  let _seed = seed;
@@ -124,4 +124,11 @@ class LinearModifier {
124
124
  }
125
125
  }
126
126
 
127
+ /**
128
+ * @readonly
129
+ * @type {LinearModifier}
130
+ */
131
+ LinearModifier.CONSTANT_ONE = Object.freeze(new LinearModifier(0, 1));
132
+ LinearModifier.CONSTANT_ZERO = Object.freeze(new LinearModifier(0, 0));
133
+
127
134
  export default LinearModifier;
@@ -0,0 +1,49 @@
1
+ export class RemainingTimeEstimator {
2
+ /**
3
+ *
4
+ * @param {number} sampleCount
5
+ * @constructor
6
+ */
7
+ constructor(sampleCount) {
8
+ this.sampleCount = sampleCount;
9
+ this.rates = new Float32Array(sampleCount);
10
+ this.cursor = 0;
11
+ }
12
+
13
+ /**
14
+ *
15
+ * @param {number} timeElapsed
16
+ * @param {number} progress
17
+ */
18
+ update(timeElapsed, progress) {
19
+ const index = this.cursor;
20
+ this.cursor = (this.cursor + 1) % this.sampleCount;
21
+
22
+ const rate = progress / timeElapsed;
23
+
24
+ this.rates[index] = rate;
25
+ }
26
+
27
+ /**
28
+ *
29
+ * @param {number} currentProgress
30
+ * @returns {number}
31
+ */
32
+ estimate(currentProgress) {
33
+ let rateSum = 0;
34
+
35
+ for (let i = 0; i < this.sampleCount; i++) {
36
+ rateSum += this.rates[i];
37
+ }
38
+
39
+ const rate = rateSum / this.sampleCount;
40
+
41
+ if (rate === 0) {
42
+ return Infinity;
43
+ }
44
+
45
+ const result = (1 - currentProgress) / rate;
46
+
47
+ return result;
48
+ }
49
+ }
@@ -114,7 +114,7 @@ class Task {
114
114
  //is a task group, add all children instead
115
115
  this.addDependencies(task.children);
116
116
 
117
- } else {
117
+ } else if (task.isTask) {
118
118
 
119
119
  //check that the dependency is not registered yet
120
120
  if (this.dependencies.indexOf(task) === -1) {
@@ -123,6 +123,8 @@ class Task {
123
123
 
124
124
  }
125
125
 
126
+ } else {
127
+ throw new Error('Expected a Task or a TaskGroup, got something else');
126
128
  }
127
129
 
128
130
  return this;
package/engine/Engine.js CHANGED
@@ -14,12 +14,10 @@ import KeyboardDevice from "./input/devices/KeyboardDevice.js";
14
14
  import Grid from './grid/Grid.js';
15
15
  import Preloader from "./asset/preloader/Preloader.js";
16
16
  import SceneManager from "./scene/SceneManager.js";
17
- import TaskProgressView from '../view/common/TaskProgressView.js';
18
17
  import CompressionService from "./compression/CompressionService.js";
19
18
 
20
19
  import GUIEngine from './ui/GUIEngine.js';
21
20
  import { EntityManager } from "./ecs/EntityManager.js";
22
- import { AssetLoaderStatusView } from "../view/asset/AssetLoaderStatusView.js";
23
21
  import ObservedBoolean from "../core/model/ObservedBoolean.js";
24
22
  import Vector1 from "../core/geom/Vector1.js";
25
23
  import { ViewStack } from "../view/elements/navigation/ViewStack.js";
@@ -407,83 +405,6 @@ class Engine {
407
405
  }
408
406
  }
409
407
 
410
- makeLoadingScreen(task) {
411
- const localization = this.localization;
412
-
413
- const taskProgressView = new TaskProgressView({ task, localization });
414
- taskProgressView.el.classList.add('loading-screen');
415
-
416
- //add asset manager loading progress
417
- const loaderStatusView = new AssetLoaderStatusView({ assetManager: this.assetManager, localization });
418
- taskProgressView.addChild(loaderStatusView);
419
- taskProgressView.link();
420
-
421
- let __renderingEnabled = this.renderingEnabled;
422
- this.renderingEnabled = false;
423
-
424
-
425
- const domParent = document.body;
426
-
427
- domParent.appendChild(taskProgressView.el);
428
-
429
- // suspend rendering
430
- const graphics = this.graphics;
431
- const _autoDraw = graphics.autoDraw;
432
-
433
- graphics.autoDraw = false;
434
-
435
- const cleanup = () => {
436
- domParent.removeChild(taskProgressView.el);
437
-
438
- taskProgressView.unlink();
439
-
440
- this.renderingEnabled = __renderingEnabled;
441
-
442
- // restore rendering
443
- graphics.autoDraw = _autoDraw;
444
- };
445
-
446
- task.join(cleanup, printError);
447
- }
448
-
449
- /**
450
- *
451
- * @param {Task|TaskGroup} task
452
- * @returns {Promise<any>}
453
- */
454
- loadSlowTask(task) {
455
- assert.defined(task, 'task');
456
- assert.notNull(task, 'task');
457
-
458
- const engine = this;
459
-
460
- return new Promise(function (resolve, reject) {
461
-
462
- function cleanup() {
463
- simulator.resume();
464
- }
465
-
466
- function success() {
467
- logger.info("Task finished");
468
- cleanup();
469
- resolve();
470
- }
471
-
472
- function failure(e) {
473
- printError(e);
474
- cleanup();
475
- reject();
476
- }
477
-
478
- const simulator = engine.ticker;
479
- simulator.pause();
480
-
481
- task.join(success, failure);
482
-
483
- engine.makeLoadingScreen(task);
484
- });
485
- }
486
-
487
408
  /**
488
409
  * Startup
489
410
  * @returns {Promise}
@@ -2,8 +2,11 @@ import {AssetLoader} from "./loaders/AssetLoader";
2
2
  import {Asset} from "./Asset";
3
3
  import {CrossOriginConfig} from "./CORS/CrossOriginConfig";
4
4
  import {AssetTransformer} from "./AssetTransformer";
5
+ import Engine from "../Engine";
5
6
 
6
7
  export class AssetManager {
8
+ constructor(engine: Engine)
9
+
7
10
  public readonly crossOriginConfig: CrossOriginConfig
8
11
 
9
12
  promise<T>(path: string, type: string): Promise<Asset<T>>
@@ -12,6 +12,7 @@ import { HashSet } from "../../core/collection/HashSet.js";
12
12
  import { CrossOriginConfig } from "./CORS/CrossOriginConfig.js";
13
13
  import { array_remove_element } from "../../core/collection/array/array_remove_element.js";
14
14
  import { AssetDescription } from "./AssetDescription.js";
15
+ import { noop } from "../../core/function/Functions.js";
15
16
 
16
17
  /**
17
18
  *
@@ -147,14 +148,14 @@ export class AssetManager {
147
148
  }
148
149
 
149
150
  /**
150
- *
151
+ * @template T
151
152
  * @param {String} path
152
153
  * @param {String} type
153
- * @param {function(asset:Asset)} callback
154
- * @param {function(*)} failure
154
+ * @param {function(asset:Asset<T>)} callback
155
+ * @param {function(*)} [failure]
155
156
  * @param {function(loaded:number, total:number)} [progress]
156
157
  */
157
- get(path, type, callback, failure, progress) {
158
+ get(path, type, callback, failure = console.error, progress = noop) {
158
159
  if (typeof path !== "string") {
159
160
  throw new Error("Path must be string. Path = " + JSON.stringify(path));
160
161
  }
@@ -647,6 +647,39 @@ class Terrain {
647
647
  );
648
648
 
649
649
  this.layers.updateLayerScales(this.size.x * this.gridScale, this.size.y * this.gridScale);
650
+
651
+ m.uniformsNeedUpdate = true;
652
+ }
653
+
654
+ /**
655
+ *
656
+ * @param {TerrainLayer} layer
657
+ */
658
+ addLayer(layer) {
659
+ const layers = this.layers;
660
+ const index = layers.addLayer(layer);
661
+
662
+ // expand splat
663
+ this.splat.addWeightLayer();
664
+
665
+ if (this.getFlag(TerrainFlags.Built)) {
666
+ // terrain is built, need to do a few updates
667
+
668
+ layers.buildTexture();
669
+
670
+ layer.loadTextureData(this.__assetManager).then(() => {
671
+ // verify that layer is still present
672
+ if (layers.get(index) !== layer) {
673
+ return;
674
+ }
675
+
676
+ layers.writeLayerDataIntoTexture(index);
677
+ });
678
+
679
+ this.updateMaterial();
680
+ }
681
+
682
+ return index;
650
683
  }
651
684
 
652
685
  /**
@@ -87,6 +87,19 @@ export class TerrainLayer {
87
87
  return r;
88
88
  }
89
89
 
90
+ /**
91
+ *
92
+ * @param {TerrainLayerDescription} d
93
+ * @returns {TerrainLayer}
94
+ */
95
+ static fromDescription(d) {
96
+ return TerrainLayer.from(
97
+ d.diffuse,
98
+ d.size.x,
99
+ d.size.y
100
+ );
101
+ }
102
+
90
103
  /**
91
104
  *
92
105
  * @param {AssetManager} assetManager
@@ -23,6 +23,7 @@ import { BinaryClassSerializationAdapter } from "../../../storage/binary/BinaryC
23
23
  import { computeStringHash } from "../../../../../core/primitives/strings/computeStringHash.js";
24
24
  import { invokeObjectEquals } from "../../../../../core/model/object/invokeObjectEquals.js";
25
25
  import { invokeObjectHash } from "../../../../../core/model/object/invokeObjectHash.js";
26
+ import { typed_array_copy } from "../../../../../core/collection/array/typed/typed_array_copy.js";
26
27
 
27
28
  class ScaledTextureKey {
28
29
  constructor() {
@@ -145,7 +146,7 @@ export class TerrainLayers {
145
146
  this.texture.encoding = LinearEncoding;
146
147
  this.texture.internalFormat = 'RGB8';
147
148
 
148
- this.scalesTexture = new DataTexture(new Float32Array(512), 256, 1, RGFormat, FloatType);
149
+ this.scalesTexture = new DataTexture(new Float32Array(64), 32, 1, RGFormat, FloatType);
149
150
 
150
151
  this.scalesTexture.wrapS = ClampToEdgeWrapping;
151
152
  this.scalesTexture.wrapT = ClampToEdgeWrapping;
@@ -185,7 +186,7 @@ export class TerrainLayers {
185
186
 
186
187
  const scaleTextureDataLength = n * 2;
187
188
 
188
- if (image.data.length !== scaleTextureDataLength) {
189
+ if (image.data.length < scaleTextureDataLength) {
189
190
  // dispose of old texture data
190
191
  texture.dispose();
191
192
 
@@ -256,6 +257,43 @@ export class TerrainLayers {
256
257
  return this.layers.get(index);
257
258
  }
258
259
 
260
+ /**
261
+ *
262
+ * @param {TerrainLayerDescription} description
263
+ * @returns {number}
264
+ */
265
+ findIndexByDescription(description) {
266
+ assert.defined(description, 'description');
267
+
268
+ const layers = this.layers;
269
+ const n = layers.length;
270
+
271
+ for (let i = 0; i < n; i++) {
272
+ const terrainLayer = layers.get(i);
273
+
274
+ if (
275
+ terrainLayer.size.equals(description.size)
276
+ && terrainLayer.textureDiffuseURL === description.diffuse
277
+ ) {
278
+ return i;
279
+ }
280
+ }
281
+
282
+ // not found
283
+ return -1;
284
+ }
285
+
286
+ /**
287
+ *
288
+ * @param {string} url
289
+ * @returns {number}
290
+ */
291
+ findIndexByDiffuseTextureURL(url) {
292
+ assert.isString(url, 'url');
293
+
294
+ return this.layers.findIndex(t => t.textureDiffuseURL === url);
295
+ }
296
+
259
297
  /**
260
298
  *
261
299
  * @return {number}
@@ -397,6 +435,10 @@ export class TerrainLayers {
397
435
  //build an array to hold texture array
398
436
  const arrayData = new Uint8Array(size_x * size_y * 3 * layerCount);
399
437
 
438
+ if (image.width === size_x && image.height === size_y) {
439
+ // dimensions preserved, copy old data
440
+ typed_array_copy(image.data, arrayData);
441
+ }
400
442
 
401
443
  image.data = arrayData;
402
444
  image.width = size_x;
@@ -7,6 +7,7 @@ import { assert } from "../../../../../core/assert.js";
7
7
  import { scaleSampler2D } from "../../../../graphics/texture/sampler/scaleSampler2D.js";
8
8
  import { countTask } from "../../../../../core/process/task/TaskUtils.js";
9
9
  import { array_copy } from "../../../../../core/collection/array/copyArray.js";
10
+ import { typed_array_copy } from "../../../../../core/collection/array/typed/typed_array_copy.js";
10
11
 
11
12
 
12
13
  /**
@@ -478,12 +479,19 @@ export class SplatMapping {
478
479
  weightImage.depth = depth;
479
480
  weightImage.data = new Uint8Array(newLayerSize * depth);
480
481
 
481
- for (let d = 0; d < min2(depth, oldDepth); d++) {
482
+ if (oldWidth === width && oldHeight === height) {
483
+ // dimensions haven't changed, we can copy data directly
484
+ typed_array_copy(oldWeightData, weightImage.data);
485
+ } else {
482
486
 
483
- const source = new Sampler2D(oldWeightData.subarray(d * oldLayerSize, (d + 1) * oldLayerSize), 1, oldWidth, oldHeight);
484
- const target = new Sampler2D(weightImage.data.subarray(d * newLayerSize, (d + 1) * newLayerSize), 1, width, height);
487
+ // different dimensions, copy layer-by-layer
488
+ for (let d = 0; d < min2(depth, oldDepth); d++) {
485
489
 
486
- scaleSampler2D(source, target);
490
+ const source = new Sampler2D(oldWeightData.subarray(d * oldLayerSize, (d + 1) * oldLayerSize), 1, oldWidth, oldHeight);
491
+ const target = new Sampler2D(weightImage.data.subarray(d * newLayerSize, (d + 1) * newLayerSize), 1, width, height);
492
+
493
+ scaleSampler2D(source, target);
494
+ }
487
495
  }
488
496
 
489
497
  weightTexture.needsUpdate = true;
@@ -44,7 +44,6 @@ import { PathDisplaySystem } from "./PathDisplaySystem.js";
44
44
  import { PathDisplay } from "./PathDisplay.js";
45
45
  import { PathDisplayType } from "./PathDisplayType.js";
46
46
  import EntityBuilder from "../../../ecs/EntityBuilder.js";
47
- import { RibbonPathStyle } from "./ribbon/RibbonPathStyle.js";
48
47
  import { enableEditor } from "../../../../editor/enableEditor.js";
49
48
  import { GameAssetType } from "../../../asset/GameAssetType.js";
50
49
  import { GLTFAssetLoader } from "../../../asset/loaders/GLTFAssetLoader.js";
@@ -70,6 +69,7 @@ import { BasicMaterialDefinition } from "./tube/BasicMaterialDefinition.js";
70
69
 
71
70
  import '../../../../../../../css/game.scss';
72
71
  import { GizmoRenderingPlugin } from "../../render/gizmo/GizmoRenderingPlugin.js";
72
+ import { PathNormalType } from "./tube/PathNormalType.js";
73
73
 
74
74
  const engineHarness = new EngineHarness();
75
75
 
@@ -303,7 +303,9 @@ function sample_style_entity() {
303
303
  }
304
304
 
305
305
  function sample_style_tube() {
306
+
306
307
  const style = new TubePathStyle();
308
+ style.path_normal_type = PathNormalType.FixedStart;
307
309
 
308
310
  style.color.setRGB(1, 1, 1);
309
311
 
@@ -333,7 +335,7 @@ function sample_style_tube() {
333
335
  style.color.setHSV(0, 1, 0.9);
334
336
  }
335
337
 
336
- make_basic(style);
338
+ make_standard(style);
337
339
 
338
340
  style.width = 0.5;
339
341
  style.cast_shadow = true;
@@ -379,70 +381,73 @@ function sample_style_tube() {
379
381
  return style;
380
382
  }
381
383
 
384
+ /**
385
+ *
386
+ * @param {number[]} points
387
+ * @param {InterpolationType} [interp]
388
+ * @param {Vector3} [offset]
389
+ * @returns {EntityBuilder}
390
+ */
391
+ function makePath({
392
+ points,
393
+ interp = InterpolationType.Linear,
394
+ offset = Vector3.zero
395
+ }) {
396
+ const _p = new Path();
382
397
 
383
- function main(engine) {
384
- EngineHarness.buildBasics({ engine });
398
+ _p.interpolation = interp;
385
399
 
386
- const _p = new Path();
387
- _p.interpolation = InterpolationType.CatmullRom;
388
- // _p.interpolation = InterpolationType.Linear;
389
-
390
- // _p.setPositionsFromVectorArray(Path.smoothPath([
391
- // new Vector3(3, 1, 1),
392
- // new Vector3(3, 1, 3),
393
- // new Vector3(7, 1, 3),
394
- // new Vector3(7, 1, 7),
395
- // new Vector3(3, 1, 7),
396
- // new Vector3(3, 1, 8),
397
- // ], 50));
398
-
399
- _p.setPositionsFromVectorArray([
400
- new Vector3(3, 1, 1),
401
- new Vector3(3, 1, 3),
402
- new Vector3(7, 1, 3),
403
- new Vector3(7, 1, 7),
404
- new Vector3(3, 1, 7),
405
- new Vector3(3, 1, 8),
406
- ]);
407
-
408
-
409
- // _p.setPositionsFromVectorArray([
410
- // new Vector3(1, 1, 3),
411
- // new Vector3(3, 1, 3),
412
- // new Vector3(3, 1, 7),
413
- // new Vector3(7, 1, 7),
414
- // new Vector3(7, 1, 3),
415
- // new Vector3(8, 1, 3),
416
- // ]);
417
- // _p.reverse();
418
-
419
- const trailPathStyle = new RibbonPathStyle();
420
- trailPathStyle.material.diffuse = "data/textures/trail/Circle_04.png";
421
- trailPathStyle.resolution = 10;
422
- trailPathStyle.thickness = 0.1;
400
+ const pk = points.length / 3;
401
+
402
+ _p.setPointCount(pk);
403
+ for (let i = 0; i < pk; i++) {
404
+ const i3 = i * 3;
405
+ _p.setPosition(i,
406
+ points[i3] + offset.x,
407
+ points[i3 + 1] + offset.y,
408
+ points[i3 + 2] + offset.z
409
+ );
410
+ }
423
411
 
424
412
  const pathDisplay = new PathDisplay();
425
413
  pathDisplay.specs.push(
426
- // PathDisplaySpec.from(
427
- // PathDisplayType.Ribbon,
428
- // trailPathStyle
429
- // ),
430
- // PathDisplaySpec.from(
431
- // PathDisplayType.Entity,
432
- // sample_style_entity()
433
- // ),
434
414
  PathDisplaySpec.from(
435
415
  PathDisplayType.Tube,
436
416
  sample_style_tube()
437
417
  )
438
418
  );
439
419
 
440
-
441
- new EntityBuilder()
420
+ return new EntityBuilder()
442
421
  .add(_p)
443
422
  .add(pathDisplay)
444
- .add(Highlight.fromOne(0, 1, 1, 1))
445
- .build(engine.entityManager.dataset);
423
+ .add(Highlight.fromOne(0, 1, 1, 1));
424
+
425
+ }
426
+
427
+ function main(engine) {
428
+ EngineHarness.buildBasics({ engine });
429
+
430
+ makePath({
431
+ points: [
432
+ 3, 1, 1,
433
+ 3, 1, 3,
434
+ 7, 1, 3,
435
+ 7, 1, 7,
436
+ 3, 1, 7,
437
+ 3, 1, 8
438
+ ],
439
+ interp: InterpolationType.Linear
440
+ }).build(engine.entityManager.dataset);
441
+
442
+ makePath({
443
+ points: [
444
+ 17.600000381469727, 3.2100000381469727, -16.34000015258789,
445
+ 17.84000015258789, 4.559999942779541, -10.029999732971191,
446
+ 16.979999542236328, 3.130000114440918, -8.25,
447
+ 16.719999313354492, 3.130000114440918, -6.889999866485596
448
+ ],
449
+ interp: InterpolationType.Linear
450
+ }).build(engine.entityManager.dataset);
446
451
  }
447
452
 
448
453
  init(engineHarness);
@@ -0,0 +1,4 @@
1
+ export enum PathNormalType {
2
+ Automatic = "auto",
3
+ FixedStart = "fixed-start"
4
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @readonly
3
+ * @enum {number}
4
+ */
5
+ export const PathNormalType = {
6
+ Automatic: "auto",
7
+ /**
8
+ * Start of the path has a fixed normal, this will affect all normals along the path too
9
+ */
10
+ FixedStart: "fixed-start"
11
+ };
@@ -3,6 +3,8 @@ import {TubeMaterialType} from "./TubeMaterialType";
3
3
  import {StandardMaterialDefinition} from "./StandardMaterialDefinition";
4
4
  import {MatcapMaterialDefinition} from "./MatcapMaterialDefinition";
5
5
  import {CapType} from "./CapType";
6
+ import Vector3 from "../../../../../core/geom/Vector3";
7
+ import {PathNormalType} from "./PathNormalType";
6
8
 
7
9
  export class TubePathStyle {
8
10
  public color: Color
@@ -28,4 +30,6 @@ export class TubePathStyle {
28
30
  public receive_shadow: boolean
29
31
  public path_mask: number[]
30
32
  public cap_type: CapType
33
+ public path_normal: Vector3
34
+ public path_normal_type: PathNormalType
31
35
  }