zincjs 2.4.1-beta.3 → 2.4.1-beta.5

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.
@@ -115,7 +115,15 @@ ZincObject.prototype.getRegion = function() {
115
115
  *
116
116
  * @return {Object}
117
117
  */
118
- ZincObject.prototype.getMorph = function() {
118
+ /**
119
+ * Request the scene to draw the next frame, this is used when render on
120
+ * demand is enabled. See {@link Renderer#setRenderOnDemand}.
121
+ */
122
+ ZincObject.prototype.requestRender = function() {
123
+ this.region?.getScene?.()?.invalidate?.();
124
+ }
125
+
126
+ ZincObject.prototype.getMorph = function() {
119
127
  const morph = this._lod.getCurrentMorph();
120
128
  return morph ? morph : this.morph;
121
129
  }
@@ -159,6 +167,7 @@ ZincObject.prototype.checkTransparentMesh = function() {
159
167
  * time dependent.
160
168
  */
161
169
  ZincObject.prototype.setMesh = function(mesh, localTimeEnabled, localMorphColour) {
170
+ this.requestRender();
162
171
  //Note: we assume all layers are consistent with time frame
163
172
  //Thus adding them to the same animation group should work.
164
173
  //This step is only required for the primary (level 0) mesh.
@@ -267,6 +276,7 @@ ZincObject.prototype.getCurrentTime = function() {
267
276
  * @param {Number} time - Can be any value between 0 to duration.
268
277
  */
269
278
  ZincObject.prototype.setMorphTime = function(time) {
279
+ this.requestRender();
270
280
  let timeChanged = false;
271
281
  if (this.clipAction) {
272
282
  const ratio = time / this.duration;
@@ -293,7 +303,8 @@ ZincObject.prototype.setMorphTime = function(time) {
293
303
  }
294
304
  }
295
305
  if (timeChanged) {
296
- this.boundingBoxUpdateRequired = true;
306
+ if (this.hasMorphPositions())
307
+ this.boundingBoxUpdateRequired = true;
297
308
  this._lod.updateMorphColorAttribute(true);
298
309
  if (this.timeEnabled)
299
310
  this.markerUpdateRequired = true;
@@ -325,6 +336,7 @@ ZincObject.prototype.getVisibility = function() {
325
336
  * @param {Boolean} visible - a boolean flag indicate the visibility to be set
326
337
  */
327
338
  ZincObject.prototype.setVisibility = function(visible) {
339
+ this.requestRender();
328
340
  if (visible !== this.visible) {
329
341
  this.visible = visible;
330
342
  this.group.visible = visible;
@@ -340,6 +352,7 @@ ZincObject.prototype.setVisibility = function(visible) {
340
352
  * can be any value between from 0 to 1.0.
341
353
  */
342
354
  ZincObject.prototype.setAlpha = function(alpha) {
355
+ this.requestRender();
343
356
  const material = this._lod._material;
344
357
  let isTransparent = false;
345
358
  if (alpha < 1.0)
@@ -357,6 +370,7 @@ ZincObject.prototype.setAlpha = function(alpha) {
357
370
  * @param {Boolean} flag - Set frustrum culling on/off based on this flag.
358
371
  */
359
372
  ZincObject.prototype.setFrustumCulled = function(flag) {
373
+ this.requestRender();
360
374
  //multilayers - set for all layers
361
375
  this._lod.setFrustumCulled(flag);
362
376
  }
@@ -368,6 +382,7 @@ ZincObject.prototype.setFrustumCulled = function(flag) {
368
382
  * @param {Boolean} vertexColors - Set display with vertex color on/off.
369
383
  */
370
384
  ZincObject.prototype.setVertexColors = function(vertexColors) {
385
+ this.requestRender();
371
386
  //multilayers - set for all
372
387
  this._lod.setVertexColors(vertexColors);
373
388
 
@@ -390,6 +405,7 @@ ZincObject.prototype.getColour = function() {
390
405
  * @param {THREE.Color} colour - Colour to be set for this geometry.
391
406
  */
392
407
  ZincObject.prototype.setColour = function(colour) {
408
+ this.requestRender();
393
409
  this._lod.setColour(colour);
394
410
  }
395
411
 
@@ -399,6 +415,7 @@ ZincObject.prototype.setColour = function(colour) {
399
415
  * @param {THREE.Color} colour - Colour to be set for this geometry.
400
416
  */
401
417
  ZincObject.prototype.setGreyScale = function(flag) {
418
+ this.requestRender();
402
419
  if (flag) {
403
420
  if (!this.origColour) {
404
421
  if (this._lod._material) {
@@ -439,6 +456,7 @@ ZincObject.prototype.getColourHex = function() {
439
456
  * @param {String} hex - The colour value in hex form.
440
457
  */
441
458
  ZincObject.prototype.setColourHex = function(hex) {
459
+ this.requestRender();
442
460
  this._lod._material.color.setHex(hex);
443
461
  if (this._lod._secondaryMaterial) {
444
462
  this._lod._secondaryMaterial.color.setHex(hex);
@@ -451,6 +469,7 @@ ZincObject.prototype.setColourHex = function(hex) {
451
469
  * @param {String} colour - The colour value in rgb form.
452
470
  */
453
471
  ZincObject.prototype.setEmissiveRGB = function(colour) {
472
+ this.requestRender();
454
473
  if (this._lod._material && this._lod._material.emissive) {
455
474
  this._lod._material.emissive.setRGB(...colour);
456
475
  }
@@ -466,6 +485,7 @@ ZincObject.prototype.setEmissiveRGB = function(colour) {
466
485
  * @param {THREE.Material} material - Material to be set for this geometry.
467
486
  */
468
487
  ZincObject.prototype.setMaterial = function(material) {
488
+ this.requestRender();
469
489
  this._lod.setMaterial(material);
470
490
  }
471
491
 
@@ -486,10 +506,8 @@ ZincObject.prototype.getClosestVertexIndex = function() {
486
506
  let currentDistance = 0;
487
507
  for (let i = 0; i < position.count; i++) {
488
508
  this._v2.fromArray(position.array, i * 3);
489
- currentDistance = this._v2.distanceTo(this._v1);
490
- if (distance == -1)
491
- distance = currentDistance;
492
- else if (distance > (currentDistance)) {
509
+ currentDistance = this._v2.distanceToSquared(this._v1);
510
+ if ((distance == -1) || (distance > currentDistance)) {
493
511
  distance = currentDistance;
494
512
  closestIndex = i;
495
513
  }
@@ -565,6 +583,10 @@ ZincObject.prototype.getBoundingBox = function() {
565
583
  ZincObject.prototype.dispose = function() {
566
584
  //multilayyers
567
585
  this._lod.dispose();
586
+ if (this.marker) {
587
+ this.marker.dispose();
588
+ this.marker = undefined;
589
+ }
568
590
  this.animationGroup = undefined;
569
591
  this.mixer = undefined;
570
592
  this.morph = undefined;
@@ -588,6 +610,44 @@ ZincObject.prototype.markerIsRequired = function(options) {
588
610
  return false;
589
611
  }
590
612
 
613
+ //geometry -> whether its morph targets move the vertices
614
+ const morphPositionsCache = new WeakMap();
615
+
616
+ /**
617
+ * Check if the vertices positions change over time, only then will
618
+ * the bounding box change during playback.
619
+ *
620
+ * @return {Boolean}
621
+ */
622
+ ZincObject.prototype.hasMorphPositions = function() {
623
+ const geometry = this.getMorph()?.geometry;
624
+ const morphPositions = geometry?.morphAttributes?.position;
625
+ if (!morphPositions || morphPositions.length === 0)
626
+ return false;
627
+ const cached = morphPositionsCache.get(geometry);
628
+ if (cached && cached.morphPositions === morphPositions)
629
+ return cached.result;
630
+ //Colour only morphs use placeholder targets identical to the base
631
+ //positions, these never move the vertices.
632
+ const basePositions = geometry.getAttribute('position')?.array;
633
+ let result = false;
634
+ for (let i = 0; i < morphPositions.length && !result; i++) {
635
+ const array = morphPositions[i].array;
636
+ if (!basePositions || array.length !== basePositions.length) {
637
+ result = true;
638
+ } else if (array !== basePositions) {
639
+ for (let j = 0; j < array.length; j++) {
640
+ if (array[j] !== basePositions[j]) {
641
+ result = true;
642
+ break;
643
+ }
644
+ }
645
+ }
646
+ }
647
+ morphPositionsCache.set(geometry, { morphPositions, result });
648
+ return result;
649
+ }
650
+
591
651
  /**
592
652
  * Update the marker's position and size based on current viewport.
593
653
  */
@@ -606,6 +666,7 @@ ZincObject.prototype.updateMarker = function(playAnimation, options) {
606
666
  if (position) {
607
667
  this.marker.setPosition(position.x, position.y, position.z);
608
668
  this.markerUpdateRequired = false;
669
+ ndcToBeUpdated = true;
609
670
  }
610
671
  }
611
672
  if (!this.marker.isEnabled()) {
@@ -623,8 +684,9 @@ ZincObject.prototype.updateMarker = function(playAnimation, options) {
623
684
  } else {
624
685
  this.marker.setDefaultSprite();
625
686
  }
626
- if (options && options.camera && (ndcToBeUpdated ||
627
- options.markerCluster.markerUpdateRequired)) {
687
+ //Only update the screen position when the camera or the
688
+ //marker has moved, or the marker has just been enabled.
689
+ if (options && options.camera && ndcToBeUpdated) {
628
690
  this.marker.updateNDC(options.camera.cameraObject);
629
691
  options.markerCluster.markerUpdateRequired = true;
630
692
  }
@@ -633,7 +695,7 @@ ZincObject.prototype.updateMarker = function(playAnimation, options) {
633
695
  if (this.marker && this.marker.isEnabled()) {
634
696
  this.marker.disable();
635
697
  this.group.remove(this.marker.morph);
636
- if (options.markersList &&
698
+ if (options?.markersList &&
637
699
  (this.marker.uuid in options.markersList)) {
638
700
  options.markerCluster.markerUpdateRequired = true;
639
701
  delete options.markersList[this.marker.uuid];
@@ -657,6 +719,7 @@ ZincObject.prototype.initiateMorphColor = function() {
657
719
  }
658
720
 
659
721
  ZincObject.prototype.setRenderOrder = function(renderOrder) {
722
+ this.requestRender();
660
723
  //multiilayers
661
724
  this._lod.setRenderOrder(renderOrder);
662
725
  }
@@ -695,6 +758,7 @@ ZincObject.prototype.getClosestVertexDOMElementCoords = function(scene) {
695
758
  * @return {Boolean}
696
759
  */
697
760
  ZincObject.prototype.setMarkerMode = function(mode, options) {
761
+ this.requestRender();
698
762
  if (mode !== this.markerMode) {
699
763
  if (mode === "on" || mode === "off") {
700
764
  this.markerMode = mode;
@@ -729,7 +793,9 @@ ZincObject.prototype.render = function(delta, playAnimation,
729
793
  }
730
794
  //multilayers
731
795
  if (this.visible && delta != 0) {
732
- this.boundingBoxUpdateRequired = true;
796
+ //Only vertices morphing can change the bounding box
797
+ if (this.hasMorphPositions())
798
+ this.boundingBoxUpdateRequired = true;
733
799
  if (this.morphColour == 1) {
734
800
  this._lod.updateMorphColorAttribute(true);
735
801
  }
@@ -749,6 +815,7 @@ ZincObject.prototype.addLOD = function(loader, level, url, index, preload) {
749
815
  * Add lod from an url into the lod object.
750
816
  */
751
817
  ZincObject.prototype.addVertices = function(coords) {
818
+ this.requestRender();
752
819
  let mesh = this.getMorph();
753
820
  let geometry = undefined;
754
821
  if (!mesh) {
@@ -776,6 +843,7 @@ ZincObject.prototype.addVertices = function(coords) {
776
843
  * Add lod from an url into the lod object.
777
844
  */
778
845
  ZincObject.prototype.deleteVertices = function(index) {
846
+ this.requestRender();
779
847
  let mesh = this.getMorph();
780
848
  if (mesh?.geometry && this.drawRange >= index) {
781
849
  if (removeVertexAtIndex(mesh.geometry, index, true)) {
@@ -794,6 +862,7 @@ ZincObject.prototype.deleteVertices = function(index) {
794
862
  * Set the objects position.
795
863
  */
796
864
  ZincObject.prototype.setPosition = function(x, y, z) {
865
+ this.requestRender();
797
866
  const group = this.getGroup();
798
867
  if (group) {
799
868
  group.position.set(x, y, z);
@@ -813,6 +882,7 @@ ZincObject.prototype.loadAdditionalSources = function(primitivesLoader, sources)
813
882
  * Set the objects scale.
814
883
  */
815
884
  ZincObject.prototype.setScaleAll = function(scale) {
885
+ this.requestRender();
816
886
  const group = this.getGroup();
817
887
  if (group) {
818
888
  group.scale.set(scale, scale, scale);
package/src/region.js CHANGED
@@ -2,6 +2,7 @@ import * as THREE from 'three/webgpu';
2
2
  import { Pointset } from './primitives/pointset';
3
3
  import { Lines2 } from './primitives/lines2';
4
4
  import { Geometry } from './primitives/geometry';
5
+ import { updateWorldMatrixFromAncestors } from './utilities';
5
6
 
6
7
  let uniqueiId = 0;
7
8
 
@@ -33,6 +34,10 @@ let Region = function (parentIn, sceneIn) {
33
34
  this.isRegion = true;
34
35
  this.uuid = getUniqueId();
35
36
 
37
+ //Ask the scene to draw the next frame, used by render on demand.
38
+ const requestRender = () => {
39
+ scene?.invalidate?.();
40
+ }
36
41
 
37
42
  /**
38
43
  * Hide all primitives belong to this region.
@@ -57,6 +62,7 @@ let Region = function (parentIn, sceneIn) {
57
62
  * @param {Boolean} flag - A flag indicating either the visibilty to be on/off.
58
63
  */
59
64
  this.setVisibility = (flag) => {
65
+ requestRender();
60
66
  if (flag != group.visible) {
61
67
  group.visible = flag;
62
68
  this.pickableUpdateRequired = true;
@@ -90,9 +96,17 @@ let Region = function (parentIn, sceneIn) {
90
96
  * used for the transformation.
91
97
  */
92
98
  this.setTransformation = transformation => {
99
+ requestRender();
93
100
  tMatrix.set(...transformation);
94
101
  group.matrix.copy(tMatrix);
95
- group.updateMatrixWorld();
102
+ //The group does not update its matrix automatically, compute its world
103
+ //matrix from the ancestors and update the descendants.
104
+ updateWorldMatrixFromAncestors(group);
105
+ group.updateMatrixWorld(true);
106
+ //Bounding boxes of everything in this region have moved
107
+ this.getAllObjects(true).forEach((zincObject) => {
108
+ zincObject.boundingBoxUpdateRequired = true;
109
+ });
96
110
  }
97
111
 
98
112
  /**
@@ -178,6 +192,7 @@ let Region = function (parentIn, sceneIn) {
178
192
  * @return {Region}
179
193
  */
180
194
  this.createChild = (nameIn) => {
195
+ requestRender();
181
196
  let childRegion = new Region(this, scene);
182
197
  childRegion.setName(nameIn);
183
198
  children.push(childRegion);
@@ -306,6 +321,7 @@ let Region = function (parentIn, sceneIn) {
306
321
  * this region.
307
322
  */
308
323
  this.addZincObject = zincObject => {
324
+ requestRender();
309
325
  if (zincObject) {
310
326
  zincObject.setRegion(this);
311
327
  group.add(zincObject.getGroup());
@@ -325,6 +341,7 @@ let Region = function (parentIn, sceneIn) {
325
341
  * @param {ZincObject} zincObject - object to be removed from this region.
326
342
  */
327
343
  this.removeZincObject = zincObject => {
344
+ requestRender();
328
345
  for (let i = 0; i < zincObjects.length; i++) {
329
346
  if (zincObject === zincObjects[i]) {
330
347
  group.remove(zincObject.getGroup());
@@ -359,11 +376,28 @@ let Region = function (parentIn, sceneIn) {
359
376
  return false;
360
377
  }
361
378
 
379
+ /**
380
+ * Clear the pickable update flag for this region and its descendants,
381
+ * used when a region is hidden and none of its objects are pickable.
382
+ *
383
+ * @private
384
+ */
385
+ this.clearPickableUpdateRequired = (transverse) => {
386
+ this.pickableUpdateRequired = false;
387
+ if (transverse) {
388
+ children.forEach(childRegion => {
389
+ childRegion.clearPickableUpdateRequired(transverse);
390
+ });
391
+ }
392
+ }
393
+
362
394
  /**
363
395
  * Get all pickable objects.
364
396
  */
365
397
  this.getPickableThreeJSObjects = (objectsList, transverse) => {
366
- if (group.visible) {
398
+ if (!group.visible) {
399
+ this.clearPickableUpdateRequired(transverse);
400
+ } else {
367
401
  zincObjects.forEach(zincObject => {
368
402
  if (zincObject.isPickable && zincObject.getGroup() && zincObject.getGroup().visible) {
369
403
  let marker = zincObject.marker;
@@ -411,30 +445,35 @@ let Region = function (parentIn, sceneIn) {
411
445
  * @returns {THREE.Box3}
412
446
  */
413
447
  this.getBoundingBox = transverse => {
414
- let boundingBox1 = undefined, boundingBox2 = undefined;
415
- zincObjects.forEach(zincObject => {
416
- boundingBox2 = zincObject.getBoundingBox();
417
- if (boundingBox2) {
418
- if (boundingBox1 == undefined) {
419
- boundingBox1 = boundingBox2.clone();
420
- } else {
421
- boundingBox1.union(boundingBox2);
422
- }
448
+ const boundingBox = new THREE.Box3();
449
+ if (this.accumulateBoundingBox(boundingBox, transverse)) {
450
+ return boundingBox;
451
+ }
452
+ return undefined;
453
+ }
454
+
455
+ /**
456
+ * Expand the target by the bounding box of this region.
457
+ *
458
+ * @return {Boolean} - true if any bounding box has been found.
459
+ * @private
460
+ */
461
+ this.accumulateBoundingBox = (target, transverse) => {
462
+ let found = false;
463
+ for (let i = 0; i < zincObjects.length; i++) {
464
+ const boundingBox = zincObjects[i].getBoundingBox();
465
+ if (boundingBox) {
466
+ target.union(boundingBox);
467
+ found = true;
423
468
  }
424
- });
469
+ }
425
470
  if (transverse) {
426
- children.forEach(childRegion => {
427
- boundingBox2 = childRegion.getBoundingBox(transverse);
428
- if (boundingBox2) {
429
- if (boundingBox1 == undefined) {
430
- boundingBox1 = boundingBox2.clone();
431
- } else {
432
- boundingBox1.union(boundingBox2);
433
- }
434
- }
435
- });
471
+ for (let i = 0; i < children.length; i++) {
472
+ if (children[i].accumulateBoundingBox(target, transverse))
473
+ found = true;
474
+ }
436
475
  }
437
- return boundingBox1;
476
+ return found;
438
477
  }
439
478
 
440
479
  /**
@@ -444,14 +483,20 @@ let Region = function (parentIn, sceneIn) {
444
483
  * if this is set to true.
445
484
  */
446
485
  this.clear = transverse => {
486
+ requestRender();
447
487
  if (transverse) {
448
- children.forEach(childRegion => childRegion.clear(transverse));
488
+ children.forEach(childRegion => {
489
+ childRegion.clear(transverse);
490
+ group.remove(childRegion.getGroup());
491
+ });
449
492
  }
450
493
  zincObjects.forEach(zincObject => {
451
494
  group.remove(zincObject.getGroup());
452
495
  zincObject.dispose();
453
496
  });
454
- children = [];
497
+ if (transverse) {
498
+ children = [];
499
+ }
455
500
  zincObjects = [];
456
501
  }
457
502
 
@@ -664,13 +709,14 @@ let Region = function (parentIn, sceneIn) {
664
709
  * set to true.
665
710
  * @returns {Array}
666
711
  */
667
- this.getAllObjects = transverse => {
668
- const objectsArray = [...zincObjects];
712
+ this.getAllObjects = (transverse, objectsArray = []) => {
713
+ for (let i = 0; i < zincObjects.length; i++) {
714
+ objectsArray.push(zincObjects[i]);
715
+ }
669
716
  if (transverse) {
670
- children.forEach(childRegion => {
671
- let childObjects = childRegion.getAllObjects(transverse);
672
- objectsArray.push(...childObjects);
673
- });
717
+ for (let i = 0; i < children.length; i++) {
718
+ children[i].getAllObjects(transverse, objectsArray);
719
+ }
674
720
  }
675
721
  return objectsArray;
676
722
  }
@@ -725,7 +771,7 @@ let Region = function (parentIn, sceneIn) {
725
771
  });
726
772
  if (transverse) {
727
773
  children.forEach(childRegion => {
728
- childRegion.setMorphTime(time);
774
+ childRegion.setMorphTime(time, transverse);
729
775
  });
730
776
  }
731
777
  }
@@ -752,14 +798,23 @@ let Region = function (parentIn, sceneIn) {
752
798
 
753
799
  /**
754
800
  * Update geometries and glyphsets based on the calculated time.
801
+ * Render the objects in this region and optionally its descendants,
802
+ * the tree is walked in place to avoid allocations on every frame.
755
803
  * @private
756
804
  */
805
+ this.renderObjects = (delta, playAnimation, cameraControls, options, transverse) => {
806
+ for (let i = 0; i < zincObjects.length; i++) {
807
+ zincObjects[i].render(delta, playAnimation, cameraControls, options);
808
+ }
809
+ if (transverse) {
810
+ for (let i = 0; i < children.length; i++) {
811
+ children[i].renderObjects(delta, playAnimation, cameraControls, options, transverse);
812
+ }
813
+ }
814
+ }
815
+
757
816
  this.renderGeometries = (playRate, delta, playAnimation, cameraControls, options, transverse) => {
758
- // Let video dictates the progress if one is present
759
- const allObjects = this.getAllObjects(transverse);
760
- allObjects.forEach(zincObject => {
761
- zincObject.render(playRate * delta, playAnimation, cameraControls, options);
762
- });
817
+ this.renderObjects(playRate * delta, playAnimation, cameraControls, options, transverse);
763
818
  //process markers visibility and size, as long as there are more than
764
819
  //one entry in markersList is greater than 1, markers have been enabled.
765
820
  if (options && (playAnimation === false) &&
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Frame requests from resources that do not belong to a scene, e.g. shared
3
+ * textures loaded asynchronously. Renderers in render on demand mode
4
+ * subscribe to these so the result is drawn once it arrives.
5
+ */
6
+ const listeners = new Set();
7
+
8
+ const onRenderRequest = (callback) => {
9
+ listeners.add(callback);
10
+ }
11
+
12
+ const offRenderRequest = (callback) => {
13
+ listeners.delete(callback);
14
+ }
15
+
16
+ const requestRenderAll = () => {
17
+ listeners.forEach((callback) => callback());
18
+ }
19
+
20
+ export { onRenderRequest, offRenderRequest, requestRenderAll };