@expofp/renderer 1.4.2 → 1.5.0
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/dist/index.d.ts +24 -2
- package/dist/index.js +932 -882
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
var _a;
|
|
5
|
-
import { DataTexture, FloatType, UnsignedIntType, IntType,
|
|
5
|
+
import { Color, Matrix4, Vector3, DataTexture, RGBAFormat, FloatType, RedFormat, UnsignedIntType, IntType, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Mesh, LessEqualDepth, Quaternion, BufferGeometry, LinearSRGBColorSpace, Plane, Raycaster, Sphere, Box3, Spherical, PerspectiveCamera, Scene, Camera, MathUtils, Clock, WebGLRenderer } from "three";
|
|
6
6
|
import { traverseAncestorsGenerator } from "three/examples/jsm/utils/SceneUtils.js";
|
|
7
7
|
import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
|
|
8
8
|
import { LineMaterial, LineSegmentsGeometry } from "three/examples/jsm/Addons.js";
|
|
@@ -10,351 +10,703 @@ import { MaxRectsPacker, Rectangle } from "maxrects-packer";
|
|
|
10
10
|
import { converter, parse } from "culori";
|
|
11
11
|
import { RAD2DEG, DEG2RAD as DEG2RAD$1 } from "three/src/math/MathUtils.js";
|
|
12
12
|
import { EventManager, Rotate, Pan } from "mjolnir.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
deepMerge(target[key], source[key]);
|
|
29
|
-
} else {
|
|
30
|
-
Object.assign(target, { [key]: source[key] });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
13
|
+
const floatsPerMember = 32;
|
|
14
|
+
const tempColor = new Color();
|
|
15
|
+
const defaultStrokeColor = 8421504;
|
|
16
|
+
const tempMat4 = new Matrix4();
|
|
17
|
+
const tempVec3a = new Vector3();
|
|
18
|
+
const tempVec3b = new Vector3();
|
|
19
|
+
const origin = new Vector3();
|
|
20
|
+
const defaultOrient = "+x+y";
|
|
21
|
+
class BatchedText extends BatchedText$1 {
|
|
22
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
23
|
+
constructor() {
|
|
24
|
+
super();
|
|
25
|
+
__publicField(this, "mapInstanceIdToText", /* @__PURE__ */ new Map());
|
|
26
|
+
__publicField(this, "textArray", []);
|
|
27
|
+
__publicField(this, "textureNeedsUpdate", false);
|
|
33
28
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return Math.max(pixelsPerInstance, Math.ceil(Math.sqrt(capacity / pixelsPerInstance)) * pixelsPerInstance);
|
|
38
|
-
}
|
|
39
|
-
function getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity) {
|
|
40
|
-
if (channels === 3) {
|
|
41
|
-
console.warn('"channels" cannot be 3. Set to 4. More info: https://github.com/mrdoob/three.js/pull/23228');
|
|
42
|
-
channels = 4;
|
|
29
|
+
/** Number of texts in the batch */
|
|
30
|
+
get size() {
|
|
31
|
+
return this._members.size;
|
|
43
32
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const isUnsignedInt = arrayType.name.includes("Uint");
|
|
48
|
-
const type = isFloat ? FloatType : isUnsignedInt ? UnsignedIntType : IntType;
|
|
49
|
-
let format;
|
|
50
|
-
switch (channels) {
|
|
51
|
-
case 1:
|
|
52
|
-
format = isFloat ? RedFormat : RedIntegerFormat;
|
|
53
|
-
break;
|
|
54
|
-
case 2:
|
|
55
|
-
format = isFloat ? RGFormat : RGIntegerFormat;
|
|
56
|
-
break;
|
|
57
|
-
case 4:
|
|
58
|
-
format = isFloat ? RGBAFormat : RGBAIntegerFormat;
|
|
59
|
-
break;
|
|
33
|
+
/** Base material before patching */
|
|
34
|
+
get baseMaterial() {
|
|
35
|
+
return this._baseMaterial;
|
|
60
36
|
}
|
|
61
|
-
return { array, size, type, format };
|
|
62
|
-
}
|
|
63
|
-
class SquareDataTexture extends DataTexture {
|
|
64
37
|
/**
|
|
65
|
-
* @
|
|
66
|
-
* @param
|
|
67
|
-
* @
|
|
68
|
-
* @param capacity The total number of instances.
|
|
69
|
-
* @param uniformMap Optional map for handling uniform values.
|
|
70
|
-
* @param fetchInFragmentShader Optional flag that determines if uniform values should be fetched in the fragment shader instead of the vertex shader.
|
|
38
|
+
* Get the {@link Text} object by instance id
|
|
39
|
+
* @param instanceId Instance id
|
|
40
|
+
* @returns Text object
|
|
71
41
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const { array, format, size, type } = getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity);
|
|
75
|
-
super(array, size, size, format, type);
|
|
76
|
-
__publicField(this, "data");
|
|
77
|
-
__publicField(this, "channels");
|
|
78
|
-
__publicField(this, "pixelsPerInstance");
|
|
79
|
-
__publicField(this, "stride");
|
|
80
|
-
__publicField(this, "uniformMap");
|
|
81
|
-
__publicField(this, "fetchUniformsInFragmentShader");
|
|
82
|
-
__publicField(this, "uniformPrefix", "batch_");
|
|
83
|
-
this.data = array;
|
|
84
|
-
this.channels = channels;
|
|
85
|
-
this.pixelsPerInstance = pixelsPerInstance;
|
|
86
|
-
this.stride = pixelsPerInstance * channels;
|
|
87
|
-
this.uniformMap = uniformMap;
|
|
88
|
-
this.fetchUniformsInFragmentShader = fetchInFragmentShader;
|
|
89
|
-
this.needsUpdate = true;
|
|
42
|
+
getText(instanceId) {
|
|
43
|
+
return this.mapInstanceIdToText.get(instanceId);
|
|
90
44
|
}
|
|
91
45
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* @param
|
|
95
|
-
* @param
|
|
46
|
+
* Set the visibility of the {@link Text} object by instance id.
|
|
47
|
+
* This is for interface compatibility with {@link BatchedMesh}.
|
|
48
|
+
* @param instanceId Instance id
|
|
49
|
+
* @param visible Visibility flag
|
|
96
50
|
*/
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
console.warn(`SquareDataTexture.setUniformAt: uniform ${name} not found`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const { offset, size } = schema;
|
|
104
|
-
const stride = this.stride;
|
|
105
|
-
if (size === 1) {
|
|
106
|
-
this.data[id * stride + offset] = value;
|
|
107
|
-
} else {
|
|
108
|
-
value.toArray(this.data, id * stride + offset);
|
|
109
|
-
}
|
|
51
|
+
setVisibleAt(instanceId, visible) {
|
|
52
|
+
const text = this.getText(instanceId);
|
|
53
|
+
text.visible = visible;
|
|
110
54
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* @param target Optional target object to store the uniform value.
|
|
116
|
-
* @returns The uniform value for the specified instance.
|
|
117
|
-
*/
|
|
118
|
-
getUniformAt(id, name, target) {
|
|
119
|
-
const schema = this.uniformMap.get(name);
|
|
120
|
-
if (!schema) {
|
|
121
|
-
console.warn(`SquareDataTexture.getUniformAt: uniform ${name} not found`);
|
|
122
|
-
return 0;
|
|
123
|
-
}
|
|
124
|
-
const { offset, size } = schema;
|
|
125
|
-
const stride = this.stride;
|
|
126
|
-
if (size === 1) {
|
|
127
|
-
return this.data[id * stride + offset];
|
|
55
|
+
addText(text, instanceId) {
|
|
56
|
+
super.addText(text);
|
|
57
|
+
if (instanceId !== void 0) {
|
|
58
|
+
this.mapInstanceIdToText.set(instanceId, text);
|
|
128
59
|
}
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
/** Mark the texture as needing an update. */
|
|
132
|
-
update() {
|
|
133
|
-
this.needsUpdate = true;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Generates the GLSL code for accessing the uniform data stored in the texture.
|
|
137
|
-
* @param textureName The name of the texture in the GLSL shader.
|
|
138
|
-
* @param indexName The name of the index in the GLSL shader.
|
|
139
|
-
* @param indexType The type of the index in the GLSL shader.
|
|
140
|
-
* @returns An object containing the GLSL code for the vertex and fragment shaders.
|
|
141
|
-
*/
|
|
142
|
-
getUniformsGLSL(textureName, indexName, indexType) {
|
|
143
|
-
const vertex = this.getUniformsVertexGLSL(textureName, indexName, indexType);
|
|
144
|
-
const fragment = this.getUniformsFragmentGLSL(textureName, indexName, indexType);
|
|
145
|
-
return { vertex, fragment };
|
|
60
|
+
this.textArray.push(text);
|
|
146
61
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
/*glsl*/
|
|
151
|
-
`
|
|
152
|
-
flat varying ${indexType} ${this.uniformPrefix}${indexName};
|
|
153
|
-
void main() {
|
|
154
|
-
${this.uniformPrefix}${indexName} = ${indexName};
|
|
155
|
-
`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
const texelsFetch = this.texelsFetchGLSL(textureName, indexName);
|
|
159
|
-
const getFromTexels = this.getFromTexelsGLSL();
|
|
160
|
-
const { assignVarying, declareVarying } = this.getVarying();
|
|
161
|
-
return (
|
|
162
|
-
/*glsl*/
|
|
163
|
-
`
|
|
164
|
-
uniform highp sampler2D ${textureName};
|
|
165
|
-
${declareVarying}
|
|
166
|
-
void main() {
|
|
167
|
-
#ifdef USE_BATCHING
|
|
168
|
-
${indexType} ${indexName} = ${indexType}(getIndirectIndex(gl_DrawID));
|
|
169
|
-
#endif
|
|
170
|
-
${texelsFetch}
|
|
171
|
-
${getFromTexels}
|
|
172
|
-
${assignVarying}`
|
|
173
|
-
);
|
|
62
|
+
dispose() {
|
|
63
|
+
super.dispose();
|
|
64
|
+
this.dispatchEvent({ type: "dispose" });
|
|
174
65
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
66
|
+
// TODO: Check performance
|
|
67
|
+
_prepareForRender(material) {
|
|
68
|
+
var _a2;
|
|
69
|
+
const isOutline = material.isTextOutlineMaterial;
|
|
70
|
+
material.uniforms.uTroikaIsOutline.value = isOutline;
|
|
71
|
+
let texture = this._dataTextures[isOutline ? "outline" : "main"];
|
|
72
|
+
const dataLength = Math.pow(2, Math.ceil(Math.log2(this._members.size * floatsPerMember)));
|
|
73
|
+
if (!texture || dataLength !== texture.image.data.length) {
|
|
74
|
+
if (texture) texture.dispose();
|
|
75
|
+
const width = Math.min(dataLength / 4, 1024);
|
|
76
|
+
texture = this._dataTextures[isOutline ? "outline" : "main"] = new DataTexture(
|
|
77
|
+
new Float32Array(dataLength),
|
|
78
|
+
width,
|
|
79
|
+
dataLength / 4 / width,
|
|
80
|
+
RGBAFormat,
|
|
81
|
+
FloatType
|
|
184
82
|
);
|
|
185
83
|
}
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
84
|
+
const texData = texture.image.data;
|
|
85
|
+
this.textureNeedsUpdate = false;
|
|
86
|
+
for (const text of this.textArray) {
|
|
87
|
+
const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
|
|
88
|
+
const textRenderInfo = text.textRenderInfo;
|
|
89
|
+
if (index < 0 || !textRenderInfo) continue;
|
|
90
|
+
const startIndex = index * floatsPerMember;
|
|
91
|
+
if (!text.visible) {
|
|
92
|
+
for (let i = 0; i < 16; i++) {
|
|
93
|
+
this.setTexData(startIndex + i, 0, texData);
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const matrix = text.matrix.elements;
|
|
98
|
+
for (let i = 0; i < 16; i++) {
|
|
99
|
+
this.setTexData(startIndex + i, matrix[i], texData);
|
|
100
|
+
}
|
|
101
|
+
text._prepareForRender(material);
|
|
102
|
+
const {
|
|
103
|
+
uTroikaTotalBounds,
|
|
104
|
+
uTroikaClipRect,
|
|
105
|
+
uTroikaPositionOffset,
|
|
106
|
+
uTroikaEdgeOffset,
|
|
107
|
+
uTroikaBlurRadius,
|
|
108
|
+
uTroikaStrokeWidth,
|
|
109
|
+
uTroikaStrokeColor,
|
|
110
|
+
uTroikaStrokeOpacity,
|
|
111
|
+
uTroikaFillOpacity,
|
|
112
|
+
uTroikaCurveRadius
|
|
113
|
+
} = material.uniforms;
|
|
114
|
+
for (let i = 0; i < 4; i++) {
|
|
115
|
+
this.setTexData(startIndex + 16 + i, uTroikaTotalBounds.value.getComponent(i), texData);
|
|
116
|
+
}
|
|
117
|
+
for (let i = 0; i < 4; i++) {
|
|
118
|
+
this.setTexData(startIndex + 20 + i, uTroikaClipRect.value.getComponent(i), texData);
|
|
119
|
+
}
|
|
120
|
+
let color = isOutline ? text.outlineColor || 0 : text.color;
|
|
121
|
+
color ?? (color = this.color);
|
|
122
|
+
color ?? (color = this.material.color);
|
|
123
|
+
color ?? (color = 16777215);
|
|
124
|
+
this.setTexData(startIndex + 24, tempColor.set(color).getHex(), texData);
|
|
125
|
+
this.setTexData(startIndex + 25, uTroikaFillOpacity.value, texData);
|
|
126
|
+
this.setTexData(startIndex + 26, uTroikaCurveRadius.value, texData);
|
|
127
|
+
if (isOutline) {
|
|
128
|
+
this.setTexData(startIndex + 28, uTroikaPositionOffset.value.x, texData);
|
|
129
|
+
this.setTexData(startIndex + 29, uTroikaPositionOffset.value.y, texData);
|
|
130
|
+
this.setTexData(startIndex + 30, uTroikaEdgeOffset.value, texData);
|
|
131
|
+
this.setTexData(startIndex + 31, uTroikaBlurRadius.value, texData);
|
|
229
132
|
} else {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
`;
|
|
133
|
+
this.setTexData(startIndex + 28, uTroikaStrokeWidth.value, texData);
|
|
134
|
+
this.setTexData(startIndex + 29, tempColor.set(uTroikaStrokeColor.value).getHex(), texData);
|
|
135
|
+
this.setTexData(startIndex + 30, uTroikaStrokeOpacity.value, texData);
|
|
234
136
|
}
|
|
235
137
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
getVarying() {
|
|
239
|
-
const uniforms = this.uniformMap;
|
|
240
|
-
let declareVarying = "";
|
|
241
|
-
let assignVarying = "";
|
|
242
|
-
let getVarying = "";
|
|
243
|
-
for (const [name, { type }] of uniforms) {
|
|
244
|
-
declareVarying += /*glsl*/
|
|
245
|
-
`flat varying ${type} ${this.uniformPrefix}${name};
|
|
246
|
-
`;
|
|
247
|
-
assignVarying += /*glsl*/
|
|
248
|
-
`${this.uniformPrefix}${name} = ${name};
|
|
249
|
-
`;
|
|
250
|
-
getVarying += /*glsl*/
|
|
251
|
-
`${type} ${name} = ${this.uniformPrefix}${name};
|
|
252
|
-
`;
|
|
253
|
-
}
|
|
254
|
-
return { declareVarying, assignVarying, getVarying };
|
|
138
|
+
texture.needsUpdate = this.textureNeedsUpdate;
|
|
139
|
+
material.setMatrixTexture(texture);
|
|
255
140
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
components += componentsArray[startIndex + i];
|
|
141
|
+
setTexData(index, value, texData) {
|
|
142
|
+
if (value !== texData[index]) {
|
|
143
|
+
texData[index] = value;
|
|
144
|
+
this.textureNeedsUpdate = true;
|
|
261
145
|
}
|
|
262
|
-
return components;
|
|
263
146
|
}
|
|
264
147
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
this.
|
|
148
|
+
class Text extends Text$1 {
|
|
149
|
+
_prepareForRender(material) {
|
|
150
|
+
const isOutline = material.isTextOutlineMaterial;
|
|
151
|
+
const uniforms = material.uniforms;
|
|
152
|
+
const textInfo = this.textRenderInfo;
|
|
153
|
+
if (textInfo) {
|
|
154
|
+
const { sdfTexture, blockBounds } = textInfo;
|
|
155
|
+
const { width, height } = sdfTexture.image;
|
|
156
|
+
uniforms.uTroikaSDFTexture.value = sdfTexture;
|
|
157
|
+
uniforms.uTroikaSDFTextureSize.value.set(width, height);
|
|
158
|
+
uniforms.uTroikaSDFGlyphSize.value = textInfo.sdfGlyphSize;
|
|
159
|
+
uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent;
|
|
160
|
+
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds);
|
|
161
|
+
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors;
|
|
162
|
+
let distanceOffset = 0;
|
|
163
|
+
let blurRadius = 0;
|
|
164
|
+
let strokeWidth = 0;
|
|
165
|
+
let fillOpacity;
|
|
166
|
+
let strokeOpacity = 1;
|
|
167
|
+
let strokeColor;
|
|
168
|
+
let offsetX = 0;
|
|
169
|
+
let offsetY = 0;
|
|
170
|
+
if (isOutline) {
|
|
171
|
+
const { outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity } = this;
|
|
172
|
+
distanceOffset = this._parsePercent(outlineWidth) || 0;
|
|
173
|
+
blurRadius = Math.max(0, this._parsePercent(outlineBlur) || 0);
|
|
174
|
+
fillOpacity = outlineOpacity;
|
|
175
|
+
offsetX = this._parsePercent(outlineOffsetX) || 0;
|
|
176
|
+
offsetY = this._parsePercent(outlineOffsetY) || 0;
|
|
177
|
+
} else {
|
|
178
|
+
strokeWidth = Math.max(0, this._parsePercent(this.strokeWidth) || 0);
|
|
179
|
+
if (strokeWidth) {
|
|
180
|
+
strokeColor = this.strokeColor;
|
|
181
|
+
uniforms.uTroikaStrokeColor.value.set(strokeColor ?? defaultStrokeColor);
|
|
182
|
+
strokeOpacity = this.strokeOpacity;
|
|
183
|
+
strokeOpacity ?? (strokeOpacity = 1);
|
|
184
|
+
}
|
|
185
|
+
fillOpacity = this.fillOpacity;
|
|
294
186
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (this.useMultiDraw) return super.addGeometry(geometry, reservedVertexRange, reservedIndexRange);
|
|
316
|
-
this.addBatchIdBuffer(geometry, this.instanceCount);
|
|
317
|
-
const geometryId = super.addGeometry(geometry, reservedVertexRange, reservedIndexRange);
|
|
318
|
-
this.geometryById.set(geometryId, geometry);
|
|
319
|
-
return geometryId;
|
|
320
|
-
}
|
|
321
|
-
addInstance(geometryId) {
|
|
322
|
-
if (this.useMultiDraw) return super.addInstance(geometryId);
|
|
323
|
-
if (this.mapGeometryToInstanceId.has(geometryId)) {
|
|
324
|
-
const geometry = this.geometryById.get(geometryId);
|
|
325
|
-
this.resizeToFitGeometry(geometry);
|
|
326
|
-
geometryId = this.addGeometry(geometry);
|
|
187
|
+
uniforms.uTroikaEdgeOffset.value = distanceOffset;
|
|
188
|
+
uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY);
|
|
189
|
+
uniforms.uTroikaBlurRadius.value = blurRadius;
|
|
190
|
+
uniforms.uTroikaStrokeWidth.value = strokeWidth;
|
|
191
|
+
uniforms.uTroikaStrokeOpacity.value = strokeOpacity;
|
|
192
|
+
uniforms.uTroikaFillOpacity.value = fillOpacity ?? 1;
|
|
193
|
+
uniforms.uTroikaCurveRadius.value = this.curveRadius || 0;
|
|
194
|
+
const clipRect = this.clipRect;
|
|
195
|
+
if (clipRect && Array.isArray(clipRect) && clipRect.length === 4) {
|
|
196
|
+
uniforms.uTroikaClipRect.value.fromArray(clipRect);
|
|
197
|
+
} else {
|
|
198
|
+
const pad = (this.fontSize || 0.1) * 100;
|
|
199
|
+
uniforms.uTroikaClipRect.value.set(
|
|
200
|
+
blockBounds[0] - pad,
|
|
201
|
+
blockBounds[1] - pad,
|
|
202
|
+
blockBounds[2] + pad,
|
|
203
|
+
blockBounds[3] + pad
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
|
|
327
207
|
}
|
|
328
|
-
|
|
329
|
-
this.
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
208
|
+
uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
|
|
209
|
+
material.polygonOffset = !!this.depthOffset;
|
|
210
|
+
material.polygonOffsetFactor = material.polygonOffsetUnits = this.depthOffset || 0;
|
|
211
|
+
const color = isOutline ? this.outlineColor || 0 : this.color;
|
|
212
|
+
if (color == null) {
|
|
213
|
+
delete material.color;
|
|
214
|
+
} else {
|
|
215
|
+
const colorObj = material.hasOwnProperty("color") ? material.color : material.color = new Color();
|
|
216
|
+
if (color !== colorObj._input || typeof color === "object") {
|
|
217
|
+
colorObj.set(colorObj._input = color);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
let orient = this.orientation || defaultOrient;
|
|
221
|
+
if (orient !== material._orientation) {
|
|
222
|
+
const rotMat = uniforms.uTroikaOrient.value;
|
|
223
|
+
orient = orient.replace(/[^-+xyz]/g, "");
|
|
224
|
+
const match = orient !== defaultOrient && /^([-+])([xyz])([-+])([xyz])$/.exec(orient);
|
|
225
|
+
if (match) {
|
|
226
|
+
const [, hSign, hAxis, vSign, vAxis] = match;
|
|
227
|
+
tempVec3a.set(0, 0, 0)[hAxis] = hSign === "-" ? 1 : -1;
|
|
228
|
+
tempVec3b.set(0, 0, 0)[vAxis] = vSign === "-" ? -1 : 1;
|
|
229
|
+
tempMat4.lookAt(origin, tempVec3a.cross(tempVec3b), tempVec3b);
|
|
230
|
+
rotMat.setFromMatrix4(tempMat4);
|
|
231
|
+
} else {
|
|
232
|
+
rotMat.identity();
|
|
233
|
+
}
|
|
234
|
+
material._orientation = orient;
|
|
340
235
|
}
|
|
341
|
-
super.onBeforeRender(renderer, scene, camera, geometry, material, group);
|
|
342
|
-
this.batchCount = this.updateIndexBuffer(geometry);
|
|
343
|
-
this._multiDrawCount = 0;
|
|
344
236
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
geometry.setIndex(null);
|
|
237
|
+
}
|
|
238
|
+
function setDimming(root, dim) {
|
|
239
|
+
root.userData["uDim"] = dim === void 0 ? void 0 : +dim;
|
|
240
|
+
}
|
|
241
|
+
function toggleInstanceDim(object, instanceId, dim) {
|
|
242
|
+
const value = dim === void 0 ? 0 : (+dim - 0.5) * 2;
|
|
243
|
+
if (object instanceof BatchedMesh) {
|
|
244
|
+
object.setUniformAt(instanceId, "skipDimInstance", value);
|
|
245
|
+
return;
|
|
355
246
|
}
|
|
356
|
-
|
|
357
|
-
|
|
247
|
+
const skipDimTexture = object.userData["skipDimTexture"];
|
|
248
|
+
if (skipDimTexture) {
|
|
249
|
+
const skipDimData = skipDimTexture.image.data;
|
|
250
|
+
skipDimData[instanceId] = value;
|
|
251
|
+
skipDimTexture.needsUpdate = true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function addDimToMaterial(material) {
|
|
255
|
+
if (material.userData.hasDimShader) return;
|
|
256
|
+
const onBeforeCompile = material.onBeforeCompile.bind(material);
|
|
257
|
+
const onBeforeRender = material.onBeforeRender.bind(material);
|
|
258
|
+
material.onBeforeCompile = (shader, renderer) => {
|
|
259
|
+
onBeforeCompile(shader, renderer);
|
|
260
|
+
shader.uniforms["uDim"] = { value: material.userData.uDim ?? 0 };
|
|
261
|
+
shader.uniforms["skipDimTexture"] = { value: material.userData.skipDimTexture ?? null };
|
|
262
|
+
shader.vertexShader = shader.vertexShader.replace("void main() {", `${dimColorVertexDefs}
|
|
263
|
+
void main() {`).replace(
|
|
264
|
+
"#include <fog_vertex>",
|
|
265
|
+
/*glsl*/
|
|
266
|
+
`
|
|
267
|
+
#include <fog_vertex>
|
|
268
|
+
setDimAmount();
|
|
269
|
+
`
|
|
270
|
+
).concat(dimColorVertexImpl);
|
|
271
|
+
shader.fragmentShader = /*glsl*/
|
|
272
|
+
`
|
|
273
|
+
${dimColorFrag}
|
|
274
|
+
${shader.fragmentShader}
|
|
275
|
+
`.replace(
|
|
276
|
+
"#include <colorspace_fragment>",
|
|
277
|
+
/*glsl*/
|
|
278
|
+
`
|
|
279
|
+
gl_FragColor = dimColor(gl_FragColor);
|
|
280
|
+
#include <colorspace_fragment>
|
|
281
|
+
`
|
|
282
|
+
);
|
|
283
|
+
material.userData.shader = shader;
|
|
284
|
+
};
|
|
285
|
+
material.onBeforeRender = (renderer, scene, camera, geometry, object, group) => {
|
|
286
|
+
onBeforeRender(renderer, scene, camera, geometry, object, group);
|
|
287
|
+
const skipDimTexture = object.userData["skipDimTexture"];
|
|
288
|
+
let uDim = object.userData["uDim"];
|
|
289
|
+
if (uDim === void 0) {
|
|
290
|
+
for (const ancestor of traverseAncestorsGenerator(object)) {
|
|
291
|
+
if (ancestor.userData["uDim"] !== void 0) {
|
|
292
|
+
uDim = ancestor.userData["uDim"];
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const shader = material.userData.shader;
|
|
298
|
+
if (!shader) {
|
|
299
|
+
material.userData.uDim = uDim;
|
|
300
|
+
material.userData.skipDimTexture = object.userData["skipDimTexture"];
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
shader.uniforms["uDim"].value = uDim ?? 0;
|
|
304
|
+
shader.uniforms["skipDimTexture"].value = skipDimTexture ?? null;
|
|
305
|
+
};
|
|
306
|
+
material.userData.hasDimShader = true;
|
|
307
|
+
}
|
|
308
|
+
function addDim(mesh) {
|
|
309
|
+
if (mesh instanceof BatchedMesh) mesh.addPerInstanceUniforms({ vertex: { skipDimInstance: "float" } });
|
|
310
|
+
if (mesh instanceof BatchedText) addSkipDimTexture(mesh);
|
|
311
|
+
}
|
|
312
|
+
function addSkipDimTexture(text) {
|
|
313
|
+
const count = text.size;
|
|
314
|
+
const size = Math.ceil(Math.sqrt(count));
|
|
315
|
+
const array = new Float32Array(size * size);
|
|
316
|
+
array.fill(0);
|
|
317
|
+
const texture = new DataTexture(array, size, size, RedFormat, FloatType);
|
|
318
|
+
texture.needsUpdate = true;
|
|
319
|
+
text.userData["skipDimTexture"] = texture;
|
|
320
|
+
text.addEventListener("dispose", () => texture.dispose());
|
|
321
|
+
return texture;
|
|
322
|
+
}
|
|
323
|
+
const dimColorVertexDefs = (
|
|
324
|
+
/*glsl*/
|
|
325
|
+
`
|
|
326
|
+
uniform float uDim;
|
|
327
|
+
out float dimAmount;
|
|
328
|
+
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
329
|
+
uniform sampler2D skipDimTexture;
|
|
330
|
+
#endif
|
|
331
|
+
void setDimAmount();
|
|
332
|
+
`
|
|
333
|
+
);
|
|
334
|
+
const dimColorVertexImpl = (
|
|
335
|
+
/*glsl*/
|
|
336
|
+
`
|
|
337
|
+
void setDimAmount() {
|
|
338
|
+
float instanceDim = 0.;
|
|
339
|
+
#ifdef USE_BATCH_UNIFORMS
|
|
340
|
+
instanceDim = batch_skipDimInstance;
|
|
341
|
+
#endif
|
|
342
|
+
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
343
|
+
float indirectIndex = aTroikaTextBatchMemberIndex;
|
|
344
|
+
int size = textureSize(skipDimTexture, 0).x;
|
|
345
|
+
int i = int(indirectIndex);
|
|
346
|
+
int x = i % size;
|
|
347
|
+
int y = i / size;
|
|
348
|
+
instanceDim = texelFetch(skipDimTexture, ivec2(x, y), 0).r;
|
|
349
|
+
#endif
|
|
350
|
+
dimAmount = instanceDim == 0. ? uDim : instanceDim / 2. + 0.5;
|
|
351
|
+
}
|
|
352
|
+
`
|
|
353
|
+
);
|
|
354
|
+
const dimColorFrag = (
|
|
355
|
+
/*glsl*/
|
|
356
|
+
`
|
|
357
|
+
in float dimAmount;
|
|
358
|
+
|
|
359
|
+
const vec3 grayWeights = vec3(0.299, 0.587, 0.114);
|
|
360
|
+
const float darkenFactor = pow(2., 2.2); // Gamma corrected
|
|
361
|
+
|
|
362
|
+
vec4 dimColor(vec4 col) {
|
|
363
|
+
vec3 color = col.rgb / col.a;
|
|
364
|
+
vec3 gray = vec3(dot(grayWeights, color));
|
|
365
|
+
vec3 m = mix(color, gray / darkenFactor, dimAmount);
|
|
366
|
+
return vec4(m * col.a, col.a);
|
|
367
|
+
}`
|
|
368
|
+
);
|
|
369
|
+
function isObject(item) {
|
|
370
|
+
return !!item && typeof item === "object" && !Array.isArray(item);
|
|
371
|
+
}
|
|
372
|
+
function deepMerge(target, ...sources) {
|
|
373
|
+
if (!sources.length) return target;
|
|
374
|
+
const source = sources.shift();
|
|
375
|
+
if (source === void 0) {
|
|
376
|
+
return target;
|
|
377
|
+
}
|
|
378
|
+
if (isObject(target) && isObject(source)) {
|
|
379
|
+
for (const key in source) {
|
|
380
|
+
if (isObject(source[key])) {
|
|
381
|
+
if (!target[key]) {
|
|
382
|
+
Object.assign(target, { [key]: {} });
|
|
383
|
+
}
|
|
384
|
+
deepMerge(target[key], source[key]);
|
|
385
|
+
} else {
|
|
386
|
+
Object.assign(target, { [key]: source[key] });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return deepMerge(target, ...sources);
|
|
391
|
+
}
|
|
392
|
+
function getSquareTextureSize(capacity, pixelsPerInstance) {
|
|
393
|
+
return Math.max(pixelsPerInstance, Math.ceil(Math.sqrt(capacity / pixelsPerInstance)) * pixelsPerInstance);
|
|
394
|
+
}
|
|
395
|
+
function getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity) {
|
|
396
|
+
if (channels === 3) {
|
|
397
|
+
console.warn('"channels" cannot be 3. Set to 4. More info: https://github.com/mrdoob/three.js/pull/23228');
|
|
398
|
+
channels = 4;
|
|
399
|
+
}
|
|
400
|
+
const size = getSquareTextureSize(capacity, pixelsPerInstance);
|
|
401
|
+
const array = new arrayType(size * size * channels);
|
|
402
|
+
const isFloat = arrayType.name.includes("Float");
|
|
403
|
+
const isUnsignedInt = arrayType.name.includes("Uint");
|
|
404
|
+
const type = isFloat ? FloatType : isUnsignedInt ? UnsignedIntType : IntType;
|
|
405
|
+
let format;
|
|
406
|
+
switch (channels) {
|
|
407
|
+
case 1:
|
|
408
|
+
format = isFloat ? RedFormat : RedIntegerFormat;
|
|
409
|
+
break;
|
|
410
|
+
case 2:
|
|
411
|
+
format = isFloat ? RGFormat : RGIntegerFormat;
|
|
412
|
+
break;
|
|
413
|
+
case 4:
|
|
414
|
+
format = isFloat ? RGBAFormat : RGBAIntegerFormat;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
return { array, size, type, format };
|
|
418
|
+
}
|
|
419
|
+
class SquareDataTexture extends DataTexture {
|
|
420
|
+
/**
|
|
421
|
+
* @param arrayType The constructor for the TypedArray.
|
|
422
|
+
* @param channels The number of channels in the texture.
|
|
423
|
+
* @param pixelsPerInstance The number of pixels required for each instance.
|
|
424
|
+
* @param capacity The total number of instances.
|
|
425
|
+
* @param uniformMap Optional map for handling uniform values.
|
|
426
|
+
* @param fetchInFragmentShader Optional flag that determines if uniform values should be fetched in the fragment shader instead of the vertex shader.
|
|
427
|
+
*/
|
|
428
|
+
constructor(arrayType, channels, pixelsPerInstance, capacity, uniformMap, fetchInFragmentShader) {
|
|
429
|
+
if (channels === 3) channels = 4;
|
|
430
|
+
const { array, format, size, type } = getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity);
|
|
431
|
+
super(array, size, size, format, type);
|
|
432
|
+
__publicField(this, "data");
|
|
433
|
+
__publicField(this, "channels");
|
|
434
|
+
__publicField(this, "pixelsPerInstance");
|
|
435
|
+
__publicField(this, "stride");
|
|
436
|
+
__publicField(this, "uniformMap");
|
|
437
|
+
__publicField(this, "fetchUniformsInFragmentShader");
|
|
438
|
+
__publicField(this, "uniformPrefix", "batch_");
|
|
439
|
+
this.data = array;
|
|
440
|
+
this.channels = channels;
|
|
441
|
+
this.pixelsPerInstance = pixelsPerInstance;
|
|
442
|
+
this.stride = pixelsPerInstance * channels;
|
|
443
|
+
this.uniformMap = uniformMap;
|
|
444
|
+
this.fetchUniformsInFragmentShader = fetchInFragmentShader;
|
|
445
|
+
this.needsUpdate = true;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Sets a uniform value at the specified instance ID in the texture.
|
|
449
|
+
* @param id The instance ID to set the uniform for.
|
|
450
|
+
* @param name The name of the uniform.
|
|
451
|
+
* @param value The value to set for the uniform.
|
|
452
|
+
*/
|
|
453
|
+
setUniformAt(id, name, value) {
|
|
454
|
+
const schema = this.uniformMap.get(name);
|
|
455
|
+
if (!schema) {
|
|
456
|
+
console.warn(`SquareDataTexture.setUniformAt: uniform ${name} not found`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const { offset, size } = schema;
|
|
460
|
+
const stride = this.stride;
|
|
461
|
+
if (size === 1) {
|
|
462
|
+
this.data[id * stride + offset] = value;
|
|
463
|
+
} else {
|
|
464
|
+
value.toArray(this.data, id * stride + offset);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Retrieves a uniform value at the specified instance ID from the texture.
|
|
469
|
+
* @param id The instance ID to retrieve the uniform from.
|
|
470
|
+
* @param name The name of the uniform.
|
|
471
|
+
* @param target Optional target object to store the uniform value.
|
|
472
|
+
* @returns The uniform value for the specified instance.
|
|
473
|
+
*/
|
|
474
|
+
getUniformAt(id, name, target) {
|
|
475
|
+
const schema = this.uniformMap.get(name);
|
|
476
|
+
if (!schema) {
|
|
477
|
+
console.warn(`SquareDataTexture.getUniformAt: uniform ${name} not found`);
|
|
478
|
+
return 0;
|
|
479
|
+
}
|
|
480
|
+
const { offset, size } = schema;
|
|
481
|
+
const stride = this.stride;
|
|
482
|
+
if (size === 1) {
|
|
483
|
+
return this.data[id * stride + offset];
|
|
484
|
+
}
|
|
485
|
+
return target.fromArray(this.data, id * stride + offset);
|
|
486
|
+
}
|
|
487
|
+
/** Mark the texture as needing an update. */
|
|
488
|
+
update() {
|
|
489
|
+
this.needsUpdate = true;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Generates the GLSL code for accessing the uniform data stored in the texture.
|
|
493
|
+
* @param textureName The name of the texture in the GLSL shader.
|
|
494
|
+
* @param indexName The name of the index in the GLSL shader.
|
|
495
|
+
* @param indexType The type of the index in the GLSL shader.
|
|
496
|
+
* @returns An object containing the GLSL code for the vertex and fragment shaders.
|
|
497
|
+
*/
|
|
498
|
+
getUniformsGLSL(textureName, indexName, indexType) {
|
|
499
|
+
const vertex = this.getUniformsVertexGLSL(textureName, indexName, indexType);
|
|
500
|
+
const fragment = this.getUniformsFragmentGLSL(textureName, indexName, indexType);
|
|
501
|
+
return { vertex, fragment };
|
|
502
|
+
}
|
|
503
|
+
getUniformsVertexGLSL(textureName, indexName, indexType) {
|
|
504
|
+
if (this.fetchUniformsInFragmentShader) {
|
|
505
|
+
return (
|
|
506
|
+
/*glsl*/
|
|
507
|
+
`
|
|
508
|
+
varying ${indexType} ${this.uniformPrefix}${indexName};
|
|
509
|
+
void main() {
|
|
510
|
+
${this.uniformPrefix}${indexName} = ${indexName};
|
|
511
|
+
`
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
const texelsFetch = this.texelsFetchGLSL(textureName, indexName);
|
|
515
|
+
const getFromTexels = this.getFromTexelsGLSL();
|
|
516
|
+
const { assignVarying, declareVarying } = this.getVarying();
|
|
517
|
+
return (
|
|
518
|
+
/*glsl*/
|
|
519
|
+
`
|
|
520
|
+
uniform highp sampler2D ${textureName};
|
|
521
|
+
${declareVarying}
|
|
522
|
+
void main() {
|
|
523
|
+
#ifdef USE_BATCHING
|
|
524
|
+
${indexType} ${indexName} = ${indexType}(getIndirectIndex(gl_DrawID));
|
|
525
|
+
#endif
|
|
526
|
+
${texelsFetch}
|
|
527
|
+
${getFromTexels}
|
|
528
|
+
${assignVarying}`
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
getUniformsFragmentGLSL(textureName, indexName, indexType) {
|
|
532
|
+
if (!this.fetchUniformsInFragmentShader) {
|
|
533
|
+
const { declareVarying, getVarying } = this.getVarying();
|
|
534
|
+
return (
|
|
535
|
+
/*glsl*/
|
|
536
|
+
`
|
|
537
|
+
${declareVarying}
|
|
538
|
+
void main() {
|
|
539
|
+
${getVarying}`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const texelsFetch = this.texelsFetchGLSL(textureName, `${this.uniformPrefix}${indexName}`);
|
|
543
|
+
const getFromTexels = this.getFromTexelsGLSL();
|
|
544
|
+
return (
|
|
545
|
+
/*glsl*/
|
|
546
|
+
`
|
|
547
|
+
uniform highp sampler2D ${textureName};
|
|
548
|
+
varying ${indexType} ${this.uniformPrefix}${indexName};
|
|
549
|
+
void main() {
|
|
550
|
+
${texelsFetch}
|
|
551
|
+
${getFromTexels}`
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
texelsFetchGLSL(textureName, indexName) {
|
|
555
|
+
const pixelsPerInstance = this.pixelsPerInstance;
|
|
556
|
+
let texelsFetch = (
|
|
557
|
+
/*glsl*/
|
|
558
|
+
`
|
|
559
|
+
int size = textureSize(${textureName}, 0).x;
|
|
560
|
+
int j = int(${indexName}) * ${pixelsPerInstance};
|
|
561
|
+
int x = j % size;
|
|
562
|
+
int y = j / size;
|
|
563
|
+
`
|
|
564
|
+
);
|
|
565
|
+
for (let i = 0; i < pixelsPerInstance; i++) {
|
|
566
|
+
texelsFetch += /*glsl*/
|
|
567
|
+
`vec4 ${this.uniformPrefix}texel${i} = texelFetch(${textureName}, ivec2(x + ${i}, y), 0);
|
|
568
|
+
`;
|
|
569
|
+
}
|
|
570
|
+
return texelsFetch;
|
|
571
|
+
}
|
|
572
|
+
getFromTexelsGLSL() {
|
|
573
|
+
const uniforms = this.uniformMap;
|
|
574
|
+
let getFromTexels = "";
|
|
575
|
+
for (const [name, { type, offset, size }] of uniforms) {
|
|
576
|
+
const tId = Math.floor(offset / this.channels);
|
|
577
|
+
if (type === "mat3") {
|
|
578
|
+
getFromTexels += /*glsl*/
|
|
579
|
+
`mat3 ${name} = mat3(${this.uniformPrefix}texel${tId}.rgb, vec3(${this.uniformPrefix}texel${tId}.a, ${this.uniformPrefix}texel${tId + 1}.rg), vec3(${this.uniformPrefix}texel${tId + 1}.ba, ${this.uniformPrefix}texel${tId + 2}.r));
|
|
580
|
+
`;
|
|
581
|
+
} else if (type === "mat4") {
|
|
582
|
+
getFromTexels += /*glsl*/
|
|
583
|
+
`mat4 ${name} = mat4(${this.uniformPrefix}texel${tId}, ${this.uniformPrefix}texel${tId + 1}, ${this.uniformPrefix}texel${tId + 2}, ${this.uniformPrefix}texel${tId + 3});
|
|
584
|
+
`;
|
|
585
|
+
} else {
|
|
586
|
+
const components = this.getUniformComponents(offset, size);
|
|
587
|
+
getFromTexels += /*glsl*/
|
|
588
|
+
`${type} ${name} = ${this.uniformPrefix}texel${tId}.${components};
|
|
589
|
+
`;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return getFromTexels;
|
|
593
|
+
}
|
|
594
|
+
getVarying() {
|
|
595
|
+
const uniforms = this.uniformMap;
|
|
596
|
+
let declareVarying = "";
|
|
597
|
+
let assignVarying = "";
|
|
598
|
+
let getVarying = "";
|
|
599
|
+
for (const [name, { type }] of uniforms) {
|
|
600
|
+
declareVarying += /*glsl*/
|
|
601
|
+
`varying ${type} ${this.uniformPrefix}${name};
|
|
602
|
+
`;
|
|
603
|
+
assignVarying += /*glsl*/
|
|
604
|
+
`${this.uniformPrefix}${name} = ${name};
|
|
605
|
+
`;
|
|
606
|
+
getVarying += /*glsl*/
|
|
607
|
+
`${type} ${name} = ${this.uniformPrefix}${name};
|
|
608
|
+
`;
|
|
609
|
+
}
|
|
610
|
+
return { declareVarying, assignVarying, getVarying };
|
|
611
|
+
}
|
|
612
|
+
getUniformComponents(offset, size) {
|
|
613
|
+
const startIndex = offset % this.channels;
|
|
614
|
+
let components = "";
|
|
615
|
+
for (let i = 0; i < size; i++) {
|
|
616
|
+
components += componentsArray[startIndex + i];
|
|
617
|
+
}
|
|
618
|
+
return components;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
const componentsArray = ["r", "g", "b", "a"];
|
|
622
|
+
const batchIdName = "batchId";
|
|
623
|
+
const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
624
|
+
/**
|
|
625
|
+
* @param instanceCount the max number of individual geometries planned to be added.
|
|
626
|
+
* @param vertexCount the max number of vertices to be used by all geometries.
|
|
627
|
+
* @param indexCount the max number of indices to be used by all geometries.
|
|
628
|
+
* @param material an instance of {@link Material}. Default is a new {@link MeshBasicMaterial}.
|
|
629
|
+
*/
|
|
630
|
+
constructor(instanceCount, vertexCount, indexCount, material) {
|
|
631
|
+
super(instanceCount, vertexCount, indexCount, material);
|
|
632
|
+
__publicField(this, "uniformsTexture");
|
|
633
|
+
__publicField(this, "uniformSchema", {});
|
|
634
|
+
__publicField(this, "isMaterialPatched", false);
|
|
635
|
+
__publicField(this, "boundsNeedsUpdate", false);
|
|
636
|
+
__publicField(this, "batchCount", 0);
|
|
637
|
+
__publicField(this, "indexBuffer");
|
|
638
|
+
__publicField(this, "geometryById", /* @__PURE__ */ new Map());
|
|
639
|
+
__publicField(this, "mapGeometryToInstanceId", /* @__PURE__ */ new Map());
|
|
640
|
+
material.forceSinglePass = true;
|
|
641
|
+
addDim(this);
|
|
642
|
+
this.addEventListener("added", () => {
|
|
643
|
+
if (!_BatchedMesh.useMultiDraw && this.geometry.index !== null) {
|
|
644
|
+
this.geometry = this.geometry.toNonIndexed();
|
|
645
|
+
this.resizeToFitGeometry(this.geometry);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Appends uniform definitions to the current schema, and creates a new {@link SquareDataTexture} if needed.
|
|
651
|
+
* @param schema description of per-instance uniforms by shader stage (vertex/fragment)
|
|
652
|
+
*/
|
|
653
|
+
addPerInstanceUniforms(schema) {
|
|
654
|
+
this.uniformSchema = deepMerge(this.uniformSchema, schema);
|
|
655
|
+
const parsedSchema = this.parseUniformSchema(this.uniformSchema);
|
|
656
|
+
if (this.uniformsTexture) this.uniformsTexture.dispose();
|
|
657
|
+
this.uniformsTexture = new SquareDataTexture(
|
|
658
|
+
Float32Array,
|
|
659
|
+
parsedSchema.channels,
|
|
660
|
+
parsedSchema.texelsPerInstance,
|
|
661
|
+
this.maxInstanceCount,
|
|
662
|
+
parsedSchema.uniformMap,
|
|
663
|
+
parsedSchema.fetchInFragmentShader
|
|
664
|
+
);
|
|
665
|
+
if (!this.isMaterialPatched) this.patchMaterial(this.material);
|
|
666
|
+
}
|
|
667
|
+
addGeometry(geometry, reservedVertexRange, reservedIndexRange) {
|
|
668
|
+
if (_BatchedMesh.useMultiDraw) return super.addGeometry(geometry, reservedVertexRange, reservedIndexRange);
|
|
669
|
+
this.addBatchIdBuffer(geometry, this.instanceCount);
|
|
670
|
+
const geometryId = super.addGeometry(geometry, reservedVertexRange, reservedIndexRange);
|
|
671
|
+
this.geometryById.set(geometryId, geometry);
|
|
672
|
+
return geometryId;
|
|
673
|
+
}
|
|
674
|
+
addInstance(geometryId) {
|
|
675
|
+
if (_BatchedMesh.useMultiDraw) return super.addInstance(geometryId);
|
|
676
|
+
if (this.mapGeometryToInstanceId.has(geometryId)) {
|
|
677
|
+
const geometry = this.geometryById.get(geometryId);
|
|
678
|
+
this.resizeToFitGeometry(geometry);
|
|
679
|
+
geometryId = this.addGeometry(geometry);
|
|
680
|
+
}
|
|
681
|
+
const instanceId = super.addInstance(geometryId);
|
|
682
|
+
this.mapGeometryToInstanceId.set(geometryId, instanceId);
|
|
683
|
+
return instanceId;
|
|
684
|
+
}
|
|
685
|
+
onBeforeRender(renderer, scene, camera, geometry, material, group) {
|
|
686
|
+
var _a2;
|
|
687
|
+
(_a2 = this.uniformsTexture) == null ? void 0 : _a2.update();
|
|
688
|
+
if (_BatchedMesh.useMultiDraw) return super.onBeforeRender(renderer, scene, camera, geometry, material, group);
|
|
689
|
+
if (!this.indexBuffer) {
|
|
690
|
+
const vertexCount = geometry.getAttribute("position").count;
|
|
691
|
+
this.indexBuffer = new BufferAttribute(new Uint32Array(vertexCount), 1).setUsage(StreamDrawUsage);
|
|
692
|
+
}
|
|
693
|
+
super.onBeforeRender(renderer, scene, camera, geometry, material, group);
|
|
694
|
+
this.batchCount = this.updateIndexBuffer(geometry);
|
|
695
|
+
this._multiDrawCount = 0;
|
|
696
|
+
}
|
|
697
|
+
onAfterRender(renderer, scene, camera, geometry) {
|
|
698
|
+
var _a2;
|
|
699
|
+
if (_BatchedMesh.useMultiDraw) return;
|
|
700
|
+
const batchCount = this.batchCount;
|
|
701
|
+
const gl = renderer.getContext();
|
|
702
|
+
if (geometry.index == null) return console.warn("No index buffer", (_a2 = this.parent) == null ? void 0 : _a2.name);
|
|
703
|
+
const type = this.getIndexType(gl, geometry.index);
|
|
704
|
+
gl.drawElements(gl.TRIANGLES, batchCount, type, 0);
|
|
705
|
+
renderer.info.update(batchCount, gl.TRIANGLES, 1);
|
|
706
|
+
geometry.setIndex(null);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Retrieves the value of a uniform at the specified instance ID.
|
|
358
710
|
* @param id instance ID
|
|
359
711
|
* @param name uniform name
|
|
360
712
|
* @param target Optional target object to store the uniform value
|
|
@@ -388,530 +740,179 @@ class BatchedMesh extends BatchedMesh$1 {
|
|
|
388
740
|
this.boundsNeedsUpdate = false;
|
|
389
741
|
}
|
|
390
742
|
}
|
|
391
|
-
setMatrixAt(instanceId, matrix) {
|
|
392
|
-
super.setMatrixAt(instanceId, matrix);
|
|
393
|
-
this.boundsNeedsUpdate = true;
|
|
394
|
-
return this;
|
|
395
|
-
}
|
|
396
|
-
dispose() {
|
|
397
|
-
var _a2;
|
|
398
|
-
this.geometry.setIndex(this.indexBuffer ?? null);
|
|
399
|
-
super.dispose();
|
|
400
|
-
this.uniformSchema = {};
|
|
401
|
-
(_a2 = this.uniformsTexture) == null ? void 0 : _a2.dispose();
|
|
402
|
-
this.uniformsTexture = void 0;
|
|
403
|
-
this.indexBuffer = void 0;
|
|
404
|
-
this.geometryById.forEach((geometry) => geometry.dispose());
|
|
405
|
-
this.geometryById.clear();
|
|
406
|
-
this.mapGeometryToInstanceId.clear();
|
|
407
|
-
return this;
|
|
408
|
-
}
|
|
409
|
-
resizeToFitGeometry(geometry) {
|
|
410
|
-
var _a2;
|
|
411
|
-
const vertexCount = geometry.attributes["position"].count;
|
|
412
|
-
const indexCount = ((_a2 = geometry.index) == null ? void 0 : _a2.count) ?? 0;
|
|
413
|
-
this._maxVertexCount += vertexCount;
|
|
414
|
-
this._maxIndexCount += indexCount;
|
|
415
|
-
this.setGeometrySize(this._maxVertexCount, this._maxIndexCount);
|
|
416
|
-
}
|
|
417
|
-
updateIndexBuffer(geometry) {
|
|
418
|
-
const { _multiDrawStarts, _multiDrawCounts, _multiDrawCount } = this;
|
|
419
|
-
const indexBuffer = this.indexBuffer;
|
|
420
|
-
const indexArray = indexBuffer.array;
|
|
421
|
-
const batchIdBuffer = geometry.getAttribute(batchIdName);
|
|
422
|
-
const batchIdArray = batchIdBuffer.array;
|
|
423
|
-
const indirectArray = this._indirectTexture.image.data;
|
|
424
|
-
let indexCount = 0;
|
|
425
|
-
for (let i = 0; i < _multiDrawCount; i++) {
|
|
426
|
-
const start = _multiDrawStarts[i];
|
|
427
|
-
const count = _multiDrawCounts[i];
|
|
428
|
-
const batchId = batchIdArray[start];
|
|
429
|
-
indirectArray[batchId] = batchId;
|
|
430
|
-
for (let j = start; j < start + count; j++) {
|
|
431
|
-
indexArray[indexCount++] = j;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
indexBuffer.needsUpdate = true;
|
|
435
|
-
geometry.setIndex(indexBuffer);
|
|
436
|
-
return indexCount;
|
|
437
|
-
}
|
|
438
|
-
addBatchIdBuffer(geometry, geometryId) {
|
|
439
|
-
const hasAttribute = geometry.hasAttribute(batchIdName);
|
|
440
|
-
const vertexCount = geometry.getAttribute("position").count;
|
|
441
|
-
const batchIdArray = hasAttribute ? geometry.getAttribute(batchIdName).array : new Int32Array(vertexCount);
|
|
442
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
443
|
-
batchIdArray[i] = geometryId;
|
|
444
|
-
}
|
|
445
|
-
if (!hasAttribute) {
|
|
446
|
-
const batchIdBuffer = new BufferAttribute(batchIdArray, 1).setUsage(StreamDrawUsage);
|
|
447
|
-
geometry.setAttribute(batchIdName, batchIdBuffer);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
patchMaterial(material) {
|
|
451
|
-
const onBeforeCompile = material.onBeforeCompile.bind(material);
|
|
452
|
-
const customProgramCacheKey = material.customProgramCacheKey.bind(material);
|
|
453
|
-
material.onBeforeCompile = (shader, renderer) => {
|
|
454
|
-
var _a2;
|
|
455
|
-
onBeforeCompile(shader, renderer);
|
|
456
|
-
shader.defines ?? (shader.defines = {});
|
|
457
|
-
shader.defines["USE_BATCH_UNIFORMS"] = "";
|
|
458
|
-
shader.uniforms["uniformsTexture"] = { value: this.uniformsTexture };
|
|
459
|
-
const { vertex, fragment } = ((_a2 = this.uniformsTexture) == null ? void 0 : _a2.getUniformsGLSL("uniformsTexture", "instanceId", "int")) ?? {
|
|
460
|
-
vertex: "",
|
|
461
|
-
fragment: ""
|
|
462
|
-
};
|
|
463
|
-
const patch = (
|
|
464
|
-
/*glsl*/
|
|
465
|
-
`
|
|
466
|
-
#ifdef gl_DrawID
|
|
467
|
-
#define _gl_DrawID ${batchIdName}
|
|
468
|
-
#else
|
|
469
|
-
#define gl_DrawID ${batchIdName}
|
|
470
|
-
#endif
|
|
471
|
-
in int ${batchIdName};
|
|
472
|
-
`
|
|
473
|
-
);
|
|
474
|
-
const main = "void main() {";
|
|
475
|
-
if (!this.useMultiDraw) shader.vertexShader = shader.vertexShader.replace(main, `${patch}${main}`);
|
|
476
|
-
if (vertex) shader.vertexShader = shader.vertexShader.replace(main, vertex);
|
|
477
|
-
if (fragment) shader.fragmentShader = shader.fragmentShader.replace(main, fragment);
|
|
478
|
-
};
|
|
479
|
-
material.customProgramCacheKey = () => {
|
|
480
|
-
return `batch_${this.id}_${!!this.uniformsTexture}_${customProgramCacheKey()}`;
|
|
481
|
-
};
|
|
482
|
-
this.isMaterialPatched = true;
|
|
483
|
-
}
|
|
484
|
-
// Taken from https://github.com/agargaro/instanced-mesh/blob/master/src/core/feature/Uniforms.ts
|
|
485
|
-
parseUniformSchema(schema) {
|
|
486
|
-
let totalSize = 0;
|
|
487
|
-
const uniformMap = /* @__PURE__ */ new Map();
|
|
488
|
-
const uniforms = [];
|
|
489
|
-
const vertexSchema = schema.vertex ?? {};
|
|
490
|
-
const fragmentSchema = schema.fragment ?? {};
|
|
491
|
-
let fetchInFragmentShader = true;
|
|
492
|
-
for (const name in vertexSchema) {
|
|
493
|
-
const type = vertexSchema[name];
|
|
494
|
-
const size = this.getUniformSize(type);
|
|
495
|
-
totalSize += size;
|
|
496
|
-
uniforms.push({ name, type, size });
|
|
497
|
-
fetchInFragmentShader = false;
|
|
498
|
-
}
|
|
499
|
-
for (const name in fragmentSchema) {
|
|
500
|
-
if (!vertexSchema[name]) {
|
|
501
|
-
const type = fragmentSchema[name];
|
|
502
|
-
const size = this.getUniformSize(type);
|
|
503
|
-
totalSize += size;
|
|
504
|
-
uniforms.push({ name, type, size });
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
uniforms.sort((a, b) => b.size - a.size);
|
|
508
|
-
const tempOffset = [];
|
|
509
|
-
for (const { name, size, type } of uniforms) {
|
|
510
|
-
const offset = this.getUniformOffset(size, tempOffset);
|
|
511
|
-
uniformMap.set(name, { offset, size, type });
|
|
512
|
-
}
|
|
513
|
-
const pixelsPerInstance = Math.ceil(totalSize / 4);
|
|
514
|
-
const channels = Math.min(totalSize, 4);
|
|
515
|
-
return { channels, texelsPerInstance: pixelsPerInstance, uniformMap, fetchInFragmentShader };
|
|
516
|
-
}
|
|
517
|
-
getUniformOffset(size, tempOffset) {
|
|
518
|
-
if (size < 4) {
|
|
519
|
-
for (let i = 0; i < tempOffset.length; i++) {
|
|
520
|
-
if (tempOffset[i] + size <= 4) {
|
|
521
|
-
const offset2 = i * 4 + tempOffset[i];
|
|
522
|
-
tempOffset[i] += size;
|
|
523
|
-
return offset2;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
const offset = tempOffset.length * 4;
|
|
528
|
-
for (; size > 0; size -= 4) {
|
|
529
|
-
tempOffset.push(size);
|
|
530
|
-
}
|
|
531
|
-
return offset;
|
|
532
|
-
}
|
|
533
|
-
getUniformSize(type) {
|
|
534
|
-
switch (type) {
|
|
535
|
-
case "float":
|
|
536
|
-
return 1;
|
|
537
|
-
case "vec2":
|
|
538
|
-
return 2;
|
|
539
|
-
case "vec3":
|
|
540
|
-
return 3;
|
|
541
|
-
case "vec4":
|
|
542
|
-
return 4;
|
|
543
|
-
case "mat3":
|
|
544
|
-
return 9;
|
|
545
|
-
case "mat4":
|
|
546
|
-
return 16;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
getIndexType(gl, index) {
|
|
550
|
-
const array = index.array;
|
|
551
|
-
if (array instanceof Uint16Array) return gl.UNSIGNED_SHORT;
|
|
552
|
-
if (array instanceof Uint32Array) return gl.UNSIGNED_INT;
|
|
553
|
-
return gl.UNSIGNED_BYTE;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
const floatsPerMember = 32;
|
|
557
|
-
const tempColor = new Color();
|
|
558
|
-
const defaultStrokeColor = 8421504;
|
|
559
|
-
const tempMat4 = new Matrix4();
|
|
560
|
-
const tempVec3a = new Vector3();
|
|
561
|
-
const tempVec3b = new Vector3();
|
|
562
|
-
const origin = new Vector3();
|
|
563
|
-
const defaultOrient = "+x+y";
|
|
564
|
-
class BatchedText extends BatchedText$1 {
|
|
565
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
566
|
-
constructor() {
|
|
567
|
-
super();
|
|
568
|
-
__publicField(this, "mapInstanceIdToText", /* @__PURE__ */ new Map());
|
|
569
|
-
__publicField(this, "textArray", []);
|
|
570
|
-
__publicField(this, "textureNeedsUpdate", false);
|
|
571
|
-
}
|
|
572
|
-
/** Number of texts in the batch */
|
|
573
|
-
get size() {
|
|
574
|
-
return this._members.size;
|
|
575
|
-
}
|
|
576
|
-
/** Base material before patching */
|
|
577
|
-
get baseMaterial() {
|
|
578
|
-
return this._baseMaterial;
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* Get the {@link Text} object by instance id
|
|
582
|
-
* @param instanceId Instance id
|
|
583
|
-
* @returns Text object
|
|
584
|
-
*/
|
|
585
|
-
getText(instanceId) {
|
|
586
|
-
return this.mapInstanceIdToText.get(instanceId);
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Set the visibility of the {@link Text} object by instance id.
|
|
590
|
-
* This is for interface compatibility with {@link BatchedMesh}.
|
|
591
|
-
* @param instanceId Instance id
|
|
592
|
-
* @param visible Visibility flag
|
|
593
|
-
*/
|
|
594
|
-
setVisibleAt(instanceId, visible) {
|
|
595
|
-
const text = this.getText(instanceId);
|
|
596
|
-
text.visible = visible;
|
|
597
|
-
}
|
|
598
|
-
addText(text, instanceId) {
|
|
599
|
-
super.addText(text);
|
|
600
|
-
if (instanceId !== void 0) {
|
|
601
|
-
this.mapInstanceIdToText.set(instanceId, text);
|
|
602
|
-
}
|
|
603
|
-
this.textArray.push(text);
|
|
743
|
+
setMatrixAt(instanceId, matrix) {
|
|
744
|
+
super.setMatrixAt(instanceId, matrix);
|
|
745
|
+
this.boundsNeedsUpdate = true;
|
|
746
|
+
return this;
|
|
604
747
|
}
|
|
605
748
|
dispose() {
|
|
749
|
+
var _a2;
|
|
750
|
+
this.geometry.setIndex(this.indexBuffer ?? null);
|
|
606
751
|
super.dispose();
|
|
607
|
-
this.
|
|
752
|
+
this.uniformSchema = {};
|
|
753
|
+
(_a2 = this.uniformsTexture) == null ? void 0 : _a2.dispose();
|
|
754
|
+
this.uniformsTexture = void 0;
|
|
755
|
+
this.indexBuffer = void 0;
|
|
756
|
+
this.geometryById.forEach((geometry) => geometry.dispose());
|
|
757
|
+
this.geometryById.clear();
|
|
758
|
+
this.mapGeometryToInstanceId.clear();
|
|
759
|
+
return this;
|
|
608
760
|
}
|
|
609
|
-
|
|
761
|
+
resizeToFitGeometry(geometry) {
|
|
610
762
|
var _a2;
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (texture) texture.dispose();
|
|
617
|
-
const width = Math.min(dataLength / 4, 1024);
|
|
618
|
-
texture = this._dataTextures[isOutline ? "outline" : "main"] = new DataTexture(
|
|
619
|
-
new Float32Array(dataLength),
|
|
620
|
-
width,
|
|
621
|
-
dataLength / 4 / width,
|
|
622
|
-
RGBAFormat,
|
|
623
|
-
FloatType
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
const texData = texture.image.data;
|
|
627
|
-
this.textureNeedsUpdate = false;
|
|
628
|
-
for (const text of this.textArray) {
|
|
629
|
-
const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
|
|
630
|
-
const textRenderInfo = text.textRenderInfo;
|
|
631
|
-
if (index < 0 || !textRenderInfo) continue;
|
|
632
|
-
const startIndex = index * floatsPerMember;
|
|
633
|
-
if (!text.visible) {
|
|
634
|
-
for (let i = 0; i < 16; i++) {
|
|
635
|
-
this.setTexData(startIndex + i, 0, texData);
|
|
636
|
-
}
|
|
637
|
-
continue;
|
|
638
|
-
}
|
|
639
|
-
const matrix = text.matrix.elements;
|
|
640
|
-
for (let i = 0; i < 16; i++) {
|
|
641
|
-
this.setTexData(startIndex + i, matrix[i], texData);
|
|
642
|
-
}
|
|
643
|
-
text._prepareForRender(material);
|
|
644
|
-
const {
|
|
645
|
-
uTroikaTotalBounds,
|
|
646
|
-
uTroikaClipRect,
|
|
647
|
-
uTroikaPositionOffset,
|
|
648
|
-
uTroikaEdgeOffset,
|
|
649
|
-
uTroikaBlurRadius,
|
|
650
|
-
uTroikaStrokeWidth,
|
|
651
|
-
uTroikaStrokeColor,
|
|
652
|
-
uTroikaStrokeOpacity,
|
|
653
|
-
uTroikaFillOpacity,
|
|
654
|
-
uTroikaCurveRadius
|
|
655
|
-
} = material.uniforms;
|
|
656
|
-
for (let i = 0; i < 4; i++) {
|
|
657
|
-
this.setTexData(startIndex + 16 + i, uTroikaTotalBounds.value.getComponent(i), texData);
|
|
658
|
-
}
|
|
659
|
-
for (let i = 0; i < 4; i++) {
|
|
660
|
-
this.setTexData(startIndex + 20 + i, uTroikaClipRect.value.getComponent(i), texData);
|
|
661
|
-
}
|
|
662
|
-
let color = isOutline ? text.outlineColor || 0 : text.color;
|
|
663
|
-
color ?? (color = this.color);
|
|
664
|
-
color ?? (color = this.material.color);
|
|
665
|
-
color ?? (color = 16777215);
|
|
666
|
-
this.setTexData(startIndex + 24, tempColor.set(color).getHex(), texData);
|
|
667
|
-
this.setTexData(startIndex + 25, uTroikaFillOpacity.value, texData);
|
|
668
|
-
this.setTexData(startIndex + 26, uTroikaCurveRadius.value, texData);
|
|
669
|
-
if (isOutline) {
|
|
670
|
-
this.setTexData(startIndex + 28, uTroikaPositionOffset.value.x, texData);
|
|
671
|
-
this.setTexData(startIndex + 29, uTroikaPositionOffset.value.y, texData);
|
|
672
|
-
this.setTexData(startIndex + 30, uTroikaEdgeOffset.value, texData);
|
|
673
|
-
this.setTexData(startIndex + 31, uTroikaBlurRadius.value, texData);
|
|
674
|
-
} else {
|
|
675
|
-
this.setTexData(startIndex + 28, uTroikaStrokeWidth.value, texData);
|
|
676
|
-
this.setTexData(startIndex + 29, tempColor.set(uTroikaStrokeColor.value).getHex(), texData);
|
|
677
|
-
this.setTexData(startIndex + 30, uTroikaStrokeOpacity.value, texData);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
texture.needsUpdate = this.textureNeedsUpdate;
|
|
681
|
-
material.setMatrixTexture(texture);
|
|
763
|
+
const vertexCount = geometry.attributes["position"].count;
|
|
764
|
+
const indexCount = ((_a2 = geometry.index) == null ? void 0 : _a2.count) ?? 0;
|
|
765
|
+
this._maxVertexCount += vertexCount;
|
|
766
|
+
this._maxIndexCount += indexCount;
|
|
767
|
+
this.setGeometrySize(this._maxVertexCount, this._maxIndexCount);
|
|
682
768
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
769
|
+
updateIndexBuffer(geometry) {
|
|
770
|
+
const { _multiDrawStarts, _multiDrawCounts, _multiDrawCount } = this;
|
|
771
|
+
const indexBuffer = this.indexBuffer;
|
|
772
|
+
const indexArray = indexBuffer.array;
|
|
773
|
+
const batchIdBuffer = geometry.getAttribute(batchIdName);
|
|
774
|
+
const batchIdArray = batchIdBuffer.array;
|
|
775
|
+
const indirectArray = this._indirectTexture.image.data;
|
|
776
|
+
let indexCount = 0;
|
|
777
|
+
for (let i = 0; i < _multiDrawCount; i++) {
|
|
778
|
+
const start = _multiDrawStarts[i];
|
|
779
|
+
const count = _multiDrawCounts[i];
|
|
780
|
+
const batchId = batchIdArray[start];
|
|
781
|
+
indirectArray[batchId] = batchId;
|
|
782
|
+
for (let j = start; j < start + count; j++) {
|
|
783
|
+
indexArray[indexCount++] = j;
|
|
784
|
+
}
|
|
687
785
|
}
|
|
786
|
+
indexBuffer.needsUpdate = true;
|
|
787
|
+
geometry.setIndex(indexBuffer);
|
|
788
|
+
return indexCount;
|
|
688
789
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if (textInfo) {
|
|
696
|
-
const { sdfTexture, blockBounds } = textInfo;
|
|
697
|
-
const { width, height } = sdfTexture.image;
|
|
698
|
-
uniforms.uTroikaSDFTexture.value = sdfTexture;
|
|
699
|
-
uniforms.uTroikaSDFTextureSize.value.set(width, height);
|
|
700
|
-
uniforms.uTroikaSDFGlyphSize.value = textInfo.sdfGlyphSize;
|
|
701
|
-
uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent;
|
|
702
|
-
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds);
|
|
703
|
-
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors;
|
|
704
|
-
let distanceOffset = 0;
|
|
705
|
-
let blurRadius = 0;
|
|
706
|
-
let strokeWidth = 0;
|
|
707
|
-
let fillOpacity;
|
|
708
|
-
let strokeOpacity = 1;
|
|
709
|
-
let strokeColor;
|
|
710
|
-
let offsetX = 0;
|
|
711
|
-
let offsetY = 0;
|
|
712
|
-
if (isOutline) {
|
|
713
|
-
const { outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity } = this;
|
|
714
|
-
distanceOffset = this._parsePercent(outlineWidth) || 0;
|
|
715
|
-
blurRadius = Math.max(0, this._parsePercent(outlineBlur) || 0);
|
|
716
|
-
fillOpacity = outlineOpacity;
|
|
717
|
-
offsetX = this._parsePercent(outlineOffsetX) || 0;
|
|
718
|
-
offsetY = this._parsePercent(outlineOffsetY) || 0;
|
|
719
|
-
} else {
|
|
720
|
-
strokeWidth = Math.max(0, this._parsePercent(this.strokeWidth) || 0);
|
|
721
|
-
if (strokeWidth) {
|
|
722
|
-
strokeColor = this.strokeColor;
|
|
723
|
-
uniforms.uTroikaStrokeColor.value.set(strokeColor ?? defaultStrokeColor);
|
|
724
|
-
strokeOpacity = this.strokeOpacity;
|
|
725
|
-
strokeOpacity ?? (strokeOpacity = 1);
|
|
726
|
-
}
|
|
727
|
-
fillOpacity = this.fillOpacity;
|
|
728
|
-
}
|
|
729
|
-
uniforms.uTroikaEdgeOffset.value = distanceOffset;
|
|
730
|
-
uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY);
|
|
731
|
-
uniforms.uTroikaBlurRadius.value = blurRadius;
|
|
732
|
-
uniforms.uTroikaStrokeWidth.value = strokeWidth;
|
|
733
|
-
uniforms.uTroikaStrokeOpacity.value = strokeOpacity;
|
|
734
|
-
uniforms.uTroikaFillOpacity.value = fillOpacity ?? 1;
|
|
735
|
-
uniforms.uTroikaCurveRadius.value = this.curveRadius || 0;
|
|
736
|
-
const clipRect = this.clipRect;
|
|
737
|
-
if (clipRect && Array.isArray(clipRect) && clipRect.length === 4) {
|
|
738
|
-
uniforms.uTroikaClipRect.value.fromArray(clipRect);
|
|
739
|
-
} else {
|
|
740
|
-
const pad = (this.fontSize || 0.1) * 100;
|
|
741
|
-
uniforms.uTroikaClipRect.value.set(
|
|
742
|
-
blockBounds[0] - pad,
|
|
743
|
-
blockBounds[1] - pad,
|
|
744
|
-
blockBounds[2] + pad,
|
|
745
|
-
blockBounds[3] + pad
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
|
|
790
|
+
addBatchIdBuffer(geometry, geometryId) {
|
|
791
|
+
const hasAttribute = geometry.hasAttribute(batchIdName);
|
|
792
|
+
const vertexCount = geometry.getAttribute("position").count;
|
|
793
|
+
const batchIdArray = hasAttribute ? geometry.getAttribute(batchIdName).array : new Int32Array(vertexCount);
|
|
794
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
795
|
+
batchIdArray[i] = geometryId;
|
|
749
796
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const color = isOutline ? this.outlineColor || 0 : this.color;
|
|
754
|
-
if (color == null) {
|
|
755
|
-
delete material.color;
|
|
756
|
-
} else {
|
|
757
|
-
const colorObj = material.hasOwnProperty("color") ? material.color : material.color = new Color();
|
|
758
|
-
if (color !== colorObj._input || typeof color === "object") {
|
|
759
|
-
colorObj.set(colorObj._input = color);
|
|
760
|
-
}
|
|
797
|
+
if (!hasAttribute) {
|
|
798
|
+
const batchIdBuffer = new BufferAttribute(batchIdArray, 1).setUsage(StreamDrawUsage);
|
|
799
|
+
geometry.setAttribute(batchIdName, batchIdBuffer);
|
|
761
800
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
801
|
+
}
|
|
802
|
+
patchMaterial(material) {
|
|
803
|
+
const onBeforeCompile = material.onBeforeCompile.bind(material);
|
|
804
|
+
const customProgramCacheKey = material.customProgramCacheKey.bind(material);
|
|
805
|
+
material.onBeforeCompile = (shader, renderer) => {
|
|
806
|
+
var _a2;
|
|
807
|
+
onBeforeCompile(shader, renderer);
|
|
808
|
+
shader.defines ?? (shader.defines = {});
|
|
809
|
+
shader.defines["USE_BATCH_UNIFORMS"] = "";
|
|
810
|
+
shader.uniforms["uniformsTexture"] = { value: this.uniformsTexture };
|
|
811
|
+
const { vertex, fragment } = ((_a2 = this.uniformsTexture) == null ? void 0 : _a2.getUniformsGLSL("uniformsTexture", "instanceId", "int")) ?? {
|
|
812
|
+
vertex: "",
|
|
813
|
+
fragment: ""
|
|
814
|
+
};
|
|
815
|
+
const patch = (
|
|
816
|
+
/*glsl*/
|
|
817
|
+
`
|
|
818
|
+
#ifdef gl_DrawID
|
|
819
|
+
#define _gl_DrawID ${batchIdName}
|
|
820
|
+
#else
|
|
821
|
+
#define gl_DrawID ${batchIdName}
|
|
822
|
+
#endif
|
|
823
|
+
in int ${batchIdName};
|
|
824
|
+
`
|
|
825
|
+
);
|
|
826
|
+
const main = "void main() {";
|
|
827
|
+
if (!_BatchedMesh.useMultiDraw) shader.vertexShader = shader.vertexShader.replace(main, `${patch}${main}`);
|
|
828
|
+
if (vertex) shader.vertexShader = shader.vertexShader.replace(main, vertex);
|
|
829
|
+
if (fragment) shader.fragmentShader = shader.fragmentShader.replace(main, fragment);
|
|
830
|
+
};
|
|
831
|
+
material.customProgramCacheKey = () => {
|
|
832
|
+
return `batch_${this.id}_${!!this.uniformsTexture}_${customProgramCacheKey()}`;
|
|
833
|
+
};
|
|
834
|
+
this.isMaterialPatched = true;
|
|
835
|
+
}
|
|
836
|
+
// Taken from https://github.com/agargaro/instanced-mesh/blob/master/src/core/feature/Uniforms.ts
|
|
837
|
+
parseUniformSchema(schema) {
|
|
838
|
+
let totalSize = 0;
|
|
839
|
+
const uniformMap = /* @__PURE__ */ new Map();
|
|
840
|
+
const uniforms = [];
|
|
841
|
+
const vertexSchema = schema.vertex ?? {};
|
|
842
|
+
const fragmentSchema = schema.fragment ?? {};
|
|
843
|
+
let fetchInFragmentShader = true;
|
|
844
|
+
for (const name in vertexSchema) {
|
|
845
|
+
const type = vertexSchema[name];
|
|
846
|
+
const size = this.getUniformSize(type);
|
|
847
|
+
totalSize += size;
|
|
848
|
+
uniforms.push({ name, type, size });
|
|
849
|
+
fetchInFragmentShader = false;
|
|
850
|
+
}
|
|
851
|
+
for (const name in fragmentSchema) {
|
|
852
|
+
if (!vertexSchema[name]) {
|
|
853
|
+
const type = fragmentSchema[name];
|
|
854
|
+
const size = this.getUniformSize(type);
|
|
855
|
+
totalSize += size;
|
|
856
|
+
uniforms.push({ name, type, size });
|
|
775
857
|
}
|
|
776
|
-
material._orientation = orient;
|
|
777
858
|
}
|
|
859
|
+
uniforms.sort((a, b) => b.size - a.size);
|
|
860
|
+
const tempOffset = [];
|
|
861
|
+
for (const { name, size, type } of uniforms) {
|
|
862
|
+
const offset = this.getUniformOffset(size, tempOffset);
|
|
863
|
+
uniformMap.set(name, { offset, size, type });
|
|
864
|
+
}
|
|
865
|
+
const pixelsPerInstance = Math.ceil(totalSize / 4);
|
|
866
|
+
const channels = Math.min(totalSize, 4);
|
|
867
|
+
return { channels, texelsPerInstance: pixelsPerInstance, uniformMap, fetchInFragmentShader };
|
|
778
868
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
object.setUniformAt(instanceId, "skipDimInstance", value);
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
const skipDimTexture = object.userData["skipDimTexture"];
|
|
790
|
-
if (skipDimTexture) {
|
|
791
|
-
const skipDimData = skipDimTexture.image.data;
|
|
792
|
-
skipDimData[instanceId] = value;
|
|
793
|
-
skipDimTexture.needsUpdate = true;
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
function addDimToMaterial(material) {
|
|
797
|
-
if (material.userData.hasDimShader) return;
|
|
798
|
-
const onBeforeCompile = material.onBeforeCompile.bind(material);
|
|
799
|
-
const onBeforeRender = material.onBeforeRender.bind(material);
|
|
800
|
-
material.onBeforeCompile = (shader, renderer) => {
|
|
801
|
-
onBeforeCompile(shader, renderer);
|
|
802
|
-
shader.uniforms["uDim"] = { value: material.userData.uDim ?? 0 };
|
|
803
|
-
shader.uniforms["skipDimTexture"] = { value: material.userData.skipDimTexture ?? null };
|
|
804
|
-
shader.vertexShader = shader.vertexShader.replace("void main() {", `${dimColorVertexDefs}
|
|
805
|
-
void main() {`).replace(
|
|
806
|
-
"#include <fog_vertex>",
|
|
807
|
-
/*glsl*/
|
|
808
|
-
`
|
|
809
|
-
#include <fog_vertex>
|
|
810
|
-
setDimAmount();
|
|
811
|
-
`
|
|
812
|
-
).concat(dimColorVertexImpl);
|
|
813
|
-
shader.fragmentShader = /*glsl*/
|
|
814
|
-
`
|
|
815
|
-
${dimColorFrag}
|
|
816
|
-
${shader.fragmentShader}
|
|
817
|
-
`.replace(
|
|
818
|
-
"#include <colorspace_fragment>",
|
|
819
|
-
/*glsl*/
|
|
820
|
-
`
|
|
821
|
-
gl_FragColor = dimColor(gl_FragColor);
|
|
822
|
-
#include <colorspace_fragment>
|
|
823
|
-
`
|
|
824
|
-
);
|
|
825
|
-
material.userData.shader = shader;
|
|
826
|
-
};
|
|
827
|
-
material.onBeforeRender = (renderer, scene, camera, geometry, object, group) => {
|
|
828
|
-
onBeforeRender(renderer, scene, camera, geometry, object, group);
|
|
829
|
-
const skipDimTexture = object.userData["skipDimTexture"];
|
|
830
|
-
let uDim = object.userData["uDim"];
|
|
831
|
-
if (uDim === void 0) {
|
|
832
|
-
for (const ancestor of traverseAncestorsGenerator(object)) {
|
|
833
|
-
if (ancestor.userData["uDim"] !== void 0) {
|
|
834
|
-
uDim = ancestor.userData["uDim"];
|
|
835
|
-
break;
|
|
869
|
+
getUniformOffset(size, tempOffset) {
|
|
870
|
+
if (size < 4) {
|
|
871
|
+
for (let i = 0; i < tempOffset.length; i++) {
|
|
872
|
+
if (tempOffset[i] + size <= 4) {
|
|
873
|
+
const offset2 = i * 4 + tempOffset[i];
|
|
874
|
+
tempOffset[i] += size;
|
|
875
|
+
return offset2;
|
|
836
876
|
}
|
|
837
877
|
}
|
|
838
878
|
}
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
material.userData.skipDimTexture = object.userData["skipDimTexture"];
|
|
843
|
-
return;
|
|
879
|
+
const offset = tempOffset.length * 4;
|
|
880
|
+
for (; size > 0; size -= 4) {
|
|
881
|
+
tempOffset.push(size);
|
|
844
882
|
}
|
|
845
|
-
|
|
846
|
-
shader.uniforms["skipDimTexture"].value = skipDimTexture ?? null;
|
|
847
|
-
};
|
|
848
|
-
material.userData.hasDimShader = true;
|
|
849
|
-
}
|
|
850
|
-
function addDim(mesh) {
|
|
851
|
-
if (mesh instanceof BatchedMesh) mesh.addPerInstanceUniforms({ vertex: { skipDimInstance: "float" } });
|
|
852
|
-
if (mesh instanceof BatchedText) addSkipDimTexture(mesh);
|
|
853
|
-
}
|
|
854
|
-
function addSkipDimTexture(text) {
|
|
855
|
-
const count = text.size;
|
|
856
|
-
const size = Math.ceil(Math.sqrt(count));
|
|
857
|
-
const array = new Float32Array(size * size);
|
|
858
|
-
array.fill(0);
|
|
859
|
-
const texture = new DataTexture(array, size, size, RedFormat, FloatType);
|
|
860
|
-
texture.needsUpdate = true;
|
|
861
|
-
text.userData["skipDimTexture"] = texture;
|
|
862
|
-
text.addEventListener("dispose", () => texture.dispose());
|
|
863
|
-
return texture;
|
|
864
|
-
}
|
|
865
|
-
const dimColorVertexDefs = (
|
|
866
|
-
/*glsl*/
|
|
867
|
-
`
|
|
868
|
-
uniform float uDim;
|
|
869
|
-
out float dimAmount;
|
|
870
|
-
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
871
|
-
uniform sampler2D skipDimTexture;
|
|
872
|
-
#endif
|
|
873
|
-
void setDimAmount();
|
|
874
|
-
`
|
|
875
|
-
);
|
|
876
|
-
const dimColorVertexImpl = (
|
|
877
|
-
/*glsl*/
|
|
878
|
-
`
|
|
879
|
-
void setDimAmount() {
|
|
880
|
-
float instanceDim = 0.;
|
|
881
|
-
#ifdef USE_BATCHING
|
|
882
|
-
instanceDim = batch_skipDimInstance;
|
|
883
|
-
#endif
|
|
884
|
-
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
885
|
-
float indirectIndex = aTroikaTextBatchMemberIndex;
|
|
886
|
-
int size = textureSize(skipDimTexture, 0).x;
|
|
887
|
-
int i = int(indirectIndex);
|
|
888
|
-
int x = i % size;
|
|
889
|
-
int y = i / size;
|
|
890
|
-
instanceDim = texelFetch(skipDimTexture, ivec2(x, y), 0).r;
|
|
891
|
-
#endif
|
|
892
|
-
dimAmount = instanceDim == 0. ? uDim : instanceDim / 2. + 0.5;
|
|
883
|
+
return offset;
|
|
893
884
|
}
|
|
894
|
-
|
|
895
|
-
)
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
)
|
|
885
|
+
getUniformSize(type) {
|
|
886
|
+
switch (type) {
|
|
887
|
+
case "float":
|
|
888
|
+
return 1;
|
|
889
|
+
case "vec2":
|
|
890
|
+
return 2;
|
|
891
|
+
case "vec3":
|
|
892
|
+
return 3;
|
|
893
|
+
case "vec4":
|
|
894
|
+
return 4;
|
|
895
|
+
case "mat3":
|
|
896
|
+
return 9;
|
|
897
|
+
case "mat4":
|
|
898
|
+
return 16;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
getIndexType(gl, index) {
|
|
902
|
+
const array = index.array;
|
|
903
|
+
if (array instanceof Uint16Array) return gl.UNSIGNED_SHORT;
|
|
904
|
+
if (array instanceof Uint32Array) return gl.UNSIGNED_INT;
|
|
905
|
+
return gl.UNSIGNED_BYTE;
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
/** Whether to use WebGL_multi_draw extension or less performant fallback (Firefox only) */
|
|
909
|
+
__publicField(_BatchedMesh, "useMultiDraw", true);
|
|
910
|
+
let BatchedMesh = _BatchedMesh;
|
|
911
911
|
const sharedParameters = {
|
|
912
912
|
side: DoubleSide,
|
|
913
913
|
transparent: true,
|
|
914
|
-
|
|
914
|
+
forceSinglePass: true,
|
|
915
|
+
depthFunc: AlwaysDepth
|
|
915
916
|
};
|
|
916
917
|
class MaterialSystem {
|
|
917
918
|
constructor() {
|
|
@@ -950,7 +951,6 @@ class MaterialSystem {
|
|
|
950
951
|
uniforms["resolution"].value.set(this.viewport.z, this.viewport.w);
|
|
951
952
|
}
|
|
952
953
|
};
|
|
953
|
-
this.addPolygonOffset(material);
|
|
954
954
|
addDimToMaterial(material);
|
|
955
955
|
return material;
|
|
956
956
|
}
|
|
@@ -969,7 +969,6 @@ class MaterialSystem {
|
|
|
969
969
|
color: params.color,
|
|
970
970
|
opacity: params.opacity
|
|
971
971
|
});
|
|
972
|
-
this.addPolygonOffset(material);
|
|
973
972
|
addDimToMaterial(material);
|
|
974
973
|
return material;
|
|
975
974
|
}
|
|
@@ -993,7 +992,6 @@ class MaterialSystem {
|
|
|
993
992
|
);
|
|
994
993
|
};
|
|
995
994
|
}
|
|
996
|
-
this.addPolygonOffset(material);
|
|
997
995
|
addDimToMaterial(material);
|
|
998
996
|
return material;
|
|
999
997
|
}
|
|
@@ -1014,14 +1012,10 @@ class MaterialSystem {
|
|
|
1014
1012
|
`
|
|
1015
1013
|
);
|
|
1016
1014
|
};
|
|
1017
|
-
this.addPolygonOffset(this.backgroundMaterial);
|
|
1018
1015
|
addDimToMaterial(this.backgroundMaterial);
|
|
1019
1016
|
}
|
|
1020
1017
|
return this.backgroundMaterial;
|
|
1021
1018
|
}
|
|
1022
|
-
addPolygonOffset(material) {
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
1019
|
}
|
|
1026
1020
|
function isShapeDef(def) {
|
|
1027
1021
|
return def.shape !== void 0;
|
|
@@ -1053,6 +1047,31 @@ function isLineLayer(layer) {
|
|
|
1053
1047
|
function isLayerLayer(layer) {
|
|
1054
1048
|
return layer.children[0] && isLayerDef(layer.children[0]);
|
|
1055
1049
|
}
|
|
1050
|
+
function groupBy(list, keyGetter) {
|
|
1051
|
+
const map = /* @__PURE__ */ new Map();
|
|
1052
|
+
list.forEach((item) => {
|
|
1053
|
+
const key = keyGetter(item);
|
|
1054
|
+
const collection = map.get(key);
|
|
1055
|
+
if (!collection) {
|
|
1056
|
+
map.set(key, [item]);
|
|
1057
|
+
} else {
|
|
1058
|
+
collection.push(item);
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
return map;
|
|
1062
|
+
}
|
|
1063
|
+
function partition(list, pred) {
|
|
1064
|
+
const truthy = [];
|
|
1065
|
+
const falsy = [];
|
|
1066
|
+
for (const item of list) {
|
|
1067
|
+
if (pred(item)) {
|
|
1068
|
+
truthy.push(item);
|
|
1069
|
+
} else {
|
|
1070
|
+
falsy.push(item);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
return [truthy, falsy];
|
|
1074
|
+
}
|
|
1056
1075
|
const INTERACTIVE_LAYER = 1;
|
|
1057
1076
|
function setInteractive(object, isInteractive) {
|
|
1058
1077
|
if (isInteractive) object.layers.enable(INTERACTIVE_LAYER);
|
|
@@ -1126,6 +1145,15 @@ class RenderableSystem {
|
|
|
1126
1145
|
const shapeDef = this.getDefsByObject(mesh)[batchId];
|
|
1127
1146
|
return shapeDef;
|
|
1128
1147
|
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Dispose all objects and clear the mappings
|
|
1150
|
+
*/
|
|
1151
|
+
dispose() {
|
|
1152
|
+
for (const object of this.getAllObjects()) {
|
|
1153
|
+
this.disposeObject(object);
|
|
1154
|
+
}
|
|
1155
|
+
this.clearMappings();
|
|
1156
|
+
}
|
|
1129
1157
|
/**
|
|
1130
1158
|
* Protected implementation method that subclasses can override.
|
|
1131
1159
|
* This ensures logging always happens even when the method is overridden.
|
|
@@ -1298,10 +1326,10 @@ class ImageSystem extends RenderableSystem {
|
|
|
1298
1326
|
/** Textures memory limit in megabytes */
|
|
1299
1327
|
__publicField(this, "memoryLimitMb");
|
|
1300
1328
|
__publicField(this, "packer");
|
|
1301
|
-
__publicField(this, "
|
|
1302
|
-
__publicField(this, "
|
|
1303
|
-
__publicField(this, "
|
|
1304
|
-
__publicField(this, "
|
|
1329
|
+
__publicField(this, "globalTranslationMatrix", new Matrix4());
|
|
1330
|
+
__publicField(this, "originTranslationMatrix", new Matrix4());
|
|
1331
|
+
__publicField(this, "rotationMatrix", new Matrix4());
|
|
1332
|
+
__publicField(this, "scaleMatrix", new Matrix4());
|
|
1305
1333
|
this.materialSystem = materialSystem;
|
|
1306
1334
|
const atlasTextureSize = renderer.context.capabilities.maxTextureSize;
|
|
1307
1335
|
console.log(`Max texture size: ${atlasTextureSize}`);
|
|
@@ -1401,17 +1429,13 @@ class ImageSystem extends RenderableSystem {
|
|
|
1401
1429
|
}
|
|
1402
1430
|
updateDefImpl(imageDef, mesh, instanceIds) {
|
|
1403
1431
|
const bounds = imageDef.bounds;
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
this.scale.set(bounds.size.width, bounds.size.height, 1);
|
|
1412
|
-
this.rotation.setFromAxisAngle(new Vector3(0, 0, 1), bounds.rotation);
|
|
1413
|
-
this.matrix.compose(this.position, this.rotation, this.scale);
|
|
1414
|
-
mesh.setMatrixAt(instanceIds[0], this.matrix);
|
|
1432
|
+
const origin2 = imageDef.origin ?? [0.5, 0.5];
|
|
1433
|
+
this.originTranslationMatrix.makeTranslation(0.5 - origin2[0], 0.5 - origin2[1], 0);
|
|
1434
|
+
this.globalTranslationMatrix.makeTranslation(bounds.center.x, bounds.center.y, 0);
|
|
1435
|
+
this.scaleMatrix.makeScale(bounds.size.width, bounds.size.height, 1);
|
|
1436
|
+
this.rotationMatrix.makeRotationZ(bounds.rotation);
|
|
1437
|
+
const matrix = this.originTranslationMatrix.premultiply(this.scaleMatrix).premultiply(this.rotationMatrix).premultiply(this.globalTranslationMatrix);
|
|
1438
|
+
mesh.setMatrixAt(instanceIds[0], matrix);
|
|
1415
1439
|
}
|
|
1416
1440
|
packImages(images) {
|
|
1417
1441
|
this.packer.reset();
|
|
@@ -1545,7 +1569,15 @@ class LineSystem extends RenderableSystem {
|
|
|
1545
1569
|
}
|
|
1546
1570
|
}
|
|
1547
1571
|
function createVector2(vector2) {
|
|
1548
|
-
|
|
1572
|
+
if (vector2 instanceof Vector2) return vector2;
|
|
1573
|
+
if (Array.isArray(vector2)) return new Vector2(vector2[0], vector2[1]);
|
|
1574
|
+
return new Vector2(vector2.x, vector2.y);
|
|
1575
|
+
}
|
|
1576
|
+
function createVector3(vector3) {
|
|
1577
|
+
if (vector3 instanceof Vector2) return new Vector3(vector3.x, vector3.y, 0);
|
|
1578
|
+
if (vector3 instanceof Vector3) return vector3;
|
|
1579
|
+
if (Array.isArray(vector3)) return new Vector3(vector3[0], vector3[1], vector3[2] ?? 0);
|
|
1580
|
+
return new Vector3(vector3.x, vector3.y, "z" in vector3 ? vector3.z : 0);
|
|
1549
1581
|
}
|
|
1550
1582
|
class Rect {
|
|
1551
1583
|
/**
|
|
@@ -1613,7 +1645,7 @@ class Polygon {
|
|
|
1613
1645
|
__publicField(this, "vertices");
|
|
1614
1646
|
/** Array of polygon indices. Each index is a triplet of vertex indices forming a triangle. */
|
|
1615
1647
|
__publicField(this, "indices");
|
|
1616
|
-
this.vertices = vertices.map(
|
|
1648
|
+
this.vertices = vertices.map(createVector3);
|
|
1617
1649
|
this.indices = indices;
|
|
1618
1650
|
}
|
|
1619
1651
|
/**
|
|
@@ -1657,37 +1689,15 @@ class Polygon {
|
|
|
1657
1689
|
* @returns this {@link Polygon} instance
|
|
1658
1690
|
*/
|
|
1659
1691
|
rotate(rotation, center) {
|
|
1692
|
+
const tempVec2 = new Vector2();
|
|
1660
1693
|
this.vertices.forEach((vertex) => {
|
|
1661
|
-
vertex.
|
|
1694
|
+
tempVec2.set(vertex.x, vertex.y);
|
|
1695
|
+
tempVec2.rotateAround(center, rotation);
|
|
1696
|
+
vertex.set(tempVec2.x, tempVec2.y, vertex.z);
|
|
1662
1697
|
});
|
|
1663
1698
|
return this;
|
|
1664
1699
|
}
|
|
1665
1700
|
}
|
|
1666
|
-
function groupBy(list, keyGetter) {
|
|
1667
|
-
const map = /* @__PURE__ */ new Map();
|
|
1668
|
-
list.forEach((item) => {
|
|
1669
|
-
const key = keyGetter(item);
|
|
1670
|
-
const collection = map.get(key);
|
|
1671
|
-
if (!collection) {
|
|
1672
|
-
map.set(key, [item]);
|
|
1673
|
-
} else {
|
|
1674
|
-
collection.push(item);
|
|
1675
|
-
}
|
|
1676
|
-
});
|
|
1677
|
-
return map;
|
|
1678
|
-
}
|
|
1679
|
-
function partition(list, pred) {
|
|
1680
|
-
const truthy = [];
|
|
1681
|
-
const falsy = [];
|
|
1682
|
-
for (const item of list) {
|
|
1683
|
-
if (pred(item)) {
|
|
1684
|
-
truthy.push(item);
|
|
1685
|
-
} else {
|
|
1686
|
-
falsy.push(item);
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
return [truthy, falsy];
|
|
1690
|
-
}
|
|
1691
1701
|
class MeshSystem extends RenderableSystem {
|
|
1692
1702
|
/**
|
|
1693
1703
|
* @param materialSystem {@link MaterialSystem}
|
|
@@ -1725,6 +1735,12 @@ class MeshSystem extends RenderableSystem {
|
|
|
1725
1735
|
opaqueMesh.name = "opaque";
|
|
1726
1736
|
group.add(opaqueMesh);
|
|
1727
1737
|
}
|
|
1738
|
+
if (layer.mode === "3d") {
|
|
1739
|
+
group.children.filter((child) => child instanceof Mesh).forEach((mesh) => {
|
|
1740
|
+
const material = mesh.material;
|
|
1741
|
+
material.depthFunc = LessEqualDepth;
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1728
1744
|
return group;
|
|
1729
1745
|
}
|
|
1730
1746
|
updateDefImpl(shapeDef, mesh, instanceIds) {
|
|
@@ -1741,12 +1757,10 @@ class MeshSystem extends RenderableSystem {
|
|
|
1741
1757
|
let rectGeometry = void 0;
|
|
1742
1758
|
const shapeDefToGeometry = /* @__PURE__ */ new Map();
|
|
1743
1759
|
for (const shapeDef of shapes) {
|
|
1744
|
-
if (shapeDef.shape instanceof Rect) {
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
rectGeometry.deleteAttribute("uv");
|
|
1749
|
-
}
|
|
1760
|
+
if (shapeDef.shape instanceof Rect && !rectGeometry) {
|
|
1761
|
+
rectGeometry = new PlaneGeometry(1, 1);
|
|
1762
|
+
rectGeometry.deleteAttribute("normal");
|
|
1763
|
+
rectGeometry.deleteAttribute("uv");
|
|
1750
1764
|
vertexCount += rectGeometry.getAttribute("position").count;
|
|
1751
1765
|
indexCount += ((_a2 = rectGeometry.index) == null ? void 0 : _a2.count) ?? 0;
|
|
1752
1766
|
} else if (shapeDef.shape instanceof Polygon) {
|
|
@@ -1758,6 +1772,7 @@ class MeshSystem extends RenderableSystem {
|
|
|
1758
1772
|
}
|
|
1759
1773
|
const material = this.materialSystem.createColorMaterial({ opacity });
|
|
1760
1774
|
const batchedMesh = new BatchedMesh(shapes.length, vertexCount, indexCount, material);
|
|
1775
|
+
const rectGeometryId = rectGeometry ? batchedMesh.addGeometry(rectGeometry) : void 0;
|
|
1761
1776
|
batchedMesh.setCustomSort((list) => this.sortInstances(batchedMesh, list));
|
|
1762
1777
|
const position = new Vector3();
|
|
1763
1778
|
const rotation = new Quaternion();
|
|
@@ -1765,8 +1780,7 @@ class MeshSystem extends RenderableSystem {
|
|
|
1765
1780
|
const matrix = new Matrix4();
|
|
1766
1781
|
for (const shapeDef of shapes) {
|
|
1767
1782
|
let instanceId = void 0;
|
|
1768
|
-
if (shapeDef.shape instanceof Rect) {
|
|
1769
|
-
const rectGeometryId = rectGeometry ? batchedMesh.addGeometry(rectGeometry) : void 0;
|
|
1783
|
+
if (shapeDef.shape instanceof Rect && rectGeometryId !== void 0) {
|
|
1770
1784
|
instanceId = batchedMesh.addInstance(rectGeometryId);
|
|
1771
1785
|
position.set(shapeDef.shape.center.x, shapeDef.shape.center.y, 0);
|
|
1772
1786
|
rotation.setFromAxisAngle(new Vector3(0, 0, 1), shapeDef.shape.rotation ?? 0);
|
|
@@ -1990,6 +2004,7 @@ class LayerSystem {
|
|
|
1990
2004
|
* @param renderer {@link Renderer}
|
|
1991
2005
|
*/
|
|
1992
2006
|
constructor(renderer) {
|
|
2007
|
+
__publicField(this, "backgroundMesh");
|
|
1993
2008
|
__publicField(this, "materialSystem");
|
|
1994
2009
|
__publicField(this, "meshSystem");
|
|
1995
2010
|
__publicField(this, "imageSystem");
|
|
@@ -2089,6 +2104,18 @@ class LayerSystem {
|
|
|
2089
2104
|
}
|
|
2090
2105
|
return rootGroup;
|
|
2091
2106
|
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Dispose all objects and clear the mappings
|
|
2109
|
+
*/
|
|
2110
|
+
disposeScene() {
|
|
2111
|
+
var _a2, _b, _c;
|
|
2112
|
+
for (const system of this.systems) {
|
|
2113
|
+
system.dispose();
|
|
2114
|
+
}
|
|
2115
|
+
(_a2 = this.backgroundMesh) == null ? void 0 : _a2.geometry.dispose();
|
|
2116
|
+
(_c = (_b = this.backgroundMesh) == null ? void 0 : _b.material) == null ? void 0 : _c.dispose();
|
|
2117
|
+
this.backgroundMesh = void 0;
|
|
2118
|
+
}
|
|
2092
2119
|
updateDef(def) {
|
|
2093
2120
|
if (isShapeDef(def)) this.meshSystem.updateDef(def);
|
|
2094
2121
|
else if (isImageDef(def)) this.imageSystem.updateDef(def);
|
|
@@ -2153,6 +2180,7 @@ class LayerSystem {
|
|
|
2153
2180
|
backgroundMesh.frustumCulled = false;
|
|
2154
2181
|
backgroundMesh.renderOrder = 0;
|
|
2155
2182
|
backgroundMesh.name = "background";
|
|
2183
|
+
this.backgroundMesh = backgroundMesh;
|
|
2156
2184
|
return backgroundMesh;
|
|
2157
2185
|
}
|
|
2158
2186
|
initRenderOrder(rootLayer) {
|
|
@@ -2170,6 +2198,8 @@ class LayerSystem {
|
|
|
2170
2198
|
this.layerDefRenderOrder.push(layer);
|
|
2171
2199
|
}
|
|
2172
2200
|
}
|
|
2201
|
+
const [threeDLayers, twoDLayers] = partition(this.layerDefRenderOrder, (layer) => layer.mode === "3d");
|
|
2202
|
+
this.layerDefRenderOrder = [...twoDLayers, ...threeDLayers];
|
|
2173
2203
|
}
|
|
2174
2204
|
getFullLayerName(layerDef) {
|
|
2175
2205
|
let fullName = layerDef.name;
|
|
@@ -4899,7 +4929,7 @@ class ViewportSystem {
|
|
|
4899
4929
|
__publicField(this, "raycaster", new Raycaster());
|
|
4900
4930
|
__publicField(this, "intersectionPoint", new Vector3());
|
|
4901
4931
|
__publicField(this, "viewboxPlane", new Plane(new Vector3(0, 0, 1), 0));
|
|
4902
|
-
__publicField(this, "pxToSvgScaleThreshold", 1e-
|
|
4932
|
+
__publicField(this, "pxToSvgScaleThreshold", 1e-4);
|
|
4903
4933
|
__publicField(this, "prevPxToSvgScale");
|
|
4904
4934
|
__publicField(this, "externalStaticTransformMatrix", new Matrix4());
|
|
4905
4935
|
this.renderer = renderer;
|
|
@@ -5960,6 +5990,7 @@ class Renderer {
|
|
|
5960
5990
|
constructor(opts) {
|
|
5961
5991
|
/** Whether to log debug information */
|
|
5962
5992
|
__publicField(this, "debugLog");
|
|
5993
|
+
//FIXME: Add https://www.npmjs.com/package/debug
|
|
5963
5994
|
/** {@link HTMLCanvasElement} that this renderer is rendering to */
|
|
5964
5995
|
__publicField(this, "canvas");
|
|
5965
5996
|
__publicField(this, "ui");
|
|
@@ -5999,6 +6030,7 @@ class Renderer {
|
|
|
5999
6030
|
this.canvas.addEventListener("webglcontextlost", (e) => this.onContextLost(e), false);
|
|
6000
6031
|
this.canvas.addEventListener("webglcontextrestored", (e) => this.onContextRestored(e), false);
|
|
6001
6032
|
void ((_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.init(this.renderer.getContext()));
|
|
6033
|
+
BatchedMesh.useMultiDraw = this.renderer.extensions.has("WEBGL_multi_draw");
|
|
6002
6034
|
}
|
|
6003
6035
|
/**
|
|
6004
6036
|
* {@link ControlsAPI} instance for controlling the viewport
|
|
@@ -6106,6 +6138,20 @@ class Renderer {
|
|
|
6106
6138
|
(_f = (_e = this.ui) == null ? void 0 : _e.stats) == null ? void 0 : _f.update();
|
|
6107
6139
|
this.updateMemoryInfo();
|
|
6108
6140
|
}
|
|
6141
|
+
/**
|
|
6142
|
+
* Stop the rendering loop
|
|
6143
|
+
*/
|
|
6144
|
+
stop() {
|
|
6145
|
+
this.renderer.setAnimationLoop(null);
|
|
6146
|
+
this.clock.stop();
|
|
6147
|
+
}
|
|
6148
|
+
/**
|
|
6149
|
+
* Dispose all WebGL resources
|
|
6150
|
+
*/
|
|
6151
|
+
dispose() {
|
|
6152
|
+
this.layerSystem.disposeScene();
|
|
6153
|
+
this.viewportSystem.scene.clear();
|
|
6154
|
+
}
|
|
6109
6155
|
// https://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
|
|
6110
6156
|
resizeCanvasToDisplaySize() {
|
|
6111
6157
|
const dpr = window.devicePixelRatio;
|
|
@@ -6124,16 +6170,19 @@ class Renderer {
|
|
|
6124
6170
|
var _a2;
|
|
6125
6171
|
if (this.memoryInfoExtension && ((_a2 = this.ui) == null ? void 0 : _a2.memoryInfoPanel)) {
|
|
6126
6172
|
const memoryInfo = this.memoryInfoExtension.getMemoryInfo();
|
|
6173
|
+
memoryInfo.resources["drawCalls"] = this.renderer.info.render.calls;
|
|
6127
6174
|
const memoryInfoContent = JSON.stringify(memoryInfo.memory, null, 2);
|
|
6128
6175
|
const elapsedTime = this.clock.getElapsedTime() * 1e3;
|
|
6129
6176
|
if (memoryInfoContent !== this.memoryInfo) {
|
|
6130
6177
|
const logMarker = `memoryInfo [${elapsedTime.toFixed(2)}ms since start]`;
|
|
6131
6178
|
if (this.debugLog) console.log(logMarker, memoryInfo);
|
|
6179
|
+
console.log("Buffers", this.memoryInfoExtension.getResourcesInfo(WebGLBuffer));
|
|
6132
6180
|
this.memoryInfo = memoryInfoContent;
|
|
6133
6181
|
}
|
|
6134
6182
|
this.ui.memoryInfoPanel.textContent = JSON.stringify(memoryInfo, null, 2);
|
|
6135
6183
|
}
|
|
6136
6184
|
}
|
|
6185
|
+
// FIXME: Test with mapbox
|
|
6137
6186
|
onContextLost(event) {
|
|
6138
6187
|
event.preventDefault();
|
|
6139
6188
|
console.log("webglcontextlost event", event);
|
|
@@ -6153,6 +6202,7 @@ export {
|
|
|
6153
6202
|
Rect,
|
|
6154
6203
|
Renderer,
|
|
6155
6204
|
createVector2,
|
|
6205
|
+
createVector3,
|
|
6156
6206
|
isImageDef,
|
|
6157
6207
|
isImageLayer,
|
|
6158
6208
|
isLayerDef,
|