@expofp/renderer 2.1.2 → 2.2.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 +70 -26
- package/dist/index.js +656 -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,78 @@ 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 expectedShapeType = this.mapInstanceIdToShapeType.get(instanceId);
|
|
1846
|
+
const shape = shapeDef.shape;
|
|
1847
|
+
const isPolygon = shape instanceof Polygon;
|
|
1848
|
+
const isRect = shape instanceof Rect;
|
|
1849
|
+
if (expectedShapeType === "polygon" && !isPolygon || expectedShapeType === "rect" && !isRect) {
|
|
1850
|
+
logger$7.warn("Shape type changing not supported %O", shapeDef);
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
if (isPolygon) {
|
|
1854
|
+
const geometryRange = mesh.getGeometryRangeAt(geometryId);
|
|
1855
|
+
if (!geometryRange || shape.vertices.length != geometryRange.reservedVertexCount || shape.indices.length * 3 != geometryRange.reservedIndexCount) {
|
|
1856
|
+
logger$7.warn("Polygon geometry changing not supported %O", shapeDef);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
mesh.setGeometryAt(geometryId, this.buildPolygonGeometry(shape));
|
|
1860
|
+
} else if (isRect) {
|
|
1861
|
+
this.position.set(shape.center.x, shape.center.y, 0);
|
|
1862
|
+
this.rotation.setFromAxisAngle(new Vector3(0, 0, 1), shape.rotation ?? 0);
|
|
1863
|
+
this.scale.set(shape.size.x, shape.size.y, 1);
|
|
1864
|
+
this.matrix.compose(this.position, this.rotation, this.scale);
|
|
1865
|
+
mesh.setMatrixAt(instanceId, this.matrix);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
updateColor(shapeDef, mesh, instanceId) {
|
|
1772
1869
|
const color = this.normalizeColor(shapeDef.color);
|
|
1773
1870
|
if (!color) {
|
|
1774
1871
|
logger$7.warn(`Invalid color: ${shapeDef.color} %O`, shapeDef);
|
|
1775
1872
|
return;
|
|
1776
1873
|
}
|
|
1777
|
-
|
|
1778
|
-
mesh.setColorAt(instanceId, this.meshColor.setRGB(color.r / 255, color.g / 255, color.b / 255, SRGBColorSpace));
|
|
1779
|
-
}
|
|
1874
|
+
mesh.setColorAt(instanceId, this.color.setRGB(color.r / 255, color.g / 255, color.b / 255, SRGBColorSpace));
|
|
1780
1875
|
}
|
|
1781
1876
|
buildBatchedMesh(shapes, opacity = 1) {
|
|
1782
|
-
var _a2, _b;
|
|
1783
1877
|
let vertexCount = 0;
|
|
1784
1878
|
let indexCount = 0;
|
|
1785
|
-
let
|
|
1879
|
+
let rectAdded = false;
|
|
1786
1880
|
const shapeDefToGeometry = /* @__PURE__ */ new Map();
|
|
1787
1881
|
for (const shapeDef of shapes) {
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
indexCount += ((_a2 = rectGeometry.index) == null ? void 0 : _a2.count) ?? 0;
|
|
1882
|
+
let vertices = 0;
|
|
1883
|
+
let indices = 0;
|
|
1884
|
+
if (shapeDef.shape instanceof Rect && !rectAdded) {
|
|
1885
|
+
rectAdded = true;
|
|
1886
|
+
({ vertices, indices } = countGeometry(this.rectGeometry));
|
|
1794
1887
|
} else if (shapeDef.shape instanceof Polygon) {
|
|
1795
1888
|
const geometry = this.buildPolygonGeometry(shapeDef.shape);
|
|
1796
1889
|
shapeDefToGeometry.set(shapeDef, geometry);
|
|
1797
|
-
|
|
1798
|
-
indexCount += ((_b = geometry.index) == null ? void 0 : _b.count) ?? 0;
|
|
1890
|
+
({ vertices, indices } = countGeometry(geometry));
|
|
1799
1891
|
}
|
|
1892
|
+
vertexCount += vertices;
|
|
1893
|
+
indexCount += indices;
|
|
1800
1894
|
}
|
|
1801
1895
|
const material = this.materialSystem.createColorMaterial({ opacity });
|
|
1802
1896
|
const batchedMesh = new BatchedMesh(shapes.length, vertexCount, indexCount, material);
|
|
1803
|
-
const rectGeometryId =
|
|
1897
|
+
const rectGeometryId = rectAdded ? batchedMesh.addGeometry(this.rectGeometry) : void 0;
|
|
1804
1898
|
batchedMesh.setCustomSort((list) => this.sortInstances(batchedMesh, list));
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
const scale = new Vector3();
|
|
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;
|
|
1899
|
+
for (const shapeDef of shapes) {
|
|
1900
|
+
let instanceId;
|
|
1813
1901
|
if (shapeDef.shape instanceof Rect && rectGeometryId !== void 0) {
|
|
1814
1902
|
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) {
|
|
1903
|
+
this.mapInstanceIdToShapeType.set(instanceId, "rect");
|
|
1904
|
+
} else {
|
|
1821
1905
|
const geometry = shapeDefToGeometry.get(shapeDef);
|
|
1822
|
-
const
|
|
1906
|
+
const { vertices, indices } = countGeometry(geometry);
|
|
1907
|
+
const polygonGeometryId = batchedMesh.addGeometry(this.placeholderPolygonGeometry, vertices, indices);
|
|
1823
1908
|
instanceId = batchedMesh.addInstance(polygonGeometryId);
|
|
1909
|
+
this.mapInstanceIdToShapeType.set(instanceId, "polygon");
|
|
1824
1910
|
}
|
|
1825
|
-
if (instanceId === void 0) continue;
|
|
1826
1911
|
this.registerDefObject(shapeDef, batchedMesh, instanceId);
|
|
1827
1912
|
}
|
|
1828
1913
|
return batchedMesh;
|
|
@@ -1834,8 +1919,7 @@ class MeshSystem extends RenderableSystem {
|
|
|
1834
1919
|
return color.toRgb();
|
|
1835
1920
|
}
|
|
1836
1921
|
buildPolygonGeometry(polygon) {
|
|
1837
|
-
|
|
1838
|
-
return geometry;
|
|
1922
|
+
return new BufferGeometry().setFromPoints(polygon.vertices).setIndex(polygon.indices.flat());
|
|
1839
1923
|
}
|
|
1840
1924
|
sortInstances(mesh, list) {
|
|
1841
1925
|
const shapeDefs = this.getDefsByObject(mesh);
|
|
@@ -2005,11 +2089,10 @@ class TextSystem extends RenderableSystem {
|
|
|
2005
2089
|
return lines;
|
|
2006
2090
|
}
|
|
2007
2091
|
calculateStartInBoundsPosition(textDef, lines, alignmentDirection, alignmentOffset, inBoundsPosition) {
|
|
2008
|
-
const [w, h] = textDef.bounds.size;
|
|
2009
2092
|
const padding = textDef.padding;
|
|
2010
2093
|
const alignment = textDef.alignment;
|
|
2011
2094
|
alignmentDirection.set(...getAlignmentDirection(alignment));
|
|
2012
|
-
inBoundsPosition.
|
|
2095
|
+
inBoundsPosition.copy(textDef.bounds.size).multiplyScalar(0.5);
|
|
2013
2096
|
if (alignment.vertical === "center") {
|
|
2014
2097
|
const totalTextHeight = lines.filter((l) => l.height !== void 0).reduce((acc, l) => acc + l.height, 0) * this.renderer.context.getPixelRatio();
|
|
2015
2098
|
alignmentOffset.set(0, -(textDef.bounds.size.y - totalTextHeight) / 2);
|
|
@@ -4788,7 +4871,6 @@ class CameraController extends CameraControls {
|
|
|
4788
4871
|
__publicField(this, "touchCancelListener");
|
|
4789
4872
|
this.dollyToCursor = true;
|
|
4790
4873
|
this.draggingSmoothTime = 0;
|
|
4791
|
-
void this.rotatePolarTo(0, false);
|
|
4792
4874
|
this.mouseButtons = {
|
|
4793
4875
|
left: CameraController.ACTION.NONE,
|
|
4794
4876
|
middle: CameraController.ACTION.NONE,
|
|
@@ -4845,9 +4927,8 @@ class CameraSystem {
|
|
|
4845
4927
|
const h = renderer.size[1];
|
|
4846
4928
|
this.prevViewportHeightPx = h;
|
|
4847
4929
|
this.camera = new PerspectiveCamera(this.defaultFov);
|
|
4848
|
-
this.camera.up.set(0, 0, -1);
|
|
4849
4930
|
this.controller = new CameraController(this.camera);
|
|
4850
|
-
this.controller.
|
|
4931
|
+
void this.controller.rotatePolarTo(0, false);
|
|
4851
4932
|
}
|
|
4852
4933
|
/** Current camera zoom factor. */
|
|
4853
4934
|
get zoomFactor() {
|
|
@@ -4869,6 +4950,12 @@ class CameraSystem {
|
|
|
4869
4950
|
initCamera(zoomBounds) {
|
|
4870
4951
|
this.zoomBounds = zoomBounds;
|
|
4871
4952
|
this.updateCamera();
|
|
4953
|
+
this.camera.up.set(0, -1, 0);
|
|
4954
|
+
this.camera.position.set(0, 0, -this.zoomIdentityDistance);
|
|
4955
|
+
this.camera.lookAt(0, 0, 0);
|
|
4956
|
+
this.camera.updateMatrixWorld();
|
|
4957
|
+
this.camera.up.set(0, 0, -1);
|
|
4958
|
+
this.controller.updateCameraUp();
|
|
4872
4959
|
}
|
|
4873
4960
|
/** Updates the camera when the renderer size changes. */
|
|
4874
4961
|
updateCamera() {
|
|
@@ -5070,7 +5157,10 @@ class SceneSystem {
|
|
|
5070
5157
|
__publicField(this, "worldMatrix", new Matrix4());
|
|
5071
5158
|
/** Inverse world matrix - world → model transform */
|
|
5072
5159
|
__publicField(this, "inverseWorldMatrix", new Matrix4());
|
|
5160
|
+
/** Used as a scratch vector for coordinate space conversions */
|
|
5073
5161
|
__publicField(this, "tempVector3", new Vector3());
|
|
5162
|
+
/** Used as a scratch vector for matrix calculations */
|
|
5163
|
+
__publicField(this, "tempVector2", new Vector2());
|
|
5074
5164
|
__publicField(this, "translationMatrix", new Matrix4());
|
|
5075
5165
|
__publicField(this, "scaleMatrix", new Matrix4());
|
|
5076
5166
|
__publicField(this, "visibleRectOffsetMatrix", new Matrix4());
|
|
@@ -5121,14 +5211,14 @@ class SceneSystem {
|
|
|
5121
5211
|
composeMatrices(viewbox) {
|
|
5122
5212
|
const dpr = this.renderer.context.getPixelRatio();
|
|
5123
5213
|
const visibleRect = this.renderer.visibleRect;
|
|
5124
|
-
const
|
|
5125
|
-
|
|
5126
|
-
const scaleFactor = Math.min(
|
|
5127
|
-
const
|
|
5214
|
+
const visibleRectSize = visibleRect ? this.tempVector2.copy(visibleRect.size).multiplyScalar(dpr) : this.tempVector2.set(...this.renderer.size);
|
|
5215
|
+
visibleRectSize.divide(viewbox.size);
|
|
5216
|
+
const scaleFactor = Math.min(visibleRectSize.width, visibleRectSize.height);
|
|
5217
|
+
const { x: centerX, y: centerY } = viewbox.center;
|
|
5128
5218
|
this.translationMatrix.makeTranslation(-centerX, -centerY, 0);
|
|
5129
5219
|
this.scaleMatrix.makeScale(scaleFactor, scaleFactor, 1);
|
|
5130
5220
|
if (visibleRect) {
|
|
5131
|
-
const visibleRectCenter = visibleRect.center
|
|
5221
|
+
const visibleRectCenter = this.tempVector2.copy(visibleRect.center).multiplyScalar(dpr);
|
|
5132
5222
|
const canvasCenter = { x: this.renderer.size[0] / 2, y: this.renderer.size[1] / 2 };
|
|
5133
5223
|
const offset = visibleRectCenter.sub(canvasCenter);
|
|
5134
5224
|
this.visibleRectOffsetMatrix.makeTranslation(offset.x, offset.y, 0);
|
|
@@ -5198,7 +5288,7 @@ class ViewportSystem {
|
|
|
5198
5288
|
*/
|
|
5199
5289
|
initViewport(sceneDef) {
|
|
5200
5290
|
if (!this.renderer.isExternalMode) this.sceneSystem.initScene(sceneDef.viewbox);
|
|
5201
|
-
this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.
|
|
5291
|
+
this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.x > 1e5 ? 100 : 35]);
|
|
5202
5292
|
}
|
|
5203
5293
|
/** Updates the viewport when the renderer size changes. */
|
|
5204
5294
|
updateViewport() {
|
|
@@ -5355,27 +5445,23 @@ class ControlsSystem {
|
|
|
5355
5445
|
const dpr = this.renderer.context.getPixelRatio();
|
|
5356
5446
|
const visibleRect = this.renderer.visibleRect;
|
|
5357
5447
|
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)]
|
|
5448
|
+
const worldRect = new Rect(
|
|
5449
|
+
this.viewportSystem.modelToWorld(rect.min),
|
|
5450
|
+
this.viewportSystem.modelToWorld(rect.max)
|
|
5367
5451
|
);
|
|
5452
|
+
const sourceRect = Polygon.fromRect(worldRect).rotate(bearingAngle).bounds;
|
|
5368
5453
|
if (sourceRect.size.x <= 0 || sourceRect.size.y <= 0) {
|
|
5369
5454
|
logger$1.warn("zoomTo: sourceRect size is 0");
|
|
5370
5455
|
return;
|
|
5371
5456
|
}
|
|
5372
|
-
const targetRect = visibleRect ? new Rect(visibleRect.min.
|
|
5373
|
-
if (paddingPercent)
|
|
5457
|
+
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]);
|
|
5458
|
+
if (paddingPercent)
|
|
5459
|
+
targetRect.expand({ x: -targetRect.size.x * paddingPercent, y: -targetRect.size.y * paddingPercent });
|
|
5374
5460
|
const zoomByWidth = targetRect.size.x / sourceRect.size.x;
|
|
5375
5461
|
const zoomByHeight = targetRect.size.y / sourceRect.size.y;
|
|
5376
5462
|
const minZoom = Math.min(zoomByWidth, zoomByHeight);
|
|
5377
5463
|
const zoom = maxZoom ? Math.min(minZoom, maxZoom) : minZoom;
|
|
5378
|
-
const translate = sourceRect.center;
|
|
5464
|
+
const translate = new Vector2().copy(sourceRect.center);
|
|
5379
5465
|
if (visibleRect) {
|
|
5380
5466
|
const offset = new Vector2(...this.renderer.size).multiplyScalar(0.5).sub(targetRect.center).multiplyScalar(1 / (zoom || 1)).rotateAround({ x: 0, y: 0 }, bearingAngle);
|
|
5381
5467
|
translate.add(offset);
|
|
@@ -6280,8 +6366,7 @@ class Renderer {
|
|
|
6280
6366
|
this.controlsSystem = new ControlsSystem(this, this.viewportSystem, this.interactionsSystem);
|
|
6281
6367
|
this.canvas.addEventListener("webglcontextlost", (e) => this.onContextLost(e), false);
|
|
6282
6368
|
this.canvas.addEventListener("webglcontextrestored", (e) => this.onContextRestored(e), false);
|
|
6283
|
-
this.
|
|
6284
|
-
BatchedMesh.useMultiDraw = this.renderer.extensions.has("WEBGL_multi_draw");
|
|
6369
|
+
this.initStatsContext(this.renderer.getContext());
|
|
6285
6370
|
}
|
|
6286
6371
|
/**
|
|
6287
6372
|
* {@link ControlsAPI} instance for controlling the viewport
|
|
@@ -6494,7 +6579,11 @@ class Renderer {
|
|
|
6494
6579
|
const logMarker = `memoryInfo [${elapsedTime.toFixed(2)}ms since start]`;
|
|
6495
6580
|
logger.debug(logMarker, memoryInfo);
|
|
6496
6581
|
this.memoryInfo = memoryInfo;
|
|
6497
|
-
this.ui.memoryInfoPanel.textContent = JSON.stringify(
|
|
6582
|
+
this.ui.memoryInfoPanel.textContent = JSON.stringify(
|
|
6583
|
+
memoryInfo,
|
|
6584
|
+
(_, value) => typeof value === "number" ? value.toLocaleString() : value,
|
|
6585
|
+
2
|
|
6586
|
+
).replaceAll('"', "");
|
|
6498
6587
|
}
|
|
6499
6588
|
}
|
|
6500
6589
|
}
|
|
@@ -6507,7 +6596,7 @@ class Renderer {
|
|
|
6507
6596
|
if (stats && "deleteQuery" in context) {
|
|
6508
6597
|
const gpuQueries = stats.gpuQueries;
|
|
6509
6598
|
for (const queryInfo of gpuQueries) {
|
|
6510
|
-
|
|
6599
|
+
context.deleteQuery(queryInfo.query);
|
|
6511
6600
|
}
|
|
6512
6601
|
stats.gpuQueries = [];
|
|
6513
6602
|
if (stats.gpuPanel) {
|
|
@@ -6521,11 +6610,11 @@ class Renderer {
|
|
|
6521
6610
|
onContextRestored(event) {
|
|
6522
6611
|
event.preventDefault();
|
|
6523
6612
|
logger.debug("webglcontextrestored event", event);
|
|
6524
|
-
this.
|
|
6613
|
+
this.initStatsContext(this.renderer.getContext());
|
|
6525
6614
|
this.needsRedraw = true;
|
|
6526
6615
|
this.start();
|
|
6527
6616
|
}
|
|
6528
|
-
|
|
6617
|
+
initStatsContext(context) {
|
|
6529
6618
|
var _a2, _b;
|
|
6530
6619
|
this.memoryInfoExtension = context.getExtension("GMAN_webgl_memory");
|
|
6531
6620
|
void ((_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.init(context));
|