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.
- package/README.md +86 -70
- package/build/zinc.js +5 -5
- package/build/zinc.js.map +1 -1
- package/package.json +1 -1
- package/src/controls.js +71 -49
- package/src/loaders/GLTFToZincJSLoader.js +4 -1
- package/src/loaders/JSONLoader.js +2 -2
- package/src/loaders/primitivesLoader.js +60 -17
- package/src/minimap.js +48 -13
- package/src/primitives/geometry.js +2 -0
- package/src/primitives/glyph.js +11 -1
- package/src/primitives/glyphset.js +33 -7
- package/src/primitives/label.js +39 -24
- package/src/primitives/lines.js +2 -0
- package/src/primitives/lines2.js +76 -24
- package/src/primitives/lod.js +18 -4
- package/src/primitives/marker.js +6 -1
- package/src/primitives/markerCluster.js +5 -1
- package/src/primitives/pointset.js +33 -28
- package/src/primitives/texturePrimitive.js +12 -0
- package/src/primitives/textureSlides.js +24 -3
- package/src/primitives/textureVolume.js +2 -0
- package/src/primitives/tubeLines.js +4 -0
- package/src/primitives/zincObject.js +80 -10
- package/src/region.js +92 -37
- package/src/renderRequests.js +20 -0
- package/src/renderer.js +81 -9
- package/src/scene.js +133 -23
- package/src/sceneExporter.js +4 -0
- package/src/sceneLoader.js +114 -18
- package/src/textSprite.js +211 -0
- package/src/texture/texture.js +10 -0
- package/src/texture/textureArray.js +9 -0
- package/src/utilities.js +147 -47
package/src/primitives/glyph.js
CHANGED
|
@@ -63,6 +63,7 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
63
63
|
* @param {String} text - Label to be set for this instance
|
|
64
64
|
*/
|
|
65
65
|
this.setLabel = text => {
|
|
66
|
+
parent?.requestRender?.();
|
|
66
67
|
if (text && (typeof text === 'string' || text instanceof String)) {
|
|
67
68
|
labelString = text;
|
|
68
69
|
if (this.morph)
|
|
@@ -78,6 +79,7 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
78
79
|
* getColour()) - e.g. genuinely no colour data at all for this glyphset.
|
|
79
80
|
*/
|
|
80
81
|
this.showLabel = (colour) => {
|
|
82
|
+
parent?.requestRender?.();
|
|
81
83
|
if (label) {
|
|
82
84
|
_position = label.getPosition();
|
|
83
85
|
this.group.remove(label.getSprite());
|
|
@@ -88,7 +90,8 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
88
90
|
label = new Label(labelString, _colour || colour);
|
|
89
91
|
label.setPosition(_position[0], _position[1], _position[2]);
|
|
90
92
|
const sprite = label.getSprite();
|
|
91
|
-
|
|
93
|
+
//Only discard fully transparent pixels to keep the smooth edges
|
|
94
|
+
sprite.material.alphaTest = 0.05;
|
|
92
95
|
sprite.material.transparent = true;
|
|
93
96
|
sprite.material.depthWrite = false;
|
|
94
97
|
this.group.add(label.getSprite());
|
|
@@ -99,6 +102,7 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
99
102
|
* Hide label with the choosen colour.
|
|
100
103
|
*/
|
|
101
104
|
this.hideLabel = () => {
|
|
105
|
+
parent?.requestRender?.();
|
|
102
106
|
if (label) {
|
|
103
107
|
_position = label.getPosition();
|
|
104
108
|
this.group.remove(label.getSprite());
|
|
@@ -135,6 +139,7 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
135
139
|
* transformation.
|
|
136
140
|
*/
|
|
137
141
|
this.setTransformation = (position, axis1, axis2, axis3) => {
|
|
142
|
+
parent?.requestRender?.();
|
|
138
143
|
if (this.morph) {
|
|
139
144
|
this.morph.matrix.elements[0] = axis1[0];
|
|
140
145
|
this.morph.matrix.elements[1] = axis1[1];
|
|
@@ -170,6 +175,7 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
170
175
|
* @param {THREE.Color} color - Colour to be set.
|
|
171
176
|
*/
|
|
172
177
|
this.setColour = (color) => {
|
|
178
|
+
parent?.requestRender?.();
|
|
173
179
|
_colour = color ? color.clone() : undefined;
|
|
174
180
|
if (label)
|
|
175
181
|
label.setColour(color);
|
|
@@ -190,6 +196,10 @@ const Glyph = function (geometry, materialIn, idIn, glyphsetIn) {
|
|
|
190
196
|
* Clear and free its memory.
|
|
191
197
|
*/
|
|
192
198
|
this.dispose = () => {
|
|
199
|
+
if (label) {
|
|
200
|
+
label.dispose();
|
|
201
|
+
label = undefined;
|
|
202
|
+
}
|
|
193
203
|
if (this.material)
|
|
194
204
|
this.material.dispose();
|
|
195
205
|
this.morph = undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three/webgpu';
|
|
2
2
|
import { Glyph } from './glyph';
|
|
3
3
|
import { ZincObject } from './zincObject';
|
|
4
|
+
import { updateWorldMatrixFromAncestors } from '../utilities';
|
|
4
5
|
import { JSONLoader } from '../loaders/JSONLoader';
|
|
5
6
|
import { createGlyphTransformCompute, dispatchAndReadbackGlyphTransform, readbackGlyphTransform } from '../tsl/glyphTransform';
|
|
6
7
|
import { createGlyphInstancedMaterial } from '../tsl/glyphRenderMaterial';
|
|
@@ -22,6 +23,8 @@ const Glyphset = function () {
|
|
|
22
23
|
let axis2s = undefined;
|
|
23
24
|
let axis3s = undefined;
|
|
24
25
|
let positions = undefined;
|
|
26
|
+
//Glyph geometry loaded after dispose is discarded
|
|
27
|
+
let disposed = false;
|
|
25
28
|
let scales = undefined;
|
|
26
29
|
let colors = undefined;
|
|
27
30
|
let labels = undefined;
|
|
@@ -69,6 +72,9 @@ const Glyphset = function () {
|
|
|
69
72
|
// for ACCURATE_RESYNC_DEBOUNCE_MS of quiet.
|
|
70
73
|
let accurateResyncTimer = undefined;
|
|
71
74
|
const ACCURATE_RESYNC_DEBOUNCE_MS = 500;
|
|
75
|
+
//Set to false when the static (non morphing) transformation needs to be
|
|
76
|
+
//re-applied, e.g. after a scale change.
|
|
77
|
+
let staticTransformationApplied = false;
|
|
72
78
|
for (let i = 0; i < 8; i++) {
|
|
73
79
|
_points[i] = new THREE.Vector3();
|
|
74
80
|
}
|
|
@@ -149,6 +155,7 @@ const Glyphset = function () {
|
|
|
149
155
|
* geometry is loaded.
|
|
150
156
|
*/
|
|
151
157
|
this.load = (glyphsetData, glyphURL, finishCallback, isInline, displayLabels) => {
|
|
158
|
+
disposed = false;
|
|
152
159
|
axis1s = glyphsetData.axis1;
|
|
153
160
|
axis2s = glyphsetData.axis2;
|
|
154
161
|
axis3s = glyphsetData.axis3;
|
|
@@ -440,6 +447,7 @@ const Glyphset = function () {
|
|
|
440
447
|
* completely unchanged downstream.
|
|
441
448
|
*/
|
|
442
449
|
const applyGlyphComputeResult = (result) => {
|
|
450
|
+
this.requestRender();
|
|
443
451
|
const matrixArray = this.morph.instanceMatrix.array;
|
|
444
452
|
const updateLabels = this.canShowLabel();
|
|
445
453
|
for (let i = 0; i < numberOfVertices; i++) {
|
|
@@ -540,6 +548,7 @@ const Glyphset = function () {
|
|
|
540
548
|
* all, bouding box will not be updated.
|
|
541
549
|
*/
|
|
542
550
|
const dispatchGlyphComputeFast = (bottom_frame, top_frame, proportion) => {
|
|
551
|
+
this.requestRender();
|
|
543
552
|
const renderer = this.region?.getScene?.()?.getRenderer?.();
|
|
544
553
|
if (!renderer) return false;
|
|
545
554
|
glyphCompute.uniforms.bottomFrame.value = bottom_frame;
|
|
@@ -663,8 +672,13 @@ const Glyphset = function () {
|
|
|
663
672
|
current_axis3s = axis3s["0"];
|
|
664
673
|
current_scales = scales["0"];
|
|
665
674
|
}
|
|
666
|
-
|
|
667
|
-
|
|
675
|
+
//Colour only morphing does not change the transformation, skip it
|
|
676
|
+
//unless it is out of date.
|
|
677
|
+
if (morphVertices || !staticTransformationApplied) {
|
|
678
|
+
updateGlyphsetTransformation(current_positions, current_axis1s, current_axis2s, current_axis3s,
|
|
679
|
+
current_scales);
|
|
680
|
+
staticTransformationApplied = !morphVertices;
|
|
681
|
+
}
|
|
668
682
|
if (colors != undefined) {
|
|
669
683
|
if (morphColours) {
|
|
670
684
|
const bottom_colors = colors[bottom_frame.toString()];
|
|
@@ -710,6 +724,7 @@ const Glyphset = function () {
|
|
|
710
724
|
* Display the label of the glyphs in the glyphset.
|
|
711
725
|
*/
|
|
712
726
|
this.showLabel = () => {
|
|
727
|
+
this.requestRender();
|
|
713
728
|
if (glyphList?.length) {
|
|
714
729
|
labelsOn = true;
|
|
715
730
|
for (let i = 0; i < glyphList.length; i++) {
|
|
@@ -722,6 +737,7 @@ const Glyphset = function () {
|
|
|
722
737
|
* Hide label with the choosen colour.
|
|
723
738
|
*/
|
|
724
739
|
this.hideLabel = () => {
|
|
740
|
+
this.requestRender();
|
|
725
741
|
for (let i = 0; i < glyphList.length; i++) {
|
|
726
742
|
glyphList[i].hideLabel();
|
|
727
743
|
}
|
|
@@ -801,6 +817,7 @@ const Glyphset = function () {
|
|
|
801
817
|
//Update the transformation of the glyphs.
|
|
802
818
|
updateGlyphsetTransformation(positions["0"], axis1s["0"],
|
|
803
819
|
axis2s["0"], axis3s["0"], scales["0"]);
|
|
820
|
+
staticTransformationApplied = true;
|
|
804
821
|
if (colors != undefined) {
|
|
805
822
|
updateGlyphsetHexColors(colors["0"], !!(glyphCompute && glyphCompute.outputs.color));
|
|
806
823
|
}
|
|
@@ -816,6 +833,7 @@ const Glyphset = function () {
|
|
|
816
833
|
* @param {Glyph} Glyph to be added.
|
|
817
834
|
*/
|
|
818
835
|
this.addCustomGlyph = glyph => {
|
|
836
|
+
this.requestRender();
|
|
819
837
|
if (glyph.isGlyph)
|
|
820
838
|
glyphList.push(glyph);
|
|
821
839
|
this.ready = true;
|
|
@@ -829,6 +847,7 @@ const Glyphset = function () {
|
|
|
829
847
|
* @param {Number} id of the mesh.
|
|
830
848
|
*/
|
|
831
849
|
this.addMeshAsGlyph = (mesh, id) => {
|
|
850
|
+
this.requestRender();
|
|
832
851
|
if (mesh.isMesh) {
|
|
833
852
|
const glyph = new Glyph(undefined, undefined, id, this);
|
|
834
853
|
glyph.fromMesh(mesh);
|
|
@@ -856,6 +875,10 @@ const Glyphset = function () {
|
|
|
856
875
|
|
|
857
876
|
var meshloader = (finishCallback, displayLabels) => {
|
|
858
877
|
return (geometry, materials) => {
|
|
878
|
+
if (disposed) {
|
|
879
|
+
geometry.dispose();
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
859
882
|
this.geometry.copy(geometry);
|
|
860
883
|
this.geometry.computeBoundingSphere();
|
|
861
884
|
this.geometry.computeBoundingBox();
|
|
@@ -937,8 +960,7 @@ const Glyphset = function () {
|
|
|
937
960
|
if (this.morph && this.ready && this.morph.visible) {
|
|
938
961
|
if (glyphCompute && !boundsAreExact && allFramesBoundingBox) {
|
|
939
962
|
this.cachedBoundingBox.copy(allFramesBoundingBox);
|
|
940
|
-
this.
|
|
941
|
-
this.cachedBoundingBox.applyMatrix4(this.morph.matrixWorld);
|
|
963
|
+
this.cachedBoundingBox.applyMatrix4(updateWorldMatrixFromAncestors(this.morph));
|
|
942
964
|
return this.cachedBoundingBox;
|
|
943
965
|
}
|
|
944
966
|
if (this.boundingBoxUpdateRequired) {
|
|
@@ -955,8 +977,7 @@ const Glyphset = function () {
|
|
|
955
977
|
}
|
|
956
978
|
if (_boundingBox3) {
|
|
957
979
|
this.cachedBoundingBox.copy(_boundingBox3);
|
|
958
|
-
this.
|
|
959
|
-
this.cachedBoundingBox.applyMatrix4(this.morph.matrixWorld);
|
|
980
|
+
this.cachedBoundingBox.applyMatrix4(updateWorldMatrixFromAncestors(this.morph));
|
|
960
981
|
this.boundingBoxUpdateRequired = false;
|
|
961
982
|
} else
|
|
962
983
|
return undefined;
|
|
@@ -972,6 +993,7 @@ const Glyphset = function () {
|
|
|
972
993
|
* @param {Number} time - Can be any value between 0 to duration.
|
|
973
994
|
*/
|
|
974
995
|
this.setMorphTime = time => {
|
|
996
|
+
this.requestRender();
|
|
975
997
|
if (time > this.duration)
|
|
976
998
|
this.inbuildTime = this.duration;
|
|
977
999
|
else if (0 > time)
|
|
@@ -1055,7 +1077,9 @@ const Glyphset = function () {
|
|
|
1055
1077
|
* @return {THREE.Box3}.
|
|
1056
1078
|
*/
|
|
1057
1079
|
this.setScaleAll = function(scale) {
|
|
1080
|
+
this.requestRender();
|
|
1058
1081
|
this.globalScale = scale;
|
|
1082
|
+
staticTransformationApplied = false;
|
|
1059
1083
|
updateMorphGlyphsets(false);
|
|
1060
1084
|
}
|
|
1061
1085
|
|
|
@@ -1063,6 +1087,7 @@ const Glyphset = function () {
|
|
|
1063
1087
|
* Clear this glyphset and its list of glyphs which will release them from the memory.
|
|
1064
1088
|
*/
|
|
1065
1089
|
this.dispose = () => {
|
|
1090
|
+
disposed = true;
|
|
1066
1091
|
for (let i = glyphList.length - 1; i >= 0; i--) {
|
|
1067
1092
|
glyphList[i].dispose();
|
|
1068
1093
|
}
|
|
@@ -1090,12 +1115,13 @@ const Glyphset = function () {
|
|
|
1090
1115
|
/**
|
|
1091
1116
|
* Update the glyphsets if required the render.
|
|
1092
1117
|
*/
|
|
1093
|
-
this.render = (delta, playAnimation, options) => {
|
|
1118
|
+
this.render = (delta, playAnimation, cameraControls, options) => {
|
|
1094
1119
|
if (glyphCompute && !renderBuffersInitialized) {
|
|
1095
1120
|
const renderer = this.region?.getScene?.()?.getRenderer?.();
|
|
1096
1121
|
if (renderer) {
|
|
1097
1122
|
renderer.compute(glyphCompute.compute);
|
|
1098
1123
|
renderBuffersInitialized = true;
|
|
1124
|
+
this.requestRender();
|
|
1099
1125
|
}
|
|
1100
1126
|
}
|
|
1101
1127
|
if (playAnimation == true) {
|
package/src/primitives/label.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
applyTextSpriteTextureSettings,
|
|
3
|
+
createTextSprite,
|
|
4
|
+
releaseTextSprite,
|
|
5
|
+
setTextSpriteHeight,
|
|
6
|
+
} from '../textSprite';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Bitmap labels, this is used with {@link Glyph} to
|
|
@@ -21,25 +24,16 @@ const Label = function (textIn, colourIn) {
|
|
|
21
24
|
let size = 1.0;
|
|
22
25
|
let fontWeight = 500;
|
|
23
26
|
const textHeight = 0.012;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
sprite = new SpriteText(text, textHeight);
|
|
28
|
-
sprite.fontFace = "Asap";
|
|
29
|
-
sprite.fontSize = 90;
|
|
30
|
-
sprite.fontWeight = fontWeight;
|
|
31
|
-
const canvasTexture = sprite.material.map; // Snag the canvas texture generated by the library
|
|
32
|
-
sprite.material = new THREE.SpriteNodeMaterial({
|
|
33
|
-
map: canvasTexture,
|
|
34
|
-
});
|
|
35
|
-
sprite.material.map.generateMipmaps = true;
|
|
36
|
-
sprite.material.map.anisotropy = 4;
|
|
37
|
-
sprite.material.minFilter = THREE.LinearMipmapLinearFilter; // Smooth downscaling
|
|
38
|
-
sprite.material.magFilter = THREE.LinearFilter;
|
|
39
|
-
sprite.material.sizeAttenuation = false;
|
|
27
|
+
//The canvas resolution is chosen from the size of the text on screen
|
|
28
|
+
sprite = createTextSprite(text, textHeight,
|
|
29
|
+
colourIn ? colourIn.getStyle() : undefined, "Asap", fontWeight);
|
|
40
30
|
sprite.center.x = -0.05;
|
|
41
31
|
sprite.center.y = 0;
|
|
42
|
-
|
|
32
|
+
//SpriteText replaces the texture whenever the text, colour or size
|
|
33
|
+
//changes, the settings need to be re-applied to the new texture.
|
|
34
|
+
const applyTextureSettings = () => {
|
|
35
|
+
applyTextSpriteTextureSettings(sprite);
|
|
36
|
+
}
|
|
43
37
|
|
|
44
38
|
|
|
45
39
|
/**
|
|
@@ -78,7 +72,13 @@ const Label = function (textIn, colourIn) {
|
|
|
78
72
|
*/
|
|
79
73
|
this.setColour = colourIn => {
|
|
80
74
|
if (colourIn) {
|
|
81
|
-
|
|
75
|
+
//Changing the colour redraws the canvas and creates a new texture,
|
|
76
|
+
//only do it when the colour has changed.
|
|
77
|
+
const style = colourIn.getStyle();
|
|
78
|
+
if (style !== sprite.color) {
|
|
79
|
+
sprite.color = style;
|
|
80
|
+
applyTextureSettings();
|
|
81
|
+
}
|
|
82
82
|
colour = colourIn;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -111,7 +111,8 @@ const Label = function (textIn, colourIn) {
|
|
|
111
111
|
*/
|
|
112
112
|
this.setSize = sizeIn => {
|
|
113
113
|
if (sizeIn > 0.0) {
|
|
114
|
-
|
|
114
|
+
//Redraw at the resolution matching the new size
|
|
115
|
+
setTextSpriteHeight(sprite, textHeight * sizeIn);
|
|
115
116
|
size = sizeIn;
|
|
116
117
|
}
|
|
117
118
|
}
|
|
@@ -124,6 +125,7 @@ const Label = function (textIn, colourIn) {
|
|
|
124
125
|
this.setFontWeight = fontWeightIn => {
|
|
125
126
|
if (fontWeightIn && fontWeightIn !== fontWeight) {
|
|
126
127
|
sprite.fontWeight = fontWeightIn;
|
|
128
|
+
applyTextureSettings();
|
|
127
129
|
fontWeight = fontWeightIn;
|
|
128
130
|
}
|
|
129
131
|
}
|
|
@@ -149,9 +151,10 @@ const Label = function (textIn, colourIn) {
|
|
|
149
151
|
sprite._canvas.height = 1;
|
|
150
152
|
}
|
|
151
153
|
sprite.text = textIn;
|
|
152
|
-
sprite
|
|
154
|
+
setTextSpriteHeight(sprite, textHeight * size);
|
|
153
155
|
text = textIn;
|
|
154
156
|
if (sprite.material && sprite.material.map) {
|
|
157
|
+
applyTextureSettings();
|
|
155
158
|
sprite.material.map.needsUpdate = true;
|
|
156
159
|
}
|
|
157
160
|
}
|
|
@@ -170,7 +173,19 @@ const Label = function (textIn, colourIn) {
|
|
|
170
173
|
* Free up the memory
|
|
171
174
|
*/
|
|
172
175
|
this.dispose = () => {
|
|
173
|
-
|
|
176
|
+
if (sprite) {
|
|
177
|
+
releaseTextSprite(sprite);
|
|
178
|
+
sprite.removeFromParent();
|
|
179
|
+
if (sprite.material) {
|
|
180
|
+
sprite.material.map?.dispose();
|
|
181
|
+
sprite.material.dispose();
|
|
182
|
+
}
|
|
183
|
+
//Release the canvas memory
|
|
184
|
+
if (sprite._canvas) {
|
|
185
|
+
sprite._canvas.width = 0;
|
|
186
|
+
sprite._canvas.height = 0;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
/**
|
package/src/primitives/lines.js
CHANGED
|
@@ -53,6 +53,7 @@ const Lines = function () {
|
|
|
53
53
|
* @param {Number} width - Width of the lines.
|
|
54
54
|
*/
|
|
55
55
|
this.setWidth = width => {
|
|
56
|
+
this.requestRender();
|
|
56
57
|
if (this.morph && this.morph.material) {
|
|
57
58
|
this.morph.material.linewidth = width;
|
|
58
59
|
this.morph.material.needsUpdate = true;
|
|
@@ -67,6 +68,7 @@ const Lines = function () {
|
|
|
67
68
|
* @param {Number} colour - A hex value of the colour for the points
|
|
68
69
|
*/
|
|
69
70
|
this.addLines = (coords, colour) => {
|
|
71
|
+
this.requestRender();
|
|
70
72
|
if (coords && coords.length > 0) {
|
|
71
73
|
const geometry = this.addVertices(coords);
|
|
72
74
|
let mesh = this.getMorph();
|
package/src/primitives/lines2.js
CHANGED
|
@@ -16,7 +16,70 @@ import { LineSegmentsGeometry } from '../three/line/LineSegmentsGeometry';
|
|
|
16
16
|
const Lines2 = function () {
|
|
17
17
|
Lines.call(this);
|
|
18
18
|
this.isLines2 = true;
|
|
19
|
-
|
|
19
|
+
//Shared with the GPU buffer of the mesh, edits are made in place
|
|
20
|
+
let positions = new Float32Array(300);
|
|
21
|
+
|
|
22
|
+
//Grow the positions array if required, returns true if it has
|
|
23
|
+
//been reallocated.
|
|
24
|
+
const ensureCapacity = (length) => {
|
|
25
|
+
if (length > positions.length) {
|
|
26
|
+
//Each segment has two vertices, six values
|
|
27
|
+
const capacity = Math.ceil(Math.max(positions.length * 2, length) / 6) * 6;
|
|
28
|
+
const newPositions = new Float32Array(capacity);
|
|
29
|
+
newPositions.set(positions);
|
|
30
|
+
positions = newPositions;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//Fill the unused part of the array with degenerate segments at coord
|
|
37
|
+
const fillUnused = (start, coord) => {
|
|
38
|
+
for (let index = start; index + 2 < positions.length; index += 3) {
|
|
39
|
+
positions[index] = coord[0];
|
|
40
|
+
positions[index + 1] = coord[1];
|
|
41
|
+
positions[index + 2] = coord[2];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//Update the line distances without allocating new buffers
|
|
46
|
+
const updateLineDistances = (mesh) => {
|
|
47
|
+
const geometry = mesh.geometry;
|
|
48
|
+
const distanceStart = geometry.attributes.instanceDistanceStart;
|
|
49
|
+
const instanceStart = geometry.attributes.instanceStart;
|
|
50
|
+
if (!distanceStart || distanceStart.data.array.length !== instanceStart.count * 2) {
|
|
51
|
+
mesh.computeLineDistances();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const distances = distanceStart.data.array;
|
|
55
|
+
let total = 0;
|
|
56
|
+
for (let i = 0; i < instanceStart.count; i++) {
|
|
57
|
+
const offset = i * 6;
|
|
58
|
+
const dx = positions[offset + 3] - positions[offset];
|
|
59
|
+
const dy = positions[offset + 4] - positions[offset + 1];
|
|
60
|
+
const dz = positions[offset + 5] - positions[offset + 2];
|
|
61
|
+
distances[i * 2] = total;
|
|
62
|
+
total += Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
63
|
+
distances[i * 2 + 1] = total;
|
|
64
|
+
}
|
|
65
|
+
distanceStart.data.needsUpdate = true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//Upload the positions after they have been changed
|
|
69
|
+
const updatePositions = (mesh, reallocated) => {
|
|
70
|
+
const instanceStart = mesh.geometry.getAttribute('instanceStart');
|
|
71
|
+
if (reallocated || !instanceStart || instanceStart.data.array !== positions) {
|
|
72
|
+
//Only happens when the capacity has been increased
|
|
73
|
+
mesh.geometry.setPositions(positions);
|
|
74
|
+
mesh.computeLineDistances();
|
|
75
|
+
} else {
|
|
76
|
+
instanceStart.data.needsUpdate = true;
|
|
77
|
+
updateLineDistances(mesh);
|
|
78
|
+
mesh.geometry.computeBoundingBox();
|
|
79
|
+
mesh.geometry.computeBoundingSphere();
|
|
80
|
+
}
|
|
81
|
+
this.boundingBoxUpdateRequired = true;
|
|
82
|
+
}
|
|
20
83
|
|
|
21
84
|
/**
|
|
22
85
|
* Create the line segements using geometry and material.
|
|
@@ -47,6 +110,7 @@ const Lines2 = function () {
|
|
|
47
110
|
* @param {Number} width - Width of the lines.
|
|
48
111
|
*/
|
|
49
112
|
this.setWidth = width => {
|
|
113
|
+
this.requestRender();
|
|
50
114
|
if (this.morph && this.morph.material) {
|
|
51
115
|
this.morph.material.linewidth = width;
|
|
52
116
|
this.morph.material.needsUpdate = true;
|
|
@@ -57,12 +121,14 @@ const Lines2 = function () {
|
|
|
57
121
|
* Add new vertices into the array
|
|
58
122
|
*/
|
|
59
123
|
this.addVertices = function(coords) {
|
|
124
|
+
this.requestRender();
|
|
60
125
|
if (coords && coords.length) {
|
|
61
126
|
let mesh = this.getMorph();
|
|
62
127
|
if (!mesh) {
|
|
63
128
|
this.drawRange = 0;
|
|
64
129
|
}
|
|
65
130
|
let index = this.drawRange * 3;
|
|
131
|
+
const reallocated = ensureCapacity(index + coords.length * 3);
|
|
66
132
|
coords.forEach(coord => {
|
|
67
133
|
positions[index++] = coord[0];
|
|
68
134
|
positions[index++] = coord[1];
|
|
@@ -71,19 +137,12 @@ const Lines2 = function () {
|
|
|
71
137
|
});
|
|
72
138
|
//fill the rest of the array.
|
|
73
139
|
if (!mesh) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
140
|
+
fillUnused(index, coords[0]);
|
|
141
|
+
} else {
|
|
142
|
+
if (reallocated) {
|
|
143
|
+
fillUnused(index, coords[0]);
|
|
78
144
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (mesh) {
|
|
82
|
-
mesh.geometry.setPositions(positions);
|
|
83
|
-
mesh.computeLineDistances();
|
|
84
|
-
mesh.geometry.computeBoundingBox();
|
|
85
|
-
mesh.geometry.computeBoundingSphere();
|
|
86
|
-
this.boundingBoxUpdateRequired = true;
|
|
145
|
+
updatePositions(mesh, reallocated);
|
|
87
146
|
}
|
|
88
147
|
}
|
|
89
148
|
return positions;
|
|
@@ -117,6 +176,7 @@ const Lines2 = function () {
|
|
|
117
176
|
* Edit Vertice in index.
|
|
118
177
|
*/
|
|
119
178
|
this.editVertices = function(coords, i) {
|
|
179
|
+
this.requestRender();
|
|
120
180
|
if (coords && coords.length) {
|
|
121
181
|
let mesh = this.getMorph();
|
|
122
182
|
const maxIndex = i + coords.length - 1;
|
|
@@ -130,17 +190,8 @@ const Lines2 = function () {
|
|
|
130
190
|
positions[index++] = coord[1];
|
|
131
191
|
positions[index++] = coord[2];
|
|
132
192
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
positions[index++] = coords[0][0];
|
|
136
|
-
positions[index++] = coords[0][1];
|
|
137
|
-
positions[index++] = coords[0][2];
|
|
138
|
-
}
|
|
139
|
-
mesh.geometry.setPositions(positions);
|
|
140
|
-
mesh.computeLineDistances();
|
|
141
|
-
mesh.geometry.computeBoundingBox();
|
|
142
|
-
mesh.geometry.computeBoundingSphere();
|
|
143
|
-
this.boundingBoxUpdateRequired = true;
|
|
193
|
+
fillUnused(this.drawRange * 3, coords[0]);
|
|
194
|
+
updatePositions(mesh, false);
|
|
144
195
|
}
|
|
145
196
|
}
|
|
146
197
|
return positions;
|
|
@@ -154,6 +205,7 @@ const Lines2 = function () {
|
|
|
154
205
|
* @param {Number} colour - A hex value of the colour for the points
|
|
155
206
|
*/
|
|
156
207
|
this.addLines = (coords, colour) => {
|
|
208
|
+
this.requestRender();
|
|
157
209
|
if (coords && coords.length > 0) {
|
|
158
210
|
this.addVertices(coords);
|
|
159
211
|
let mesh = this.getMorph();
|
package/src/primitives/lod.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE from 'three/webgpu';
|
|
2
|
-
import { toBufferGeometry, updateMorphColorAttribute } from '../utilities';
|
|
2
|
+
import { keepIndexType, toBufferGeometry, updateMorphColorAttribute } from '../utilities';
|
|
3
3
|
import { LineSegments } from '../three/line/LineSegments';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -26,12 +26,15 @@ const LOD = function (parent) {
|
|
|
26
26
|
this._loader = undefined;
|
|
27
27
|
//The owning Zinc Object
|
|
28
28
|
this._parent = parent;
|
|
29
|
+
//Levels loaded after dispose are discarded
|
|
30
|
+
this._disposed = false;
|
|
29
31
|
|
|
30
32
|
/*
|
|
31
33
|
* Add a level of LOD at the specified distance
|
|
32
34
|
*/
|
|
33
35
|
this.addLevel = (object, distanceIn) => {
|
|
34
36
|
if (object) {
|
|
37
|
+
keepIndexType(object.geometry);
|
|
35
38
|
const distance = Math.abs(distanceIn);
|
|
36
39
|
let l;
|
|
37
40
|
for (l = 0; l < this.levels.length; l++) {
|
|
@@ -57,6 +60,7 @@ const LOD = function (parent) {
|
|
|
57
60
|
*/
|
|
58
61
|
this.levelLoaded = (object, distanceIn) => {
|
|
59
62
|
if (object) {
|
|
63
|
+
keepIndexType(object.geometry);
|
|
60
64
|
const distance = Math.abs(distanceIn);
|
|
61
65
|
for (let l = 0; l < this.levels.length; l++) {
|
|
62
66
|
if (distance === this.levels[l].distance) {
|
|
@@ -68,6 +72,7 @@ const LOD = function (parent) {
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
this.checkTransparentMesh();
|
|
75
|
+
this._parent?.requestRender?.();
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
|
|
@@ -182,6 +187,7 @@ const LOD = function (parent) {
|
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
this.dispose = () => {
|
|
190
|
+
this._disposed = true;
|
|
185
191
|
this.levels.forEach((level) => {
|
|
186
192
|
if (level.morph && level.morph.geometry) {
|
|
187
193
|
level.morph.geometry.dispose();
|
|
@@ -212,6 +218,10 @@ const LOD = function (parent) {
|
|
|
212
218
|
*/
|
|
213
219
|
this.lodLoader = function (distance) {
|
|
214
220
|
return (geometryIn) => {
|
|
221
|
+
if (this._disposed) {
|
|
222
|
+
geometryIn.dispose();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
215
225
|
const material = this._material;
|
|
216
226
|
const options = {
|
|
217
227
|
localTimeEnabled: this._parent.timeEnabled,
|
|
@@ -281,10 +291,13 @@ const LOD = function (parent) {
|
|
|
281
291
|
this._material = material;
|
|
282
292
|
if (this._secondaryMaterial) {
|
|
283
293
|
this._secondaryMaterial.dispose();
|
|
294
|
+
this._secondaryMaterial = undefined;
|
|
295
|
+
}
|
|
296
|
+
if (material.transparent || this.levels.some((level) => level.secondaryMesh)) {
|
|
297
|
+
this._secondaryMaterial = material.clone()
|
|
298
|
+
this._secondaryMaterial.side = THREE.FrontSide;
|
|
299
|
+
this._secondaryMaterial.transparent = true;
|
|
284
300
|
}
|
|
285
|
-
this._secondaryMaterial = material.clone()
|
|
286
|
-
this._secondaryMaterial.side = THREE.FrontSide;
|
|
287
|
-
this._secondaryMaterial.transparent = true;
|
|
288
301
|
this.levels.forEach((level) => {
|
|
289
302
|
if (level.morph) {
|
|
290
303
|
level.morph.material = this._material;
|
|
@@ -377,6 +390,7 @@ const LOD = function (parent) {
|
|
|
377
390
|
if (this._currentLevel != visibleIndex) {
|
|
378
391
|
this._currentLevel = visibleIndex;
|
|
379
392
|
this.checkTransparentMesh();
|
|
393
|
+
this._parent?.requestRender?.();
|
|
380
394
|
}
|
|
381
395
|
}
|
|
382
396
|
}
|
package/src/primitives/marker.js
CHANGED
|
@@ -2,9 +2,11 @@ import * as THREE from 'three/webgpu';
|
|
|
2
2
|
import myImage from '../assets/mapMarker.svg';
|
|
3
3
|
import { createNewSpriteText } from '../utilities';
|
|
4
4
|
import { ZincObject } from './zincObject';
|
|
5
|
+
import { requestRenderAll } from '../renderRequests';
|
|
6
|
+
import { releaseTextSprite } from '../textSprite';
|
|
5
7
|
|
|
6
8
|
const textureLoader = new THREE.TextureLoader();
|
|
7
|
-
const texture = textureLoader.load(myImage);
|
|
9
|
+
const texture = textureLoader.load(myImage, requestRenderAll);
|
|
8
10
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
9
11
|
const size = [0.02, 0.03, 1];
|
|
10
12
|
const spriteMaterial = new THREE.SpriteMaterial({
|
|
@@ -153,6 +155,7 @@ const Marker = function(zincObject) {
|
|
|
153
155
|
userImage.crossOrigin = "anonymous"
|
|
154
156
|
userImage.onload = () => {
|
|
155
157
|
this.setImageForUserSprite(userImage, size);
|
|
158
|
+
this.parent?.requestRender?.();
|
|
156
159
|
};
|
|
157
160
|
userImage.src = url;
|
|
158
161
|
} else {
|
|
@@ -173,6 +176,7 @@ const Marker = function(zincObject) {
|
|
|
173
176
|
sprite = undefined;
|
|
174
177
|
}
|
|
175
178
|
if (label) {
|
|
179
|
+
releaseTextSprite(label);
|
|
176
180
|
label.material.map.dispose();
|
|
177
181
|
label.material.dispose();
|
|
178
182
|
label = undefined;
|
|
@@ -188,6 +192,7 @@ const Marker = function(zincObject) {
|
|
|
188
192
|
//remove label
|
|
189
193
|
if (label) {
|
|
190
194
|
this.morph.remove(label);
|
|
195
|
+
releaseTextSprite(label);
|
|
191
196
|
label.material.map.dispose();
|
|
192
197
|
label.material.dispose();
|
|
193
198
|
label = undefined;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as THREE from 'three/webgpu';
|
|
2
2
|
import { ZincObject } from './zincObject';
|
|
3
|
+
import { requestRenderAll } from '../renderRequests';
|
|
4
|
+
import { releaseTextSprite } from '../textSprite';
|
|
3
5
|
import mapMarkerOrange from '../assets/mapMarkerOrange.svg';
|
|
4
6
|
import { createNewSpriteText } from '../utilities';
|
|
5
7
|
|
|
6
8
|
const textureLoader = new THREE.TextureLoader();
|
|
7
|
-
const texture = textureLoader.load(mapMarkerOrange);
|
|
9
|
+
const texture = textureLoader.load(mapMarkerOrange, requestRenderAll);
|
|
8
10
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
9
11
|
const size = [0.02, 0.03, 1];
|
|
10
12
|
const spriteMaterial = new THREE.SpriteMaterial({
|
|
@@ -96,6 +98,7 @@ const MarkerCluster = function(sceneIn) {
|
|
|
96
98
|
if (sprite.label === undefined || (number !== sprite.number)) {
|
|
97
99
|
if (sprite.label) {
|
|
98
100
|
sprite.group.remove(sprite.label);
|
|
101
|
+
releaseTextSprite(sprite.label);
|
|
99
102
|
sprite.label.material.map.dispose();
|
|
100
103
|
sprite.label.material.dispose();
|
|
101
104
|
}
|
|
@@ -178,6 +181,7 @@ const MarkerCluster = function(sceneIn) {
|
|
|
178
181
|
let clusters = [];
|
|
179
182
|
getCluster({...this.markers}, clusters);
|
|
180
183
|
drawClusters(clusters);
|
|
184
|
+
scene?.invalidate?.();
|
|
181
185
|
start = Date.now();
|
|
182
186
|
this.markerUpdateRequired = false;
|
|
183
187
|
}
|