@expofp/renderer 2.1.2 → 2.2.1
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 +70 -26
- package/dist/index.js +660 -567
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,372 +2,16 @@ 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 {
|
|
5
|
+
import { DataTexture, FloatType, UnsignedIntType, IntType, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Color, Matrix4, Vector3, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Quaternion, BufferGeometry, Mesh, LessEqualDepth, LinearSRGBColorSpace, Plane, Raycaster, Sphere, Box3, Spherical, PerspectiveCamera, Camera, Scene, MathUtils, Clock, WebGLRenderer } from "three";
|
|
6
6
|
import { traverseAncestorsGenerator } from "three/examples/jsm/utils/SceneUtils.js";
|
|
7
|
-
import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
|
|
8
7
|
import createLog from "debug";
|
|
8
|
+
import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
|
|
9
9
|
import { LineMaterial, LineSegmentsGeometry } from "three/examples/jsm/Addons.js";
|
|
10
10
|
import { MaxRectsPacker, Rectangle } from "maxrects-packer";
|
|
11
11
|
import { extend, colord } from "colord";
|
|
12
12
|
import namesPlugin from "colord/plugins/names";
|
|
13
13
|
import { RAD2DEG, DEG2RAD as DEG2RAD$1 } from "three/src/math/MathUtils.js";
|
|
14
14
|
import { EventManager, Rotate, Pan } from "mjolnir.js";
|
|
15
|
-
const floatsPerMember = 32;
|
|
16
|
-
const tempColor = new Color();
|
|
17
|
-
const defaultStrokeColor = 8421504;
|
|
18
|
-
const tempMat4 = new Matrix4();
|
|
19
|
-
const tempVec3a = new Vector3();
|
|
20
|
-
const tempVec3b = new Vector3();
|
|
21
|
-
const origin = new Vector3();
|
|
22
|
-
const defaultOrient = "+x+y";
|
|
23
|
-
class BatchedText extends BatchedText$1 {
|
|
24
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
25
|
-
constructor() {
|
|
26
|
-
super();
|
|
27
|
-
__publicField(this, "mapInstanceIdToText", /* @__PURE__ */ new Map());
|
|
28
|
-
__publicField(this, "textArray", []);
|
|
29
|
-
__publicField(this, "textureNeedsUpdate", false);
|
|
30
|
-
}
|
|
31
|
-
/** Number of texts in the batch */
|
|
32
|
-
get size() {
|
|
33
|
-
return this._members.size;
|
|
34
|
-
}
|
|
35
|
-
/** Base material before patching */
|
|
36
|
-
get baseMaterial() {
|
|
37
|
-
return this._baseMaterial;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get the {@link Text} object by instance id
|
|
41
|
-
* @param instanceId Instance id
|
|
42
|
-
* @returns Text object
|
|
43
|
-
*/
|
|
44
|
-
getText(instanceId) {
|
|
45
|
-
return this.mapInstanceIdToText.get(instanceId);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Set the visibility of the {@link Text} object by instance id.
|
|
49
|
-
* This is for interface compatibility with {@link BatchedMesh}.
|
|
50
|
-
* @param instanceId Instance id
|
|
51
|
-
* @param visible Visibility flag
|
|
52
|
-
*/
|
|
53
|
-
setVisibleAt(instanceId, visible) {
|
|
54
|
-
const text = this.getText(instanceId);
|
|
55
|
-
text.visible = visible;
|
|
56
|
-
}
|
|
57
|
-
addText(text, instanceId) {
|
|
58
|
-
super.addText(text);
|
|
59
|
-
if (instanceId !== void 0) {
|
|
60
|
-
this.mapInstanceIdToText.set(instanceId, text);
|
|
61
|
-
}
|
|
62
|
-
this.textArray.push(text);
|
|
63
|
-
}
|
|
64
|
-
dispose() {
|
|
65
|
-
super.dispose();
|
|
66
|
-
this.dispatchEvent({ type: "dispose" });
|
|
67
|
-
}
|
|
68
|
-
// TODO: Check performance
|
|
69
|
-
_prepareForRender(material) {
|
|
70
|
-
var _a2;
|
|
71
|
-
const isOutline = material.isTextOutlineMaterial;
|
|
72
|
-
material.uniforms.uTroikaIsOutline.value = isOutline;
|
|
73
|
-
let texture = this._dataTextures[isOutline ? "outline" : "main"];
|
|
74
|
-
const dataLength = Math.pow(2, Math.ceil(Math.log2(this._members.size * floatsPerMember)));
|
|
75
|
-
if (!texture || dataLength !== texture.image.data.length) {
|
|
76
|
-
if (texture) texture.dispose();
|
|
77
|
-
const width = Math.min(dataLength / 4, 1024);
|
|
78
|
-
texture = this._dataTextures[isOutline ? "outline" : "main"] = new DataTexture(
|
|
79
|
-
new Float32Array(dataLength),
|
|
80
|
-
width,
|
|
81
|
-
dataLength / 4 / width,
|
|
82
|
-
RGBAFormat,
|
|
83
|
-
FloatType
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
const texData = texture.image.data;
|
|
87
|
-
this.textureNeedsUpdate = false;
|
|
88
|
-
for (const text of this.textArray) {
|
|
89
|
-
const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
|
|
90
|
-
const textRenderInfo = text.textRenderInfo;
|
|
91
|
-
if (index < 0 || !textRenderInfo) continue;
|
|
92
|
-
const startIndex = index * floatsPerMember;
|
|
93
|
-
if (!text.visible) {
|
|
94
|
-
for (let i = 0; i < 16; i++) {
|
|
95
|
-
this.setTexData(startIndex + i, 0, texData);
|
|
96
|
-
}
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
const matrix = text.matrix.elements;
|
|
100
|
-
for (let i = 0; i < 16; i++) {
|
|
101
|
-
this.setTexData(startIndex + i, matrix[i], texData);
|
|
102
|
-
}
|
|
103
|
-
text._prepareForRender(material);
|
|
104
|
-
const {
|
|
105
|
-
uTroikaTotalBounds,
|
|
106
|
-
uTroikaClipRect,
|
|
107
|
-
uTroikaPositionOffset,
|
|
108
|
-
uTroikaEdgeOffset,
|
|
109
|
-
uTroikaBlurRadius,
|
|
110
|
-
uTroikaStrokeWidth,
|
|
111
|
-
uTroikaStrokeColor,
|
|
112
|
-
uTroikaStrokeOpacity,
|
|
113
|
-
uTroikaFillOpacity,
|
|
114
|
-
uTroikaCurveRadius
|
|
115
|
-
} = material.uniforms;
|
|
116
|
-
for (let i = 0; i < 4; i++) {
|
|
117
|
-
this.setTexData(startIndex + 16 + i, uTroikaTotalBounds.value.getComponent(i), texData);
|
|
118
|
-
}
|
|
119
|
-
for (let i = 0; i < 4; i++) {
|
|
120
|
-
this.setTexData(startIndex + 20 + i, uTroikaClipRect.value.getComponent(i), texData);
|
|
121
|
-
}
|
|
122
|
-
let color = isOutline ? text.outlineColor || 0 : text.color;
|
|
123
|
-
color ?? (color = this.color);
|
|
124
|
-
color ?? (color = this.material.color);
|
|
125
|
-
color ?? (color = 16777215);
|
|
126
|
-
this.setTexData(startIndex + 24, tempColor.set(color).getHex(), texData);
|
|
127
|
-
this.setTexData(startIndex + 25, uTroikaFillOpacity.value, texData);
|
|
128
|
-
this.setTexData(startIndex + 26, uTroikaCurveRadius.value, texData);
|
|
129
|
-
if (isOutline) {
|
|
130
|
-
this.setTexData(startIndex + 28, uTroikaPositionOffset.value.x, texData);
|
|
131
|
-
this.setTexData(startIndex + 29, uTroikaPositionOffset.value.y, texData);
|
|
132
|
-
this.setTexData(startIndex + 30, uTroikaEdgeOffset.value, texData);
|
|
133
|
-
this.setTexData(startIndex + 31, uTroikaBlurRadius.value, texData);
|
|
134
|
-
} else {
|
|
135
|
-
this.setTexData(startIndex + 28, uTroikaStrokeWidth.value, texData);
|
|
136
|
-
this.setTexData(startIndex + 29, tempColor.set(uTroikaStrokeColor.value).getHex(), texData);
|
|
137
|
-
this.setTexData(startIndex + 30, uTroikaStrokeOpacity.value, texData);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
texture.needsUpdate = this.textureNeedsUpdate;
|
|
141
|
-
material.setMatrixTexture(texture);
|
|
142
|
-
}
|
|
143
|
-
setTexData(index, value, texData) {
|
|
144
|
-
if (value !== texData[index]) {
|
|
145
|
-
texData[index] = value;
|
|
146
|
-
this.textureNeedsUpdate = true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
class Text extends Text$1 {
|
|
151
|
-
_prepareForRender(material) {
|
|
152
|
-
const isOutline = material.isTextOutlineMaterial;
|
|
153
|
-
const uniforms = material.uniforms;
|
|
154
|
-
const textInfo = this.textRenderInfo;
|
|
155
|
-
if (textInfo) {
|
|
156
|
-
const { sdfTexture, blockBounds } = textInfo;
|
|
157
|
-
const { width, height } = sdfTexture.image;
|
|
158
|
-
uniforms.uTroikaSDFTexture.value = sdfTexture;
|
|
159
|
-
uniforms.uTroikaSDFTextureSize.value.set(width, height);
|
|
160
|
-
uniforms.uTroikaSDFGlyphSize.value = textInfo.sdfGlyphSize;
|
|
161
|
-
uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent;
|
|
162
|
-
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds);
|
|
163
|
-
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors;
|
|
164
|
-
let distanceOffset = 0;
|
|
165
|
-
let blurRadius = 0;
|
|
166
|
-
let strokeWidth = 0;
|
|
167
|
-
let fillOpacity;
|
|
168
|
-
let strokeOpacity = 1;
|
|
169
|
-
let strokeColor;
|
|
170
|
-
let offsetX = 0;
|
|
171
|
-
let offsetY = 0;
|
|
172
|
-
if (isOutline) {
|
|
173
|
-
const { outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity } = this;
|
|
174
|
-
distanceOffset = this._parsePercent(outlineWidth) || 0;
|
|
175
|
-
blurRadius = Math.max(0, this._parsePercent(outlineBlur) || 0);
|
|
176
|
-
fillOpacity = outlineOpacity;
|
|
177
|
-
offsetX = this._parsePercent(outlineOffsetX) || 0;
|
|
178
|
-
offsetY = this._parsePercent(outlineOffsetY) || 0;
|
|
179
|
-
} else {
|
|
180
|
-
strokeWidth = Math.max(0, this._parsePercent(this.strokeWidth) || 0);
|
|
181
|
-
if (strokeWidth) {
|
|
182
|
-
strokeColor = this.strokeColor;
|
|
183
|
-
uniforms.uTroikaStrokeColor.value.set(strokeColor ?? defaultStrokeColor);
|
|
184
|
-
strokeOpacity = this.strokeOpacity;
|
|
185
|
-
strokeOpacity ?? (strokeOpacity = 1);
|
|
186
|
-
}
|
|
187
|
-
fillOpacity = this.fillOpacity;
|
|
188
|
-
}
|
|
189
|
-
uniforms.uTroikaEdgeOffset.value = distanceOffset;
|
|
190
|
-
uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY);
|
|
191
|
-
uniforms.uTroikaBlurRadius.value = blurRadius;
|
|
192
|
-
uniforms.uTroikaStrokeWidth.value = strokeWidth;
|
|
193
|
-
uniforms.uTroikaStrokeOpacity.value = strokeOpacity;
|
|
194
|
-
uniforms.uTroikaFillOpacity.value = fillOpacity ?? 1;
|
|
195
|
-
uniforms.uTroikaCurveRadius.value = this.curveRadius || 0;
|
|
196
|
-
const clipRect = this.clipRect;
|
|
197
|
-
if (clipRect && Array.isArray(clipRect) && clipRect.length === 4) {
|
|
198
|
-
uniforms.uTroikaClipRect.value.fromArray(clipRect);
|
|
199
|
-
} else {
|
|
200
|
-
const pad = (this.fontSize || 0.1) * 100;
|
|
201
|
-
uniforms.uTroikaClipRect.value.set(
|
|
202
|
-
blockBounds[0] - pad,
|
|
203
|
-
blockBounds[1] - pad,
|
|
204
|
-
blockBounds[2] + pad,
|
|
205
|
-
blockBounds[3] + pad
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
|
|
209
|
-
}
|
|
210
|
-
uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
|
|
211
|
-
material.polygonOffset = !!this.depthOffset;
|
|
212
|
-
material.polygonOffsetFactor = material.polygonOffsetUnits = this.depthOffset || 0;
|
|
213
|
-
const color = isOutline ? this.outlineColor || 0 : this.color;
|
|
214
|
-
if (color == null) {
|
|
215
|
-
delete material.color;
|
|
216
|
-
} else {
|
|
217
|
-
const colorObj = material.hasOwnProperty("color") ? material.color : material.color = new Color();
|
|
218
|
-
if (color !== colorObj._input || typeof color === "object") {
|
|
219
|
-
colorObj.set(colorObj._input = color);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
let orient = this.orientation || defaultOrient;
|
|
223
|
-
if (orient !== material._orientation) {
|
|
224
|
-
const rotMat = uniforms.uTroikaOrient.value;
|
|
225
|
-
orient = orient.replace(/[^-+xyz]/g, "");
|
|
226
|
-
const match = orient !== defaultOrient && /^([-+])([xyz])([-+])([xyz])$/.exec(orient);
|
|
227
|
-
if (match) {
|
|
228
|
-
const [, hSign, hAxis, vSign, vAxis] = match;
|
|
229
|
-
tempVec3a.set(0, 0, 0)[hAxis] = hSign === "-" ? 1 : -1;
|
|
230
|
-
tempVec3b.set(0, 0, 0)[vAxis] = vSign === "-" ? -1 : 1;
|
|
231
|
-
tempMat4.lookAt(origin, tempVec3a.cross(tempVec3b), tempVec3b);
|
|
232
|
-
rotMat.setFromMatrix4(tempMat4);
|
|
233
|
-
} else {
|
|
234
|
-
rotMat.identity();
|
|
235
|
-
}
|
|
236
|
-
material._orientation = orient;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function setDimming(root, dim) {
|
|
241
|
-
root.userData["uDim"] = dim === void 0 ? void 0 : +dim;
|
|
242
|
-
}
|
|
243
|
-
function toggleInstanceDim(object, instanceId, dim) {
|
|
244
|
-
const value = dim === void 0 ? 0 : (+dim - 0.5) * 2;
|
|
245
|
-
if (object instanceof BatchedMesh) {
|
|
246
|
-
object.setUniformAt(instanceId, "skipDimInstance", value);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const skipDimTexture = object.userData["skipDimTexture"];
|
|
250
|
-
if (skipDimTexture) {
|
|
251
|
-
const skipDimData = skipDimTexture.image.data;
|
|
252
|
-
skipDimData[instanceId] = value;
|
|
253
|
-
skipDimTexture.needsUpdate = true;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
function addDimToMaterial(material) {
|
|
257
|
-
if (material.userData.hasDimShader) return;
|
|
258
|
-
const onBeforeCompile = material.onBeforeCompile.bind(material);
|
|
259
|
-
const onBeforeRender = material.onBeforeRender.bind(material);
|
|
260
|
-
material.onBeforeCompile = (shader, renderer) => {
|
|
261
|
-
onBeforeCompile(shader, renderer);
|
|
262
|
-
shader.uniforms["uDim"] = { value: material.userData.uDim ?? 0 };
|
|
263
|
-
shader.uniforms["skipDimTexture"] = { value: material.userData.skipDimTexture ?? null };
|
|
264
|
-
shader.vertexShader = shader.vertexShader.replace("void main() {", `${dimColorVertexDefs}
|
|
265
|
-
void main() {`).replace(
|
|
266
|
-
"#include <fog_vertex>",
|
|
267
|
-
/*glsl*/
|
|
268
|
-
`
|
|
269
|
-
#include <fog_vertex>
|
|
270
|
-
setDimAmount();
|
|
271
|
-
`
|
|
272
|
-
).concat(dimColorVertexImpl);
|
|
273
|
-
shader.fragmentShader = /*glsl*/
|
|
274
|
-
`
|
|
275
|
-
${dimColorFrag}
|
|
276
|
-
${shader.fragmentShader}
|
|
277
|
-
`.replace(
|
|
278
|
-
"#include <colorspace_fragment>",
|
|
279
|
-
/*glsl*/
|
|
280
|
-
`
|
|
281
|
-
gl_FragColor = dimColor(gl_FragColor);
|
|
282
|
-
#include <colorspace_fragment>
|
|
283
|
-
`
|
|
284
|
-
);
|
|
285
|
-
material.userData.shader = shader;
|
|
286
|
-
};
|
|
287
|
-
material.onBeforeRender = (renderer, scene, camera, geometry, object, group) => {
|
|
288
|
-
onBeforeRender(renderer, scene, camera, geometry, object, group);
|
|
289
|
-
const skipDimTexture = object.userData["skipDimTexture"];
|
|
290
|
-
let uDim = object.userData["uDim"];
|
|
291
|
-
if (uDim === void 0) {
|
|
292
|
-
for (const ancestor of traverseAncestorsGenerator(object)) {
|
|
293
|
-
if (ancestor.userData["uDim"] !== void 0) {
|
|
294
|
-
uDim = ancestor.userData["uDim"];
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
const shader = material.userData.shader;
|
|
300
|
-
if (!shader) {
|
|
301
|
-
material.userData.uDim = uDim;
|
|
302
|
-
material.userData.skipDimTexture = object.userData["skipDimTexture"];
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
shader.uniforms["uDim"].value = uDim ?? 0;
|
|
306
|
-
shader.uniforms["skipDimTexture"].value = skipDimTexture ?? null;
|
|
307
|
-
};
|
|
308
|
-
material.userData.hasDimShader = true;
|
|
309
|
-
}
|
|
310
|
-
function addDim(mesh) {
|
|
311
|
-
if (mesh instanceof BatchedMesh) mesh.addPerInstanceUniforms({ vertex: { skipDimInstance: "float" } });
|
|
312
|
-
if (mesh instanceof BatchedText) addSkipDimTexture(mesh);
|
|
313
|
-
}
|
|
314
|
-
function addSkipDimTexture(text) {
|
|
315
|
-
const count = text.size;
|
|
316
|
-
const size = Math.ceil(Math.sqrt(count));
|
|
317
|
-
const array = new Float32Array(size * size);
|
|
318
|
-
array.fill(0);
|
|
319
|
-
const texture = new DataTexture(array, size, size, RedFormat, FloatType);
|
|
320
|
-
texture.needsUpdate = true;
|
|
321
|
-
text.userData["skipDimTexture"] = texture;
|
|
322
|
-
text.addEventListener("dispose", () => texture.dispose());
|
|
323
|
-
return texture;
|
|
324
|
-
}
|
|
325
|
-
const dimColorVertexDefs = (
|
|
326
|
-
/*glsl*/
|
|
327
|
-
`
|
|
328
|
-
uniform float uDim;
|
|
329
|
-
out float dimAmount;
|
|
330
|
-
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
331
|
-
uniform sampler2D skipDimTexture;
|
|
332
|
-
#endif
|
|
333
|
-
void setDimAmount();
|
|
334
|
-
`
|
|
335
|
-
);
|
|
336
|
-
const dimColorVertexImpl = (
|
|
337
|
-
/*glsl*/
|
|
338
|
-
`
|
|
339
|
-
void setDimAmount() {
|
|
340
|
-
float instanceDim = 0.;
|
|
341
|
-
#ifdef USE_BATCH_UNIFORMS
|
|
342
|
-
instanceDim = batch_skipDimInstance;
|
|
343
|
-
#endif
|
|
344
|
-
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
345
|
-
float indirectIndex = aTroikaTextBatchMemberIndex;
|
|
346
|
-
int size = textureSize(skipDimTexture, 0).x;
|
|
347
|
-
int i = int(indirectIndex);
|
|
348
|
-
int x = i % size;
|
|
349
|
-
int y = i / size;
|
|
350
|
-
instanceDim = texelFetch(skipDimTexture, ivec2(x, y), 0).r;
|
|
351
|
-
#endif
|
|
352
|
-
dimAmount = instanceDim == 0. ? uDim : instanceDim / 2. + 0.5;
|
|
353
|
-
}
|
|
354
|
-
`
|
|
355
|
-
);
|
|
356
|
-
const dimColorFrag = (
|
|
357
|
-
/*glsl*/
|
|
358
|
-
`
|
|
359
|
-
in float dimAmount;
|
|
360
|
-
|
|
361
|
-
const vec3 grayWeights = vec3(0.299, 0.587, 0.114);
|
|
362
|
-
const float darkenFactor = pow(2., 2.2); // Gamma corrected
|
|
363
|
-
|
|
364
|
-
vec4 dimColor(vec4 col) {
|
|
365
|
-
vec3 color = col.rgb / col.a;
|
|
366
|
-
vec3 gray = vec3(dot(grayWeights, color));
|
|
367
|
-
vec3 m = mix(color, gray / darkenFactor, dimAmount);
|
|
368
|
-
return vec4(m * col.a, col.a);
|
|
369
|
-
}`
|
|
370
|
-
);
|
|
371
15
|
function createLogger(namespace) {
|
|
372
16
|
const fullNamespace = namespace ? `renderer:${namespace}` : "renderer";
|
|
373
17
|
const info = createLog(fullNamespace);
|
|
@@ -653,7 +297,7 @@ class SquareDataTexture extends DataTexture {
|
|
|
653
297
|
const componentsArray = ["r", "g", "b", "a"];
|
|
654
298
|
const batchIdName = "batchId";
|
|
655
299
|
const logger$a = createLogger("BatchedMesh");
|
|
656
|
-
|
|
300
|
+
class BatchedMesh extends BatchedMesh$1 {
|
|
657
301
|
/**
|
|
658
302
|
* @param instanceCount the max number of individual geometries planned to be added.
|
|
659
303
|
* @param vertexCount the max number of vertices to be used by all geometries.
|
|
@@ -671,13 +315,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
671
315
|
__publicField(this, "geometryById", /* @__PURE__ */ new Map());
|
|
672
316
|
__publicField(this, "mapGeometryToInstanceId", /* @__PURE__ */ new Map());
|
|
673
317
|
material.forceSinglePass = true;
|
|
674
|
-
addDim(this);
|
|
675
|
-
this.addEventListener("added", () => {
|
|
676
|
-
if (!_BatchedMesh.useMultiDraw && this.geometry.index !== null) {
|
|
677
|
-
this.geometry = this.geometry.toNonIndexed();
|
|
678
|
-
this.resizeToFitGeometry(this.geometry);
|
|
679
|
-
}
|
|
680
|
-
});
|
|
318
|
+
addDim(this);
|
|
681
319
|
}
|
|
682
320
|
/**
|
|
683
321
|
* Appends uniform definitions to the current schema, and creates a new {@link SquareDataTexture} if needed.
|
|
@@ -698,45 +336,18 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
698
336
|
if (!this.isMaterialPatched) this.patchMaterial(this.material);
|
|
699
337
|
}
|
|
700
338
|
addGeometry(geometry, reservedVertexRange, reservedIndexRange) {
|
|
701
|
-
|
|
702
|
-
this.addBatchIdBuffer(geometry, this.instanceCount);
|
|
703
|
-
const geometryId = super.addGeometry(geometry, reservedVertexRange, reservedIndexRange);
|
|
704
|
-
this.geometryById.set(geometryId, geometry);
|
|
705
|
-
return geometryId;
|
|
339
|
+
return super.addGeometry(geometry, reservedVertexRange, reservedIndexRange);
|
|
706
340
|
}
|
|
707
341
|
addInstance(geometryId) {
|
|
708
|
-
|
|
709
|
-
if (this.mapGeometryToInstanceId.has(geometryId)) {
|
|
710
|
-
const geometry = this.geometryById.get(geometryId);
|
|
711
|
-
this.resizeToFitGeometry(geometry);
|
|
712
|
-
geometryId = this.addGeometry(geometry);
|
|
713
|
-
}
|
|
714
|
-
const instanceId = super.addInstance(geometryId);
|
|
715
|
-
this.mapGeometryToInstanceId.set(geometryId, instanceId);
|
|
716
|
-
return instanceId;
|
|
342
|
+
return super.addInstance(geometryId);
|
|
717
343
|
}
|
|
718
344
|
onBeforeRender(renderer, scene, camera, geometry, material, group) {
|
|
719
345
|
var _a2;
|
|
720
346
|
(_a2 = this.uniformsTexture) == null ? void 0 : _a2.update();
|
|
721
|
-
|
|
722
|
-
if (!this.indexBuffer) {
|
|
723
|
-
const vertexCount = geometry.getAttribute("position").count;
|
|
724
|
-
this.indexBuffer = new BufferAttribute(new Uint32Array(vertexCount), 1).setUsage(StreamDrawUsage);
|
|
725
|
-
}
|
|
726
|
-
super.onBeforeRender(renderer, scene, camera, geometry, material, group);
|
|
727
|
-
this.batchCount = this.updateIndexBuffer(geometry);
|
|
728
|
-
this._multiDrawCount = 0;
|
|
347
|
+
return super.onBeforeRender(renderer, scene, camera, geometry, material, group);
|
|
729
348
|
}
|
|
730
349
|
onAfterRender(renderer, scene, camera, geometry) {
|
|
731
|
-
|
|
732
|
-
if (_BatchedMesh.useMultiDraw) return;
|
|
733
|
-
const batchCount = this.batchCount;
|
|
734
|
-
const gl = renderer.getContext();
|
|
735
|
-
if (geometry.index == null) return logger$a.debug("No index buffer", (_a2 = this.parent) == null ? void 0 : _a2.name);
|
|
736
|
-
const type = this.getIndexType(gl, geometry.index);
|
|
737
|
-
gl.drawElements(gl.TRIANGLES, batchCount, type, 0);
|
|
738
|
-
renderer.info.update(batchCount, gl.TRIANGLES, 1);
|
|
739
|
-
geometry.setIndex(null);
|
|
350
|
+
return;
|
|
740
351
|
}
|
|
741
352
|
/**
|
|
742
353
|
* Retrieves the value of a uniform at the specified instance ID.
|
|
@@ -845,19 +456,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
845
456
|
vertex: "",
|
|
846
457
|
fragment: ""
|
|
847
458
|
};
|
|
848
|
-
const patch = (
|
|
849
|
-
/*glsl*/
|
|
850
|
-
`
|
|
851
|
-
#ifdef gl_DrawID
|
|
852
|
-
#define _gl_DrawID ${batchIdName}
|
|
853
|
-
#else
|
|
854
|
-
#define gl_DrawID ${batchIdName}
|
|
855
|
-
#endif
|
|
856
|
-
in int ${batchIdName};
|
|
857
|
-
`
|
|
858
|
-
);
|
|
859
459
|
const main = "void main() {";
|
|
860
|
-
if (!_BatchedMesh.useMultiDraw) shader.vertexShader = shader.vertexShader.replace(main, `${patch}${main}`);
|
|
861
460
|
if (vertex) shader.vertexShader = shader.vertexShader.replace(main, vertex);
|
|
862
461
|
if (fragment) shader.fragmentShader = shader.fragmentShader.replace(main, fragment);
|
|
863
462
|
};
|
|
@@ -881,66 +480,421 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
881
480
|
uniforms.push({ name, type, size });
|
|
882
481
|
fetchInFragmentShader = false;
|
|
883
482
|
}
|
|
884
|
-
for (const name in fragmentSchema) {
|
|
885
|
-
if (!vertexSchema[name]) {
|
|
886
|
-
const type = fragmentSchema[name];
|
|
887
|
-
const size = this.getUniformSize(type);
|
|
888
|
-
totalSize += size;
|
|
889
|
-
uniforms.push({ name, type, size });
|
|
483
|
+
for (const name in fragmentSchema) {
|
|
484
|
+
if (!vertexSchema[name]) {
|
|
485
|
+
const type = fragmentSchema[name];
|
|
486
|
+
const size = this.getUniformSize(type);
|
|
487
|
+
totalSize += size;
|
|
488
|
+
uniforms.push({ name, type, size });
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
uniforms.sort((a, b) => b.size - a.size);
|
|
492
|
+
const tempOffset = [];
|
|
493
|
+
for (const { name, size, type } of uniforms) {
|
|
494
|
+
const offset = this.getUniformOffset(size, tempOffset);
|
|
495
|
+
uniformMap.set(name, { offset, size, type });
|
|
496
|
+
}
|
|
497
|
+
const pixelsPerInstance = Math.ceil(totalSize / 4);
|
|
498
|
+
const channels = Math.min(totalSize, 4);
|
|
499
|
+
return { channels, texelsPerInstance: pixelsPerInstance, uniformMap, fetchInFragmentShader };
|
|
500
|
+
}
|
|
501
|
+
getUniformOffset(size, tempOffset) {
|
|
502
|
+
if (size < 4) {
|
|
503
|
+
for (let i = 0; i < tempOffset.length; i++) {
|
|
504
|
+
if (tempOffset[i] + size <= 4) {
|
|
505
|
+
const offset2 = i * 4 + tempOffset[i];
|
|
506
|
+
tempOffset[i] += size;
|
|
507
|
+
return offset2;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const offset = tempOffset.length * 4;
|
|
512
|
+
for (; size > 0; size -= 4) {
|
|
513
|
+
tempOffset.push(size);
|
|
514
|
+
}
|
|
515
|
+
return offset;
|
|
516
|
+
}
|
|
517
|
+
getUniformSize(type) {
|
|
518
|
+
switch (type) {
|
|
519
|
+
case "float":
|
|
520
|
+
return 1;
|
|
521
|
+
case "vec2":
|
|
522
|
+
return 2;
|
|
523
|
+
case "vec3":
|
|
524
|
+
return 3;
|
|
525
|
+
case "vec4":
|
|
526
|
+
return 4;
|
|
527
|
+
case "mat3":
|
|
528
|
+
return 9;
|
|
529
|
+
case "mat4":
|
|
530
|
+
return 16;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
getIndexType(gl, index) {
|
|
534
|
+
const array = index.array;
|
|
535
|
+
if (array instanceof Uint16Array) return gl.UNSIGNED_SHORT;
|
|
536
|
+
if (array instanceof Uint32Array) return gl.UNSIGNED_INT;
|
|
537
|
+
return gl.UNSIGNED_BYTE;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
/** Whether to use WebGL_multi_draw extension or less performant fallback */
|
|
541
|
+
__publicField(BatchedMesh, "useMultiDraw", true);
|
|
542
|
+
const floatsPerMember = 32;
|
|
543
|
+
const tempColor = new Color();
|
|
544
|
+
const defaultStrokeColor = 8421504;
|
|
545
|
+
const tempMat4 = new Matrix4();
|
|
546
|
+
const tempVec3a = new Vector3();
|
|
547
|
+
const tempVec3b = new Vector3();
|
|
548
|
+
const origin = new Vector3();
|
|
549
|
+
const defaultOrient = "+x+y";
|
|
550
|
+
class BatchedText extends BatchedText$1 {
|
|
551
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
552
|
+
constructor() {
|
|
553
|
+
super();
|
|
554
|
+
__publicField(this, "mapInstanceIdToText", /* @__PURE__ */ new Map());
|
|
555
|
+
__publicField(this, "textArray", []);
|
|
556
|
+
__publicField(this, "textureNeedsUpdate", false);
|
|
557
|
+
}
|
|
558
|
+
/** Number of texts in the batch */
|
|
559
|
+
get size() {
|
|
560
|
+
return this._members.size;
|
|
561
|
+
}
|
|
562
|
+
/** Base material before patching */
|
|
563
|
+
get baseMaterial() {
|
|
564
|
+
return this._baseMaterial;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get the {@link Text} object by instance id
|
|
568
|
+
* @param instanceId Instance id
|
|
569
|
+
* @returns Text object
|
|
570
|
+
*/
|
|
571
|
+
getText(instanceId) {
|
|
572
|
+
return this.mapInstanceIdToText.get(instanceId);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Set the visibility of the {@link Text} object by instance id.
|
|
576
|
+
* This is for interface compatibility with {@link BatchedMesh}.
|
|
577
|
+
* @param instanceId Instance id
|
|
578
|
+
* @param visible Visibility flag
|
|
579
|
+
*/
|
|
580
|
+
setVisibleAt(instanceId, visible) {
|
|
581
|
+
const text = this.getText(instanceId);
|
|
582
|
+
text.visible = visible;
|
|
583
|
+
}
|
|
584
|
+
addText(text, instanceId) {
|
|
585
|
+
super.addText(text);
|
|
586
|
+
if (instanceId !== void 0) {
|
|
587
|
+
this.mapInstanceIdToText.set(instanceId, text);
|
|
588
|
+
}
|
|
589
|
+
this.textArray.push(text);
|
|
590
|
+
}
|
|
591
|
+
dispose() {
|
|
592
|
+
super.dispose();
|
|
593
|
+
this.dispatchEvent({ type: "dispose" });
|
|
594
|
+
}
|
|
595
|
+
// TODO: Check performance
|
|
596
|
+
_prepareForRender(material) {
|
|
597
|
+
var _a2;
|
|
598
|
+
const isOutline = material.isTextOutlineMaterial;
|
|
599
|
+
material.uniforms.uTroikaIsOutline.value = isOutline;
|
|
600
|
+
let texture = this._dataTextures[isOutline ? "outline" : "main"];
|
|
601
|
+
const dataLength = Math.pow(2, Math.ceil(Math.log2(this._members.size * floatsPerMember)));
|
|
602
|
+
if (!texture || dataLength !== texture.image.data.length) {
|
|
603
|
+
if (texture) texture.dispose();
|
|
604
|
+
const width = Math.min(dataLength / 4, 1024);
|
|
605
|
+
texture = this._dataTextures[isOutline ? "outline" : "main"] = new DataTexture(
|
|
606
|
+
new Float32Array(dataLength),
|
|
607
|
+
width,
|
|
608
|
+
dataLength / 4 / width,
|
|
609
|
+
RGBAFormat,
|
|
610
|
+
FloatType
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
const texData = texture.image.data;
|
|
614
|
+
this.textureNeedsUpdate = false;
|
|
615
|
+
for (const text of this.textArray) {
|
|
616
|
+
const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
|
|
617
|
+
const textRenderInfo = text.textRenderInfo;
|
|
618
|
+
if (index < 0 || !textRenderInfo) continue;
|
|
619
|
+
const startIndex = index * floatsPerMember;
|
|
620
|
+
if (!text.visible) {
|
|
621
|
+
for (let i = 0; i < 16; i++) {
|
|
622
|
+
this.setTexData(startIndex + i, 0, texData);
|
|
623
|
+
}
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const matrix = text.matrix.elements;
|
|
627
|
+
for (let i = 0; i < 16; i++) {
|
|
628
|
+
this.setTexData(startIndex + i, matrix[i], texData);
|
|
629
|
+
}
|
|
630
|
+
text._prepareForRender(material);
|
|
631
|
+
const {
|
|
632
|
+
uTroikaTotalBounds,
|
|
633
|
+
uTroikaClipRect,
|
|
634
|
+
uTroikaPositionOffset,
|
|
635
|
+
uTroikaEdgeOffset,
|
|
636
|
+
uTroikaBlurRadius,
|
|
637
|
+
uTroikaStrokeWidth,
|
|
638
|
+
uTroikaStrokeColor,
|
|
639
|
+
uTroikaStrokeOpacity,
|
|
640
|
+
uTroikaFillOpacity,
|
|
641
|
+
uTroikaCurveRadius
|
|
642
|
+
} = material.uniforms;
|
|
643
|
+
for (let i = 0; i < 4; i++) {
|
|
644
|
+
this.setTexData(startIndex + 16 + i, uTroikaTotalBounds.value.getComponent(i), texData);
|
|
645
|
+
}
|
|
646
|
+
for (let i = 0; i < 4; i++) {
|
|
647
|
+
this.setTexData(startIndex + 20 + i, uTroikaClipRect.value.getComponent(i), texData);
|
|
648
|
+
}
|
|
649
|
+
let color = isOutline ? text.outlineColor || 0 : text.color;
|
|
650
|
+
color ?? (color = this.color);
|
|
651
|
+
color ?? (color = this.material.color);
|
|
652
|
+
color ?? (color = 16777215);
|
|
653
|
+
this.setTexData(startIndex + 24, tempColor.set(color).getHex(), texData);
|
|
654
|
+
this.setTexData(startIndex + 25, uTroikaFillOpacity.value, texData);
|
|
655
|
+
this.setTexData(startIndex + 26, uTroikaCurveRadius.value, texData);
|
|
656
|
+
if (isOutline) {
|
|
657
|
+
this.setTexData(startIndex + 28, uTroikaPositionOffset.value.x, texData);
|
|
658
|
+
this.setTexData(startIndex + 29, uTroikaPositionOffset.value.y, texData);
|
|
659
|
+
this.setTexData(startIndex + 30, uTroikaEdgeOffset.value, texData);
|
|
660
|
+
this.setTexData(startIndex + 31, uTroikaBlurRadius.value, texData);
|
|
661
|
+
} else {
|
|
662
|
+
this.setTexData(startIndex + 28, uTroikaStrokeWidth.value, texData);
|
|
663
|
+
this.setTexData(startIndex + 29, tempColor.set(uTroikaStrokeColor.value).getHex(), texData);
|
|
664
|
+
this.setTexData(startIndex + 30, uTroikaStrokeOpacity.value, texData);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
texture.needsUpdate = this.textureNeedsUpdate;
|
|
668
|
+
material.setMatrixTexture(texture);
|
|
669
|
+
}
|
|
670
|
+
setTexData(index, value, texData) {
|
|
671
|
+
if (value !== texData[index]) {
|
|
672
|
+
texData[index] = value;
|
|
673
|
+
this.textureNeedsUpdate = true;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
class Text extends Text$1 {
|
|
678
|
+
_prepareForRender(material) {
|
|
679
|
+
const isOutline = material.isTextOutlineMaterial;
|
|
680
|
+
const uniforms = material.uniforms;
|
|
681
|
+
const textInfo = this.textRenderInfo;
|
|
682
|
+
if (textInfo) {
|
|
683
|
+
const { sdfTexture, blockBounds } = textInfo;
|
|
684
|
+
const { width, height } = sdfTexture.image;
|
|
685
|
+
uniforms.uTroikaSDFTexture.value = sdfTexture;
|
|
686
|
+
uniforms.uTroikaSDFTextureSize.value.set(width, height);
|
|
687
|
+
uniforms.uTroikaSDFGlyphSize.value = textInfo.sdfGlyphSize;
|
|
688
|
+
uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent;
|
|
689
|
+
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds);
|
|
690
|
+
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors;
|
|
691
|
+
let distanceOffset = 0;
|
|
692
|
+
let blurRadius = 0;
|
|
693
|
+
let strokeWidth = 0;
|
|
694
|
+
let fillOpacity;
|
|
695
|
+
let strokeOpacity = 1;
|
|
696
|
+
let strokeColor;
|
|
697
|
+
let offsetX = 0;
|
|
698
|
+
let offsetY = 0;
|
|
699
|
+
if (isOutline) {
|
|
700
|
+
const { outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity } = this;
|
|
701
|
+
distanceOffset = this._parsePercent(outlineWidth) || 0;
|
|
702
|
+
blurRadius = Math.max(0, this._parsePercent(outlineBlur) || 0);
|
|
703
|
+
fillOpacity = outlineOpacity;
|
|
704
|
+
offsetX = this._parsePercent(outlineOffsetX) || 0;
|
|
705
|
+
offsetY = this._parsePercent(outlineOffsetY) || 0;
|
|
706
|
+
} else {
|
|
707
|
+
strokeWidth = Math.max(0, this._parsePercent(this.strokeWidth) || 0);
|
|
708
|
+
if (strokeWidth) {
|
|
709
|
+
strokeColor = this.strokeColor;
|
|
710
|
+
uniforms.uTroikaStrokeColor.value.set(strokeColor ?? defaultStrokeColor);
|
|
711
|
+
strokeOpacity = this.strokeOpacity;
|
|
712
|
+
strokeOpacity ?? (strokeOpacity = 1);
|
|
713
|
+
}
|
|
714
|
+
fillOpacity = this.fillOpacity;
|
|
715
|
+
}
|
|
716
|
+
uniforms.uTroikaEdgeOffset.value = distanceOffset;
|
|
717
|
+
uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY);
|
|
718
|
+
uniforms.uTroikaBlurRadius.value = blurRadius;
|
|
719
|
+
uniforms.uTroikaStrokeWidth.value = strokeWidth;
|
|
720
|
+
uniforms.uTroikaStrokeOpacity.value = strokeOpacity;
|
|
721
|
+
uniforms.uTroikaFillOpacity.value = fillOpacity ?? 1;
|
|
722
|
+
uniforms.uTroikaCurveRadius.value = this.curveRadius || 0;
|
|
723
|
+
const clipRect = this.clipRect;
|
|
724
|
+
if (clipRect && Array.isArray(clipRect) && clipRect.length === 4) {
|
|
725
|
+
uniforms.uTroikaClipRect.value.fromArray(clipRect);
|
|
726
|
+
} else {
|
|
727
|
+
const pad = (this.fontSize || 0.1) * 100;
|
|
728
|
+
uniforms.uTroikaClipRect.value.set(
|
|
729
|
+
blockBounds[0] - pad,
|
|
730
|
+
blockBounds[1] - pad,
|
|
731
|
+
blockBounds[2] + pad,
|
|
732
|
+
blockBounds[3] + pad
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
|
|
736
|
+
}
|
|
737
|
+
uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
|
|
738
|
+
material.polygonOffset = !!this.depthOffset;
|
|
739
|
+
material.polygonOffsetFactor = material.polygonOffsetUnits = this.depthOffset || 0;
|
|
740
|
+
const color = isOutline ? this.outlineColor || 0 : this.color;
|
|
741
|
+
if (color == null) {
|
|
742
|
+
delete material.color;
|
|
743
|
+
} else {
|
|
744
|
+
const colorObj = material.hasOwnProperty("color") ? material.color : material.color = new Color();
|
|
745
|
+
if (color !== colorObj._input || typeof color === "object") {
|
|
746
|
+
colorObj.set(colorObj._input = color);
|
|
890
747
|
}
|
|
891
748
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
749
|
+
let orient = this.orientation || defaultOrient;
|
|
750
|
+
if (orient !== material._orientation) {
|
|
751
|
+
const rotMat = uniforms.uTroikaOrient.value;
|
|
752
|
+
orient = orient.replace(/[^-+xyz]/g, "");
|
|
753
|
+
const match = orient !== defaultOrient && /^([-+])([xyz])([-+])([xyz])$/.exec(orient);
|
|
754
|
+
if (match) {
|
|
755
|
+
const [, hSign, hAxis, vSign, vAxis] = match;
|
|
756
|
+
tempVec3a.set(0, 0, 0)[hAxis] = hSign === "-" ? 1 : -1;
|
|
757
|
+
tempVec3b.set(0, 0, 0)[vAxis] = vSign === "-" ? -1 : 1;
|
|
758
|
+
tempMat4.lookAt(origin, tempVec3a.cross(tempVec3b), tempVec3b);
|
|
759
|
+
rotMat.setFromMatrix4(tempMat4);
|
|
760
|
+
} else {
|
|
761
|
+
rotMat.identity();
|
|
762
|
+
}
|
|
763
|
+
material._orientation = orient;
|
|
897
764
|
}
|
|
898
|
-
const pixelsPerInstance = Math.ceil(totalSize / 4);
|
|
899
|
-
const channels = Math.min(totalSize, 4);
|
|
900
|
-
return { channels, texelsPerInstance: pixelsPerInstance, uniformMap, fetchInFragmentShader };
|
|
901
765
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
766
|
+
}
|
|
767
|
+
function setDimming(root, dim) {
|
|
768
|
+
root.userData["uDim"] = dim === void 0 ? void 0 : +dim;
|
|
769
|
+
}
|
|
770
|
+
function toggleInstanceDim(object, instanceId, dim) {
|
|
771
|
+
const value = dim === void 0 ? 0 : (+dim - 0.5) * 2;
|
|
772
|
+
if (object instanceof BatchedMesh) {
|
|
773
|
+
object.setUniformAt(instanceId, "skipDimInstance", value);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
const skipDimTexture = object.userData["skipDimTexture"];
|
|
777
|
+
if (skipDimTexture) {
|
|
778
|
+
const skipDimData = skipDimTexture.image.data;
|
|
779
|
+
skipDimData[instanceId] = value;
|
|
780
|
+
skipDimTexture.needsUpdate = true;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
function addDimToMaterial(material) {
|
|
784
|
+
if (material.userData.hasDimShader) return;
|
|
785
|
+
const onBeforeCompile = material.onBeforeCompile.bind(material);
|
|
786
|
+
const onBeforeRender = material.onBeforeRender.bind(material);
|
|
787
|
+
material.onBeforeCompile = (shader, renderer) => {
|
|
788
|
+
onBeforeCompile(shader, renderer);
|
|
789
|
+
shader.uniforms["uDim"] = { value: material.userData.uDim ?? 0 };
|
|
790
|
+
shader.uniforms["skipDimTexture"] = { value: material.userData.skipDimTexture ?? null };
|
|
791
|
+
shader.vertexShader = shader.vertexShader.replace("void main() {", `${dimColorVertexDefs}
|
|
792
|
+
void main() {`).replace(
|
|
793
|
+
"#include <fog_vertex>",
|
|
794
|
+
/*glsl*/
|
|
795
|
+
`
|
|
796
|
+
#include <fog_vertex>
|
|
797
|
+
setDimAmount();
|
|
798
|
+
`
|
|
799
|
+
).concat(dimColorVertexImpl);
|
|
800
|
+
shader.fragmentShader = /*glsl*/
|
|
801
|
+
`
|
|
802
|
+
${dimColorFrag}
|
|
803
|
+
${shader.fragmentShader}
|
|
804
|
+
`.replace(
|
|
805
|
+
"#include <colorspace_fragment>",
|
|
806
|
+
/*glsl*/
|
|
807
|
+
`
|
|
808
|
+
gl_FragColor = dimColor(gl_FragColor);
|
|
809
|
+
#include <colorspace_fragment>
|
|
810
|
+
`
|
|
811
|
+
);
|
|
812
|
+
material.userData.shader = shader;
|
|
813
|
+
};
|
|
814
|
+
material.onBeforeRender = (renderer, scene, camera, geometry, object, group) => {
|
|
815
|
+
onBeforeRender(renderer, scene, camera, geometry, object, group);
|
|
816
|
+
const skipDimTexture = object.userData["skipDimTexture"];
|
|
817
|
+
let uDim = object.userData["uDim"];
|
|
818
|
+
if (uDim === void 0) {
|
|
819
|
+
for (const ancestor of traverseAncestorsGenerator(object)) {
|
|
820
|
+
if (ancestor.userData["uDim"] !== void 0) {
|
|
821
|
+
uDim = ancestor.userData["uDim"];
|
|
822
|
+
break;
|
|
909
823
|
}
|
|
910
824
|
}
|
|
911
825
|
}
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
}
|
|
918
|
-
getUniformSize(type) {
|
|
919
|
-
switch (type) {
|
|
920
|
-
case "float":
|
|
921
|
-
return 1;
|
|
922
|
-
case "vec2":
|
|
923
|
-
return 2;
|
|
924
|
-
case "vec3":
|
|
925
|
-
return 3;
|
|
926
|
-
case "vec4":
|
|
927
|
-
return 4;
|
|
928
|
-
case "mat3":
|
|
929
|
-
return 9;
|
|
930
|
-
case "mat4":
|
|
931
|
-
return 16;
|
|
826
|
+
const shader = material.userData.shader;
|
|
827
|
+
if (!shader) {
|
|
828
|
+
material.userData.uDim = uDim;
|
|
829
|
+
material.userData.skipDimTexture = object.userData["skipDimTexture"];
|
|
830
|
+
return;
|
|
932
831
|
}
|
|
832
|
+
shader.uniforms["uDim"].value = uDim ?? 0;
|
|
833
|
+
shader.uniforms["skipDimTexture"].value = skipDimTexture ?? null;
|
|
834
|
+
};
|
|
835
|
+
material.userData.hasDimShader = true;
|
|
836
|
+
}
|
|
837
|
+
function addDim(mesh) {
|
|
838
|
+
if (mesh instanceof BatchedMesh) mesh.addPerInstanceUniforms({ vertex: { skipDimInstance: "float" } });
|
|
839
|
+
if (mesh instanceof BatchedText) addSkipDimTexture(mesh);
|
|
840
|
+
}
|
|
841
|
+
function addSkipDimTexture(text) {
|
|
842
|
+
const count = text.size;
|
|
843
|
+
const size = Math.ceil(Math.sqrt(count));
|
|
844
|
+
const array = new Float32Array(size * size);
|
|
845
|
+
array.fill(0);
|
|
846
|
+
const texture = new DataTexture(array, size, size, RedFormat, FloatType);
|
|
847
|
+
texture.needsUpdate = true;
|
|
848
|
+
text.userData["skipDimTexture"] = texture;
|
|
849
|
+
text.addEventListener("dispose", () => texture.dispose());
|
|
850
|
+
return texture;
|
|
851
|
+
}
|
|
852
|
+
const dimColorVertexDefs = (
|
|
853
|
+
/*glsl*/
|
|
854
|
+
`
|
|
855
|
+
uniform float uDim;
|
|
856
|
+
out float dimAmount;
|
|
857
|
+
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
858
|
+
uniform sampler2D skipDimTexture;
|
|
859
|
+
#endif
|
|
860
|
+
void setDimAmount();
|
|
861
|
+
`
|
|
862
|
+
);
|
|
863
|
+
const dimColorVertexImpl = (
|
|
864
|
+
/*glsl*/
|
|
865
|
+
`
|
|
866
|
+
void setDimAmount() {
|
|
867
|
+
float instanceDim = 0.;
|
|
868
|
+
#ifdef USE_BATCH_UNIFORMS
|
|
869
|
+
instanceDim = batch_skipDimInstance;
|
|
870
|
+
#endif
|
|
871
|
+
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
872
|
+
float indirectIndex = aTroikaTextBatchMemberIndex;
|
|
873
|
+
int size = textureSize(skipDimTexture, 0).x;
|
|
874
|
+
int i = int(indirectIndex);
|
|
875
|
+
int x = i % size;
|
|
876
|
+
int y = i / size;
|
|
877
|
+
instanceDim = texelFetch(skipDimTexture, ivec2(x, y), 0).r;
|
|
878
|
+
#endif
|
|
879
|
+
dimAmount = instanceDim == 0. ? uDim : instanceDim / 2. + 0.5;
|
|
933
880
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
881
|
+
`
|
|
882
|
+
);
|
|
883
|
+
const dimColorFrag = (
|
|
884
|
+
/*glsl*/
|
|
885
|
+
`
|
|
886
|
+
in float dimAmount;
|
|
887
|
+
|
|
888
|
+
const vec3 grayWeights = vec3(0.299, 0.587, 0.114);
|
|
889
|
+
const float darkenFactor = pow(2., 2.2); // Gamma corrected
|
|
890
|
+
|
|
891
|
+
vec4 dimColor(vec4 col) {
|
|
892
|
+
vec3 color = col.rgb / col.a;
|
|
893
|
+
vec3 gray = vec3(dot(grayWeights, color));
|
|
894
|
+
vec3 m = mix(color, gray / darkenFactor, dimAmount);
|
|
895
|
+
return vec4(m * col.a, col.a);
|
|
896
|
+
}`
|
|
897
|
+
);
|
|
944
898
|
const sharedParameters = {
|
|
945
899
|
side: DoubleSide,
|
|
946
900
|
transparent: true,
|
|
@@ -1451,14 +1405,15 @@ class ImageSystem extends RenderableSystem {
|
|
|
1451
1405
|
logger$9.debug(`New memory usage after resizing: ${newTotal} bytes`);
|
|
1452
1406
|
}
|
|
1453
1407
|
updateDefImpl(imageDef, mesh, instanceIds) {
|
|
1408
|
+
const instanceId = instanceIds[0];
|
|
1454
1409
|
const bounds = imageDef.bounds;
|
|
1455
1410
|
const origin2 = imageDef.origin ?? [0.5, 0.5];
|
|
1456
1411
|
this.originTranslationMatrix.makeTranslation(0.5 - origin2[0], 0.5 - origin2[1], 0);
|
|
1457
1412
|
this.globalTranslationMatrix.makeTranslation(bounds.center.x, bounds.center.y, 0);
|
|
1458
|
-
this.scaleMatrix.makeScale(bounds.size.
|
|
1413
|
+
this.scaleMatrix.makeScale(bounds.size.x, bounds.size.y, 1);
|
|
1459
1414
|
this.rotationMatrix.makeRotationZ(bounds.rotation);
|
|
1460
1415
|
const matrix = this.originTranslationMatrix.premultiply(this.scaleMatrix).premultiply(this.rotationMatrix).premultiply(this.globalTranslationMatrix);
|
|
1461
|
-
mesh.setMatrixAt(
|
|
1416
|
+
mesh.setMatrixAt(instanceId, matrix);
|
|
1462
1417
|
}
|
|
1463
1418
|
packImages(images) {
|
|
1464
1419
|
this.packer.reset();
|
|
@@ -1472,8 +1427,8 @@ class ImageSystem extends RenderableSystem {
|
|
|
1472
1427
|
const sourceWidth = image.source.width;
|
|
1473
1428
|
const sourceHeight = image.source.height;
|
|
1474
1429
|
const sourceArea = sourceWidth * sourceHeight;
|
|
1475
|
-
const boundsWidth = image.bounds.size.
|
|
1476
|
-
const boundsHeight = image.bounds.size.
|
|
1430
|
+
const boundsWidth = image.bounds.size.x;
|
|
1431
|
+
const boundsHeight = image.bounds.size.y;
|
|
1477
1432
|
const boundsArea = boundsWidth * boundsHeight;
|
|
1478
1433
|
const ratio = sourceArea / boundsArea;
|
|
1479
1434
|
if (ratio > 1e3) {
|
|
@@ -1593,13 +1548,10 @@ class LineSystem extends RenderableSystem {
|
|
|
1593
1548
|
}
|
|
1594
1549
|
}
|
|
1595
1550
|
function createVector2(vector2) {
|
|
1596
|
-
if (vector2 instanceof Vector2) return vector2;
|
|
1597
1551
|
if (Array.isArray(vector2)) return new Vector2(vector2[0], vector2[1]);
|
|
1598
1552
|
return new Vector2(vector2.x, vector2.y);
|
|
1599
1553
|
}
|
|
1600
1554
|
function createVector3(vector3) {
|
|
1601
|
-
if (vector3 instanceof Vector2) return new Vector3(vector3.x, vector3.y, 0);
|
|
1602
|
-
if (vector3 instanceof Vector3) return vector3;
|
|
1603
1555
|
if (Array.isArray(vector3)) return new Vector3(vector3[0], vector3[1], vector3[2] ?? 0);
|
|
1604
1556
|
return new Vector3(vector3.x, vector3.y, "z" in vector3 ? vector3.z : 0);
|
|
1605
1557
|
}
|
|
@@ -1610,28 +1562,53 @@ class Rect {
|
|
|
1610
1562
|
* @param rotation Optional rotation of the rectangle. In radians, around center. Positive values rotate clockwise.
|
|
1611
1563
|
*/
|
|
1612
1564
|
constructor(min, max, rotation) {
|
|
1613
|
-
/** Top left corner of the rectangle. */
|
|
1614
|
-
__publicField(this, "min");
|
|
1615
|
-
/** Bottom right corner of the rectangle. */
|
|
1616
|
-
__publicField(this, "max");
|
|
1617
1565
|
/** Optional rotation of the rectangle. In radians, around center. Positive values rotate clockwise. */
|
|
1618
1566
|
__publicField(this, "rotation");
|
|
1619
|
-
__publicField(this, "
|
|
1620
|
-
__publicField(this, "
|
|
1621
|
-
this
|
|
1622
|
-
this
|
|
1567
|
+
__publicField(this, "_min");
|
|
1568
|
+
__publicField(this, "_max");
|
|
1569
|
+
__publicField(this, "_center", new Vector2());
|
|
1570
|
+
__publicField(this, "_size", new Vector2());
|
|
1571
|
+
this._min = createVector2(min);
|
|
1572
|
+
this._max = createVector2(max);
|
|
1623
1573
|
this.rotation = rotation ?? 0;
|
|
1574
|
+
this.updateCenterAndSize();
|
|
1575
|
+
}
|
|
1576
|
+
/** Top left corner of the rectangle. */
|
|
1577
|
+
get min() {
|
|
1578
|
+
return this._min;
|
|
1579
|
+
}
|
|
1580
|
+
/** Set top left corner of the rectangle. */
|
|
1581
|
+
set min(min) {
|
|
1582
|
+
this._min.copy(min);
|
|
1583
|
+
this.updateCenterAndSize();
|
|
1584
|
+
}
|
|
1585
|
+
/** Bottom right corner of the rectangle. */
|
|
1586
|
+
get max() {
|
|
1587
|
+
return this._max;
|
|
1624
1588
|
}
|
|
1625
|
-
/**
|
|
1589
|
+
/** Set bottom right corner of the rectangle. */
|
|
1590
|
+
set max(max) {
|
|
1591
|
+
this._max.copy(max);
|
|
1592
|
+
this.updateCenterAndSize();
|
|
1593
|
+
}
|
|
1594
|
+
/** Center of the rectangle. */
|
|
1626
1595
|
get center() {
|
|
1627
|
-
this._center ?? (this._center = this.min.clone().add(this.max).multiplyScalar(0.5));
|
|
1628
1596
|
return this._center;
|
|
1629
1597
|
}
|
|
1630
|
-
/**
|
|
1598
|
+
/** Set center of the rectangle. */
|
|
1599
|
+
set center(center) {
|
|
1600
|
+
this._center.copy(center);
|
|
1601
|
+
this.updateMinAndMax();
|
|
1602
|
+
}
|
|
1603
|
+
/** Size of the rectangle. */
|
|
1631
1604
|
get size() {
|
|
1632
|
-
this._size ?? (this._size = this.max.clone().sub(this.min));
|
|
1633
1605
|
return this._size;
|
|
1634
1606
|
}
|
|
1607
|
+
/** Set size of the rectangle. */
|
|
1608
|
+
set size(size) {
|
|
1609
|
+
this._size.copy(size);
|
|
1610
|
+
this.updateMinAndMax();
|
|
1611
|
+
}
|
|
1635
1612
|
/**
|
|
1636
1613
|
* Creates a rectangle from an SVG rectangle element.
|
|
1637
1614
|
* @param rect {@link SVGRectElement} or {@link SVGImageElement}
|
|
@@ -1646,18 +1623,37 @@ class Rect {
|
|
|
1646
1623
|
return new Rect([x, y], [x + width, y + height], rotation);
|
|
1647
1624
|
}
|
|
1648
1625
|
/**
|
|
1649
|
-
*
|
|
1650
|
-
* @param
|
|
1651
|
-
* @
|
|
1626
|
+
* Moves the rectangle by the given offset.
|
|
1627
|
+
* @param offset Offset to move the rectangle by.
|
|
1628
|
+
* @returns this {@link Rect} instance
|
|
1629
|
+
*/
|
|
1630
|
+
translate(offset) {
|
|
1631
|
+
this._center.add(offset);
|
|
1632
|
+
this.updateMinAndMax();
|
|
1633
|
+
return this;
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Expands the rectangle by the given amount on all sides.
|
|
1637
|
+
* Use positive values to increase the size of the rectangle, negative values to decrease it.
|
|
1638
|
+
* @param expansion Can be a single number or a 2D vector. If a single number is provided, both horizontal and vertical sizes will be expanded by the same amount.
|
|
1652
1639
|
* @returns this {@link Rect} instance
|
|
1653
1640
|
*/
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
this.
|
|
1657
|
-
this.
|
|
1658
|
-
this._size
|
|
1641
|
+
expand(expansion) {
|
|
1642
|
+
const expansionVector = typeof expansion === "number" ? { x: expansion, y: expansion } : expansion;
|
|
1643
|
+
this._min.sub(expansionVector);
|
|
1644
|
+
this._max.add(expansionVector);
|
|
1645
|
+
this._size.subVectors(this._max, this._min);
|
|
1659
1646
|
return this;
|
|
1660
1647
|
}
|
|
1648
|
+
updateCenterAndSize() {
|
|
1649
|
+
this._center.addVectors(this._min, this._max).multiplyScalar(0.5);
|
|
1650
|
+
this._size.subVectors(this._max, this._min);
|
|
1651
|
+
}
|
|
1652
|
+
updateMinAndMax() {
|
|
1653
|
+
const halfSize = tempVector2.copy(this._size).multiplyScalar(0.5);
|
|
1654
|
+
this._min.subVectors(this._center, halfSize);
|
|
1655
|
+
this._max.addVectors(this._center, halfSize);
|
|
1656
|
+
}
|
|
1661
1657
|
}
|
|
1662
1658
|
class Polygon {
|
|
1663
1659
|
/**
|
|
@@ -1665,12 +1661,24 @@ class Polygon {
|
|
|
1665
1661
|
* @param indices Array of polygon indices. Each index is a triplet of vertex indices forming a triangle.
|
|
1666
1662
|
*/
|
|
1667
1663
|
constructor(vertices, indices) {
|
|
1668
|
-
|
|
1669
|
-
__publicField(this, "
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
this.
|
|
1673
|
-
this.
|
|
1664
|
+
__publicField(this, "_indices");
|
|
1665
|
+
__publicField(this, "_vertices");
|
|
1666
|
+
__publicField(this, "_bbox");
|
|
1667
|
+
this._vertices = vertices.map(createVector3);
|
|
1668
|
+
this._indices = indices;
|
|
1669
|
+
this._bbox = this.computeBoundingRect();
|
|
1670
|
+
}
|
|
1671
|
+
/** Array of polygon vertices. */
|
|
1672
|
+
get vertices() {
|
|
1673
|
+
return this._vertices;
|
|
1674
|
+
}
|
|
1675
|
+
/** Array of polygon indices. Each index is a triplet of vertex indices forming a triangle. */
|
|
1676
|
+
get indices() {
|
|
1677
|
+
return this._indices;
|
|
1678
|
+
}
|
|
1679
|
+
/** Bounding rectangle of the polygon. */
|
|
1680
|
+
get bounds() {
|
|
1681
|
+
return this._bbox;
|
|
1674
1682
|
}
|
|
1675
1683
|
/**
|
|
1676
1684
|
* Converts a {@link Rect} to a {@link Polygon}.
|
|
@@ -1699,28 +1707,76 @@ class Polygon {
|
|
|
1699
1707
|
let indexOffset = 0;
|
|
1700
1708
|
const vertices = [];
|
|
1701
1709
|
const indices = [];
|
|
1702
|
-
for (const
|
|
1703
|
-
vertices.push(...
|
|
1704
|
-
indices.push(...
|
|
1705
|
-
indexOffset +=
|
|
1710
|
+
for (const p of polygons) {
|
|
1711
|
+
vertices.push(...p.vertices);
|
|
1712
|
+
indices.push(...p.indices.map(([i1, i2, i3]) => [i1 + indexOffset, i2 + indexOffset, i3 + indexOffset]));
|
|
1713
|
+
indexOffset += p.vertices.length;
|
|
1706
1714
|
}
|
|
1707
1715
|
return new Polygon(vertices, indices);
|
|
1708
1716
|
}
|
|
1709
1717
|
/**
|
|
1710
|
-
*
|
|
1718
|
+
* Translates all vertices of the polygon by the given offset.
|
|
1719
|
+
* @param offset Offset to translate the polygon by. Can be a 2D or 3D vector.
|
|
1720
|
+
* @returns this {@link Polygon} instance
|
|
1721
|
+
*/
|
|
1722
|
+
translate(offset) {
|
|
1723
|
+
var _a2;
|
|
1724
|
+
const vec3 = { z: 0, ...offset };
|
|
1725
|
+
this._vertices.forEach((vertex) => vertex.add(vec3));
|
|
1726
|
+
(_a2 = this._bbox) == null ? void 0 : _a2.translate(offset);
|
|
1727
|
+
return this;
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Rotates all vertices of the polygon around the given center. Only 2D rotation (around Z axis) is supported.
|
|
1711
1731
|
* @param rotation Rotation angle in radians. Positive values rotate clockwise.
|
|
1712
|
-
* @param center Center of the rotation.
|
|
1732
|
+
* @param center Center of the rotation. If omitted, defaults to the bounding rectangle center.
|
|
1733
|
+
* @returns this {@link Polygon} instance
|
|
1734
|
+
*/
|
|
1735
|
+
rotate(rotation, center = this.bounds.center) {
|
|
1736
|
+
this._vertices.forEach((vertex) => {
|
|
1737
|
+
tempVector2.set(vertex.x, vertex.y).rotateAround(center, rotation);
|
|
1738
|
+
vertex.set(tempVector2.x, tempVector2.y, vertex.z);
|
|
1739
|
+
});
|
|
1740
|
+
this._bbox = this.computeBoundingRect();
|
|
1741
|
+
return this;
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* Scales the polygon around the given origin.
|
|
1745
|
+
* @param scaleFactor Can be a single number or a 2D vector. If a single number is provided, both horizontal and vertical axes will be scaled by the same factor.
|
|
1746
|
+
* @param origin Origin of the scaling. If omitted, defaults to the bounding rectangle center.
|
|
1713
1747
|
* @returns this {@link Polygon} instance
|
|
1714
1748
|
*/
|
|
1715
|
-
|
|
1716
|
-
const
|
|
1717
|
-
this.
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
vertex.set(tempVec2.x, tempVec2.y, vertex.z);
|
|
1749
|
+
scale(scaleFactor, origin2 = this.bounds.center) {
|
|
1750
|
+
const scaleVector = typeof scaleFactor === "number" ? { x: scaleFactor, y: scaleFactor } : scaleFactor;
|
|
1751
|
+
this._vertices.forEach((vertex) => {
|
|
1752
|
+
tempVector2.set(vertex.x, vertex.y).sub(origin2).multiply(scaleVector).add(origin2);
|
|
1753
|
+
vertex.set(tempVector2.x, tempVector2.y, vertex.z);
|
|
1721
1754
|
});
|
|
1755
|
+
this._bbox = this.computeBoundingRect();
|
|
1722
1756
|
return this;
|
|
1723
1757
|
}
|
|
1758
|
+
computeBoundingRect() {
|
|
1759
|
+
let minX = Infinity;
|
|
1760
|
+
let minY = Infinity;
|
|
1761
|
+
let maxX = -Infinity;
|
|
1762
|
+
let maxY = -Infinity;
|
|
1763
|
+
for (const vertex of this.vertices) {
|
|
1764
|
+
minX = Math.min(minX, vertex.x);
|
|
1765
|
+
minY = Math.min(minY, vertex.y);
|
|
1766
|
+
maxX = Math.max(maxX, vertex.x);
|
|
1767
|
+
maxY = Math.max(maxY, vertex.y);
|
|
1768
|
+
}
|
|
1769
|
+
return new Rect([minX, minY], [maxX, maxY]);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
const tempVector2 = new Vector2();
|
|
1773
|
+
function countGeometry(geometry) {
|
|
1774
|
+
var _a2;
|
|
1775
|
+
if (geometry.index == null) return { vertices: geometry.getAttribute("position").count, indices: 0 };
|
|
1776
|
+
return {
|
|
1777
|
+
vertices: geometry.getAttribute("position").count,
|
|
1778
|
+
indices: ((_a2 = geometry.index) == null ? void 0 : _a2.count) ?? 0
|
|
1779
|
+
};
|
|
1724
1780
|
}
|
|
1725
1781
|
const logger$7 = createLogger("mesh");
|
|
1726
1782
|
extend([namesPlugin]);
|
|
@@ -1731,8 +1787,19 @@ class MeshSystem extends RenderableSystem {
|
|
|
1731
1787
|
*/
|
|
1732
1788
|
constructor(materialSystem, renderer) {
|
|
1733
1789
|
super("mesh", renderer, logger$7);
|
|
1734
|
-
__publicField(this, "
|
|
1790
|
+
__publicField(this, "color", new Color());
|
|
1791
|
+
__publicField(this, "position", new Vector3());
|
|
1792
|
+
__publicField(this, "rotation", new Quaternion());
|
|
1793
|
+
__publicField(this, "scale", new Vector3());
|
|
1794
|
+
__publicField(this, "matrix", new Matrix4());
|
|
1795
|
+
__publicField(this, "rectGeometry", new PlaneGeometry(1, 1));
|
|
1796
|
+
__publicField(this, "placeholderPolygonGeometry", new BufferGeometry());
|
|
1797
|
+
__publicField(this, "mapInstanceIdToShapeType", /* @__PURE__ */ new Map());
|
|
1735
1798
|
this.materialSystem = materialSystem;
|
|
1799
|
+
this.rectGeometry.deleteAttribute("normal");
|
|
1800
|
+
this.rectGeometry.deleteAttribute("uv");
|
|
1801
|
+
this.placeholderPolygonGeometry.setAttribute("position", new BufferAttribute(new Float32Array(), 3));
|
|
1802
|
+
this.placeholderPolygonGeometry.index = new BufferAttribute(new Uint32Array(), 0);
|
|
1736
1803
|
}
|
|
1737
1804
|
buildLayer(layer) {
|
|
1738
1805
|
const shapes = layer.children;
|
|
@@ -1769,60 +1836,82 @@ class MeshSystem extends RenderableSystem {
|
|
|
1769
1836
|
return group;
|
|
1770
1837
|
}
|
|
1771
1838
|
updateDefImpl(shapeDef, mesh, instanceIds) {
|
|
1839
|
+
const instanceId = instanceIds[0];
|
|
1840
|
+
this.updateShape(shapeDef, mesh, instanceId);
|
|
1841
|
+
this.updateColor(shapeDef, mesh, instanceId);
|
|
1842
|
+
}
|
|
1843
|
+
updateShape(shapeDef, mesh, instanceId) {
|
|
1844
|
+
const geometryId = mesh.getGeometryIdAt(instanceId);
|
|
1845
|
+
const key = `${mesh.uuid}-${instanceId}`;
|
|
1846
|
+
const expectedShapeType = this.mapInstanceIdToShapeType.get(key);
|
|
1847
|
+
const shape = shapeDef.shape;
|
|
1848
|
+
const isPolygon = shape instanceof Polygon;
|
|
1849
|
+
const isRect = shape instanceof Rect;
|
|
1850
|
+
if (expectedShapeType === "polygon" && !isPolygon || expectedShapeType === "rect" && !isRect) {
|
|
1851
|
+
logger$7.warn("Shape type changing not supported %O", shapeDef);
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
if (isPolygon) {
|
|
1855
|
+
const geometryRange = mesh.getGeometryRangeAt(geometryId);
|
|
1856
|
+
if (!geometryRange || shape.vertices.length != geometryRange.reservedVertexCount || shape.indices.length * 3 != geometryRange.reservedIndexCount) {
|
|
1857
|
+
logger$7.warn("Polygon geometry changing not supported %O", shapeDef);
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
mesh.setGeometryAt(geometryId, this.buildPolygonGeometry(shape));
|
|
1861
|
+
} else if (isRect) {
|
|
1862
|
+
this.position.set(shape.center.x, shape.center.y, 0);
|
|
1863
|
+
this.rotation.setFromAxisAngle(new Vector3(0, 0, 1), shape.rotation ?? 0);
|
|
1864
|
+
this.scale.set(shape.size.x, shape.size.y, 1);
|
|
1865
|
+
this.matrix.compose(this.position, this.rotation, this.scale);
|
|
1866
|
+
mesh.setMatrixAt(instanceId, this.matrix);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
updateColor(shapeDef, mesh, instanceId) {
|
|
1772
1870
|
const color = this.normalizeColor(shapeDef.color);
|
|
1773
1871
|
if (!color) {
|
|
1774
1872
|
logger$7.warn(`Invalid color: ${shapeDef.color} %O`, shapeDef);
|
|
1775
1873
|
return;
|
|
1776
1874
|
}
|
|
1777
|
-
|
|
1778
|
-
mesh.setColorAt(instanceId, this.meshColor.setRGB(color.r / 255, color.g / 255, color.b / 255, SRGBColorSpace));
|
|
1779
|
-
}
|
|
1875
|
+
mesh.setColorAt(instanceId, this.color.setRGB(color.r / 255, color.g / 255, color.b / 255, SRGBColorSpace));
|
|
1780
1876
|
}
|
|
1781
1877
|
buildBatchedMesh(shapes, opacity = 1) {
|
|
1782
|
-
var _a2, _b;
|
|
1783
1878
|
let vertexCount = 0;
|
|
1784
1879
|
let indexCount = 0;
|
|
1785
|
-
let
|
|
1880
|
+
let rectAdded = false;
|
|
1786
1881
|
const shapeDefToGeometry = /* @__PURE__ */ new Map();
|
|
1787
1882
|
for (const shapeDef of shapes) {
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
indexCount += ((_a2 = rectGeometry.index) == null ? void 0 : _a2.count) ?? 0;
|
|
1883
|
+
let vertices = 0;
|
|
1884
|
+
let indices = 0;
|
|
1885
|
+
if (shapeDef.shape instanceof Rect && !rectAdded) {
|
|
1886
|
+
rectAdded = true;
|
|
1887
|
+
({ vertices, indices } = countGeometry(this.rectGeometry));
|
|
1794
1888
|
} else if (shapeDef.shape instanceof Polygon) {
|
|
1795
1889
|
const geometry = this.buildPolygonGeometry(shapeDef.shape);
|
|
1796
1890
|
shapeDefToGeometry.set(shapeDef, geometry);
|
|
1797
|
-
|
|
1798
|
-
indexCount += ((_b = geometry.index) == null ? void 0 : _b.count) ?? 0;
|
|
1891
|
+
({ vertices, indices } = countGeometry(geometry));
|
|
1799
1892
|
}
|
|
1893
|
+
vertexCount += vertices;
|
|
1894
|
+
indexCount += indices;
|
|
1800
1895
|
}
|
|
1801
1896
|
const material = this.materialSystem.createColorMaterial({ opacity });
|
|
1802
1897
|
const batchedMesh = new BatchedMesh(shapes.length, vertexCount, indexCount, material);
|
|
1803
|
-
const rectGeometryId =
|
|
1898
|
+
const rectGeometryId = rectAdded ? batchedMesh.addGeometry(this.rectGeometry) : void 0;
|
|
1804
1899
|
batchedMesh.setCustomSort((list) => this.sortInstances(batchedMesh, list));
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
const matrix = new Matrix4();
|
|
1809
|
-
const [rects, polygons] = partition(shapes, (shapeDef) => shapeDef.shape instanceof Rect);
|
|
1810
|
-
const sortedShapes = [...rects, ...polygons];
|
|
1811
|
-
for (const shapeDef of sortedShapes) {
|
|
1812
|
-
let instanceId = void 0;
|
|
1900
|
+
for (const shapeDef of shapes) {
|
|
1901
|
+
let instanceId;
|
|
1902
|
+
let type;
|
|
1813
1903
|
if (shapeDef.shape instanceof Rect && rectGeometryId !== void 0) {
|
|
1814
1904
|
instanceId = batchedMesh.addInstance(rectGeometryId);
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
scale.set(shapeDef.shape.size.x, shapeDef.shape.size.y, 1);
|
|
1818
|
-
matrix.compose(position, rotation, scale);
|
|
1819
|
-
batchedMesh.setMatrixAt(instanceId, matrix);
|
|
1820
|
-
} else if (shapeDef.shape instanceof Polygon) {
|
|
1905
|
+
type = "rect";
|
|
1906
|
+
} else {
|
|
1821
1907
|
const geometry = shapeDefToGeometry.get(shapeDef);
|
|
1822
|
-
const
|
|
1908
|
+
const { vertices, indices } = countGeometry(geometry);
|
|
1909
|
+
const polygonGeometryId = batchedMesh.addGeometry(this.placeholderPolygonGeometry, vertices, indices);
|
|
1823
1910
|
instanceId = batchedMesh.addInstance(polygonGeometryId);
|
|
1911
|
+
type = "polygon";
|
|
1824
1912
|
}
|
|
1825
|
-
|
|
1913
|
+
const key = `${batchedMesh.uuid}-${instanceId}`;
|
|
1914
|
+
this.mapInstanceIdToShapeType.set(key, type);
|
|
1826
1915
|
this.registerDefObject(shapeDef, batchedMesh, instanceId);
|
|
1827
1916
|
}
|
|
1828
1917
|
return batchedMesh;
|
|
@@ -1834,8 +1923,7 @@ class MeshSystem extends RenderableSystem {
|
|
|
1834
1923
|
return color.toRgb();
|
|
1835
1924
|
}
|
|
1836
1925
|
buildPolygonGeometry(polygon) {
|
|
1837
|
-
|
|
1838
|
-
return geometry;
|
|
1926
|
+
return new BufferGeometry().setFromPoints(polygon.vertices).setIndex(polygon.indices.flat());
|
|
1839
1927
|
}
|
|
1840
1928
|
sortInstances(mesh, list) {
|
|
1841
1929
|
const shapeDefs = this.getDefsByObject(mesh);
|
|
@@ -2005,11 +2093,10 @@ class TextSystem extends RenderableSystem {
|
|
|
2005
2093
|
return lines;
|
|
2006
2094
|
}
|
|
2007
2095
|
calculateStartInBoundsPosition(textDef, lines, alignmentDirection, alignmentOffset, inBoundsPosition) {
|
|
2008
|
-
const [w, h] = textDef.bounds.size;
|
|
2009
2096
|
const padding = textDef.padding;
|
|
2010
2097
|
const alignment = textDef.alignment;
|
|
2011
2098
|
alignmentDirection.set(...getAlignmentDirection(alignment));
|
|
2012
|
-
inBoundsPosition.
|
|
2099
|
+
inBoundsPosition.copy(textDef.bounds.size).multiplyScalar(0.5);
|
|
2013
2100
|
if (alignment.vertical === "center") {
|
|
2014
2101
|
const totalTextHeight = lines.filter((l) => l.height !== void 0).reduce((acc, l) => acc + l.height, 0) * this.renderer.context.getPixelRatio();
|
|
2015
2102
|
alignmentOffset.set(0, -(textDef.bounds.size.y - totalTextHeight) / 2);
|
|
@@ -4788,7 +4875,6 @@ class CameraController extends CameraControls {
|
|
|
4788
4875
|
__publicField(this, "touchCancelListener");
|
|
4789
4876
|
this.dollyToCursor = true;
|
|
4790
4877
|
this.draggingSmoothTime = 0;
|
|
4791
|
-
void this.rotatePolarTo(0, false);
|
|
4792
4878
|
this.mouseButtons = {
|
|
4793
4879
|
left: CameraController.ACTION.NONE,
|
|
4794
4880
|
middle: CameraController.ACTION.NONE,
|
|
@@ -4845,9 +4931,8 @@ class CameraSystem {
|
|
|
4845
4931
|
const h = renderer.size[1];
|
|
4846
4932
|
this.prevViewportHeightPx = h;
|
|
4847
4933
|
this.camera = new PerspectiveCamera(this.defaultFov);
|
|
4848
|
-
this.camera.up.set(0, 0, -1);
|
|
4849
4934
|
this.controller = new CameraController(this.camera);
|
|
4850
|
-
this.controller.
|
|
4935
|
+
void this.controller.rotatePolarTo(0, false);
|
|
4851
4936
|
}
|
|
4852
4937
|
/** Current camera zoom factor. */
|
|
4853
4938
|
get zoomFactor() {
|
|
@@ -4869,6 +4954,12 @@ class CameraSystem {
|
|
|
4869
4954
|
initCamera(zoomBounds) {
|
|
4870
4955
|
this.zoomBounds = zoomBounds;
|
|
4871
4956
|
this.updateCamera();
|
|
4957
|
+
this.camera.up.set(0, -1, 0);
|
|
4958
|
+
this.camera.position.set(0, 0, -this.zoomIdentityDistance);
|
|
4959
|
+
this.camera.lookAt(0, 0, 0);
|
|
4960
|
+
this.camera.updateMatrixWorld();
|
|
4961
|
+
this.camera.up.set(0, 0, -1);
|
|
4962
|
+
this.controller.updateCameraUp();
|
|
4872
4963
|
}
|
|
4873
4964
|
/** Updates the camera when the renderer size changes. */
|
|
4874
4965
|
updateCamera() {
|
|
@@ -5070,7 +5161,10 @@ class SceneSystem {
|
|
|
5070
5161
|
__publicField(this, "worldMatrix", new Matrix4());
|
|
5071
5162
|
/** Inverse world matrix - world → model transform */
|
|
5072
5163
|
__publicField(this, "inverseWorldMatrix", new Matrix4());
|
|
5164
|
+
/** Used as a scratch vector for coordinate space conversions */
|
|
5073
5165
|
__publicField(this, "tempVector3", new Vector3());
|
|
5166
|
+
/** Used as a scratch vector for matrix calculations */
|
|
5167
|
+
__publicField(this, "tempVector2", new Vector2());
|
|
5074
5168
|
__publicField(this, "translationMatrix", new Matrix4());
|
|
5075
5169
|
__publicField(this, "scaleMatrix", new Matrix4());
|
|
5076
5170
|
__publicField(this, "visibleRectOffsetMatrix", new Matrix4());
|
|
@@ -5121,14 +5215,14 @@ class SceneSystem {
|
|
|
5121
5215
|
composeMatrices(viewbox) {
|
|
5122
5216
|
const dpr = this.renderer.context.getPixelRatio();
|
|
5123
5217
|
const visibleRect = this.renderer.visibleRect;
|
|
5124
|
-
const
|
|
5125
|
-
|
|
5126
|
-
const scaleFactor = Math.min(
|
|
5127
|
-
const
|
|
5218
|
+
const visibleRectSize = visibleRect ? this.tempVector2.copy(visibleRect.size).multiplyScalar(dpr) : this.tempVector2.set(...this.renderer.size);
|
|
5219
|
+
visibleRectSize.divide(viewbox.size);
|
|
5220
|
+
const scaleFactor = Math.min(visibleRectSize.width, visibleRectSize.height);
|
|
5221
|
+
const { x: centerX, y: centerY } = viewbox.center;
|
|
5128
5222
|
this.translationMatrix.makeTranslation(-centerX, -centerY, 0);
|
|
5129
5223
|
this.scaleMatrix.makeScale(scaleFactor, scaleFactor, 1);
|
|
5130
5224
|
if (visibleRect) {
|
|
5131
|
-
const visibleRectCenter = visibleRect.center
|
|
5225
|
+
const visibleRectCenter = this.tempVector2.copy(visibleRect.center).multiplyScalar(dpr);
|
|
5132
5226
|
const canvasCenter = { x: this.renderer.size[0] / 2, y: this.renderer.size[1] / 2 };
|
|
5133
5227
|
const offset = visibleRectCenter.sub(canvasCenter);
|
|
5134
5228
|
this.visibleRectOffsetMatrix.makeTranslation(offset.x, offset.y, 0);
|
|
@@ -5198,7 +5292,7 @@ class ViewportSystem {
|
|
|
5198
5292
|
*/
|
|
5199
5293
|
initViewport(sceneDef) {
|
|
5200
5294
|
if (!this.renderer.isExternalMode) this.sceneSystem.initScene(sceneDef.viewbox);
|
|
5201
|
-
this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.
|
|
5295
|
+
this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.x > 1e5 ? 100 : 35]);
|
|
5202
5296
|
}
|
|
5203
5297
|
/** Updates the viewport when the renderer size changes. */
|
|
5204
5298
|
updateViewport() {
|
|
@@ -5355,27 +5449,23 @@ class ControlsSystem {
|
|
|
5355
5449
|
const dpr = this.renderer.context.getPixelRatio();
|
|
5356
5450
|
const visibleRect = this.renderer.visibleRect;
|
|
5357
5451
|
const bearingAngle = -this.controller.azimuthAngle;
|
|
5358
|
-
const
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
const worldPolygon = Polygon.fromRect(worldRect).rotate(bearingAngle, worldRect.center);
|
|
5362
|
-
const xValues = worldPolygon.vertices.map((p) => p.x);
|
|
5363
|
-
const yValues = worldPolygon.vertices.map((p) => p.y);
|
|
5364
|
-
const sourceRect = new Rect(
|
|
5365
|
-
[Math.min(...xValues), Math.min(...yValues)],
|
|
5366
|
-
[Math.max(...xValues), Math.max(...yValues)]
|
|
5452
|
+
const worldRect = new Rect(
|
|
5453
|
+
this.viewportSystem.modelToWorld(rect.min),
|
|
5454
|
+
this.viewportSystem.modelToWorld(rect.max)
|
|
5367
5455
|
);
|
|
5456
|
+
const sourceRect = Polygon.fromRect(worldRect).rotate(bearingAngle).bounds;
|
|
5368
5457
|
if (sourceRect.size.x <= 0 || sourceRect.size.y <= 0) {
|
|
5369
5458
|
logger$1.warn("zoomTo: sourceRect size is 0");
|
|
5370
5459
|
return;
|
|
5371
5460
|
}
|
|
5372
|
-
const targetRect = visibleRect ? new Rect(visibleRect.min.
|
|
5373
|
-
if (paddingPercent)
|
|
5461
|
+
const targetRect = visibleRect ? new Rect([visibleRect.min.x * dpr, visibleRect.min.y * dpr], [visibleRect.max.x * dpr, visibleRect.max.y * dpr]) : new Rect([0, 0], [...this.renderer.size]);
|
|
5462
|
+
if (paddingPercent)
|
|
5463
|
+
targetRect.expand({ x: -targetRect.size.x * paddingPercent, y: -targetRect.size.y * paddingPercent });
|
|
5374
5464
|
const zoomByWidth = targetRect.size.x / sourceRect.size.x;
|
|
5375
5465
|
const zoomByHeight = targetRect.size.y / sourceRect.size.y;
|
|
5376
5466
|
const minZoom = Math.min(zoomByWidth, zoomByHeight);
|
|
5377
5467
|
const zoom = maxZoom ? Math.min(minZoom, maxZoom) : minZoom;
|
|
5378
|
-
const translate = sourceRect.center;
|
|
5468
|
+
const translate = new Vector2().copy(sourceRect.center);
|
|
5379
5469
|
if (visibleRect) {
|
|
5380
5470
|
const offset = new Vector2(...this.renderer.size).multiplyScalar(0.5).sub(targetRect.center).multiplyScalar(1 / (zoom || 1)).rotateAround({ x: 0, y: 0 }, bearingAngle);
|
|
5381
5471
|
translate.add(offset);
|
|
@@ -6280,8 +6370,7 @@ class Renderer {
|
|
|
6280
6370
|
this.controlsSystem = new ControlsSystem(this, this.viewportSystem, this.interactionsSystem);
|
|
6281
6371
|
this.canvas.addEventListener("webglcontextlost", (e) => this.onContextLost(e), false);
|
|
6282
6372
|
this.canvas.addEventListener("webglcontextrestored", (e) => this.onContextRestored(e), false);
|
|
6283
|
-
this.
|
|
6284
|
-
BatchedMesh.useMultiDraw = this.renderer.extensions.has("WEBGL_multi_draw");
|
|
6373
|
+
this.initStatsContext(this.renderer.getContext());
|
|
6285
6374
|
}
|
|
6286
6375
|
/**
|
|
6287
6376
|
* {@link ControlsAPI} instance for controlling the viewport
|
|
@@ -6494,7 +6583,11 @@ class Renderer {
|
|
|
6494
6583
|
const logMarker = `memoryInfo [${elapsedTime.toFixed(2)}ms since start]`;
|
|
6495
6584
|
logger.debug(logMarker, memoryInfo);
|
|
6496
6585
|
this.memoryInfo = memoryInfo;
|
|
6497
|
-
this.ui.memoryInfoPanel.textContent = JSON.stringify(
|
|
6586
|
+
this.ui.memoryInfoPanel.textContent = JSON.stringify(
|
|
6587
|
+
memoryInfo,
|
|
6588
|
+
(_, value) => typeof value === "number" ? value.toLocaleString() : value,
|
|
6589
|
+
2
|
|
6590
|
+
).replaceAll('"', "");
|
|
6498
6591
|
}
|
|
6499
6592
|
}
|
|
6500
6593
|
}
|
|
@@ -6507,7 +6600,7 @@ class Renderer {
|
|
|
6507
6600
|
if (stats && "deleteQuery" in context) {
|
|
6508
6601
|
const gpuQueries = stats.gpuQueries;
|
|
6509
6602
|
for (const queryInfo of gpuQueries) {
|
|
6510
|
-
|
|
6603
|
+
context.deleteQuery(queryInfo.query);
|
|
6511
6604
|
}
|
|
6512
6605
|
stats.gpuQueries = [];
|
|
6513
6606
|
if (stats.gpuPanel) {
|
|
@@ -6521,11 +6614,11 @@ class Renderer {
|
|
|
6521
6614
|
onContextRestored(event) {
|
|
6522
6615
|
event.preventDefault();
|
|
6523
6616
|
logger.debug("webglcontextrestored event", event);
|
|
6524
|
-
this.
|
|
6617
|
+
this.initStatsContext(this.renderer.getContext());
|
|
6525
6618
|
this.needsRedraw = true;
|
|
6526
6619
|
this.start();
|
|
6527
6620
|
}
|
|
6528
|
-
|
|
6621
|
+
initStatsContext(context) {
|
|
6529
6622
|
var _a2, _b;
|
|
6530
6623
|
this.memoryInfoExtension = context.getExtension("GMAN_webgl_memory");
|
|
6531
6624
|
void ((_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.init(context));
|