@lingbi/studio 0.8.3 → 0.9.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/Application.d.ts +3 -0
- package/ImportProgressCompositor.d.ts +13 -0
- package/StudioToolbarState.d.ts +2 -0
- package/StudioWorkspace.d.ts +5 -0
- package/archive/ArchiveImportWorkerClient.d.ts +3 -1
- package/archive/ArchiveImportWorkerMessage.d.ts +7 -1
- package/archive/ArchiveImportWorkerMessageParser.d.ts +2 -0
- package/archive/ArchiveImportWorkerRuntime.d.ts +3 -0
- package/assets/{ArchiveImportWorkerEntry-Dh4RZo22.js → ArchiveImportWorkerEntry-wkxjE56k.js} +36 -0
- package/index.js +616 -60
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -29202,7 +29202,8 @@ const MINIMUM_FRUSTUM_HEIGHT = 1e-4;
|
|
|
29202
29202
|
const MINIMUM_NEAR = 0.01;
|
|
29203
29203
|
const MINIMUM_ZOOM = 0.01;
|
|
29204
29204
|
const MAXIMUM_ZOOM = 100;
|
|
29205
|
-
const CLIP_MARGIN_RATIO = 0.
|
|
29205
|
+
const CLIP_MARGIN_RATIO = 0.5;
|
|
29206
|
+
const VIEW_MARGIN_RATIO = 1.3;
|
|
29206
29207
|
const WHEEL_ZOOM_SPEED = 15e-4;
|
|
29207
29208
|
var CameraNavigationMode = /* @__PURE__ */ ((CameraNavigationMode2) => {
|
|
29208
29209
|
CameraNavigationMode2[CameraNavigationMode2["Rotate"] = 0] = "Rotate";
|
|
@@ -29716,7 +29717,7 @@ class CameraController {
|
|
|
29716
29717
|
);
|
|
29717
29718
|
const frustumHeight = this.getFullFrustumHeight(safeFrustumHeight);
|
|
29718
29719
|
const depthRange = maximumForward - minimumForward;
|
|
29719
|
-
const clipMargin =
|
|
29720
|
+
const clipMargin = this.createClipMargin(depthRange, frustumHeight);
|
|
29720
29721
|
const distance = Math.max(
|
|
29721
29722
|
this.initialCameraDistance,
|
|
29722
29723
|
-minimumForward + clipMargin * 2,
|
|
@@ -29921,7 +29922,10 @@ class CameraController {
|
|
|
29921
29922
|
maximumDepth = Math.max(maximumDepth, depth);
|
|
29922
29923
|
}
|
|
29923
29924
|
const depthRange = maximumDepth - minimumDepth;
|
|
29924
|
-
const clipMargin =
|
|
29925
|
+
const clipMargin = this.createClipMargin(
|
|
29926
|
+
depthRange,
|
|
29927
|
+
(this.camera.top - this.camera.bottom) / this.camera.zoom
|
|
29928
|
+
);
|
|
29925
29929
|
const minimumVisibleDepth = MINIMUM_NEAR + clipMargin;
|
|
29926
29930
|
if (minimumDepth >= minimumVisibleDepth) {
|
|
29927
29931
|
return;
|
|
@@ -29944,7 +29948,10 @@ class CameraController {
|
|
|
29944
29948
|
maximumDepth = Math.max(maximumDepth, depth);
|
|
29945
29949
|
}
|
|
29946
29950
|
const depthRange = maximumDepth - minimumDepth;
|
|
29947
|
-
const clipMargin =
|
|
29951
|
+
const clipMargin = this.createClipMargin(
|
|
29952
|
+
depthRange,
|
|
29953
|
+
(this.camera.top - this.camera.bottom) / this.camera.zoom
|
|
29954
|
+
);
|
|
29948
29955
|
this.camera.near = Math.max(MINIMUM_NEAR, minimumDepth - clipMargin);
|
|
29949
29956
|
this.camera.far = Math.max(
|
|
29950
29957
|
this.camera.near + MINIMUM_CLIP_RANGE,
|
|
@@ -29984,6 +29991,19 @@ class CameraController {
|
|
|
29984
29991
|
getSafeAspect() {
|
|
29985
29992
|
return this.viewportWidth / this.getSafeViewportHeight();
|
|
29986
29993
|
}
|
|
29994
|
+
/**
|
|
29995
|
+
* Orthographic depth is linear, so generous margins cost nothing. The
|
|
29996
|
+
* model ratio covers scene depth, and the viewport ratio keeps the ground
|
|
29997
|
+
* grid inside the clip window as the camera zooms out.
|
|
29998
|
+
*/
|
|
29999
|
+
createClipMargin(depthRange, frustumHeight) {
|
|
30000
|
+
const aspect2 = this.getSafeAspect();
|
|
30001
|
+
const halfDiagonal = frustumHeight / 2 * Math.sqrt(1 + aspect2 * aspect2);
|
|
30002
|
+
return Math.max(
|
|
30003
|
+
MINIMUM_CLIP_MARGIN,
|
|
30004
|
+
Math.max(depthRange * CLIP_MARGIN_RATIO, halfDiagonal * VIEW_MARGIN_RATIO)
|
|
30005
|
+
);
|
|
30006
|
+
}
|
|
29987
30007
|
getSafeViewportHeight() {
|
|
29988
30008
|
const maximumInset = Math.max(0, this.viewportHeight - 1);
|
|
29989
30009
|
const effectiveInset = Math.min(this.viewportBottomInset, maximumInset);
|
|
@@ -30545,6 +30565,148 @@ class CanvasController {
|
|
|
30545
30565
|
}
|
|
30546
30566
|
}
|
|
30547
30567
|
}
|
|
30568
|
+
const CENTER_LINE_COLOR = 10132122;
|
|
30569
|
+
const CENTER_LINE_EXTRA_WIDTH = "2.0";
|
|
30570
|
+
const FADE_END_RATIO = 1.4;
|
|
30571
|
+
const FADE_START_RATIO = 0.8;
|
|
30572
|
+
const GRID_DIVISIONS = 24;
|
|
30573
|
+
const GRID_LINE_COLOR = 9474192;
|
|
30574
|
+
const GRID_LINE_OPACITY = 1;
|
|
30575
|
+
const GRID_LINE_WIDTH_PIXELS = 2;
|
|
30576
|
+
const GRID_SEPARATION_RATIO = 2e-3;
|
|
30577
|
+
const MINIMUM_ALPHA = "0.01";
|
|
30578
|
+
const QUAD_COVERAGE = 2.1;
|
|
30579
|
+
const VIEW_RATIO = 0.6;
|
|
30580
|
+
const VERTEX_SHADER = (
|
|
30581
|
+
/* glsl */
|
|
30582
|
+
`
|
|
30583
|
+
varying vec2 vUv;
|
|
30584
|
+
|
|
30585
|
+
void main() {
|
|
30586
|
+
vUv = uv;
|
|
30587
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
30588
|
+
}
|
|
30589
|
+
`
|
|
30590
|
+
);
|
|
30591
|
+
const FRAGMENT_SHADER = (
|
|
30592
|
+
/* glsl */
|
|
30593
|
+
`
|
|
30594
|
+
uniform vec3 uCenterColor;
|
|
30595
|
+
uniform float uDivisionCount;
|
|
30596
|
+
uniform float uFadeEnd;
|
|
30597
|
+
uniform float uFadeStart;
|
|
30598
|
+
uniform vec3 uGridColor;
|
|
30599
|
+
uniform float uGridSize;
|
|
30600
|
+
uniform float uLineOpacity;
|
|
30601
|
+
uniform float uLineWidth;
|
|
30602
|
+
varying vec2 vUv;
|
|
30603
|
+
|
|
30604
|
+
void main() {
|
|
30605
|
+
vec2 coord = (vUv - 0.5) * uGridSize;
|
|
30606
|
+
float cellSize = uGridSize / uDivisionCount;
|
|
30607
|
+
vec2 cellCoord = coord / cellSize;
|
|
30608
|
+
vec2 gridDistance = abs(fract(cellCoord - 0.5) - 0.5) / (fwidth(cellCoord) * uLineWidth);
|
|
30609
|
+
float gridLine = 1.0 - min(min(gridDistance.x, gridDistance.y), 1.0);
|
|
30610
|
+
vec2 centerDistance = abs(coord) / (fwidth(coord) * uLineWidth * ${CENTER_LINE_EXTRA_WIDTH});
|
|
30611
|
+
float centerLine = 1.0 - min(min(centerDistance.x, centerDistance.y), 1.0);
|
|
30612
|
+
float lineStrength = max(gridLine, centerLine);
|
|
30613
|
+
vec3 lineColor = mix(uGridColor, uCenterColor, centerLine);
|
|
30614
|
+
float radial = length(coord);
|
|
30615
|
+
float alpha = lineStrength * (1.0 - smoothstep(uFadeStart, uFadeEnd, radial)) * uLineOpacity;
|
|
30616
|
+
|
|
30617
|
+
if (alpha < ${MINIMUM_ALPHA}) {
|
|
30618
|
+
discard;
|
|
30619
|
+
}
|
|
30620
|
+
|
|
30621
|
+
gl_FragColor = vec4(lineColor, alpha);
|
|
30622
|
+
}
|
|
30623
|
+
`
|
|
30624
|
+
);
|
|
30625
|
+
class GroundGrid {
|
|
30626
|
+
material;
|
|
30627
|
+
mesh;
|
|
30628
|
+
anchorCenterX = 0;
|
|
30629
|
+
anchorCenterY = 0;
|
|
30630
|
+
anchorMinZ = 0;
|
|
30631
|
+
anchorSpan = 1;
|
|
30632
|
+
gridUniforms = {
|
|
30633
|
+
uCenterColor: { value: new Color(CENTER_LINE_COLOR) },
|
|
30634
|
+
uDivisionCount: { value: GRID_DIVISIONS },
|
|
30635
|
+
uFadeEnd: { value: FADE_END_RATIO },
|
|
30636
|
+
uFadeStart: { value: FADE_START_RATIO },
|
|
30637
|
+
uGridColor: { value: new Color(GRID_LINE_COLOR) },
|
|
30638
|
+
uGridSize: { value: 1 },
|
|
30639
|
+
uLineOpacity: { value: GRID_LINE_OPACITY },
|
|
30640
|
+
uLineWidth: { value: 1 / GRID_LINE_WIDTH_PIXELS }
|
|
30641
|
+
};
|
|
30642
|
+
constructor() {
|
|
30643
|
+
this.material = new ShaderMaterial({
|
|
30644
|
+
depthWrite: false,
|
|
30645
|
+
fragmentShader: FRAGMENT_SHADER,
|
|
30646
|
+
side: DoubleSide,
|
|
30647
|
+
transparent: true,
|
|
30648
|
+
uniforms: this.gridUniforms,
|
|
30649
|
+
vertexShader: VERTEX_SHADER
|
|
30650
|
+
});
|
|
30651
|
+
this.mesh = new Mesh(new PlaneGeometry(1, 1), this.material);
|
|
30652
|
+
this.mesh.visible = false;
|
|
30653
|
+
}
|
|
30654
|
+
getHelper() {
|
|
30655
|
+
return this.mesh;
|
|
30656
|
+
}
|
|
30657
|
+
attach(scene) {
|
|
30658
|
+
scene.add(this.mesh);
|
|
30659
|
+
}
|
|
30660
|
+
updateFromBounds(bounds) {
|
|
30661
|
+
this.anchorCenterX = (bounds.min.x + bounds.max.x) / 2;
|
|
30662
|
+
this.anchorCenterY = (bounds.min.y + bounds.max.y) / 2;
|
|
30663
|
+
this.anchorMinZ = bounds.min.z;
|
|
30664
|
+
this.anchorSpan = Math.max(bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y);
|
|
30665
|
+
this.applyLayout(this.anchorSpan * FADE_END_RATIO);
|
|
30666
|
+
this.mesh.visible = true;
|
|
30667
|
+
}
|
|
30668
|
+
/**
|
|
30669
|
+
* Grows the grid when the viewport zooms out beyond the model-anchored
|
|
30670
|
+
* radius, so the ground keeps covering most of the visible area instead
|
|
30671
|
+
* of shrinking into a patch around the model.
|
|
30672
|
+
*/
|
|
30673
|
+
updateForViewport(halfDiagonal) {
|
|
30674
|
+
if (!this.mesh.visible) {
|
|
30675
|
+
return;
|
|
30676
|
+
}
|
|
30677
|
+
const viewportRadius = halfDiagonal * VIEW_RATIO;
|
|
30678
|
+
const fadeRadius = Math.max(this.anchorSpan * FADE_END_RATIO, viewportRadius);
|
|
30679
|
+
this.applyLayout(fadeRadius);
|
|
30680
|
+
}
|
|
30681
|
+
applyLayout(fadeRadius) {
|
|
30682
|
+
const size = fadeRadius * QUAD_COVERAGE;
|
|
30683
|
+
this.gridUniforms.uGridSize.value = size;
|
|
30684
|
+
this.gridUniforms.uFadeStart.value = fadeRadius * (FADE_START_RATIO / FADE_END_RATIO);
|
|
30685
|
+
this.gridUniforms.uFadeEnd.value = fadeRadius;
|
|
30686
|
+
this.mesh.scale.set(size, size, 1);
|
|
30687
|
+
this.mesh.position.set(
|
|
30688
|
+
this.anchorCenterX,
|
|
30689
|
+
this.anchorCenterY,
|
|
30690
|
+
this.anchorMinZ - this.anchorSpan * GRID_SEPARATION_RATIO
|
|
30691
|
+
);
|
|
30692
|
+
}
|
|
30693
|
+
/**
|
|
30694
|
+
* Keeps the drawn line width at the intended CSS pixel size on high-DPI
|
|
30695
|
+
* outputs: fwidth-based lines live in framebuffer pixels, so the inverse
|
|
30696
|
+
* width must scale with the device pixel ratio.
|
|
30697
|
+
*/
|
|
30698
|
+
setPixelRatio(pixelRatio) {
|
|
30699
|
+
this.gridUniforms.uLineWidth.value = 1 / (GRID_LINE_WIDTH_PIXELS * pixelRatio);
|
|
30700
|
+
}
|
|
30701
|
+
hide() {
|
|
30702
|
+
this.mesh.visible = false;
|
|
30703
|
+
}
|
|
30704
|
+
dispose() {
|
|
30705
|
+
this.mesh.removeFromParent();
|
|
30706
|
+
this.mesh.geometry.dispose();
|
|
30707
|
+
this.material.dispose();
|
|
30708
|
+
}
|
|
30709
|
+
}
|
|
30548
30710
|
class ConsoleImportPerformanceLogSink {
|
|
30549
30711
|
categoryByStageName = {
|
|
30550
30712
|
"boolean-source-snapshot": "scene",
|
|
@@ -30840,6 +31002,51 @@ class ImportPerformanceTracer {
|
|
|
30840
31002
|
return trace;
|
|
30841
31003
|
}
|
|
30842
31004
|
}
|
|
31005
|
+
const COMMITTED_PERCENT = 100;
|
|
31006
|
+
const ROLE_SPAN_TOTAL = 0.96;
|
|
31007
|
+
class ImportProgressTracker {
|
|
31008
|
+
listener;
|
|
31009
|
+
percent = 0;
|
|
31010
|
+
rolePositions = /* @__PURE__ */ new Map();
|
|
31011
|
+
roleSpans;
|
|
31012
|
+
constructor(roles, listener) {
|
|
31013
|
+
this.listener = listener;
|
|
31014
|
+
const roleSpans = /* @__PURE__ */ new Map();
|
|
31015
|
+
const spanPerRole = ROLE_SPAN_TOTAL / roles.length;
|
|
31016
|
+
for (const [roleIndex, role] of roles.entries()) {
|
|
31017
|
+
roleSpans.set(role, { span: spanPerRole, start: spanPerRole * roleIndex });
|
|
31018
|
+
}
|
|
31019
|
+
this.roleSpans = roleSpans;
|
|
31020
|
+
}
|
|
31021
|
+
completeRole(role) {
|
|
31022
|
+
this.setRolePosition(role, 1);
|
|
31023
|
+
}
|
|
31024
|
+
completeScene() {
|
|
31025
|
+
this.setPercent(COMMITTED_PERCENT);
|
|
31026
|
+
}
|
|
31027
|
+
getPercent() {
|
|
31028
|
+
return this.percent;
|
|
31029
|
+
}
|
|
31030
|
+
setRolePosition(role, position) {
|
|
31031
|
+
const roleSpan = this.roleSpans.get(role);
|
|
31032
|
+
if (roleSpan === void 0) {
|
|
31033
|
+
return;
|
|
31034
|
+
}
|
|
31035
|
+
const nextPosition = Math.max(this.rolePositions.get(role) ?? 0, Math.min(1, position));
|
|
31036
|
+
this.rolePositions.set(role, nextPosition);
|
|
31037
|
+
this.setPercent(
|
|
31038
|
+
Math.floor((roleSpan.start + roleSpan.span * nextPosition) * COMMITTED_PERCENT)
|
|
31039
|
+
);
|
|
31040
|
+
}
|
|
31041
|
+
setPercent(nextPercent) {
|
|
31042
|
+
const percent = Math.max(this.percent, Math.min(COMMITTED_PERCENT, nextPercent));
|
|
31043
|
+
if (percent === this.percent) {
|
|
31044
|
+
return;
|
|
31045
|
+
}
|
|
31046
|
+
this.percent = percent;
|
|
31047
|
+
this.listener?.(this);
|
|
31048
|
+
}
|
|
31049
|
+
}
|
|
30843
31050
|
const LOCAL_RESOURCE_BASE_URL = "https://renderer-local.invalid/";
|
|
30844
31051
|
class ObjResourceList {
|
|
30845
31052
|
mtlUrls;
|
|
@@ -32014,6 +32221,85 @@ class OBJLoader extends Loader {
|
|
|
32014
32221
|
return container;
|
|
32015
32222
|
}
|
|
32016
32223
|
}
|
|
32224
|
+
class StreamingResourceReader {
|
|
32225
|
+
async readText(url, signal, progress) {
|
|
32226
|
+
const response = await this.fetchOk(url, signal);
|
|
32227
|
+
const streamedBody = this.getStreamedBody(response, progress);
|
|
32228
|
+
if (streamedBody === null) {
|
|
32229
|
+
return response.text();
|
|
32230
|
+
}
|
|
32231
|
+
return new TextDecoder().decode(
|
|
32232
|
+
await this.readStreamingBody(
|
|
32233
|
+
streamedBody.stream,
|
|
32234
|
+
streamedBody.contentLength,
|
|
32235
|
+
streamedBody.progress
|
|
32236
|
+
)
|
|
32237
|
+
);
|
|
32238
|
+
}
|
|
32239
|
+
async readArrayBuffer(url, signal, progress) {
|
|
32240
|
+
const response = await this.fetchOk(url, signal);
|
|
32241
|
+
const streamedBody = this.getStreamedBody(response, progress);
|
|
32242
|
+
if (streamedBody === null) {
|
|
32243
|
+
return response.arrayBuffer();
|
|
32244
|
+
}
|
|
32245
|
+
const bytes = await this.readStreamingBody(
|
|
32246
|
+
streamedBody.stream,
|
|
32247
|
+
streamedBody.contentLength,
|
|
32248
|
+
streamedBody.progress
|
|
32249
|
+
);
|
|
32250
|
+
return bytes.buffer;
|
|
32251
|
+
}
|
|
32252
|
+
async fetchOk(url, signal) {
|
|
32253
|
+
const response = await fetch(url, { signal });
|
|
32254
|
+
if (!response.ok) {
|
|
32255
|
+
throw new Error(`Unable to load resource: ${url}`);
|
|
32256
|
+
}
|
|
32257
|
+
return response;
|
|
32258
|
+
}
|
|
32259
|
+
getStreamedBody(response, progress) {
|
|
32260
|
+
if (progress === void 0) {
|
|
32261
|
+
return null;
|
|
32262
|
+
}
|
|
32263
|
+
const contentLength = this.parseContentLength(response);
|
|
32264
|
+
if (contentLength === void 0) {
|
|
32265
|
+
return null;
|
|
32266
|
+
}
|
|
32267
|
+
const stream = response.body;
|
|
32268
|
+
return stream === null ? null : { contentLength, progress, stream };
|
|
32269
|
+
}
|
|
32270
|
+
async readStreamingBody(stream, contentLength, progress) {
|
|
32271
|
+
const reader = stream.getReader();
|
|
32272
|
+
const chunks = [];
|
|
32273
|
+
let loaded = 0;
|
|
32274
|
+
for (; ; ) {
|
|
32275
|
+
const next = await reader.read();
|
|
32276
|
+
if (next.done) {
|
|
32277
|
+
break;
|
|
32278
|
+
}
|
|
32279
|
+
chunks.push(next.value);
|
|
32280
|
+
loaded += next.value.byteLength;
|
|
32281
|
+
progress(Math.min(1, loaded / contentLength));
|
|
32282
|
+
}
|
|
32283
|
+
return this.mergeChunks(chunks, loaded);
|
|
32284
|
+
}
|
|
32285
|
+
mergeChunks(chunks, total) {
|
|
32286
|
+
const merged = new Uint8Array(total);
|
|
32287
|
+
let offset = 0;
|
|
32288
|
+
for (const chunk of chunks) {
|
|
32289
|
+
merged.set(chunk, offset);
|
|
32290
|
+
offset += chunk.byteLength;
|
|
32291
|
+
}
|
|
32292
|
+
return merged;
|
|
32293
|
+
}
|
|
32294
|
+
parseContentLength(response) {
|
|
32295
|
+
const raw = response.headers.get("Content-Length");
|
|
32296
|
+
if (raw === null) {
|
|
32297
|
+
return void 0;
|
|
32298
|
+
}
|
|
32299
|
+
const parsed = Number(raw);
|
|
32300
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
32301
|
+
}
|
|
32302
|
+
}
|
|
32017
32303
|
const DEFAULT_MODEL_COLOR$1 = 10265519;
|
|
32018
32304
|
const DEFAULT_POINT_SIZE$1 = 3;
|
|
32019
32305
|
class TextureLoadMonitor {
|
|
@@ -32022,12 +32308,15 @@ class TextureLoadMonitor {
|
|
|
32022
32308
|
encounteredError = false;
|
|
32023
32309
|
finishedScheduling = false;
|
|
32024
32310
|
manager;
|
|
32311
|
+
onTextureSettled;
|
|
32025
32312
|
originalTextureLoader;
|
|
32026
32313
|
pendingRequests = 0;
|
|
32314
|
+
scheduledTextureCount = 0;
|
|
32027
32315
|
settled = false;
|
|
32028
32316
|
textures = /* @__PURE__ */ new Set();
|
|
32029
|
-
constructor(manager, creator) {
|
|
32317
|
+
constructor(manager, creator, onTextureSettled) {
|
|
32030
32318
|
this.manager = manager;
|
|
32319
|
+
this.onTextureSettled = onTextureSettled;
|
|
32031
32320
|
this.completionPromise = new Promise(
|
|
32032
32321
|
this.captureCompletionResolver.bind(this)
|
|
32033
32322
|
);
|
|
@@ -32038,6 +32327,9 @@ class TextureLoadMonitor {
|
|
|
32038
32327
|
this.finishedScheduling = true;
|
|
32039
32328
|
this.resolveIfSettled();
|
|
32040
32329
|
}
|
|
32330
|
+
getScheduledTextureCount() {
|
|
32331
|
+
return this.scheduledTextureCount;
|
|
32332
|
+
}
|
|
32041
32333
|
getTextures() {
|
|
32042
32334
|
return [...this.textures];
|
|
32043
32335
|
}
|
|
@@ -32065,14 +32357,17 @@ class TextureLoadMonitor {
|
|
|
32065
32357
|
handleTextureError() {
|
|
32066
32358
|
this.encounteredError = true;
|
|
32067
32359
|
this.pendingRequests -= 1;
|
|
32360
|
+
this.onTextureSettled?.();
|
|
32068
32361
|
this.resolveIfSettled();
|
|
32069
32362
|
}
|
|
32070
32363
|
handleTextureLoad() {
|
|
32071
32364
|
this.pendingRequests -= 1;
|
|
32365
|
+
this.onTextureSettled?.();
|
|
32072
32366
|
this.resolveIfSettled();
|
|
32073
32367
|
}
|
|
32074
32368
|
loadTexture(url, mapping) {
|
|
32075
32369
|
this.pendingRequests += 1;
|
|
32370
|
+
this.scheduledTextureCount += 1;
|
|
32076
32371
|
try {
|
|
32077
32372
|
const texture = this.originalTextureLoader(
|
|
32078
32373
|
url,
|
|
@@ -32105,18 +32400,20 @@ class TextureLoadMonitor {
|
|
|
32105
32400
|
}
|
|
32106
32401
|
class ObjLoader {
|
|
32107
32402
|
parser;
|
|
32403
|
+
resourceReader = new StreamingResourceReader();
|
|
32108
32404
|
constructor(parser = new OBJLoader()) {
|
|
32109
32405
|
this.parser = parser;
|
|
32110
32406
|
}
|
|
32111
|
-
async loadResources(resources, signal, performanceTrace, role) {
|
|
32407
|
+
async loadResources(resources, signal, performanceTrace, role, progress) {
|
|
32112
32408
|
return this.loadInternal(
|
|
32113
32409
|
resources,
|
|
32114
32410
|
signal,
|
|
32115
32411
|
performanceTrace ?? ImportPerformanceTrace.createDisabled(),
|
|
32116
|
-
role ?? "stone"
|
|
32412
|
+
role ?? "stone",
|
|
32413
|
+
progress
|
|
32117
32414
|
);
|
|
32118
32415
|
}
|
|
32119
|
-
async loadInternal(resources, signal, performanceTrace, role) {
|
|
32416
|
+
async loadInternal(resources, signal, performanceTrace, role, progress) {
|
|
32120
32417
|
if (this.hasEmptyRequiredUrl(resources)) {
|
|
32121
32418
|
return {
|
|
32122
32419
|
code: ObjImportFailureCode$1.EmptySource,
|
|
@@ -32128,8 +32425,9 @@ class ObjLoader {
|
|
|
32128
32425
|
let warnings = [];
|
|
32129
32426
|
try {
|
|
32130
32427
|
const objFetchStage = performanceTrace.startStage("obj-fetch", role);
|
|
32131
|
-
const objSource = await this.loadText(resources.objUrl, signal);
|
|
32428
|
+
const objSource = await this.loadText(resources.objUrl, signal, progress);
|
|
32132
32429
|
performanceTrace.endStage(objFetchStage, "success");
|
|
32430
|
+
progress?.markModelFetched();
|
|
32133
32431
|
this.assertNotCancelled(signal);
|
|
32134
32432
|
const materialScanStage = performanceTrace.startStage("obj-material-scan", role);
|
|
32135
32433
|
const materialReferences = this.getMaterialLibraryReferences(objSource);
|
|
@@ -32149,9 +32447,11 @@ class ObjLoader {
|
|
|
32149
32447
|
materialContext = await this.loadMaterialContext(
|
|
32150
32448
|
materialUrls,
|
|
32151
32449
|
resources,
|
|
32152
|
-
signal
|
|
32450
|
+
signal,
|
|
32451
|
+
progress
|
|
32153
32452
|
);
|
|
32154
32453
|
performanceTrace.endStage(materialLoadStage, "success");
|
|
32454
|
+
progress?.markMaterialsResolved(this.getScheduledTextureCount(materialContext));
|
|
32155
32455
|
} catch {
|
|
32156
32456
|
performanceTrace.endStage(
|
|
32157
32457
|
materialLoadStage,
|
|
@@ -32175,6 +32475,7 @@ class ObjLoader {
|
|
|
32175
32475
|
resources.upAxis
|
|
32176
32476
|
);
|
|
32177
32477
|
performanceTrace.endStage(modelCreateStage, "success");
|
|
32478
|
+
progress?.markModelParsed();
|
|
32178
32479
|
if (!this.hasRenderablePrimitive(group) || !model.hasFiniteBounds()) {
|
|
32179
32480
|
model.dispose();
|
|
32180
32481
|
return {
|
|
@@ -32335,6 +32636,13 @@ class ObjLoader {
|
|
|
32335
32636
|
}
|
|
32336
32637
|
return [...textures];
|
|
32337
32638
|
}
|
|
32639
|
+
getScheduledTextureCount(context) {
|
|
32640
|
+
let scheduledTextureCount = 0;
|
|
32641
|
+
for (const monitor of context.monitors) {
|
|
32642
|
+
scheduledTextureCount += monitor.getScheduledTextureCount();
|
|
32643
|
+
}
|
|
32644
|
+
return scheduledTextureCount;
|
|
32645
|
+
}
|
|
32338
32646
|
getObjectTree(root) {
|
|
32339
32647
|
const objectTree = [root];
|
|
32340
32648
|
for (const object of objectTree) {
|
|
@@ -32451,11 +32759,11 @@ class ObjLoader {
|
|
|
32451
32759
|
isPoints(object) {
|
|
32452
32760
|
return object instanceof Points;
|
|
32453
32761
|
}
|
|
32454
|
-
async loadMaterialContext(mtlUrls, resources, signal) {
|
|
32762
|
+
async loadMaterialContext(mtlUrls, resources, signal, progress) {
|
|
32455
32763
|
const libraries = [];
|
|
32456
32764
|
try {
|
|
32457
32765
|
for (const mtlUrl of mtlUrls) {
|
|
32458
|
-
libraries.push(await this.loadMaterialLibrary(mtlUrl, resources, signal));
|
|
32766
|
+
libraries.push(await this.loadMaterialLibrary(mtlUrl, resources, signal, progress));
|
|
32459
32767
|
}
|
|
32460
32768
|
return this.createMaterialContext(libraries);
|
|
32461
32769
|
} catch (error2) {
|
|
@@ -32463,7 +32771,7 @@ class ObjLoader {
|
|
|
32463
32771
|
throw error2;
|
|
32464
32772
|
}
|
|
32465
32773
|
}
|
|
32466
|
-
async loadMaterialLibrary(mtlUrl, resources, signal) {
|
|
32774
|
+
async loadMaterialLibrary(mtlUrl, resources, signal, progress) {
|
|
32467
32775
|
const materialSource = await this.loadText(mtlUrl, signal);
|
|
32468
32776
|
this.assertNotCancelled(signal);
|
|
32469
32777
|
const materialBaseUrl = this.getResourceDirectoryUrl(resources, mtlUrl);
|
|
@@ -32474,7 +32782,11 @@ class ObjLoader {
|
|
|
32474
32782
|
side: DoubleSide
|
|
32475
32783
|
});
|
|
32476
32784
|
const creator = materialLoader.parse(materialSource, materialBaseUrl);
|
|
32477
|
-
const monitor = new TextureLoadMonitor(
|
|
32785
|
+
const monitor = new TextureLoadMonitor(
|
|
32786
|
+
manager,
|
|
32787
|
+
creator,
|
|
32788
|
+
progress === void 0 ? void 0 : progress.markTextureSettled.bind(progress)
|
|
32789
|
+
);
|
|
32478
32790
|
try {
|
|
32479
32791
|
const pbrMaterialReferences = this.parsePbrMaterialReferences(materialSource);
|
|
32480
32792
|
this.assertMaterialTextures(resources, materialSource, materialBaseUrl, creator);
|
|
@@ -32545,14 +32857,12 @@ class ObjLoader {
|
|
|
32545
32857
|
}
|
|
32546
32858
|
return outcome;
|
|
32547
32859
|
}
|
|
32548
|
-
async loadText(url, signal) {
|
|
32549
|
-
|
|
32550
|
-
|
|
32551
|
-
|
|
32552
|
-
|
|
32553
|
-
|
|
32554
|
-
}
|
|
32555
|
-
return response.text();
|
|
32860
|
+
async loadText(url, signal, progress) {
|
|
32861
|
+
return this.resourceReader.readText(
|
|
32862
|
+
url,
|
|
32863
|
+
signal,
|
|
32864
|
+
progress === void 0 ? void 0 : progress.markModelFetchProgress.bind(progress)
|
|
32865
|
+
);
|
|
32556
32866
|
}
|
|
32557
32867
|
getImportPerformanceFailureStatus(signal) {
|
|
32558
32868
|
return signal.aborted ? "cancelled" : "failure";
|
|
@@ -33445,22 +33755,24 @@ const DEFAULT_POINT_SIZE = 3;
|
|
|
33445
33755
|
const MAX_HEADER_BYTES = 65536;
|
|
33446
33756
|
class PlyLoader {
|
|
33447
33757
|
parser;
|
|
33758
|
+
resourceReader = new StreamingResourceReader();
|
|
33448
33759
|
textureLoader;
|
|
33449
33760
|
constructor(parser = new PLYLoader(), textureLoader = new PlyTextureLoader()) {
|
|
33450
33761
|
this.parser = parser;
|
|
33451
33762
|
this.textureLoader = textureLoader;
|
|
33452
33763
|
}
|
|
33453
|
-
async load(resources, signal) {
|
|
33764
|
+
async load(resources, signal, progress) {
|
|
33454
33765
|
if (signal.aborted) {
|
|
33455
33766
|
return {
|
|
33456
33767
|
code: ObjImportFailureCode$1.Cancelled,
|
|
33457
33768
|
success: false
|
|
33458
33769
|
};
|
|
33459
33770
|
}
|
|
33460
|
-
const source = await this.fetchSource(resources.plyUrl, signal);
|
|
33771
|
+
const source = await this.fetchSource(resources.plyUrl, signal, progress);
|
|
33461
33772
|
if (!(source instanceof ArrayBuffer)) {
|
|
33462
33773
|
return source;
|
|
33463
33774
|
}
|
|
33775
|
+
progress?.markModelFetched();
|
|
33464
33776
|
let geometry;
|
|
33465
33777
|
let metadata;
|
|
33466
33778
|
try {
|
|
@@ -33486,7 +33798,14 @@ class PlyLoader {
|
|
|
33486
33798
|
success: false
|
|
33487
33799
|
};
|
|
33488
33800
|
}
|
|
33489
|
-
|
|
33801
|
+
progress?.markModelParsed();
|
|
33802
|
+
const modelResult = metadata.hasFaces ? await this.createMeshModel(
|
|
33803
|
+
geometry,
|
|
33804
|
+
metadata.textureReference,
|
|
33805
|
+
resources,
|
|
33806
|
+
signal,
|
|
33807
|
+
progress
|
|
33808
|
+
) : this.createPointsModel(geometry, resources);
|
|
33490
33809
|
if ("success" in modelResult) {
|
|
33491
33810
|
return modelResult;
|
|
33492
33811
|
}
|
|
@@ -33514,13 +33833,14 @@ class PlyLoader {
|
|
|
33514
33833
|
vertexColors: geometry.hasAttribute("color")
|
|
33515
33834
|
});
|
|
33516
33835
|
}
|
|
33517
|
-
async createMeshModel(geometry, textureReference, resources, signal) {
|
|
33836
|
+
async createMeshModel(geometry, textureReference, resources, signal, progress) {
|
|
33518
33837
|
if (!geometry.hasAttribute("normal")) {
|
|
33519
33838
|
geometry.computeVertexNormals();
|
|
33520
33839
|
}
|
|
33521
33840
|
const warnings = [];
|
|
33522
33841
|
let texture;
|
|
33523
33842
|
if (textureReference !== void 0 && geometry.hasAttribute("uv")) {
|
|
33843
|
+
progress?.markMaterialsResolved(1);
|
|
33524
33844
|
const textureResult = await this.textureLoader.load(
|
|
33525
33845
|
resources.resolveTextureUrl(textureReference),
|
|
33526
33846
|
signal
|
|
@@ -33532,6 +33852,7 @@ class PlyLoader {
|
|
|
33532
33852
|
success: false
|
|
33533
33853
|
};
|
|
33534
33854
|
}
|
|
33855
|
+
progress?.markTextureSettled();
|
|
33535
33856
|
if (textureResult.outcome === "succeeded") {
|
|
33536
33857
|
texture = textureResult.texture;
|
|
33537
33858
|
} else {
|
|
@@ -33573,16 +33894,13 @@ class PlyLoader {
|
|
|
33573
33894
|
warnings: []
|
|
33574
33895
|
};
|
|
33575
33896
|
}
|
|
33576
|
-
async fetchSource(url, signal) {
|
|
33897
|
+
async fetchSource(url, signal, progress) {
|
|
33577
33898
|
try {
|
|
33578
|
-
|
|
33579
|
-
|
|
33580
|
-
|
|
33581
|
-
|
|
33582
|
-
|
|
33583
|
-
};
|
|
33584
|
-
}
|
|
33585
|
-
return await response.arrayBuffer();
|
|
33899
|
+
return await this.resourceReader.readArrayBuffer(
|
|
33900
|
+
url,
|
|
33901
|
+
signal,
|
|
33902
|
+
progress === void 0 ? void 0 : progress.markModelFetchProgress.bind(progress)
|
|
33903
|
+
);
|
|
33586
33904
|
} catch {
|
|
33587
33905
|
return {
|
|
33588
33906
|
code: signal.aborted ? ObjImportFailureCode$1.Cancelled : ObjImportFailureCode$1.InvalidSource,
|
|
@@ -33617,6 +33935,66 @@ class PlyLoader {
|
|
|
33617
33935
|
return value;
|
|
33618
33936
|
}
|
|
33619
33937
|
}
|
|
33938
|
+
const OBJ_MODEL_LOAD_PROGRESS_PROFILE = {
|
|
33939
|
+
fetchedPosition: 0.2,
|
|
33940
|
+
materialsPosition: 0.3,
|
|
33941
|
+
parsedPosition: 0.95,
|
|
33942
|
+
textureCeiling: 0.99
|
|
33943
|
+
};
|
|
33944
|
+
const PLY_MODEL_LOAD_PROGRESS_PROFILE = {
|
|
33945
|
+
fetchedPosition: 0.4,
|
|
33946
|
+
materialsPosition: 0.85,
|
|
33947
|
+
parsedPosition: 0.85,
|
|
33948
|
+
textureCeiling: 0.99
|
|
33949
|
+
};
|
|
33950
|
+
class RoleImportProgressReporter {
|
|
33951
|
+
profile;
|
|
33952
|
+
role;
|
|
33953
|
+
textureSettled = 0;
|
|
33954
|
+
textureTotal;
|
|
33955
|
+
tracker;
|
|
33956
|
+
constructor(tracker, role, profile) {
|
|
33957
|
+
this.profile = profile;
|
|
33958
|
+
this.role = role;
|
|
33959
|
+
this.tracker = tracker;
|
|
33960
|
+
}
|
|
33961
|
+
markMaterialsResolved(textureTotal) {
|
|
33962
|
+
this.textureTotal = textureTotal;
|
|
33963
|
+
this.tracker.setRolePosition(this.role, this.profile.materialsPosition);
|
|
33964
|
+
this.reportTexturePosition();
|
|
33965
|
+
}
|
|
33966
|
+
markModelFetchProgress(fraction) {
|
|
33967
|
+
this.tracker.setRolePosition(
|
|
33968
|
+
this.role,
|
|
33969
|
+
this.profile.fetchedPosition * this.clampFraction(fraction)
|
|
33970
|
+
);
|
|
33971
|
+
}
|
|
33972
|
+
markModelFetched() {
|
|
33973
|
+
this.tracker.setRolePosition(this.role, this.profile.fetchedPosition);
|
|
33974
|
+
}
|
|
33975
|
+
markModelParsed() {
|
|
33976
|
+
this.tracker.setRolePosition(this.role, this.profile.parsedPosition);
|
|
33977
|
+
}
|
|
33978
|
+
markTextureSettled() {
|
|
33979
|
+
this.textureSettled += 1;
|
|
33980
|
+
this.reportTexturePosition();
|
|
33981
|
+
}
|
|
33982
|
+
reportTexturePosition() {
|
|
33983
|
+
const textureTotal = this.textureTotal;
|
|
33984
|
+
if (textureTotal === void 0 || textureTotal <= 0 || this.textureSettled === 0) {
|
|
33985
|
+
return;
|
|
33986
|
+
}
|
|
33987
|
+
const textureSpan = this.profile.textureCeiling - this.profile.parsedPosition;
|
|
33988
|
+
const settledShare = Math.min(this.textureSettled, textureTotal) / textureTotal;
|
|
33989
|
+
this.tracker.setRolePosition(
|
|
33990
|
+
this.role,
|
|
33991
|
+
this.profile.parsedPosition + textureSpan * settledShare
|
|
33992
|
+
);
|
|
33993
|
+
}
|
|
33994
|
+
clampFraction(fraction) {
|
|
33995
|
+
return Math.max(0, Math.min(1, fraction));
|
|
33996
|
+
}
|
|
33997
|
+
}
|
|
33620
33998
|
class SimulationSnapshotFactory {
|
|
33621
33999
|
constructor(scheduler) {
|
|
33622
34000
|
this.scheduler = scheduler;
|
|
@@ -35922,6 +36300,7 @@ class Renderer {
|
|
|
35922
36300
|
fillLight;
|
|
35923
36301
|
frameHandle;
|
|
35924
36302
|
gizmoController;
|
|
36303
|
+
groundGrid;
|
|
35925
36304
|
hemisphereLight;
|
|
35926
36305
|
pendingFirstFramePerformance;
|
|
35927
36306
|
importPerformanceTracer = new ImportPerformanceTracer();
|
|
@@ -35959,6 +36338,7 @@ class Renderer {
|
|
|
35959
36338
|
let contextRestoreListenerAttached = false;
|
|
35960
36339
|
let controller;
|
|
35961
36340
|
let gizmoController;
|
|
36341
|
+
let groundGrid;
|
|
35962
36342
|
let resizeObserver;
|
|
35963
36343
|
let viewCube;
|
|
35964
36344
|
this.canvas = canvas;
|
|
@@ -35991,6 +36371,9 @@ class Renderer {
|
|
|
35991
36371
|
);
|
|
35992
36372
|
this.gizmoController = gizmoController;
|
|
35993
36373
|
this.scene.add(gizmoController.getHelper());
|
|
36374
|
+
groundGrid = new GroundGrid();
|
|
36375
|
+
this.groundGrid = groundGrid;
|
|
36376
|
+
groundGrid.attach(this.scene);
|
|
35994
36377
|
canvasController = new CanvasController(
|
|
35995
36378
|
canvas,
|
|
35996
36379
|
viewCube,
|
|
@@ -36044,9 +36427,13 @@ class Renderer {
|
|
|
36044
36427
|
viewCube?.dispose();
|
|
36045
36428
|
} finally {
|
|
36046
36429
|
try {
|
|
36047
|
-
|
|
36430
|
+
groundGrid?.dispose();
|
|
36048
36431
|
} finally {
|
|
36049
|
-
|
|
36432
|
+
try {
|
|
36433
|
+
controller?.dispose();
|
|
36434
|
+
} finally {
|
|
36435
|
+
renderer.dispose();
|
|
36436
|
+
}
|
|
36050
36437
|
}
|
|
36051
36438
|
}
|
|
36052
36439
|
}
|
|
@@ -36093,25 +36480,29 @@ class Renderer {
|
|
|
36093
36480
|
this.viewCube.dispose();
|
|
36094
36481
|
} finally {
|
|
36095
36482
|
try {
|
|
36096
|
-
this.
|
|
36483
|
+
this.groundGrid.dispose();
|
|
36097
36484
|
} finally {
|
|
36098
36485
|
try {
|
|
36099
|
-
this.
|
|
36486
|
+
this.releaseActiveModels();
|
|
36100
36487
|
} finally {
|
|
36101
36488
|
try {
|
|
36102
|
-
this.
|
|
36103
|
-
"webglcontextrestored",
|
|
36104
|
-
this.contextRestoreListener
|
|
36105
|
-
);
|
|
36489
|
+
this.resizeObserver.disconnect();
|
|
36106
36490
|
} finally {
|
|
36107
36491
|
try {
|
|
36108
36492
|
this.canvas.removeEventListener(
|
|
36109
|
-
"
|
|
36110
|
-
this.
|
|
36493
|
+
"webglcontextrestored",
|
|
36494
|
+
this.contextRestoreListener
|
|
36111
36495
|
);
|
|
36112
36496
|
} finally {
|
|
36113
|
-
|
|
36114
|
-
|
|
36497
|
+
try {
|
|
36498
|
+
this.canvas.removeEventListener(
|
|
36499
|
+
"webglcontextlost",
|
|
36500
|
+
this.contextLossListener
|
|
36501
|
+
);
|
|
36502
|
+
} finally {
|
|
36503
|
+
this.renderer.forceContextLoss();
|
|
36504
|
+
this.renderer.dispose();
|
|
36505
|
+
}
|
|
36115
36506
|
}
|
|
36116
36507
|
}
|
|
36117
36508
|
}
|
|
@@ -36149,7 +36540,10 @@ class Renderer {
|
|
|
36149
36540
|
stone: new ModelResourceList(source.stone.fileUrls, source.stone.upAxis)
|
|
36150
36541
|
};
|
|
36151
36542
|
return this.completeSceneImportFromResources(
|
|
36152
|
-
this.startImportSession(
|
|
36543
|
+
this.startImportSession(
|
|
36544
|
+
performanceTrace,
|
|
36545
|
+
this.createImportProgressTracker(resources)
|
|
36546
|
+
),
|
|
36153
36547
|
resources
|
|
36154
36548
|
);
|
|
36155
36549
|
} catch {
|
|
@@ -36191,7 +36585,10 @@ class Renderer {
|
|
|
36191
36585
|
stone: new ModelResourceList(source.stone.files, source.stone.upAxis)
|
|
36192
36586
|
};
|
|
36193
36587
|
return this.completeSceneImportFromResources(
|
|
36194
|
-
this.startImportSession(
|
|
36588
|
+
this.startImportSession(
|
|
36589
|
+
performanceTrace,
|
|
36590
|
+
this.createImportProgressTracker(resources)
|
|
36591
|
+
),
|
|
36195
36592
|
resources
|
|
36196
36593
|
);
|
|
36197
36594
|
} catch {
|
|
@@ -36238,6 +36635,7 @@ class Renderer {
|
|
|
36238
36635
|
available: this.activeStoneModel !== void 0,
|
|
36239
36636
|
contextLost: this.contextLost,
|
|
36240
36637
|
importInProgress: this.activeImport !== void 0,
|
|
36638
|
+
...this.activeImport === void 0 ? {} : { importProgress: this.activeImport.progressTracker.getPercent() },
|
|
36241
36639
|
interactionInProgress,
|
|
36242
36640
|
revision: this.sceneRevision,
|
|
36243
36641
|
resourceRevision: this.sceneResourceRevision
|
|
@@ -36700,12 +37098,22 @@ class Renderer {
|
|
|
36700
37098
|
let result;
|
|
36701
37099
|
try {
|
|
36702
37100
|
try {
|
|
37101
|
+
const progress = new RoleImportProgressReporter(
|
|
37102
|
+
session.progressTracker,
|
|
37103
|
+
role,
|
|
37104
|
+
source.resources instanceof ObjResourceList ? OBJ_MODEL_LOAD_PROGRESS_PROFILE : PLY_MODEL_LOAD_PROGRESS_PROFILE
|
|
37105
|
+
);
|
|
36703
37106
|
result = source.resources instanceof ObjResourceList ? await this.modelLoader.loadResources(
|
|
36704
37107
|
source.resources,
|
|
36705
37108
|
session.controller.signal,
|
|
36706
37109
|
session.performanceTrace,
|
|
36707
|
-
role
|
|
36708
|
-
|
|
37110
|
+
role,
|
|
37111
|
+
progress
|
|
37112
|
+
) : await this.plyLoader.load(
|
|
37113
|
+
source.resources,
|
|
37114
|
+
session.controller.signal,
|
|
37115
|
+
progress
|
|
37116
|
+
);
|
|
36709
37117
|
} catch {
|
|
36710
37118
|
return this.completeUnexpectedImportFailure(session);
|
|
36711
37119
|
}
|
|
@@ -36750,6 +37158,7 @@ class Renderer {
|
|
|
36750
37158
|
};
|
|
36751
37159
|
}
|
|
36752
37160
|
loadStatus = "success";
|
|
37161
|
+
session.progressTracker.completeRole(role);
|
|
36753
37162
|
return {
|
|
36754
37163
|
model: result.model,
|
|
36755
37164
|
warnings: result.warnings ?? []
|
|
@@ -36837,6 +37246,7 @@ class Renderer {
|
|
|
36837
37246
|
this.booleanEditSessionActive = false;
|
|
36838
37247
|
this.activeBooleanOperationSessionToken = void 0;
|
|
36839
37248
|
this.abortEditingState();
|
|
37249
|
+
this.refreshGroundGrid([stone, candidate]);
|
|
36840
37250
|
} catch {
|
|
36841
37251
|
this.activeBaseModel = previousBase;
|
|
36842
37252
|
(candidate.transformRoot ?? candidate.object).removeFromParent();
|
|
@@ -37153,6 +37563,7 @@ class Renderer {
|
|
|
37153
37563
|
...this.collectPresentModels(this.activeStoneModel, this.activeBaseModel)
|
|
37154
37564
|
];
|
|
37155
37565
|
this.gizmoController.setActiveScene({ base: void 0, stone: void 0 });
|
|
37566
|
+
this.groundGrid.hide();
|
|
37156
37567
|
this.transformedSceneBoundsDirty = false;
|
|
37157
37568
|
this.activeStoneModel = void 0;
|
|
37158
37569
|
this.activeBaseModel = void 0;
|
|
@@ -37201,6 +37612,18 @@ class Renderer {
|
|
|
37201
37612
|
this.gizmoController.setModelVisibility("stone", this.modelVisibility.stoneVisible);
|
|
37202
37613
|
this.gizmoController.setModelVisibility("base", this.modelVisibility.baseVisible);
|
|
37203
37614
|
}
|
|
37615
|
+
/**
|
|
37616
|
+
* Retargets the ground grid from the present models. The grid anchors to
|
|
37617
|
+
* models that are merely hidden as well, so toggling base visibility never
|
|
37618
|
+
* moves the ground plane; only a committed paired scene shows it.
|
|
37619
|
+
*/
|
|
37620
|
+
refreshGroundGrid(models) {
|
|
37621
|
+
if (models.length < 2) {
|
|
37622
|
+
this.groundGrid.hide();
|
|
37623
|
+
return;
|
|
37624
|
+
}
|
|
37625
|
+
this.groundGrid.updateFromBounds(this.mergeBounds(models));
|
|
37626
|
+
}
|
|
37204
37627
|
applyModelVisibility() {
|
|
37205
37628
|
const stoneRoot = this.activeStoneModel?.transformRoot ?? this.activeStoneModel?.object;
|
|
37206
37629
|
const baseRoot = this.activeBaseModel?.transformRoot ?? this.activeBaseModel?.object;
|
|
@@ -37249,6 +37672,7 @@ class Renderer {
|
|
|
37249
37672
|
}
|
|
37250
37673
|
renderScene(includeViewCube = true) {
|
|
37251
37674
|
this.syncTransformedSceneBounds();
|
|
37675
|
+
this.syncGroundGridForCamera();
|
|
37252
37676
|
this.gizmoController.updateForRender();
|
|
37253
37677
|
if (includeViewCube) {
|
|
37254
37678
|
this.viewCube.update(this.camera);
|
|
@@ -37258,6 +37682,17 @@ class Renderer {
|
|
|
37258
37682
|
this.renderViewCube();
|
|
37259
37683
|
}
|
|
37260
37684
|
}
|
|
37685
|
+
/**
|
|
37686
|
+
* Keeps the ground grid covering most of the visible area as the camera
|
|
37687
|
+
* zooms. Runs per rendered frame; updating only touches uniforms and the
|
|
37688
|
+
* mesh transform, so it stays allocation-free.
|
|
37689
|
+
*/
|
|
37690
|
+
syncGroundGridForCamera() {
|
|
37691
|
+
const frustumHeight = (this.camera.top - this.camera.bottom) / this.camera.zoom;
|
|
37692
|
+
const frustumWidth = (this.camera.right - this.camera.left) / (this.camera.top - this.camera.bottom) * frustumHeight;
|
|
37693
|
+
const halfDiagonal = Math.sqrt(frustumHeight * frustumHeight + frustumWidth * frustumWidth) / 2;
|
|
37694
|
+
this.groundGrid.updateForViewport(halfDiagonal);
|
|
37695
|
+
}
|
|
37261
37696
|
renderViewCube() {
|
|
37262
37697
|
const viewport = this.viewCube.getViewport();
|
|
37263
37698
|
const previousAutoClear = this.renderer.autoClear;
|
|
@@ -37296,6 +37731,9 @@ class Renderer {
|
|
|
37296
37731
|
}
|
|
37297
37732
|
}
|
|
37298
37733
|
this.controller.syncTransformedBounds(this.mergeBounds(models));
|
|
37734
|
+
this.refreshGroundGrid(
|
|
37735
|
+
this.collectPresentModels(this.activeStoneModel, this.activeBaseModel)
|
|
37736
|
+
);
|
|
37299
37737
|
}
|
|
37300
37738
|
/**
|
|
37301
37739
|
* Atomically replaces the committed active scene. New model references are
|
|
@@ -37370,6 +37808,7 @@ class Renderer {
|
|
|
37370
37808
|
};
|
|
37371
37809
|
this.gizmoController.setActiveScene(activeScene);
|
|
37372
37810
|
this.applyGizmoVisibility();
|
|
37811
|
+
this.refreshGroundGrid(candidateModels);
|
|
37373
37812
|
this.sceneRevision += 1;
|
|
37374
37813
|
this.sceneResourceRevision = this.sceneRevision;
|
|
37375
37814
|
this.pendingFirstFramePerformance = {
|
|
@@ -37379,6 +37818,7 @@ class Renderer {
|
|
|
37379
37818
|
};
|
|
37380
37819
|
session.performanceTrace.markStage("import-promise-resolved", "scene");
|
|
37381
37820
|
session.performanceTrace.endStage(sceneCommitStage, "success");
|
|
37821
|
+
session.progressTracker.completeScene();
|
|
37382
37822
|
this.finishImportSession(session);
|
|
37383
37823
|
this.releasePair(previousStone, previousBase);
|
|
37384
37824
|
this.requestRender();
|
|
@@ -37502,6 +37942,13 @@ class Renderer {
|
|
|
37502
37942
|
this.notifyWorkspaceState();
|
|
37503
37943
|
}
|
|
37504
37944
|
}
|
|
37945
|
+
handleImportProgressChanged(_source) {
|
|
37946
|
+
this.notifyWorkspaceState();
|
|
37947
|
+
}
|
|
37948
|
+
createImportProgressTracker(resources) {
|
|
37949
|
+
const roles = resources.base === void 0 ? ["stone"] : ["stone", "base"];
|
|
37950
|
+
return new ImportProgressTracker(roles, this.handleImportProgressChanged.bind(this));
|
|
37951
|
+
}
|
|
37505
37952
|
notifyWorkspaceState() {
|
|
37506
37953
|
this.workspaceStateListener?.(this.getWorkspaceState());
|
|
37507
37954
|
}
|
|
@@ -37550,6 +37997,7 @@ class Renderer {
|
|
|
37550
37997
|
this.renderer.setSize(width, height, false);
|
|
37551
37998
|
this.controller.resize(width, height);
|
|
37552
37999
|
this.viewCube.resize(width, height, pixelRatio);
|
|
38000
|
+
this.groundGrid.setPixelRatio(pixelRatio);
|
|
37553
38001
|
this.requestRender();
|
|
37554
38002
|
}
|
|
37555
38003
|
invalidateActiveImport(code) {
|
|
@@ -37595,7 +38043,7 @@ class Renderer {
|
|
|
37595
38043
|
this.notifyWorkspaceState();
|
|
37596
38044
|
}
|
|
37597
38045
|
}
|
|
37598
|
-
startImportSession(performanceTrace) {
|
|
38046
|
+
startImportSession(performanceTrace, progressTracker) {
|
|
37599
38047
|
this.cancelSimulation();
|
|
37600
38048
|
this.cancelBooleanOperation();
|
|
37601
38049
|
this.cancelPendingFirstFramePerformance("cancelled");
|
|
@@ -37608,7 +38056,8 @@ class Renderer {
|
|
|
37608
38056
|
const session = {
|
|
37609
38057
|
controller: new AbortController(),
|
|
37610
38058
|
invalidationCode: void 0,
|
|
37611
|
-
performanceTrace
|
|
38059
|
+
performanceTrace,
|
|
38060
|
+
progressTracker
|
|
37612
38061
|
};
|
|
37613
38062
|
performanceTrace.markStage("import-session-start", "scene");
|
|
37614
38063
|
this.activeImport = session;
|
|
@@ -37849,6 +38298,8 @@ class ArchiveImportWorkerMessageParser {
|
|
|
37849
38298
|
return this.parseEntryStart(record, taskId);
|
|
37850
38299
|
case "error":
|
|
37851
38300
|
return typeof record["message"] === "string" ? { message: record["message"], taskId, type: "error" } : void 0;
|
|
38301
|
+
case "progress":
|
|
38302
|
+
return this.parseProgress(record, taskId);
|
|
37852
38303
|
default:
|
|
37853
38304
|
return void 0;
|
|
37854
38305
|
}
|
|
@@ -37866,6 +38317,17 @@ class ArchiveImportWorkerMessageParser {
|
|
|
37866
38317
|
isPositiveSafeInteger(value) {
|
|
37867
38318
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
37868
38319
|
}
|
|
38320
|
+
isNonNegativeSafeInteger(value) {
|
|
38321
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
38322
|
+
}
|
|
38323
|
+
parseProgress(record, taskId) {
|
|
38324
|
+
const bytesLoaded = record["bytesLoaded"];
|
|
38325
|
+
const bytesTotal = record["bytesTotal"];
|
|
38326
|
+
if (!this.isNonNegativeSafeInteger(bytesLoaded) || !this.isNonNegativeSafeInteger(bytesTotal)) {
|
|
38327
|
+
return void 0;
|
|
38328
|
+
}
|
|
38329
|
+
return { bytesLoaded, bytesTotal, taskId, type: "progress" };
|
|
38330
|
+
}
|
|
37869
38331
|
isRecord(value) {
|
|
37870
38332
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37871
38333
|
}
|
|
@@ -37925,7 +38387,7 @@ class BrowserArchiveImportWorkerEndpoint {
|
|
|
37925
38387
|
constructor() {
|
|
37926
38388
|
this.worker = new Worker(new URL(
|
|
37927
38389
|
/* @vite-ignore */
|
|
37928
|
-
"" + new URL("assets/ArchiveImportWorkerEntry-
|
|
38390
|
+
"" + new URL("assets/ArchiveImportWorkerEntry-wkxjE56k.js", import.meta.url).href,
|
|
37929
38391
|
import.meta.url
|
|
37930
38392
|
), {
|
|
37931
38393
|
type: "module"
|
|
@@ -37965,9 +38427,11 @@ class ArchiveImportWorkerClient {
|
|
|
37965
38427
|
jobValidator = new ArchiveImportJobValidator();
|
|
37966
38428
|
lastStartedTaskId = 0;
|
|
37967
38429
|
messageParser = new ArchiveImportWorkerMessageParser();
|
|
38430
|
+
onProgress;
|
|
37968
38431
|
workerFactory;
|
|
37969
|
-
constructor(workerFactory = new BrowserArchiveImportWorkerFactory()) {
|
|
38432
|
+
constructor(workerFactory = new BrowserArchiveImportWorkerFactory(), onProgress) {
|
|
37970
38433
|
this.workerFactory = workerFactory;
|
|
38434
|
+
this.onProgress = onProgress;
|
|
37971
38435
|
}
|
|
37972
38436
|
cancel(taskId) {
|
|
37973
38437
|
const task = this.activeTask;
|
|
@@ -38042,6 +38506,10 @@ class ArchiveImportWorkerClient {
|
|
|
38042
38506
|
outbound.message
|
|
38043
38507
|
)
|
|
38044
38508
|
);
|
|
38509
|
+
return;
|
|
38510
|
+
case "progress":
|
|
38511
|
+
this.handleProgress(outbound);
|
|
38512
|
+
return;
|
|
38045
38513
|
}
|
|
38046
38514
|
} catch (error2) {
|
|
38047
38515
|
this.rejectTask(task, this.toError(error2));
|
|
@@ -38111,6 +38579,10 @@ class ArchiveImportWorkerClient {
|
|
|
38111
38579
|
type: "acknowledge-chunk"
|
|
38112
38580
|
});
|
|
38113
38581
|
}
|
|
38582
|
+
handleProgress(message) {
|
|
38583
|
+
const percent = message.bytesTotal <= 0 ? 0 : Math.min(100, Math.floor(message.bytesLoaded / message.bytesTotal * 100));
|
|
38584
|
+
this.onProgress?.(percent);
|
|
38585
|
+
}
|
|
38114
38586
|
rejectTask(task, error2) {
|
|
38115
38587
|
if (this.activeTask !== task) {
|
|
38116
38588
|
return;
|
|
@@ -38292,6 +38764,26 @@ var ImportPerformanceMode = /* @__PURE__ */ ((ImportPerformanceMode2) => {
|
|
|
38292
38764
|
ImportPerformanceMode2["Verbose"] = "verbose";
|
|
38293
38765
|
return ImportPerformanceMode2;
|
|
38294
38766
|
})(ImportPerformanceMode || {});
|
|
38767
|
+
const ARCHIVE_SEGMENT_PERCENT = 20;
|
|
38768
|
+
class ImportProgressCompositor {
|
|
38769
|
+
compose(input) {
|
|
38770
|
+
const archiveProgress = this.clampPercent(input.archiveProgress);
|
|
38771
|
+
const rendererProgress = this.clampPercent(input.rendererProgress);
|
|
38772
|
+
if (archiveProgress === void 0) {
|
|
38773
|
+
return rendererProgress;
|
|
38774
|
+
}
|
|
38775
|
+
if (rendererProgress === void 0) {
|
|
38776
|
+
return Math.floor(archiveProgress / 100 * ARCHIVE_SEGMENT_PERCENT);
|
|
38777
|
+
}
|
|
38778
|
+
return ARCHIVE_SEGMENT_PERCENT + Math.floor(rendererProgress / 100 * (100 - ARCHIVE_SEGMENT_PERCENT));
|
|
38779
|
+
}
|
|
38780
|
+
clampPercent(percent) {
|
|
38781
|
+
if (percent === void 0) {
|
|
38782
|
+
return void 0;
|
|
38783
|
+
}
|
|
38784
|
+
return Math.max(0, Math.min(100, percent));
|
|
38785
|
+
}
|
|
38786
|
+
}
|
|
38295
38787
|
class DownloadSaveSink {
|
|
38296
38788
|
aborted = false;
|
|
38297
38789
|
blobChunks = [];
|
|
@@ -40011,7 +40503,7 @@ var StoneBasePoseMode = /* @__PURE__ */ ((StoneBasePoseMode2) => {
|
|
|
40011
40503
|
StoneBasePoseMode2["PreservePose"] = "preserve-pose";
|
|
40012
40504
|
return StoneBasePoseMode2;
|
|
40013
40505
|
})(StoneBasePoseMode || {});
|
|
40014
|
-
const studioWorkspaceStyles = ".lingbi-studio-workspace {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport > canvas {\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background: #3d3f41;\n}\n\n.lingbi-studio-boolean-operation-overlay {\n position: absolute;\n z-index: 3;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: rgba(17, 17, 17, 0.32);\n color: #ffffff;\n cursor: wait;\n}\n\n.lingbi-studio-boolean-operation-overlay[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: 50%;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n width: min(420px, calc(100% - 32px));\n padding: 24px;\n border-radius: 8px;\n background: #ffffff;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-edit-confirmation[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation-text {\n grid-column: 1 / -1;\n margin: 0 0 8px;\n color: #171717;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n text-align: center;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel,\n.lingbi-studio-boolean-edit-confirmation-continue {\n height: 40px;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel {\n background: #ffffff;\n color: #171717;\n}\n\n.lingbi-studio-boolean-edit-confirmation-continue {\n border-color: #f27c38;\n background: #f27c38;\n color: #ffffff;\n}\n\n.lingbi-studio-boolean-operation-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 105, 0, 0.28);\n border-top-color: #ff6900;\n border-radius: 50%;\n animation: lingbi-studio-boolean-operation-spin 0.8s linear infinite;\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-operation-status {\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-bottom-toolbar {\n position: absolute;\n z-index: 1;\n bottom: 36px;\n left: 50%;\n display: grid;\n align-items: center;\n grid-template-columns: minmax(0, 1fr);\n width: min(120px, calc(100% - 32px));\n height: 64px;\n padding: 8px 12px;\n transform: translateX(-50%);\n box-sizing: border-box;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n width: min(480px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-inventory-control {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n width: min(240px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-inventory-control {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n width: min(360px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(6, minmax(0, 1fr));\n width: min(720px, calc(100% - 32px));\n}\n\n.lingbi-studio-model-visibility {\n position: absolute;\n z-index: 1;\n bottom: 108px;\n left: 50%;\n display: grid;\n gap: 8px;\n width: 144px;\n padding: 12px;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n transform: translateX(-240px);\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility[hidden] {\n display: none;\n}\n\n.lingbi-studio-model-visibility-title {\n color: #171717;\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n color: #171717;\n font-size: 14px;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n appearance: none;\n width: 20px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n line-height: 0;\n outline: none;\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility-icon {\n display: block;\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.lingbi-studio-model-visibility-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-model-visibility-button:disabled {\n color: #8c8c8c;\n}\n\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-boolean-button,\n.lingbi-studio-simulation-button,\n.lingbi-studio-download-button,\n.lingbi-studio-inventory-button,\n.lingbi-studio-edit-button,\n.lingbi-studio-model-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n height: 48px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n font-size: 18px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 1;\n box-sizing: border-box;\n white-space: nowrap;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n.lingbi-studio-operation-button-wrapper {\n display: block;\n width: 100%;\n min-width: 0;\n height: 48px;\n box-sizing: border-box;\n}\n\n.lingbi-studio-operation-button-wrapper > button:disabled {\n pointer-events: none;\n}\n\n.lingbi-studio-boolean-button[hidden],\n.lingbi-studio-simulation-button[hidden],\n.lingbi-studio-download-button[hidden],\n.lingbi-studio-inventory-button[hidden],\n.lingbi-studio-edit-button[hidden],\n.lingbi-studio-model-button[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-button:not(:disabled),\n.lingbi-studio-simulation-button:not(:disabled),\n.lingbi-studio-download-button:not(:disabled),\n.lingbi-studio-inventory-button:not(:disabled),\n.lingbi-studio-edit-button:not(:disabled),\n.lingbi-studio-model-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-button:disabled,\n.lingbi-studio-simulation-button:disabled,\n.lingbi-studio-download-button:disabled,\n.lingbi-studio-inventory-button:disabled,\n.lingbi-studio-edit-button:disabled,\n.lingbi-studio-model-button:disabled,\n.lingbi-studio-model-visibility-button:disabled {\n background: transparent;\n color: #8c8c8c;\n opacity: 1;\n}\n\n.lingbi-studio-edit-button.is-active,\n.lingbi-studio-edit-button.is-active:disabled,\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-download-status {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 6px 10px;\n border: 1px solid #f4b5b0;\n border-radius: 4px;\n background: #fff1f0;\n color: #b42318;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-download-success {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 8px 16px;\n border: 1px solid #86d39a;\n border-radius: 6px;\n background: #f0fff4;\n color: #176b32;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-success[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status {\n position: absolute;\n z-index: 2;\n bottom: 112px;\n left: 50%;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n border: 0;\n border-radius: 8px;\n background: #e9e9e9;\n color: #171717;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-edit-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status-text {\n display: inline-block;\n min-width: 0;\n font-size: 20px;\n line-height: 28px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-exit-button {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 18px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: #f27c38;\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n letter-spacing: 0;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .lingbi-studio-boolean-button:not(:disabled):hover,\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n background: #e8e8e8;\n }\n\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-visibility-button:not(:disabled):hover {\n background: transparent;\n }\n\n .lingbi-studio-exit-button:hover {\n background: #ff8a1f;\n }\n\n .lingbi-studio-boolean-edit-confirmation-cancel:hover {\n background: #f5f5f5;\n }\n\n .lingbi-studio-boolean-edit-confirmation-continue:hover {\n background: #ff8a1f;\n }\n}\n\n.lingbi-studio-boolean-button:not(:disabled):active,\n.lingbi-studio-download-button:not(:disabled):active,\n.lingbi-studio-inventory-button:not(:disabled):active,\n.lingbi-studio-edit-button:not(:disabled):active,\n.lingbi-studio-model-button:not(:disabled):active,\n.lingbi-studio-model-visibility-button:not(:disabled):active {\n background: transparent;\n}\n\n.lingbi-studio-exit-button:active {\n background: #e66d00;\n}\n\n@keyframes lingbi-studio-boolean-operation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button,\n .lingbi-studio-exit-button {\n transition: none;\n }\n\n .lingbi-studio-boolean-operation-spinner {\n animation: none;\n }\n}\n\n@media (max-width: 420px) {\n .lingbi-studio-bottom-toolbar.has-edit-control {\n width: calc(100% - 16px);\n padding: 8px 4px;\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-model-visibility {\n left: 8px;\n transform: none;\n }\n\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button {\n padding: 0 2px;\n font-size: 16px;\n }\n\n .lingbi-studio-edit-status-text {\n font-size: 16px;\n line-height: 24px;\n }\n\n .lingbi-studio-exit-button {\n height: 36px;\n padding: 0 14px;\n font-size: 16px;\n }\n}\n\n.lingbi-studio-viewport > canvas:focus-visible,\n.lingbi-studio-boolean-button:focus-visible,\n.lingbi-studio-simulation-button:focus-visible,\n.lingbi-studio-download-button:focus-visible,\n.lingbi-studio-inventory-button:focus-visible,\n.lingbi-studio-edit-button:focus-visible,\n.lingbi-studio-model-button:focus-visible,\n.lingbi-studio-model-visibility-button:focus-visible,\n.lingbi-studio-exit-button:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-cancel:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-continue:focus-visible {\n outline: 2px solid #7cc0ff;\n outline-offset: 2px;\n}\n";
|
|
40506
|
+
const studioWorkspaceStyles = ".lingbi-studio-workspace {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport > canvas {\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background: #3d3f41;\n}\n\n.lingbi-studio-boolean-operation-overlay {\n position: absolute;\n z-index: 3;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: rgba(17, 17, 17, 0.32);\n color: #ffffff;\n cursor: wait;\n}\n\n.lingbi-studio-boolean-operation-overlay[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: 50%;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n width: min(420px, calc(100% - 32px));\n padding: 24px;\n border-radius: 8px;\n background: #ffffff;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-edit-confirmation[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation-text {\n grid-column: 1 / -1;\n margin: 0 0 8px;\n color: #171717;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n text-align: center;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel,\n.lingbi-studio-boolean-edit-confirmation-continue {\n height: 40px;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel {\n background: #ffffff;\n color: #171717;\n}\n\n.lingbi-studio-boolean-edit-confirmation-continue {\n border-color: #f27c38;\n background: #f27c38;\n color: #ffffff;\n}\n\n.lingbi-studio-boolean-operation-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 105, 0, 0.28);\n border-top-color: #ff6900;\n border-radius: 50%;\n animation: lingbi-studio-boolean-operation-spin 0.8s linear infinite;\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-operation-status {\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-import-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.lingbi-studio-import-row[hidden] {\n display: none;\n}\n\n.lingbi-studio-import-progress {\n display: block;\n width: 160px;\n height: 4px;\n overflow: hidden;\n border-radius: 999px;\n background: rgba(255, 105, 0, 0.28);\n box-sizing: border-box;\n}\n\n.lingbi-studio-import-progress[hidden] {\n display: none;\n}\n\n.lingbi-studio-import-progress-fill {\n width: 0;\n height: 100%;\n border-radius: 999px;\n background: #ff6900;\n}\n\n.lingbi-studio-import-percent {\n min-width: 36px;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: left;\n}\n\n.lingbi-studio-bottom-toolbar {\n position: absolute;\n z-index: 1;\n bottom: 36px;\n left: 50%;\n display: grid;\n align-items: center;\n grid-template-columns: minmax(0, 1fr);\n width: min(120px, calc(100% - 32px));\n height: 64px;\n padding: 8px 12px;\n transform: translateX(-50%);\n box-sizing: border-box;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n width: min(480px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-inventory-control {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n width: min(240px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-inventory-control {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n width: min(360px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(6, minmax(0, 1fr));\n width: min(720px, calc(100% - 32px));\n}\n\n.lingbi-studio-model-visibility {\n position: absolute;\n z-index: 1;\n bottom: 108px;\n left: 50%;\n display: grid;\n gap: 8px;\n width: 144px;\n padding: 12px;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n transform: translateX(-240px);\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility[hidden] {\n display: none;\n}\n\n.lingbi-studio-model-visibility-title {\n color: #171717;\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n color: #171717;\n font-size: 14px;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n appearance: none;\n width: 20px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n line-height: 0;\n outline: none;\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility-icon {\n display: block;\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.lingbi-studio-model-visibility-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-model-visibility-button:disabled {\n color: #8c8c8c;\n}\n\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-boolean-button,\n.lingbi-studio-simulation-button,\n.lingbi-studio-download-button,\n.lingbi-studio-inventory-button,\n.lingbi-studio-edit-button,\n.lingbi-studio-model-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n height: 48px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n font-size: 18px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 1;\n box-sizing: border-box;\n white-space: nowrap;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n.lingbi-studio-operation-button-wrapper {\n display: block;\n width: 100%;\n min-width: 0;\n height: 48px;\n box-sizing: border-box;\n}\n\n.lingbi-studio-operation-button-wrapper > button:disabled {\n pointer-events: none;\n}\n\n.lingbi-studio-boolean-button[hidden],\n.lingbi-studio-simulation-button[hidden],\n.lingbi-studio-download-button[hidden],\n.lingbi-studio-inventory-button[hidden],\n.lingbi-studio-edit-button[hidden],\n.lingbi-studio-model-button[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-button:not(:disabled),\n.lingbi-studio-simulation-button:not(:disabled),\n.lingbi-studio-download-button:not(:disabled),\n.lingbi-studio-inventory-button:not(:disabled),\n.lingbi-studio-edit-button:not(:disabled),\n.lingbi-studio-model-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-button:disabled,\n.lingbi-studio-simulation-button:disabled,\n.lingbi-studio-download-button:disabled,\n.lingbi-studio-inventory-button:disabled,\n.lingbi-studio-edit-button:disabled,\n.lingbi-studio-model-button:disabled,\n.lingbi-studio-model-visibility-button:disabled {\n background: transparent;\n color: #8c8c8c;\n opacity: 1;\n}\n\n.lingbi-studio-edit-button.is-active,\n.lingbi-studio-edit-button.is-active:disabled,\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-download-status {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 6px 10px;\n border: 1px solid #f4b5b0;\n border-radius: 4px;\n background: #fff1f0;\n color: #b42318;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-download-success {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 8px 16px;\n border: 1px solid #86d39a;\n border-radius: 6px;\n background: #f0fff4;\n color: #176b32;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-success[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status {\n position: absolute;\n z-index: 2;\n bottom: 112px;\n left: 50%;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n border: 0;\n border-radius: 8px;\n background: #e9e9e9;\n color: #171717;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-edit-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status-text {\n display: inline-block;\n min-width: 0;\n font-size: 20px;\n line-height: 28px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-exit-button {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 18px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: #f27c38;\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n letter-spacing: 0;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .lingbi-studio-boolean-button:not(:disabled):hover,\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n background: #e8e8e8;\n }\n\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-visibility-button:not(:disabled):hover {\n background: transparent;\n }\n\n .lingbi-studio-exit-button:hover {\n background: #ff8a1f;\n }\n\n .lingbi-studio-boolean-edit-confirmation-cancel:hover {\n background: #f5f5f5;\n }\n\n .lingbi-studio-boolean-edit-confirmation-continue:hover {\n background: #ff8a1f;\n }\n}\n\n.lingbi-studio-boolean-button:not(:disabled):active,\n.lingbi-studio-download-button:not(:disabled):active,\n.lingbi-studio-inventory-button:not(:disabled):active,\n.lingbi-studio-edit-button:not(:disabled):active,\n.lingbi-studio-model-button:not(:disabled):active,\n.lingbi-studio-model-visibility-button:not(:disabled):active {\n background: transparent;\n}\n\n.lingbi-studio-exit-button:active {\n background: #e66d00;\n}\n\n@keyframes lingbi-studio-boolean-operation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button,\n .lingbi-studio-exit-button {\n transition: none;\n }\n\n .lingbi-studio-boolean-operation-spinner {\n animation: none;\n }\n}\n\n@media (max-width: 420px) {\n .lingbi-studio-bottom-toolbar.has-edit-control {\n width: calc(100% - 16px);\n padding: 8px 4px;\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-model-visibility {\n left: 8px;\n transform: none;\n }\n\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button {\n padding: 0 2px;\n font-size: 16px;\n }\n\n .lingbi-studio-edit-status-text {\n font-size: 16px;\n line-height: 24px;\n }\n\n .lingbi-studio-exit-button {\n height: 36px;\n padding: 0 14px;\n font-size: 16px;\n }\n}\n\n.lingbi-studio-viewport > canvas:focus-visible,\n.lingbi-studio-boolean-button:focus-visible,\n.lingbi-studio-simulation-button:focus-visible,\n.lingbi-studio-download-button:focus-visible,\n.lingbi-studio-inventory-button:focus-visible,\n.lingbi-studio-edit-button:focus-visible,\n.lingbi-studio-model-button:focus-visible,\n.lingbi-studio-model-visibility-button:focus-visible,\n.lingbi-studio-exit-button:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-cancel:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-continue:focus-visible {\n outline: 2px solid #7cc0ff;\n outline-offset: 2px;\n}\n";
|
|
40015
40507
|
class StudioWorkspace {
|
|
40016
40508
|
canvas;
|
|
40017
40509
|
static SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
@@ -40022,7 +40514,12 @@ class StudioWorkspace {
|
|
|
40022
40514
|
booleanButtonElement;
|
|
40023
40515
|
simulationButtonElement;
|
|
40024
40516
|
booleanOperationOverlayElement;
|
|
40517
|
+
booleanOperationSpinnerElement;
|
|
40025
40518
|
booleanOperationStatusElement;
|
|
40519
|
+
importPercentElement;
|
|
40520
|
+
importProgressElement;
|
|
40521
|
+
importProgressFillElement;
|
|
40522
|
+
importProgressRowElement;
|
|
40026
40523
|
booleanEditConfirmationElement;
|
|
40027
40524
|
bottomToolbarElement;
|
|
40028
40525
|
downloadButtonElement;
|
|
@@ -40054,6 +40551,10 @@ class StudioWorkspace {
|
|
|
40054
40551
|
const booleanOperationOverlayElement = ownerDocument.createElement("div");
|
|
40055
40552
|
const booleanOperationSpinnerElement = ownerDocument.createElement("div");
|
|
40056
40553
|
const booleanOperationStatusElement = ownerDocument.createElement("div");
|
|
40554
|
+
const importPercentElement = ownerDocument.createElement("div");
|
|
40555
|
+
const importProgressElement = ownerDocument.createElement("div");
|
|
40556
|
+
const importProgressFillElement = ownerDocument.createElement("div");
|
|
40557
|
+
const importProgressRowElement = ownerDocument.createElement("div");
|
|
40057
40558
|
const booleanEditConfirmationElement = ownerDocument.createElement("div");
|
|
40058
40559
|
const booleanEditConfirmationTextElement = ownerDocument.createElement("p");
|
|
40059
40560
|
const booleanEditConfirmationCancelButtonElement = ownerDocument.createElement("button");
|
|
@@ -40103,10 +40604,24 @@ class StudioWorkspace {
|
|
|
40103
40604
|
booleanOperationOverlayElement.hidden = true;
|
|
40104
40605
|
booleanOperationSpinnerElement.className = "lingbi-studio-boolean-operation-spinner";
|
|
40105
40606
|
booleanOperationSpinnerElement.setAttribute("aria-hidden", "true");
|
|
40607
|
+
importProgressRowElement.className = "lingbi-studio-import-row";
|
|
40608
|
+
importProgressRowElement.hidden = true;
|
|
40609
|
+
importPercentElement.className = "lingbi-studio-import-percent";
|
|
40610
|
+
importPercentElement.textContent = "0%";
|
|
40611
|
+
importProgressElement.className = "lingbi-studio-import-progress";
|
|
40612
|
+
importProgressElement.setAttribute("aria-label", "模型加载进度");
|
|
40613
|
+
importProgressElement.setAttribute("aria-valuemax", "100");
|
|
40614
|
+
importProgressElement.setAttribute("aria-valuemin", "0");
|
|
40615
|
+
importProgressElement.setAttribute("aria-valuenow", "0");
|
|
40616
|
+
importProgressElement.setAttribute("role", "progressbar");
|
|
40617
|
+
importProgressFillElement.className = "lingbi-studio-import-progress-fill";
|
|
40618
|
+
importProgressElement.append(importProgressFillElement);
|
|
40619
|
+
importProgressRowElement.append(importProgressElement, importPercentElement);
|
|
40106
40620
|
booleanOperationStatusElement.className = "lingbi-studio-boolean-operation-status";
|
|
40107
40621
|
booleanOperationStatusElement.textContent = "布尔计算中";
|
|
40108
40622
|
booleanOperationOverlayElement.append(
|
|
40109
40623
|
booleanOperationSpinnerElement,
|
|
40624
|
+
importProgressRowElement,
|
|
40110
40625
|
booleanOperationStatusElement
|
|
40111
40626
|
);
|
|
40112
40627
|
booleanEditConfirmationElement.className = "lingbi-studio-boolean-edit-confirmation";
|
|
@@ -40234,7 +40749,12 @@ class StudioWorkspace {
|
|
|
40234
40749
|
this.booleanButtonElement = booleanButtonElement;
|
|
40235
40750
|
this.simulationButtonElement = simulationButtonElement;
|
|
40236
40751
|
this.booleanOperationOverlayElement = booleanOperationOverlayElement;
|
|
40752
|
+
this.booleanOperationSpinnerElement = booleanOperationSpinnerElement;
|
|
40237
40753
|
this.booleanOperationStatusElement = booleanOperationStatusElement;
|
|
40754
|
+
this.importPercentElement = importPercentElement;
|
|
40755
|
+
this.importProgressElement = importProgressElement;
|
|
40756
|
+
this.importProgressFillElement = importProgressFillElement;
|
|
40757
|
+
this.importProgressRowElement = importProgressRowElement;
|
|
40238
40758
|
this.booleanEditConfirmationElement = booleanEditConfirmationElement;
|
|
40239
40759
|
this.bottomToolbarElement = bottomToolbarElement;
|
|
40240
40760
|
this.downloadButtonElement = downloadButtonElement;
|
|
@@ -40357,11 +40877,14 @@ class StudioWorkspace {
|
|
|
40357
40877
|
}
|
|
40358
40878
|
/** Applies Application's combined renderer and download state. */
|
|
40359
40879
|
setToolbarState(state) {
|
|
40880
|
+
const importInProgress = state.importInProgress ?? false;
|
|
40881
|
+
const importProgress = Math.max(0, Math.min(100, Math.floor(state.importProgress ?? 0)));
|
|
40360
40882
|
const booleanOperationInProgress = state.booleanInProgress ?? false;
|
|
40361
40883
|
const downloadInProgress = state.downloadInProgress ?? false;
|
|
40362
40884
|
const inventoryInProgress = state.inventoryInProgress ?? false;
|
|
40363
40885
|
const simulationInProgress = state.simulationInProgress ?? false;
|
|
40364
|
-
const
|
|
40886
|
+
const otherOperationInProgress = booleanOperationInProgress || downloadInProgress || inventoryInProgress || simulationInProgress;
|
|
40887
|
+
const operationInProgress = importInProgress || otherOperationInProgress;
|
|
40365
40888
|
const confirmationVisible = state.booleanEditConfirmationVisible ?? false;
|
|
40366
40889
|
const modelVisibility = state.modelVisibility;
|
|
40367
40890
|
this.currentEditActive = state.editActive;
|
|
@@ -40385,7 +40908,15 @@ class StudioWorkspace {
|
|
|
40385
40908
|
this.booleanButtonElement.setAttribute("aria-busy", String(booleanOperationInProgress));
|
|
40386
40909
|
this.booleanOperationOverlayElement.hidden = !operationInProgress;
|
|
40387
40910
|
this.booleanOperationOverlayElement.setAttribute("aria-busy", String(operationInProgress));
|
|
40388
|
-
this.
|
|
40911
|
+
this.booleanOperationSpinnerElement.hidden = importInProgress;
|
|
40912
|
+
this.importProgressRowElement.hidden = !importInProgress;
|
|
40913
|
+
if (importInProgress) {
|
|
40914
|
+
const percentText = `${String(importProgress)}%`;
|
|
40915
|
+
this.importProgressElement.setAttribute("aria-valuenow", String(importProgress));
|
|
40916
|
+
this.importProgressFillElement.style.width = percentText;
|
|
40917
|
+
this.importPercentElement.textContent = percentText;
|
|
40918
|
+
}
|
|
40919
|
+
this.booleanOperationStatusElement.textContent = importInProgress ? "模型加载中" : simulationInProgress ? "仿真计算中" : booleanOperationInProgress ? "布尔计算中" : inventoryInProgress ? "入库处理中" : "下载处理中";
|
|
40389
40920
|
this.canvas.toggleAttribute("inert", operationInProgress || confirmationVisible);
|
|
40390
40921
|
this.bottomToolbarElement.toggleAttribute(
|
|
40391
40922
|
"inert",
|
|
@@ -40573,6 +41104,7 @@ class StudioWorkspace {
|
|
|
40573
41104
|
class Application {
|
|
40574
41105
|
archiveImportController;
|
|
40575
41106
|
archiveImporting = false;
|
|
41107
|
+
archiveImportProgress;
|
|
40576
41108
|
booleanEditConfirmationVisible = false;
|
|
40577
41109
|
disposed = false;
|
|
40578
41110
|
downloadController;
|
|
@@ -40585,6 +41117,7 @@ class Application {
|
|
|
40585
41117
|
editedArchiveResourceContext;
|
|
40586
41118
|
importGeneration = 0;
|
|
40587
41119
|
importPerformanceMode = ImportPerformanceMode.Off;
|
|
41120
|
+
importProgressCompositor = new ImportProgressCompositor();
|
|
40588
41121
|
inventoryConfirmationController;
|
|
40589
41122
|
inventoryConfirming = false;
|
|
40590
41123
|
inventoryError;
|
|
@@ -40642,7 +41175,10 @@ class Application {
|
|
|
40642
41175
|
this.handleInventorySuccess.bind(this),
|
|
40643
41176
|
this.handleInventoryStateChanged.bind(this)
|
|
40644
41177
|
);
|
|
40645
|
-
this.archiveImportController = new ArchiveImportWorkerClient(
|
|
41178
|
+
this.archiveImportController = new ArchiveImportWorkerClient(
|
|
41179
|
+
void 0,
|
|
41180
|
+
this.handleArchiveImportProgress.bind(this)
|
|
41181
|
+
);
|
|
40646
41182
|
this.handleRendererWorkspaceStateChanged(renderer.getWorkspaceState());
|
|
40647
41183
|
} catch (error2) {
|
|
40648
41184
|
this.workspace = void 0;
|
|
@@ -40728,12 +41264,14 @@ class Application {
|
|
|
40728
41264
|
}
|
|
40729
41265
|
this.nextArchiveTaskId += 1;
|
|
40730
41266
|
this.archiveImporting = true;
|
|
41267
|
+
this.archiveImportProgress = 0;
|
|
40731
41268
|
this.downloadError = void 0;
|
|
40732
41269
|
this.downloadSuccess = void 0;
|
|
40733
41270
|
this.publishWorkspaceState();
|
|
40734
41271
|
const controller = this.archiveImportController;
|
|
40735
41272
|
if (controller === void 0) {
|
|
40736
41273
|
this.archiveImporting = false;
|
|
41274
|
+
this.archiveImportProgress = void 0;
|
|
40737
41275
|
this.publishWorkspaceState();
|
|
40738
41276
|
return Promise.resolve({
|
|
40739
41277
|
code: ObjImportFailureCode.Unavailable,
|
|
@@ -40791,6 +41329,7 @@ class Application {
|
|
|
40791
41329
|
cancelArchiveImport() {
|
|
40792
41330
|
this.archiveImportController?.cancel();
|
|
40793
41331
|
this.archiveImporting = false;
|
|
41332
|
+
this.archiveImportProgress = void 0;
|
|
40794
41333
|
this.invalidateImportGeneration();
|
|
40795
41334
|
}
|
|
40796
41335
|
async completeModelResourceSceneImport(source, candidate, generation) {
|
|
@@ -40861,6 +41400,7 @@ class Application {
|
|
|
40861
41400
|
} finally {
|
|
40862
41401
|
if (this.isCurrentImportGeneration(generation)) {
|
|
40863
41402
|
this.archiveImporting = false;
|
|
41403
|
+
this.archiveImportProgress = void 0;
|
|
40864
41404
|
this.publishWorkspaceState();
|
|
40865
41405
|
}
|
|
40866
41406
|
}
|
|
@@ -40868,6 +41408,7 @@ class Application {
|
|
|
40868
41408
|
completeEditedArchiveImportFailure(generation, error2) {
|
|
40869
41409
|
if (this.isCurrentImportGeneration(generation)) {
|
|
40870
41410
|
this.archiveImporting = false;
|
|
41411
|
+
this.archiveImportProgress = void 0;
|
|
40871
41412
|
this.publishWorkspaceState();
|
|
40872
41413
|
}
|
|
40873
41414
|
return Promise.resolve(this.mapEditedArchiveError(error2));
|
|
@@ -41238,6 +41779,13 @@ class Application {
|
|
|
41238
41779
|
}
|
|
41239
41780
|
this.clearBooleanEditConfirmation();
|
|
41240
41781
|
}
|
|
41782
|
+
handleArchiveImportProgress(percent) {
|
|
41783
|
+
if (this.disposed || !this.archiveImporting) {
|
|
41784
|
+
return;
|
|
41785
|
+
}
|
|
41786
|
+
this.archiveImportProgress = percent;
|
|
41787
|
+
this.publishWorkspaceState();
|
|
41788
|
+
}
|
|
41241
41789
|
handleRendererWorkspaceStateChanged(state) {
|
|
41242
41790
|
if (this.disposed) {
|
|
41243
41791
|
return;
|
|
@@ -41355,6 +41903,10 @@ class Application {
|
|
|
41355
41903
|
if (workspace === void 0 || state === void 0) {
|
|
41356
41904
|
return;
|
|
41357
41905
|
}
|
|
41906
|
+
const importProgress = this.importProgressCompositor.compose({
|
|
41907
|
+
archiveProgress: this.archiveImportProgress,
|
|
41908
|
+
rendererProgress: state.scene.importProgress
|
|
41909
|
+
});
|
|
41358
41910
|
const toolbarState = {
|
|
41359
41911
|
simulationEnabled: state.simulation?.enabled === true && !this.downloading && !this.inventorying && !this.inventoryConfirming && !this.archiveImporting && !this.booleanEditConfirmationVisible,
|
|
41360
41912
|
simulationInProgress: state.simulation?.inProgress ?? false,
|
|
@@ -41372,6 +41924,8 @@ class Application {
|
|
|
41372
41924
|
editEnabled: state.edit.enabled && !this.downloading && !this.inventorying && !this.archiveImporting && !this.booleanEditConfirmationVisible,
|
|
41373
41925
|
editVisible: state.edit.visible,
|
|
41374
41926
|
...this.editStatusMessage === void 0 ? {} : { editStatusMessage: this.editStatusMessage },
|
|
41927
|
+
importInProgress: state.scene.importInProgress || this.archiveImporting,
|
|
41928
|
+
...importProgress === void 0 ? {} : { importProgress },
|
|
41375
41929
|
inventoryEnabled: this.canInventory(state),
|
|
41376
41930
|
...this.inventoryError === void 0 ? {} : { inventoryError: this.inventoryError },
|
|
41377
41931
|
inventoryInProgress: this.inventorying,
|
|
@@ -41417,9 +41971,11 @@ class Application {
|
|
|
41417
41971
|
state.scene.available,
|
|
41418
41972
|
state.scene.contextLost,
|
|
41419
41973
|
state.scene.importInProgress,
|
|
41974
|
+
state.scene.importProgress ?? null,
|
|
41420
41975
|
state.scene.revision,
|
|
41421
41976
|
state.scene.resourceRevision ?? null,
|
|
41422
41977
|
this.archiveImporting,
|
|
41978
|
+
this.archiveImportProgress ?? null,
|
|
41423
41979
|
this.downloading,
|
|
41424
41980
|
this.inventoryConfirming,
|
|
41425
41981
|
this.inventorying,
|
|
@@ -41432,7 +41988,7 @@ class Application {
|
|
|
41432
41988
|
]);
|
|
41433
41989
|
}
|
|
41434
41990
|
isSameToolbarState(state, previousState) {
|
|
41435
|
-
return previousState !== void 0 && state.simulationEnabled === previousState.simulationEnabled && state.simulationInProgress === previousState.simulationInProgress && state.simulationOutcome === previousState.simulationOutcome && state.booleanEnabled === previousState.booleanEnabled && state.booleanInProgress === previousState.booleanInProgress && state.booleanReediting === previousState.booleanReediting && state.booleanResultInvalidated === previousState.booleanResultInvalidated && state.booleanEditConfirmationVisible === previousState.booleanEditConfirmationVisible && state.downloadEnabled === previousState.downloadEnabled && state.downloadError === previousState.downloadError && state.downloadInProgress === previousState.downloadInProgress && state.downloadSuccess === previousState.downloadSuccess && state.editActive === previousState.editActive && state.editEnabled === previousState.editEnabled && state.editVisible === previousState.editVisible && state.editStatusMessage === previousState.editStatusMessage && state.inventoryEnabled === previousState.inventoryEnabled && state.inventoryError === previousState.inventoryError && state.inventoryInProgress === previousState.inventoryInProgress && state.inventoryVisible === previousState.inventoryVisible && this.isSameModelVisibility(state.modelVisibility, previousState.modelVisibility);
|
|
41991
|
+
return previousState !== void 0 && state.simulationEnabled === previousState.simulationEnabled && state.simulationInProgress === previousState.simulationInProgress && state.simulationOutcome === previousState.simulationOutcome && state.booleanEnabled === previousState.booleanEnabled && state.booleanInProgress === previousState.booleanInProgress && state.booleanReediting === previousState.booleanReediting && state.booleanResultInvalidated === previousState.booleanResultInvalidated && state.booleanEditConfirmationVisible === previousState.booleanEditConfirmationVisible && state.downloadEnabled === previousState.downloadEnabled && state.downloadError === previousState.downloadError && state.downloadInProgress === previousState.downloadInProgress && state.downloadSuccess === previousState.downloadSuccess && state.editActive === previousState.editActive && state.editEnabled === previousState.editEnabled && state.editVisible === previousState.editVisible && state.editStatusMessage === previousState.editStatusMessage && state.importInProgress === previousState.importInProgress && state.importProgress === previousState.importProgress && state.inventoryEnabled === previousState.inventoryEnabled && state.inventoryError === previousState.inventoryError && state.inventoryInProgress === previousState.inventoryInProgress && state.inventoryVisible === previousState.inventoryVisible && this.isSameModelVisibility(state.modelVisibility, previousState.modelVisibility);
|
|
41436
41992
|
}
|
|
41437
41993
|
isSameModelVisibility(state, previousState) {
|
|
41438
41994
|
return state?.baseVisible === previousState?.baseVisible && state?.enabled === previousState?.enabled && state?.stoneVisible === previousState?.stoneVisible;
|