@woosh/meep-engine 2.35.9 → 2.35.13

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 (28) hide show
  1. package/core/binary/BinaryBuffer.js +65 -18
  2. package/engine/graphics/ecs/camera/Camera.d.ts +3 -0
  3. package/engine/graphics/ecs/camera/Camera.js +39 -1
  4. package/engine/graphics/ecs/camera/CameraSystem.js +25 -6
  5. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +38 -3
  6. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +19 -4
  7. package/engine/graphics/ecs/path/PathDisplaySystem.js +1 -1
  8. package/engine/graphics/ecs/path/testPathDisplaySystem.js +19 -12
  9. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +3 -0
  10. package/engine/graphics/ecs/path/tube/TubePathStyle.js +71 -2
  11. package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +160 -0
  12. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +61 -0
  13. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +56 -0
  14. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +165 -0
  15. package/engine/graphics/ecs/path/tube/build/estimatePathLength.js +17 -0
  16. package/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.js +37 -0
  17. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +161 -0
  18. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -1
  19. package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +45 -76
  20. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +19 -24
  21. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +1 -1
  22. package/engine/graphics/micron/render/v1/MaterialContext.js +1 -11
  23. package/engine/graphics/micron/render/v1/MaterialVertexSpec.js +55 -2
  24. package/engine/graphics/micron/render/v1/MicronRenderContext.js +151 -0
  25. package/engine/graphics/micron/render/v1/VGThreeRenderer.js +24 -117
  26. package/engine/navigation/ecs/components/Path.js +5 -6
  27. package/package.json +1 -1
  28. package/engine/graphics/ecs/path/tube/TubePathBuilder.js +0 -522
@@ -3,6 +3,7 @@
3
3
  * @enum {boolean}
4
4
  */
5
5
  import { assert } from "../assert.js";
6
+ import { array_copy } from "../collection/array/copyArray.js";
6
7
 
7
8
  /**
8
9
  *
@@ -30,17 +31,50 @@ function BinaryBuffer() {
30
31
  */
31
32
  this.endianness = EndianType.BigEndian;
32
33
 
34
+ /**
35
+ *
36
+ * @type {number}
37
+ */
33
38
  this.position = 0;
39
+
40
+ /**
41
+ *
42
+ * @type {number}
43
+ */
34
44
  this.length = 0;
45
+
46
+ /**
47
+ *
48
+ * @type {number}
49
+ */
35
50
  this.capacity = 0;
36
51
 
37
52
  /**
38
53
  *
39
54
  * @type {ArrayBuffer}
55
+ * @private
40
56
  */
41
57
  this.data = new ArrayBuffer(0);
58
+
59
+ /**
60
+ *
61
+ * @type {DataView}
62
+ * @private
63
+ */
42
64
  this.dataView = new DataView(this.data);
43
65
 
66
+ /**
67
+ *
68
+ * @type {Uint8Array}
69
+ * @private
70
+ */
71
+ this.__data_uint8 = new Uint8Array(0);
72
+
73
+ /**
74
+ *
75
+ * @type {number}
76
+ * @private
77
+ */
44
78
  this.__growFactor = 1.1;
45
79
  }
46
80
 
@@ -50,6 +84,7 @@ function BinaryBuffer() {
50
84
  BinaryBuffer.prototype.fromArrayBuffer = function (arrayBuffer) {
51
85
  this.data = arrayBuffer;
52
86
  this.dataView = new DataView(arrayBuffer);
87
+ this.__data_uint8 = new Uint8Array(arrayBuffer);
53
88
 
54
89
  this.capacity = arrayBuffer.byteLength;
55
90
  //assume that all the data is useful
@@ -85,7 +120,7 @@ BinaryBuffer.prototype.trim = function () {
85
120
  * @param {number} capacity
86
121
  */
87
122
  BinaryBuffer.prototype.setCapacity = function (capacity) {
88
- const oldData = new Uint8Array(this.data);
123
+ const oldData = this.__data_uint8;
89
124
  const newData = new Uint8Array(capacity);
90
125
 
91
126
  //copy old data
@@ -93,7 +128,7 @@ BinaryBuffer.prototype.setCapacity = function (capacity) {
93
128
  newData.set(oldData.subarray(0, copyLength), 0);
94
129
 
95
130
  this.data = newData.buffer;
96
-
131
+ this.__data_uint8 = newData;
97
132
  this.dataView = new DataView(this.data);
98
133
 
99
134
  this.capacity = capacity;
@@ -680,11 +715,11 @@ BinaryBuffer.prototype.writeUint32LE = function (value) {
680
715
  /**
681
716
  *
682
717
  * @param {Uint8Array|Uint8ClampedArray} array
683
- * @param {number} sourceOffset
718
+ * @param {number} source_offset
684
719
  * @param {number} length
685
720
  */
686
- BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
687
- const source_end = sourceOffset + length;
721
+ BinaryBuffer.prototype.writeBytes = function (array, source_offset, length) {
722
+ const source_end = source_offset + length;
688
723
  assert.greaterThanOrEqual(array.length, source_end, 'source array underflow');
689
724
 
690
725
  const targetAddress = this.position;
@@ -693,18 +728,21 @@ BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
693
728
 
694
729
  this.ensureCapacity(end);
695
730
 
696
- const destination = new Uint8Array(this.data, targetAddress, length);
731
+ if (source_offset === 0 && array.length === length) {
732
+ // copying entire source array
697
733
 
698
- if (typeof array.subarray === "function") {
734
+ this.__data_uint8.set(array, targetAddress);
735
+
736
+ } else if (typeof array.subarray === "function") {
699
737
  // typed array, use "subarray" method
700
738
 
701
- destination.set(array.subarray(sourceOffset, source_end));
739
+ this.__data_uint8.set(array.subarray(source_offset, source_end), targetAddress);
702
740
 
703
741
  } else {
704
742
  // not a typed array, copy byte by byte manually
705
743
 
706
744
  for (let i = 0; i < length; i++) {
707
- destination[i] = array[sourceOffset + i];
745
+ this.__data_uint8[targetAddress + i] = array[source_offset + i];
708
746
  }
709
747
 
710
748
  }
@@ -714,14 +752,23 @@ BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
714
752
 
715
753
  /**
716
754
  *
717
- * @param {Uint8Array} target
718
- * @param {number} targetOffset
755
+ * @param {Uint8Array} destination
756
+ * @param {number} destination_offset
719
757
  * @param {number} length
720
758
  */
721
- BinaryBuffer.prototype.readBytes = function (target, targetOffset, length) {
722
- const end = this.position + length;
723
- const uint8Array = new Uint8Array(this.data);
724
- target.set(uint8Array.subarray(this.position, end), targetOffset);
759
+ BinaryBuffer.prototype.readBytes = function (destination, destination_offset, length) {
760
+ const source_position = this.position;
761
+
762
+ const end = source_position + length;
763
+
764
+ const uint8 = this.__data_uint8;
765
+
766
+ if (length < 128) {
767
+ // small copy
768
+ array_copy(uint8, source_position, destination, destination_offset, length);
769
+ } else {
770
+ destination.set(uint8.subarray(source_position, end), destination_offset);
771
+ }
725
772
 
726
773
  this.position = end;
727
774
  };
@@ -764,7 +811,7 @@ BinaryBuffer.prototype.writeUTF8String = function (string) {
764
811
 
765
812
  this.ensureCapacity(tlen + at);
766
813
 
767
- let target = new Uint8Array(this.data); // ... but at 8 byte offset
814
+ let target = this.__data_uint8; // ... but at 8 byte offset
768
815
 
769
816
 
770
817
  while (pos < len) {
@@ -791,7 +838,7 @@ BinaryBuffer.prototype.writeUTF8String = function (string) {
791
838
 
792
839
  this.ensureCapacity(tlen + startPosition);
793
840
 
794
- target = new Uint8Array(this.data);
841
+ target = this.__data_uint8;
795
842
  }
796
843
 
797
844
  if ((value & 0xffffff80) === 0) { // 1-byte
@@ -851,7 +898,7 @@ BinaryBuffer.prototype.readUTF8String = function () {
851
898
  return undefined;
852
899
  }
853
900
 
854
- const bytes = new Uint8Array(this.data);
901
+ const bytes = this.__data_uint8;
855
902
 
856
903
 
857
904
  let result = "";
@@ -9,6 +9,9 @@ export class Camera {
9
9
 
10
10
  autoClip: boolean
11
11
 
12
+ clip_near: number
13
+ clip_far: number
14
+
12
15
  computeProjectionMatrix(result: Matrix4): boolean
13
16
 
14
17
  projectWorldPointOntoNearPlane(x: number, y: number, z: number, result: Vector3): void
@@ -26,7 +26,7 @@ export const ProjectionType = {
26
26
  export class Camera {
27
27
  constructor() {
28
28
  /**
29
- *
29
+ * Whether clipping planes should be automatically calculated or not
30
30
  * @type {boolean}
31
31
  */
32
32
  this.autoClip = true;
@@ -56,6 +56,44 @@ export class Camera {
56
56
  * @type {ObservedBoolean}
57
57
  */
58
58
  this.active = new ObservedBoolean(false);
59
+
60
+ /**
61
+ * Near clipping plane
62
+ * @type {number}
63
+ * @private
64
+ */
65
+ this.__clip_far = 100;
66
+
67
+ /**
68
+ * Far clipping plane
69
+ * @type {number}
70
+ * @private
71
+ */
72
+ this.__clip_near = 0.1;
73
+ }
74
+
75
+ get clip_far() {
76
+ return this.__clip_far;
77
+ }
78
+
79
+ set clip_far(v) {
80
+ this.__clip_far = v;
81
+
82
+ if (this.object !== null) {
83
+ this.object.far = v;
84
+ }
85
+ }
86
+
87
+ get clip_near() {
88
+ return this.__clip_near;
89
+ }
90
+
91
+ set clip_near(v) {
92
+ this.__clip_near = v;
93
+
94
+ if (this.object !== null) {
95
+ this.object.near = v;
96
+ }
59
97
  }
60
98
 
61
99
  updateMatrices() {
@@ -9,7 +9,6 @@ import { Transform } from '../../../ecs/transform/Transform.js';
9
9
  import {
10
10
  Frustum as ThreeFrustum,
11
11
  Matrix4,
12
- Matrix4 as ThreeMatrix4,
13
12
  OrthographicCamera as ThreeOrthographicCamera,
14
13
  PerspectiveCamera as ThreePerspectiveCamera,
15
14
  Vector3 as ThreeVector3,
@@ -76,16 +75,25 @@ function setAspectRatio(camera, width, height) {
76
75
  * @returns {PerspectiveCamera| OrthographicCamera}
77
76
  */
78
77
  function buildThreeCamera(component) {
78
+
79
+ let result;
79
80
  switch (component.projectionType.getValue()) {
80
81
  default:
81
82
  console.error(`Unsupported camera projection type "${component.projectionType.getValue()}", defaulting to perspective projection`);
82
83
  //fallthrough
83
84
  case Camera.ProjectionType.Perspective:
84
- return new ThreePerspectiveCamera(component.fov.getValue(), 1, 1, 50);
85
-
85
+ result = new ThreePerspectiveCamera(component.fov.getValue(), 1, 1, 50);
86
+ break;
86
87
  case Camera.ProjectionType.Orthographic:
87
- return new ThreeOrthographicCamera(-10, 10, 20, -20, 1, 1);
88
+ result = new ThreeOrthographicCamera(-10, 10, 20, -20, 1, 1);
89
+ break;
88
90
  }
91
+
92
+
93
+ result.far = component.clip_far;
94
+ result.near = component.clip_near;
95
+
96
+ return result;
89
97
  }
90
98
 
91
99
  export class CameraSystem extends System {
@@ -289,8 +297,19 @@ export class CameraSystem extends System {
289
297
  * @param {Camera} c
290
298
  */
291
299
  function visitCameraEntity(c) {
292
- if (c.active.getValue() && c.autoClip && c.object !== null) {
300
+ if (c.object === null) {
301
+ return;
302
+ }
303
+
304
+ if (!c.active.getValue()) {
305
+ return;
306
+ }
307
+
308
+ if (c.autoClip === true) {
293
309
  autoSetClippingPlanes(c, layers);
310
+ } else {
311
+ c.object.far = c.clip_far;
312
+ c.object.near = c.clip_near;
294
313
  }
295
314
  }
296
315
 
@@ -376,7 +395,7 @@ export class CameraSystem extends System {
376
395
  }
377
396
  }
378
397
 
379
- const matrix4 = new ThreeMatrix4();
398
+ const matrix4 = new Matrix4();
380
399
 
381
400
  /**
382
401
  *
@@ -74,6 +74,13 @@ export class ShadedGeometrySystem extends System {
74
74
  * @private
75
75
  */
76
76
  this.__adapter_suppliers = new Map();
77
+
78
+ /**
79
+ * Keeps count of usage for each geometry by ID
80
+ * @type {Map<number, number>}
81
+ * @private
82
+ */
83
+ this.__geometry_usage_counters = new Map();
77
84
  }
78
85
 
79
86
  /**
@@ -166,7 +173,7 @@ export class ShadedGeometrySystem extends System {
166
173
  this.__render_layer.buildVisibleSet = ((destination, destination_offset, view) => {
167
174
  const ctx = this.__view_contexts.get(view);
168
175
 
169
- return ctx.collect(destination, destination_offset,graphics.renderer, view, this.__bvh);
176
+ return ctx.collect(destination, destination_offset, graphics.renderer, view, this.__bvh);
170
177
  });
171
178
 
172
179
  readyCallback();
@@ -210,6 +217,13 @@ export class ShadedGeometrySystem extends System {
210
217
  sg.__entity = entity;
211
218
 
212
219
  this.__bvh.insertNode(leaf_node);
220
+
221
+ // update usage count
222
+ const geometry_id = sg.geometry.id;
223
+ const count_existing = this.__geometry_usage_counters.get(geometry_id);
224
+ const count_new = count_existing !== undefined ? count_existing + 1 : 1;
225
+
226
+ this.__geometry_usage_counters.set(geometry_id, count_new);
213
227
  }
214
228
 
215
229
  /**
@@ -224,6 +238,21 @@ export class ShadedGeometrySystem extends System {
224
238
  t.scale.onChanged.remove(sg.updateTransform, sg);
225
239
 
226
240
  sg.bvh.disconnect();
241
+
242
+ const geometry_id = sg.geometry.id;
243
+ const count_existing = this.__geometry_usage_counters.get(geometry_id);
244
+
245
+ if (count_existing === 1) {
246
+
247
+ // last instance
248
+ this.__geometry_usage_counters.delete(geometry_id);
249
+
250
+ // release GPU memory
251
+ sg.geometry.dispose();
252
+
253
+ } else {
254
+ this.__geometry_usage_counters.set(geometry_id, count_existing - 1);
255
+ }
227
256
  }
228
257
 
229
258
  /**
@@ -313,13 +342,19 @@ export class ShadedGeometrySystem extends System {
313
342
  const local_direction_x = scratch_m4[0] * direction_x + scratch_m4[4] * direction_y + scratch_m4[8] * direction_z;
314
343
  const local_direction_y = scratch_m4[1] * direction_x + scratch_m4[5] * direction_y + scratch_m4[9] * direction_z;
315
344
  const local_direction_z = scratch_m4[2] * direction_x + scratch_m4[6] * direction_y + scratch_m4[10] * direction_z;
316
- // NOTE: direction is not normalized
345
+
346
+ const local_direction_magnitude = Math.hypot(local_direction_x, local_direction_y, local_direction_z);
347
+ const local_direction_magnitude_inverse = 1 / local_direction_magnitude;
348
+
349
+ const local_direction_normalized_x = local_direction_x * local_direction_magnitude_inverse;
350
+ const local_direction_normalized_y = local_direction_y * local_direction_magnitude_inverse;
351
+ const local_direction_normalized_z = local_direction_z * local_direction_magnitude_inverse;
317
352
 
318
353
  const geometry_hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_primitive(
319
354
  scratch_point,
320
355
  sg.geometry,
321
356
  local_origin_x, local_origin_y, local_origin_z,
322
- local_direction_x, local_direction_y, local_direction_z
357
+ local_direction_normalized_x, local_direction_normalized_y, local_direction_normalized_z
323
358
  );
324
359
 
325
360
  if (!geometry_hit_found) {
@@ -20,6 +20,9 @@ import { randomFromArray } from "../../../../../core/math/random/randomFromArray
20
20
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
21
21
  import { OrbitingBehavior } from "../../../../../../model/game/util/behavior/OrbitingBehavior.js";
22
22
  import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
23
+ import { MicronShadedGeometryRenderAdapter } from "../../../micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js";
24
+ import { MicronRenderContext } from "../../../micron/render/v1/MicronRenderContext.js";
25
+ import { randomIntegerBetween } from "../../../../../core/math/random/randomIntegerBetween.js";
23
26
 
24
27
  const eh = new EngineHarness();
25
28
 
@@ -50,7 +53,17 @@ async function main(engine) {
50
53
 
51
54
  const em = engine.entityManager;
52
55
 
53
- await em.addSystem(new ShadedGeometrySystem(engine));
56
+ const sgs = new ShadedGeometrySystem(engine);
57
+
58
+ const mrc = new MicronRenderContext();
59
+ sgs.register_render_adapter(2, () => {
60
+ const adapter = new MicronShadedGeometryRenderAdapter();
61
+ adapter.ctx = mrc;
62
+
63
+ return adapter;
64
+ });
65
+
66
+ await em.addSystem(sgs);
54
67
  await em.addSystem(new BehaviorSystem(engine));
55
68
 
56
69
  EngineHarness.buildBasics({
@@ -74,7 +87,7 @@ async function main(engine) {
74
87
 
75
88
  const random = seededRandom(0);
76
89
 
77
- const INSTANCE_COUNT = 0;
90
+ const INSTANCE_COUNT = 10000;
78
91
 
79
92
  const geometries = [];
80
93
 
@@ -87,7 +100,9 @@ async function main(engine) {
87
100
  const sg = ShadedGeometry.from(geometry, material);
88
101
 
89
102
  // sg.draw_method = DRAW_METHOD_PLAIN;
90
- sg.draw_method = DRAW_METHOD_INSTANCED;
103
+ // sg.draw_method = DRAW_METHOD_INSTANCED;
104
+ // sg.draw_method = 2;
105
+ sg.draw_method = randomIntegerBetween(random, 0, 2);
91
106
  // sg.draw_method = random() > 0.01 ? DRAW_METHOD_INSTANCED : DRAW_METHOD_PLAIN;
92
107
  sg.clearFlag(ShadedGeometryFlags.CastShadow | ShadedGeometryFlags.ReceiveShadow);
93
108
 
@@ -105,7 +120,7 @@ async function main(engine) {
105
120
  .build(ecd);
106
121
  }
107
122
 
108
- makeMovingShadedGeometry().build(ecd);
123
+ // makeMovingShadedGeometry().build(ecd);
109
124
 
110
125
  debug_button(engine, () => {
111
126
  const dot = convert_bvh_to_dot_format_string(engine.graphics.layers.getLayerByName('shaded-geometry').bvh);
@@ -10,7 +10,7 @@ import { RibbonXPlugin } from "../../trail/x/RibbonXPlugin.js";
10
10
  import { RibbonPathBuilder } from "./ribbon/RibbonPathBuilder.js";
11
11
  import { PathEvents } from "../../../navigation/ecs/components/PathEvents.js";
12
12
  import { assert } from "../../../../core/assert.js";
13
- import { TubePathBuilder } from "./tube/TubePathBuilder.js";
13
+ import { TubePathBuilder } from "./tube/build/TubePathBuilder.js";
14
14
 
15
15
  const builders = {
16
16
  [PathDisplayType.None]: function (style, path, result) {
@@ -1,5 +1,4 @@
1
1
  import { EngineHarness } from "../../../EngineHarness.js";
2
- import RenderSystem from "../../../ecs/systems/RenderSystem.js";
3
2
  import { makeEngineOptionsModel } from "../../../../../model/game/options/makeEngineOptionsModel.js";
4
3
  import Path from "../../../navigation/ecs/components/Path.js";
5
4
  import Vector3 from "../../../../core/geom/Vector3.js";
@@ -68,6 +67,7 @@ import { TubePathStyle } from "./tube/TubePathStyle.js";
68
67
  import { TubeMaterialType } from "./tube/TubeMaterialType.js";
69
68
  import { MatcapMaterialDefinition } from "./tube/MatcapMaterialDefinition.js";
70
69
  import { TextureAssetLoader } from "../../../asset/loaders/texture/TextureAssetLoader.js";
70
+ import { ShadedGeometrySystem } from "../mesh-v2/ShadedGeometrySystem.js";
71
71
 
72
72
  const engineHarness = new EngineHarness();
73
73
 
@@ -156,7 +156,8 @@ function makeConfig(engine) {
156
156
  async function init(harness) {
157
157
  const engine = harness.engine;
158
158
 
159
- engine.entityManager.addSystem(new RenderSystem(engine.graphics));
159
+ // engine.entityManager.addSystem(new RenderSystem(engine.graphics));
160
+ engine.entityManager.addSystem(new ShadedGeometrySystem(engine));
160
161
 
161
162
  makeEngineOptionsModel(engine.options, engine);
162
163
 
@@ -315,6 +316,12 @@ function sample_style_tube() {
315
316
 
316
317
  style.width = 0.1;
317
318
 
319
+ style.path_mask = [
320
+ 0.1, 0.12,
321
+ 0.3, 0.8,
322
+ 0.9, 1
323
+ ];
324
+
318
325
  return style;
319
326
  }
320
327
 
@@ -323,8 +330,8 @@ function main(engine) {
323
330
  EngineHarness.buildBasics({ engine });
324
331
 
325
332
  const _p = new Path();
326
- // _p.interpolation = InterpolationType.CatmullRom;
327
- _p.interpolation = InterpolationType.Linear;
333
+ _p.interpolation = InterpolationType.CatmullRom;
334
+ // _p.interpolation = InterpolationType.Linear;
328
335
 
329
336
  // _p.setPositionsFromVectorArray(Path.smoothPath([
330
337
  // new Vector3(3, 1, 1),
@@ -352,14 +359,14 @@ function main(engine) {
352
359
 
353
360
  const pathDisplay = new PathDisplay();
354
361
  pathDisplay.specs.push(
355
- // PathDisplaySpec.from(
356
- // PathDisplayType.Ribbon,
357
- // trailPathStyle
358
- // ),
359
- // PathDisplaySpec.from(
360
- // PathDisplayType.Entity,
361
- // sample_style_entity()
362
- // ),
362
+ PathDisplaySpec.from(
363
+ PathDisplayType.Ribbon,
364
+ trailPathStyle
365
+ ),
366
+ PathDisplaySpec.from(
367
+ PathDisplayType.Entity,
368
+ sample_style_entity()
369
+ ),
363
370
  PathDisplaySpec.from(
364
371
  PathDisplayType.Tube,
365
372
  sample_style_tube()
@@ -11,4 +11,7 @@ export class TubePathStyle {
11
11
  public width: number
12
12
  public resolution: number
13
13
  public radial_resolution: number
14
+ public cast_shadow: boolean
15
+ public receive_shadow: boolean
16
+ public path_mask: number[]
14
17
  }
@@ -3,10 +3,19 @@ import { TubeMaterialType } from "./TubeMaterialType.js";
3
3
  import { assert } from "../../../../../core/assert.js";
4
4
  import { MatcapMaterialDefinition } from "./MatcapMaterialDefinition.js";
5
5
 
6
+ const DEFAULT_COLOR = Object.freeze({ r: 1, g: 1, b: 1 });
7
+
6
8
  export class TubePathStyle {
7
9
  constructor() {
10
+ /**
11
+ *
12
+ * @type {Color}
13
+ */
8
14
  this.color = new Color(1, 1, 1);
9
15
 
16
+ /**
17
+ * @type {TubeMaterialType}
18
+ */
10
19
  this.material_type = TubeMaterialType.Unknown;
11
20
 
12
21
  /**
@@ -15,13 +24,53 @@ export class TubePathStyle {
15
24
  */
16
25
  this.material = null;
17
26
 
27
+ /**
28
+ *
29
+ * @type {number}
30
+ */
18
31
  this.opacity = 1;
32
+
33
+ /**
34
+ *
35
+ * @type {number}
36
+ */
19
37
  this.width = 1;
20
38
 
21
39
  // TODO expose shading style "flat/smooth"
22
40
 
41
+ /**
42
+ *
43
+ * @type {number}
44
+ */
23
45
  this.resolution = 10;
46
+
47
+ /**
48
+ *
49
+ * @type {number}
50
+ */
24
51
  this.radial_resolution = 16;
52
+
53
+ /**
54
+ *
55
+ * @type {boolean}
56
+ */
57
+ this.cast_shadow = false;
58
+
59
+ /**
60
+ *
61
+ * @type {boolean}
62
+ */
63
+ this.receive_shadow = false;
64
+
65
+ /**
66
+ * Which parts of the path to visualise, in normalized offsets
67
+ * @example [ 0, 1 ] = whole path
68
+ * @example [ 0, 0.5 ] = first half of the path
69
+ * @example [ 0.5, 0.6 ] = segment from 50% along the path, to 60% along the path
70
+ * @example [ 0, 0.1, 0.9, 1] = first 10% of the path, and last 10% of the path
71
+ * @type {number[]}
72
+ */
73
+ this.path_mask = [0, 1];
25
74
  }
26
75
 
27
76
  static fromJSON(j) {
@@ -33,15 +82,30 @@ export class TubePathStyle {
33
82
  }
34
83
 
35
84
  fromJSON({
36
- color,
85
+ color = DEFAULT_COLOR,
37
86
  material_type,
38
87
  material,
39
88
  opacity = 1,
40
89
  width = 1,
41
90
  resolution = 10,
42
- radial_resolution = 16
91
+ radial_resolution = 16,
92
+ cast_shadow = false,
93
+ receive_shadow = false,
94
+ path_mask = [0, 1]
43
95
  }) {
44
96
  assert.enum(material_type, TubeMaterialType, 'material_type');
97
+ assert.isNumber(opacity, 'opacity');
98
+ assert.isNumber(width, 'width');
99
+ assert.isNumber(resolution, 'resolution');
100
+ assert.isNumber(radial_resolution, 'radial_resolution');
101
+ assert.isBoolean(cast_shadow, 'cast_shadow');
102
+ assert.isBoolean(receive_shadow, 'receive_shadow');
103
+
104
+ assert.defined(path_mask, 'path_mask');
105
+ assert.notNull(path_mask, 'path_mask');
106
+
107
+ assert.greaterThanOrEqual(path_mask.length, 2, 'path_mask length bust be at least 2');
108
+ assert.ok(path_mask.length % 2 === 0, 'path_mask length bust be multiple of 2');
45
109
 
46
110
  if (typeof color === 'string') {
47
111
  this.color.parse(color);
@@ -55,6 +119,11 @@ export class TubePathStyle {
55
119
  this.resolution = resolution;
56
120
  this.radial_resolution = radial_resolution;
57
121
 
122
+ this.cast_shadow = cast_shadow;
123
+ this.receive_shadow = receive_shadow;
124
+
125
+ this.path_mask = path_mask;
126
+
58
127
  switch (material_type) {
59
128
  case TubeMaterialType.Matcap:
60
129
  this.material = new MatcapMaterialDefinition();