zincjs 2.4.1-beta.4 → 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 +10 -0
- 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/primitivesLoader.js +59 -16
- 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 +25 -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 +11 -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/textureSlides.js +19 -3
- package/src/primitives/textureVolume.js +2 -0
- package/src/primitives/tubeLines.js +4 -0
- package/src/primitives/zincObject.js +76 -10
- package/src/region.js +85 -35
- package/src/renderRequests.js +20 -0
- package/src/renderer.js +81 -9
- package/src/scene.js +132 -22
- package/src/sceneExporter.js +4 -0
- package/src/sceneLoader.js +58 -19
- package/src/textSprite.js +211 -0
- package/src/utilities.js +147 -47
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import * as THREE from 'three/webgpu';
|
|
2
|
+
import SpriteTextModule from 'three-spritetext';
|
|
3
|
+
import { requestRenderAll } from './renderRequests';
|
|
4
|
+
const SpriteText = SpriteTextModule.default || SpriteTextModule;
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Text sprites (labels, marker numbers) are drawn on a canvas by
|
|
8
|
+
* three-spritetext. To keep the text sharp the canvas resolution is chosen
|
|
9
|
+
* from the size of the text on screen instead of a fixed font size, and it
|
|
10
|
+
* is redrawn once web fonts have finished loading.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
//Canvas pixels per screen pixel, a little oversampling smooths the edges
|
|
14
|
+
const OVERSAMPLE = 1.5;
|
|
15
|
+
const MIN_FONT_SIZE = 16;
|
|
16
|
+
const MAX_FONT_SIZE = 256;
|
|
17
|
+
//Only redraw when the screen scale has changed by more than this ratio
|
|
18
|
+
const RESCALE_THRESHOLD = 0.1;
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Screen pixels per unit of sprite height. Text sprites do not use size
|
|
22
|
+
* attenuation, their height is in view units at a distance of one, so this
|
|
23
|
+
* is viewportHeight * pixelRatio / (2 * tan(fov / 2)). Default to a 1000
|
|
24
|
+
* pixels viewport with the 40 degrees field of view used by the scenes.
|
|
25
|
+
*/
|
|
26
|
+
let pixelsPerUnit = 1000 / (2 * Math.tan(20 * Math.PI / 180));
|
|
27
|
+
|
|
28
|
+
//Weak references to every live text sprite, so they can be redrawn
|
|
29
|
+
const sprites = new Set();
|
|
30
|
+
let fontListenerAdded = false;
|
|
31
|
+
|
|
32
|
+
const fontSizeForHeight = (textHeight) => {
|
|
33
|
+
const size = Math.round(textHeight * pixelsPerUnit * OVERSAMPLE);
|
|
34
|
+
return Math.min(Math.max(size, MIN_FONT_SIZE), MAX_FONT_SIZE);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* The canvas matches the on screen size so mipmaps are not required,
|
|
39
|
+
* they blur the text when it is shrunk.
|
|
40
|
+
*/
|
|
41
|
+
const applyTextureSettings = (sprite) => {
|
|
42
|
+
const texture = sprite.material?.map;
|
|
43
|
+
if (texture) {
|
|
44
|
+
texture.generateMipmaps = false;
|
|
45
|
+
texture.minFilter = THREE.LinearFilter;
|
|
46
|
+
texture.magFilter = THREE.LinearFilter;
|
|
47
|
+
texture.anisotropy = 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* SpriteText redraws its canvas and creates a new texture in every setter,
|
|
53
|
+
* set all font properties before a single redraw instead.
|
|
54
|
+
*/
|
|
55
|
+
const setFont = (sprite, fontFace, fontSize, fontWeight) => {
|
|
56
|
+
if ('_fontFace' in sprite && typeof sprite._genCanvas === 'function') {
|
|
57
|
+
sprite._fontFace = fontFace;
|
|
58
|
+
sprite._fontSize = fontSize;
|
|
59
|
+
sprite._fontWeight = fontWeight;
|
|
60
|
+
sprite._genCanvas();
|
|
61
|
+
} else {
|
|
62
|
+
sprite.fontFace = fontFace;
|
|
63
|
+
sprite.fontSize = fontSize;
|
|
64
|
+
sprite.fontWeight = fontWeight;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* Redraw the sprite at the resolution required for its current height.
|
|
70
|
+
*/
|
|
71
|
+
const refreshTextSprite = (sprite, force = false) => {
|
|
72
|
+
const fontSize = fontSizeForHeight(sprite.textHeight);
|
|
73
|
+
if (force || fontSize !== sprite.fontSize) {
|
|
74
|
+
setFont(sprite, sprite.fontFace, fontSize, sprite.fontWeight);
|
|
75
|
+
applyTextureSettings(sprite);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const forEachSprite = (callback) => {
|
|
80
|
+
sprites.forEach((ref) => {
|
|
81
|
+
const sprite = ref.deref();
|
|
82
|
+
if (sprite) {
|
|
83
|
+
callback(sprite);
|
|
84
|
+
} else {
|
|
85
|
+
sprites.delete(ref);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const redrawAll = () => {
|
|
91
|
+
forEachSprite((sprite) => refreshTextSprite(sprite, true));
|
|
92
|
+
requestRenderAll();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* The canvas is drawn with a fallback font if a web font has not been
|
|
97
|
+
* loaded yet, redraw once it is available.
|
|
98
|
+
*/
|
|
99
|
+
const waitForFont = (sprite) => {
|
|
100
|
+
if (typeof document === 'undefined' || !document.fonts) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!fontListenerAdded && document.fonts.addEventListener) {
|
|
104
|
+
//Catch fonts from stylesheets added after the sprites are created
|
|
105
|
+
document.fonts.addEventListener('loadingdone', redrawAll);
|
|
106
|
+
fontListenerAdded = true;
|
|
107
|
+
}
|
|
108
|
+
const font = `${sprite.fontWeight} ${sprite.fontSize}px ${sprite.fontFace}`;
|
|
109
|
+
try {
|
|
110
|
+
if (!document.fonts.check(font)) {
|
|
111
|
+
document.fonts.load(font).then(() => {
|
|
112
|
+
refreshTextSprite(sprite, true);
|
|
113
|
+
requestRenderAll();
|
|
114
|
+
}).catch(() => {});
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
//Invalid font string, keep the fallback font
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const registerTextSprite = (sprite) => {
|
|
122
|
+
sprites.add(new WeakRef(sprite));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Stop tracking a text sprite, call this when it is disposed.
|
|
127
|
+
*/
|
|
128
|
+
const releaseTextSprite = (sprite) => {
|
|
129
|
+
sprites.forEach((ref) => {
|
|
130
|
+
const trackedSprite = ref.deref();
|
|
131
|
+
if (!trackedSprite || trackedSprite === sprite) {
|
|
132
|
+
sprites.delete(ref);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Create a text sprite rendered with a node material, the canvas resolution
|
|
139
|
+
* is chosen from the size of the text on screen.
|
|
140
|
+
*
|
|
141
|
+
* @param {String} text - Text to display.
|
|
142
|
+
* @param {Number} textHeight - Height of a line of text in view units.
|
|
143
|
+
* @param {String} colour - CSS colour of the text.
|
|
144
|
+
* @param {String} fontFace - Font family.
|
|
145
|
+
* @param {Number|String} fontWeight - Font weight.
|
|
146
|
+
* @return {SpriteText}
|
|
147
|
+
*/
|
|
148
|
+
const createTextSprite = (text, textHeight, colour, fontFace, fontWeight) => {
|
|
149
|
+
const sprite = colour ? new SpriteText(text, textHeight, colour) :
|
|
150
|
+
new SpriteText(text, textHeight);
|
|
151
|
+
setFont(sprite, fontFace, fontSizeForHeight(textHeight), fontWeight);
|
|
152
|
+
//Replace the SpriteMaterial created by SpriteText with a node material
|
|
153
|
+
//using the same canvas texture.
|
|
154
|
+
const originalMaterial = sprite.material;
|
|
155
|
+
sprite.material = new THREE.SpriteNodeMaterial({
|
|
156
|
+
map: originalMaterial.map,
|
|
157
|
+
});
|
|
158
|
+
originalMaterial.map = null;
|
|
159
|
+
originalMaterial.dispose();
|
|
160
|
+
sprite.material.sizeAttenuation = false;
|
|
161
|
+
applyTextureSettings(sprite);
|
|
162
|
+
registerTextSprite(sprite);
|
|
163
|
+
waitForFont(sprite);
|
|
164
|
+
return sprite;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Set the height of a text sprite and redraw it once at the matching
|
|
169
|
+
* resolution.
|
|
170
|
+
*/
|
|
171
|
+
const setTextSpriteHeight = (sprite, textHeight) => {
|
|
172
|
+
if ('_textHeight' in sprite) {
|
|
173
|
+
sprite._textHeight = textHeight;
|
|
174
|
+
refreshTextSprite(sprite, true);
|
|
175
|
+
} else {
|
|
176
|
+
sprite.textHeight = textHeight;
|
|
177
|
+
refreshTextSprite(sprite);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Update the number of screen pixels per unit of text height, e.g. when the
|
|
183
|
+
* viewport is resized. Text sprites are redrawn if it has changed
|
|
184
|
+
* significantly.
|
|
185
|
+
*
|
|
186
|
+
* @param {Number} value - viewportHeight * pixelRatio / (2 * tan(fov / 2))
|
|
187
|
+
*/
|
|
188
|
+
const setTextPixelsPerUnit = (value) => {
|
|
189
|
+
if (!(value > 0) || !isFinite(value)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (Math.abs(value / pixelsPerUnit - 1) > RESCALE_THRESHOLD) {
|
|
193
|
+
pixelsPerUnit = value;
|
|
194
|
+
forEachSprite((sprite) => refreshTextSprite(sprite));
|
|
195
|
+
requestRenderAll();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const getTextPixelsPerUnit = () => pixelsPerUnit;
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
applyTextureSettings as applyTextSpriteTextureSettings,
|
|
203
|
+
createTextSprite,
|
|
204
|
+
fontSizeForHeight,
|
|
205
|
+
getTextPixelsPerUnit,
|
|
206
|
+
refreshTextSprite,
|
|
207
|
+
registerTextSprite,
|
|
208
|
+
releaseTextSprite,
|
|
209
|
+
setTextPixelsPerUnit,
|
|
210
|
+
setTextSpriteHeight,
|
|
211
|
+
};
|
package/src/utilities.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three/webgpu';
|
|
2
|
-
import SpriteTextModule from 'three-spritetext';
|
|
3
|
-
const SpriteText = SpriteTextModule.default || SpriteTextModule;
|
|
4
2
|
import discPNG from './assets/disc.png';
|
|
3
|
+
import { requestRenderAll } from './renderRequests';
|
|
4
|
+
import { createTextSprite } from './textSprite';
|
|
5
5
|
|
|
6
6
|
function createNewURL(target, reference) {
|
|
7
7
|
const getNewURL = (target, reference) => {
|
|
@@ -31,6 +31,29 @@ function createNewURL(target, reference) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/*
|
|
35
|
+
* Compute the world matrix of an object from its ancestors and return it.
|
|
36
|
+
* three.js only recomputes world matrices flagged by matrixWorldNeedsUpdate,
|
|
37
|
+
* which objects with matrixAutoUpdate set to false (zinc meshes and region
|
|
38
|
+
* groups) never set, they would otherwise stay stale until the next render.
|
|
39
|
+
*/
|
|
40
|
+
function updateWorldMatrixFromAncestors(object) {
|
|
41
|
+
if (object.parent) {
|
|
42
|
+
updateWorldMatrixFromAncestors(object.parent);
|
|
43
|
+
}
|
|
44
|
+
if (object.matrixAutoUpdate) {
|
|
45
|
+
object.updateMatrix();
|
|
46
|
+
}
|
|
47
|
+
if (object.matrixWorldAutoUpdate !== false) {
|
|
48
|
+
if (object.parent) {
|
|
49
|
+
object.matrixWorld.multiplyMatrices(object.parent.matrixWorld, object.matrix);
|
|
50
|
+
} else {
|
|
51
|
+
object.matrixWorld.copy(object.matrix);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return object.matrixWorld;
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
/*
|
|
35
58
|
* Calculate the bounding box of a mesh, values will be
|
|
36
59
|
* set for cachedBox, b1, v1 and v2 and they need to be
|
|
@@ -61,8 +84,7 @@ function getBoundingBox(mesh, cachedBox, b1, v1, v2) {
|
|
|
61
84
|
cachedBox.setFromBufferAttribute(
|
|
62
85
|
mesh.geometry.attributes.position);
|
|
63
86
|
}
|
|
64
|
-
|
|
65
|
-
cachedBox.applyMatrix4(mesh.matrixWorld);
|
|
87
|
+
cachedBox.applyMatrix4(updateWorldMatrixFromAncestors(mesh));
|
|
66
88
|
}
|
|
67
89
|
|
|
68
90
|
|
|
@@ -128,31 +150,40 @@ const getColorsRGB = (colors, index) => {
|
|
|
128
150
|
return [mycolor.r, mycolor.g, mycolor.b];
|
|
129
151
|
}
|
|
130
152
|
|
|
153
|
+
//morphColor0/morphColor1 attribute -> morph colour target last copied into it
|
|
154
|
+
const morphColorSources = new WeakMap();
|
|
155
|
+
|
|
131
156
|
const updateMorphColorAttribute = function(targetGeometry, morph) {
|
|
132
157
|
if (morph && targetGeometry && targetGeometry.morphAttributes &&
|
|
133
158
|
targetGeometry.morphAttributes[ "color" ]) {
|
|
134
159
|
const morphColors = targetGeometry.morphAttributes[ "color" ];
|
|
135
160
|
const influences = morph.morphTargetInfluences;
|
|
136
161
|
const length = influences.length;
|
|
137
|
-
|
|
138
|
-
|
|
162
|
+
//Find the first two morph targets with non-zero influence
|
|
163
|
+
let found = 0;
|
|
164
|
+
let index0 = 0;
|
|
165
|
+
let index1 = 0;
|
|
166
|
+
let weight0 = 0;
|
|
167
|
+
let weight1 = 0;
|
|
168
|
+
for (let i = 0; i < length && found < 2; i++) {
|
|
139
169
|
if (influences[i] > 0) {
|
|
140
|
-
|
|
170
|
+
if (found === 0) {
|
|
171
|
+
index0 = index1 = i;
|
|
172
|
+
weight0 = influences[i];
|
|
173
|
+
} else {
|
|
174
|
+
index1 = i;
|
|
175
|
+
weight1 = influences[i];
|
|
176
|
+
}
|
|
177
|
+
found++;
|
|
141
178
|
}
|
|
142
179
|
}
|
|
143
180
|
//morphColorMix (0 = fully morphColor0, 1 = fully morphColor1) drives the
|
|
144
181
|
//TSL colorNode set up by applyMorphColorNode - keep it in sync with
|
|
145
182
|
//whichever two morph targets currently have non-zero influence.
|
|
146
183
|
let mix = 0;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
index0 = morphArray[0][0];
|
|
151
|
-
index1 = morphArray[1][0];
|
|
152
|
-
const total = morphArray[0][1] + morphArray[1][1];
|
|
153
|
-
mix = total > 0 ? morphArray[1][1] / total : 0;
|
|
154
|
-
} else if (morphArray.length == 1) {
|
|
155
|
-
index0 = index1 = morphArray[0][0];
|
|
184
|
+
if (found >= 2) {
|
|
185
|
+
const total = weight0 + weight1;
|
|
186
|
+
mix = total > 0 ? weight1 / total : 0;
|
|
156
187
|
}
|
|
157
188
|
//Keep morphColor0/morphColor1 as two persistent attributes and mutate
|
|
158
189
|
//their contents in place (array.set + needsUpdate) rather than
|
|
@@ -176,10 +207,20 @@ const updateMorphColorAttribute = function(targetGeometry, morph) {
|
|
|
176
207
|
targetGeometry.setAttribute('morphColor0', attribute0);
|
|
177
208
|
targetGeometry.setAttribute('morphColor1', attribute1);
|
|
178
209
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
210
|
+
//Only copy and upload when the pair of keyframes changes, between
|
|
211
|
+
//keyframes only morphColorMix changes.
|
|
212
|
+
const source0 = morphColors[index0];
|
|
213
|
+
const source1 = morphColors[index1];
|
|
214
|
+
if (morphColorSources.get(attribute0) !== source0) {
|
|
215
|
+
attribute0.array.set(source0.array);
|
|
216
|
+
attribute0.needsUpdate = true;
|
|
217
|
+
morphColorSources.set(attribute0, source0);
|
|
218
|
+
}
|
|
219
|
+
if (morphColorSources.get(attribute1) !== source1) {
|
|
220
|
+
attribute1.array.set(source1.array);
|
|
221
|
+
attribute1.needsUpdate = true;
|
|
222
|
+
morphColorSources.set(attribute1, source1);
|
|
223
|
+
}
|
|
183
224
|
const morphColorMix = morph.material && morph.material.userData &&
|
|
184
225
|
morph.material.userData.uniforms && morph.material.userData.uniforms.morphColorMix;
|
|
185
226
|
if (morphColorMix) {
|
|
@@ -697,29 +738,81 @@ function createBufferGeometry(length, coords) {
|
|
|
697
738
|
return undefined;
|
|
698
739
|
};
|
|
699
740
|
|
|
741
|
+
/*
|
|
742
|
+
* WebGPURenderer converts 16 bit index buffers to 32 bit and keeps the
|
|
743
|
+
* converted array, doubling the memory used by indices. WebGPU supports 16
|
|
744
|
+
* bit indices natively, three.js only converts arrays whose constructor is
|
|
745
|
+
* exactly Uint16Array while the index format is chosen with
|
|
746
|
+
* instanceof Uint16Array. Indices are viewed through this subclass so they
|
|
747
|
+
* stay 16 bit, the values and getX() are unchanged.
|
|
748
|
+
*/
|
|
749
|
+
class IndexUint16Array extends Uint16Array {}
|
|
750
|
+
|
|
751
|
+
function keepIndexType(geometry) {
|
|
752
|
+
const index = geometry?.index;
|
|
753
|
+
if (index && index.array.constructor === Uint16Array) {
|
|
754
|
+
const array = index.array;
|
|
755
|
+
//A view of the same buffer, no copy is made
|
|
756
|
+
index.array = new IndexUint16Array(array.buffer, array.byteOffset, array.length);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/*
|
|
761
|
+
* Temporarily view the indices of the object and its descendants as plain
|
|
762
|
+
* Uint16Array, for code which checks the exact array type such as
|
|
763
|
+
* GLTFExporter. Returns a function which undoes the change.
|
|
764
|
+
*/
|
|
765
|
+
function useStandardIndexType(object) {
|
|
766
|
+
const changed = [];
|
|
767
|
+
object.traverse((child) => {
|
|
768
|
+
const index = child.geometry?.index;
|
|
769
|
+
if (index && index.array instanceof IndexUint16Array) {
|
|
770
|
+
const array = index.array;
|
|
771
|
+
index.array = new Uint16Array(array.buffer, array.byteOffset, array.length);
|
|
772
|
+
changed.push([index, array, index.array]);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
return () => {
|
|
776
|
+
changed.forEach(([index, original, replacement]) => {
|
|
777
|
+
//Only restore if nothing else has replaced the array since
|
|
778
|
+
if (index.array === replacement) {
|
|
779
|
+
index.array = original;
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
//Shared by all pointsets
|
|
786
|
+
let circularTexture = undefined;
|
|
787
|
+
|
|
700
788
|
function getCircularTexture() {
|
|
789
|
+
if (circularTexture) {
|
|
790
|
+
return circularTexture;
|
|
791
|
+
}
|
|
701
792
|
const image = new Image();
|
|
702
|
-
image.src = discPNG;
|
|
703
793
|
const texture = new THREE.Texture();
|
|
704
794
|
texture.image = image;
|
|
705
|
-
|
|
795
|
+
//Only upload once the image is available
|
|
796
|
+
image.onload = () => {
|
|
797
|
+
texture.needsUpdate = true;
|
|
798
|
+
requestRenderAll();
|
|
799
|
+
};
|
|
800
|
+
image.src = discPNG;
|
|
801
|
+
if (image.complete && image.naturalWidth > 0) {
|
|
802
|
+
texture.needsUpdate = true;
|
|
803
|
+
}
|
|
804
|
+
circularTexture = texture;
|
|
706
805
|
return texture;
|
|
707
806
|
}
|
|
708
807
|
|
|
808
|
+
/*
|
|
809
|
+
* Create a text sprite, e.g. marker numbers. The canvas resolution is chosen
|
|
810
|
+
* from the size of the text on screen, pixel is no longer used.
|
|
811
|
+
*/
|
|
709
812
|
function createNewSpriteText(text, height, colour, font, pixel, weight) {
|
|
710
|
-
const sprite =
|
|
711
|
-
|
|
712
|
-
sprite.
|
|
713
|
-
sprite.fontSize = pixel;
|
|
714
|
-
sprite.fontWeight = weight;
|
|
715
|
-
const canvasTexture = sprite.material.map; // Snag the canvas texture generated by the library
|
|
716
|
-
sprite.material = new THREE.SpriteNodeMaterial({
|
|
717
|
-
map: canvasTexture,
|
|
718
|
-
});
|
|
719
|
-
sprite.material.map.generateMipmaps = true;
|
|
720
|
-
sprite.material.map.anisotropy = 4;
|
|
721
|
-
sprite.material.sizeAttenuation = false;
|
|
722
|
-
sprite.material.alphaTest = 0.5;
|
|
813
|
+
const sprite = createTextSprite(text, height, colour, font, weight);
|
|
814
|
+
//Only discard fully transparent pixels to keep the smooth edges
|
|
815
|
+
sprite.material.alphaTest = 0.05;
|
|
723
816
|
sprite.material.transparent = true;
|
|
724
817
|
sprite.material.depthWrite = false;
|
|
725
818
|
sprite.material.depthTest = false;
|
|
@@ -775,21 +868,24 @@ function removeVertexAtIndex(geometry, index, maintainLength) {
|
|
|
775
868
|
|
|
776
869
|
// Helper to remove elements from a typed array
|
|
777
870
|
const removeElements = (attribute, name) => {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
871
|
+
const array = attribute.array;
|
|
872
|
+
if (array.length < (start + deleteCount)) {
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
if (maintainLength) {
|
|
876
|
+
//Shift the remaining values in place and clear the end, this keeps
|
|
877
|
+
//the attribute and its GPU buffer.
|
|
878
|
+
array.copyWithin(start, start + deleteCount);
|
|
879
|
+
array.fill(0, array.length - deleteCount);
|
|
880
|
+
attribute.needsUpdate = true;
|
|
881
|
+
} else {
|
|
882
|
+
const newArray = new array.constructor(array.length - deleteCount);
|
|
883
|
+
newArray.set(array.subarray(0, start));
|
|
884
|
+
newArray.set(array.subarray(start + deleteCount), start);
|
|
787
885
|
geometry.setAttribute(name, new THREE.BufferAttribute(newArray, itemSize));
|
|
788
886
|
geometry.getAttribute(name).needsUpdate = true;
|
|
789
887
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
return removed;
|
|
888
|
+
return true;
|
|
793
889
|
};
|
|
794
890
|
|
|
795
891
|
removed = removeElements(positionAttribute, 'position');
|
|
@@ -875,6 +971,10 @@ export {
|
|
|
875
971
|
createNewURL,
|
|
876
972
|
getBoundingBox,
|
|
877
973
|
getCircularTexture,
|
|
974
|
+
keepIndexType,
|
|
975
|
+
updateWorldMatrixFromAncestors,
|
|
976
|
+
useStandardIndexType,
|
|
977
|
+
IndexUint16Array,
|
|
878
978
|
getColorsRGB,
|
|
879
979
|
isRegionGroup,
|
|
880
980
|
loadExternalFile,
|