three-base 2.0.180 → 2.0.187
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/MktMesh/MktMesh.d.ts +74 -3
- package/MktMesh/MktMesh.js +242 -6
- package/MktMesh/MktMesh.ts +247 -5
- package/MktMesh/imagePlane.d.ts +4 -1
- package/MktMesh/imagePlane.js +25 -9
- package/MktMesh/imagePlane.ts +26 -9
- package/MktMesh/imageTileLoader.d.ts +2 -0
- package/MktMesh/imageTileLoader.js +5 -1
- package/MktMesh/imageTileLoader.ts +6 -1
- package/MktMesh/mktMappingShader.d.ts +5 -5
- package/MktMesh/mktMappingShader.js +12 -12
- package/MktMesh/mktMappingShader.ts +12 -12
- package/MktMesh/mktShader.d.ts +57 -56
- package/MktMesh/mktShader.js +62 -60
- package/MktMesh/mktShader.ts +72 -70
- package/MktMesh/modelSlot.d.ts +21 -3
- package/MktMesh/modelSlot.js +39 -4
- package/MktMesh/modelSlot.ts +38 -4
- package/MktMesh/panoSlotRoster.d.ts +2 -2
- package/MktMesh/panoSlotRoster.js +4 -4
- package/MktMesh/panoSlotRoster.ts +4 -4
- package/MktMesh/panoTileStitcher.d.ts +7 -7
- package/MktMesh/panoTileStitcher.js +33 -28
- package/MktMesh/panoTileStitcher.ts +34 -29
- package/MktMesh/types.d.ts +10 -0
- package/MktMesh/types.ts +10 -0
- package/package.json +1 -1
package/MktMesh/MktMesh.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { BufferGeometry, Camera, Matrix4, Mesh, ShaderMaterial, Texture, Vector3
|
|
|
2
2
|
import { type PanoTexMode } from "./mktShader";
|
|
3
3
|
import { type PanoStitcher } from "./panoTileStitcher";
|
|
4
4
|
import type { MktMeshData, MktTextureInfo, WallPoint, WallSide } from "./types";
|
|
5
|
+
import { ImagePlane, type ImagePlaneData } from "./imagePlane";
|
|
6
|
+
import { ModelSlot, type ModelSlotData } from "./modelSlot";
|
|
5
7
|
export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
6
8
|
/** 纹理列表(每个纹理的 url + 世界矩阵) */
|
|
7
9
|
textures: MktTextureInfo[];
|
|
@@ -119,6 +121,32 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
119
121
|
* 必须与基类喂纹理处用同一个值,否则标记与地址不同源 ⇒ 整图被按 1/2 横向铺满球面(10-06 晚起纵向不分块,四槽时代是 1/4)。 */
|
|
120
122
|
get panoTexMode(): PanoTexMode;
|
|
121
123
|
private _tmpV3;
|
|
124
|
+
/**
|
|
125
|
+
* 图片平面层(⭐ 10-26 旨入库):对象挂在**本 MktMesh 的子树**里,⛔ 不是场景根
|
|
126
|
+
* ⇒ 移动/旋转/缩放这个展品时,墙 + 全景 + 平面图 + 模型一起走。
|
|
127
|
+
*/
|
|
128
|
+
imagePlanes: ImagePlane[];
|
|
129
|
+
/** 模型槽层(同一棵子树,口径同上) */
|
|
130
|
+
models: ModelSlot[];
|
|
131
|
+
/**
|
|
132
|
+
* 那 4 张瓦片的下载/解码缓存。⭐ 与全景 `_texCache` 同一条旨(10-02「纹理都缓存在对象里面,执行 dispose 时才销毁」)
|
|
133
|
+
* ⇒ 切工程**不**清它,回来那一圈直接命中缓存、不会再空一格;只随本对象 `dispose()` 释放。
|
|
134
|
+
*/
|
|
135
|
+
private _imgTileLoader;
|
|
136
|
+
/**
|
|
137
|
+
* 逐帧绑定用的两张账(2026-10-26 定的方案:地址一次性全部丢给下载,绑材质改成「一帧一颗平面」)。
|
|
138
|
+
* 全局 rAF 每拍只跑一项 `addWork`,而 `addWork` 按闭包引用判重 ⇒ 每次现造的新闭包它判不出来,
|
|
139
|
+
* ⛔ 去重闸门必须像全景上传那张 `_uploadQueued` 一样自备。
|
|
140
|
+
*/
|
|
141
|
+
private _imgBindQueued;
|
|
142
|
+
/** 平面 → 「它 4 条地址全部落定(就绪或失败)时的那串地址」⇒ 地址不换就不再重排;上传换图会改这串 ⇒ 自动重排 */
|
|
143
|
+
private _imgBindDone;
|
|
144
|
+
/**
|
|
145
|
+
* 模型槽「GLB 已取回、等这一帧把它挂进父元素」的在册表 —— 与上面图片绑定同一手法(用户 10-26 需求:
|
|
146
|
+
* 一次性全部拿去加载,用 `threeUtils.addWork` 逐帧加到场景里)。整层十几个槽同时到货时,
|
|
147
|
+
* 一帧只有一颗进 `_contentRoot`(进图那一拍要重算包围盒 + 排布 + 首次把几何送上 GPU)。
|
|
148
|
+
*/
|
|
149
|
+
private _modelMountQueued;
|
|
122
150
|
constructor(opts?: {
|
|
123
151
|
wallHeight?: number;
|
|
124
152
|
wallClosed?: boolean;
|
|
@@ -295,12 +323,55 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
295
323
|
projectName: string;
|
|
296
324
|
/** 注入项目名(导出 json 前调一次即可;导入时无须调——还原靠落盘地址自身的首段) */
|
|
297
325
|
setProjectName(name: string): void;
|
|
326
|
+
/** 单颗图片平面入层(编辑态双击落点用):进数组 + 挂进本对象子树 + 起排它自己的逐帧绑定 */
|
|
327
|
+
addImagePlane(ip: ImagePlane): ImagePlane;
|
|
328
|
+
/** 摘掉一颗并释放它自己的几何/材质;那 4 张瓦片纹理归 `_imgTileLoader` 管 ⇒ ⛔ 不在这里 dispose */
|
|
329
|
+
removeImagePlane(ip: ImagePlane): void;
|
|
330
|
+
/** 单颗模型槽入层(编辑态双击落点、以及「复制模型」用) */
|
|
331
|
+
addModel(ms: ModelSlot): ModelSlot;
|
|
332
|
+
/** 摘掉一槽并释放(子树里的几何/材质/贴图由 `ModelSlot.dispose` 自己收尾;排在后头的挂载帧靠「不在册」早退) */
|
|
333
|
+
removeModel(ms: ModelSlot): void;
|
|
334
|
+
/**
|
|
335
|
+
* 整层重建图片平面(`importData` 与预览那一路)。
|
|
336
|
+
* ⚠️ 进来的地址必须是**已补回域名**的绝对形态:补域名只有 `importData` 那一处(与 `textures` 同一把 `assetUrl` 尺),
|
|
337
|
+
* ⛔ 本方法里不许再补一遍,也⛔ 不许在调用方各自补。
|
|
338
|
+
*/
|
|
339
|
+
setImagePlanes(datas: ImagePlaneData[]): void;
|
|
340
|
+
/**
|
|
341
|
+
* 整层重建模型槽(`importData` 与预览那一路),地址口径的要求同 `setImagePlanes`。
|
|
342
|
+
* ⚠️ GLB 逐只**异步取、不 await**(十几只串一条队会把载入卡死),每只落地那一刻各回调一次 `onEachLoad`:
|
|
343
|
+
* 预览每帧真画 ⇒ 不传;编辑器是按需渲染 ⇒ 传一个「标脏」的钩子,否则模型挂上来屏幕不动。
|
|
344
|
+
* 2026-10-26 决定:取回与「挂进父元素」分两拍 —— 地址一次性全交给下载(网络 + 解析不占渲染帧),
|
|
345
|
+
* 真正进场景图那一步交给 `threeUtils.addWork` ⇒ 全局 rAF 每拍只跑一项 = **一帧只挂一颗槽**
|
|
346
|
+
* (挂载那一拍要重算包围盒 + 排布 + 首次把整棵子树的几何送上 GPU,十几颗挤在同一帧就是他要拆掉的那一卡)。
|
|
347
|
+
*/
|
|
348
|
+
setModels(datas: ModelSlotData[], onEachLoad?: () => void): void;
|
|
349
|
+
/** 把「备好待挂」的那一槽交给全局队列;⛔ 去重闸门自备(`addWork` 按闭包引用判重,新闭包它判不出来) */
|
|
350
|
+
private _queueModelMount;
|
|
351
|
+
/** 全局队列里的那一帧:只挂这一槽,挂好才回调(`onEachLoad` = 按需渲染宿主的标脏钩子) */
|
|
352
|
+
private _mountModelStep;
|
|
353
|
+
/**
|
|
354
|
+
* 把各颗平面那 4 张瓦片的纹理绑进材质(10-22 晚定的口径:不走拼合器 RT)。
|
|
355
|
+
* 2026-10-26 决定改成**逐帧**:一次把整层地址全丢给下载器(下载/解码在网络与工作线程侧,不占这一帧),
|
|
356
|
+
* 真正写 uniform 的那一步交给 `threeUtils.addWork` —— 全局 rAF 每拍只跑一项 ⇒ 一帧只绑一颗平面,
|
|
357
|
+
* 一整层平面的纹理上传被摊到多帧,不再挤在同一记调用里(旧写法一记 `feed` 就把所有平面全绑完)。
|
|
358
|
+
* ⛔ 不进 `onBeforeRender` 自驱;持有渲染循环的那一方(编辑态 / 预览 / 下游)每帧调一次只是「补排漏网的平面」,
|
|
359
|
+
* 而整层重建(`setImagePlanes`)末尾自己起排 ⇒ 只调 `importData(整串)` 的宿主不必自己驱动也会长出来。
|
|
360
|
+
* 还没下载完/缺格的那一格绑 null ⇒ 片元那一格整格 discard,不落像素(用户 10-26 连收两条:先「不要默认图」,
|
|
361
|
+
* 再「不是显示灰色, 而是直接 discard 丢弃」)。
|
|
362
|
+
*/
|
|
363
|
+
feedImagePlaneTiles(): void;
|
|
364
|
+
/** 一颗平面排一帧的绑定工作:先把它那 4 条地址**一次性全部**交给下载器,再把「写材质」排进全局队列。 */
|
|
365
|
+
private _queueImagePlaneBind;
|
|
366
|
+
/** 队列里的一格:只绑**这一颗**平面里已就绪的那几格;还有在途的就重新排队,⛔ 不在这里等、也⛔ 不顺手把别的平面一起做完。 */
|
|
367
|
+
private _bindImagePlaneStep;
|
|
298
368
|
/** 导出数据:墙点位 + 墙高 + 是否闭合 + 纹理数组(url + 矩阵)
|
|
299
369
|
* @param projName 项目名(可选,优先于 `setProjectName` 注入的那个)——图片地址落成 `{项目名}/xxx.jpg` */
|
|
300
370
|
exportData(projName?: string): MktMeshData;
|
|
301
371
|
/** 导入数据:重建墙体几何与纹理列表,并激活第一个纹理
|
|
302
|
-
* @param projName 项目名(可选)——用于把落盘的 `{项目名}/x_0.jpg` 还原回真身OSS 地址
|
|
303
|
-
|
|
304
|
-
|
|
372
|
+
* @param projName 项目名(可选)——用于把落盘的 `{项目名}/x_0.jpg` 还原回真身OSS 地址
|
|
373
|
+
* @param onModelLoad 每只 GLB 落地时回调一次(可选)——按需渲染的宿主(编辑器)拿它标脏;每帧真画的预览不传 */
|
|
374
|
+
importData(data: MktMeshData, projName?: string, onModelLoad?: () => void): void;
|
|
375
|
+
/** 释放 GPU 资源:瓦片缓存(**只有这里**清,皇上旨 2026-10-02)、拼合器、平面图/模型两层、几何、材质 */
|
|
305
376
|
dispose(): this;
|
|
306
377
|
}
|
package/MktMesh/MktMesh.js
CHANGED
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
* `panoSlotRoster.ts`;皇上旨 2026-10-03 起预览/发布与编辑器共用同一份算法 ⇒ 两边装同一批全景)。
|
|
17
17
|
* ⭐ 皇上旨 2026-10-07 第二十一道旨:锚点 = 外部控制器的中心点(`setRosterCenter` 注入),没注入才回落相机位置;
|
|
18
18
|
* 中心变化或名册里有空格被补上人时加载新瓦片 → 设置 shader 的 8 个全景槽 panorama + textureMatrix;
|
|
19
|
-
* 4. exportData / importData 支持导入导出(墙点位 +
|
|
19
|
+
* 4. exportData / importData 支持导入导出(墙点位 + 纹理数组 + ⭐ 10-26 旨起的平面图 / 模型两层)。
|
|
20
20
|
* 5. ⚠️ 铁律:激活纹理(墙面上正在绘制的全景)的旋转/位置变化,必须立即同步到
|
|
21
21
|
* shader 的 textureMatrix uniform(见 setTexture),否则平面纹理不跟着全景球转。
|
|
22
22
|
*
|
|
23
23
|
* 说明:所有实例共享同一份 material(单一 panorama uniform),
|
|
24
24
|
* 同一时刻只激活名册 0 号槽(中心)那一个纹理(全局共享单激活方案)。
|
|
25
25
|
*/
|
|
26
|
-
import { BufferGeometry, BackSide, Float32BufferAttribute, Matrix4, Mesh, Shape, ShapeGeometry, SRGBColorSpace, Vector3, } from "three";
|
|
26
|
+
import { BufferGeometry, BackSide, Float32BufferAttribute, Matrix4, Mesh, Shape, ShapeGeometry, SRGBColorSpace, Vector3, Vector4, } from "three";
|
|
27
27
|
import { createPlaceholderTexture, isPanoTexMode, mktPanoTiledFlag, mktPanoTexUrls, isRtStitchKey, rtStitchKeyParts, RT_STITCH_KEY_PREFIX, RT_STITCH_SLOT_COUNT } from "./mktShader";
|
|
28
28
|
import { MKT_PANO_MAX_SLOTS } from "./mktMappingShader";
|
|
29
29
|
import { createMktFloorMappingShaderMaterial } from "./mktMappingShader";
|
|
@@ -45,6 +45,12 @@ import { subdivideCapTriangles, WALL_CAP_MAX_EDGE_M } from "./wallCapTessellate"
|
|
|
45
45
|
// ⭐ 皇上旨 2026-10-08「json 里面的图片的地址,把域名去掉,只保留相对的根路径」:
|
|
46
46
|
// 导出时剥域名(`exportData`)、导入时补回来(`importData`)——**各只有这一处**,⛔ 不许在别处另剥/另补。
|
|
47
47
|
import { stripAssetHost, absAssetUrl } from "./assetUrl";
|
|
48
|
+
// ⭐ 皇上旨 2026-10-26「按这个方案,把图片平面和模型一起搬进库里」:这两层(含每帧喂瓦片那条编排)
|
|
49
|
+
// 原先各住在本站两个渲染器里一份 ⇒ 下游直接用包里的 `MktMesh` 时,存档里的 `images`/`models` 两键没人读。
|
|
50
|
+
// 入库之后它们挂在 `MktMesh` **自己的子树**下,与墙同一棵变换 ⇒ 预览那三根 transform 滑杆搬得动整份展品。
|
|
51
|
+
import { ImagePlane } from "./imagePlane";
|
|
52
|
+
import { ModelSlot } from "./modelSlot";
|
|
53
|
+
import { ImagePlaneTileLoader } from "./imageTileLoader";
|
|
48
54
|
export default class MktMesh extends Mesh {
|
|
49
55
|
/** 纹理列表(每个纹理的 url + 世界矩阵) */
|
|
50
56
|
textures = [];
|
|
@@ -172,6 +178,32 @@ export default class MktMesh extends Mesh {
|
|
|
172
178
|
}
|
|
173
179
|
// 复用临时对象(避免每帧 new,降低 GC 压力)
|
|
174
180
|
_tmpV3 = new Vector3();
|
|
181
|
+
/**
|
|
182
|
+
* 图片平面层(⭐ 10-26 旨入库):对象挂在**本 MktMesh 的子树**里,⛔ 不是场景根
|
|
183
|
+
* ⇒ 移动/旋转/缩放这个展品时,墙 + 全景 + 平面图 + 模型一起走。
|
|
184
|
+
*/
|
|
185
|
+
imagePlanes = [];
|
|
186
|
+
/** 模型槽层(同一棵子树,口径同上) */
|
|
187
|
+
models = [];
|
|
188
|
+
/**
|
|
189
|
+
* 那 4 张瓦片的下载/解码缓存。⭐ 与全景 `_texCache` 同一条旨(10-02「纹理都缓存在对象里面,执行 dispose 时才销毁」)
|
|
190
|
+
* ⇒ 切工程**不**清它,回来那一圈直接命中缓存、不会再空一格;只随本对象 `dispose()` 释放。
|
|
191
|
+
*/
|
|
192
|
+
_imgTileLoader = new ImagePlaneTileLoader();
|
|
193
|
+
/**
|
|
194
|
+
* 逐帧绑定用的两张账(2026-10-26 定的方案:地址一次性全部丢给下载,绑材质改成「一帧一颗平面」)。
|
|
195
|
+
* 全局 rAF 每拍只跑一项 `addWork`,而 `addWork` 按闭包引用判重 ⇒ 每次现造的新闭包它判不出来,
|
|
196
|
+
* ⛔ 去重闸门必须像全景上传那张 `_uploadQueued` 一样自备。
|
|
197
|
+
*/
|
|
198
|
+
_imgBindQueued = new Set();
|
|
199
|
+
/** 平面 → 「它 4 条地址全部落定(就绪或失败)时的那串地址」⇒ 地址不换就不再重排;上传换图会改这串 ⇒ 自动重排 */
|
|
200
|
+
_imgBindDone = new Map();
|
|
201
|
+
/**
|
|
202
|
+
* 模型槽「GLB 已取回、等这一帧把它挂进父元素」的在册表 —— 与上面图片绑定同一手法(用户 10-26 需求:
|
|
203
|
+
* 一次性全部拿去加载,用 `threeUtils.addWork` 逐帧加到场景里)。整层十几个槽同时到货时,
|
|
204
|
+
* 一帧只有一颗进 `_contentRoot`(进图那一拍要重算包围盒 + 排布 + 首次把几何送上 GPU)。
|
|
205
|
+
*/
|
|
206
|
+
_modelMountQueued = new Set();
|
|
175
207
|
constructor(opts = {}) {
|
|
176
208
|
// 发布/预览态墙:**固定**用「全景序号直取」着色器(皇上旨 2026-09-29:mktmesh 就只有按照 attribute
|
|
177
209
|
// 配置来显示全景这一种固定的 shader——每帧由 feedFaceMapping 按 panoFaceMapping 把被引用的全景
|
|
@@ -1008,6 +1040,168 @@ export default class MktMesh extends Mesh {
|
|
|
1008
1040
|
setProjectName(name) {
|
|
1009
1041
|
this.projectName = typeof name === "string" ? name : "";
|
|
1010
1042
|
}
|
|
1043
|
+
// ───────────── 平面图 / 模型两层(⭐ 皇上旨 2026-10-26「把图片平面和模型一起搬进库里」) ─────────────
|
|
1044
|
+
/** 单颗图片平面入层(编辑态双击落点用):进数组 + 挂进本对象子树 + 起排它自己的逐帧绑定 */
|
|
1045
|
+
addImagePlane(ip) {
|
|
1046
|
+
this.imagePlanes.push(ip);
|
|
1047
|
+
this.add(ip);
|
|
1048
|
+
this._queueImagePlaneBind(ip);
|
|
1049
|
+
return ip;
|
|
1050
|
+
}
|
|
1051
|
+
/** 摘掉一颗并释放它自己的几何/材质;那 4 张瓦片纹理归 `_imgTileLoader` 管 ⇒ ⛔ 不在这里 dispose */
|
|
1052
|
+
removeImagePlane(ip) {
|
|
1053
|
+
const i = this.imagePlanes.indexOf(ip);
|
|
1054
|
+
if (i >= 0)
|
|
1055
|
+
this.imagePlanes.splice(i, 1);
|
|
1056
|
+
this._imgBindQueued.delete(ip);
|
|
1057
|
+
this._imgBindDone.delete(ip);
|
|
1058
|
+
this.remove(ip);
|
|
1059
|
+
ip.dispose();
|
|
1060
|
+
}
|
|
1061
|
+
/** 单颗模型槽入层(编辑态双击落点、以及「复制模型」用) */
|
|
1062
|
+
addModel(ms) {
|
|
1063
|
+
this.models.push(ms);
|
|
1064
|
+
this.add(ms);
|
|
1065
|
+
return ms;
|
|
1066
|
+
}
|
|
1067
|
+
/** 摘掉一槽并释放(子树里的几何/材质/贴图由 `ModelSlot.dispose` 自己收尾;排在后头的挂载帧靠「不在册」早退) */
|
|
1068
|
+
removeModel(ms) {
|
|
1069
|
+
const i = this.models.indexOf(ms);
|
|
1070
|
+
if (i >= 0)
|
|
1071
|
+
this.models.splice(i, 1);
|
|
1072
|
+
this._modelMountQueued.delete(ms);
|
|
1073
|
+
this.remove(ms);
|
|
1074
|
+
ms.dispose();
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* 整层重建图片平面(`importData` 与预览那一路)。
|
|
1078
|
+
* ⚠️ 进来的地址必须是**已补回域名**的绝对形态:补域名只有 `importData` 那一处(与 `textures` 同一把 `assetUrl` 尺),
|
|
1079
|
+
* ⛔ 本方法里不许再补一遍,也⛔ 不许在调用方各自补。
|
|
1080
|
+
*/
|
|
1081
|
+
setImagePlanes(datas) {
|
|
1082
|
+
this._imgBindQueued.clear();
|
|
1083
|
+
this._imgBindDone.clear();
|
|
1084
|
+
for (const ip of this.imagePlanes) {
|
|
1085
|
+
this.remove(ip);
|
|
1086
|
+
ip.dispose();
|
|
1087
|
+
}
|
|
1088
|
+
this.imagePlanes = [];
|
|
1089
|
+
for (const d of datas) {
|
|
1090
|
+
const ip = new ImagePlane(d.name);
|
|
1091
|
+
ip.applyData(d);
|
|
1092
|
+
this.imagePlanes.push(ip);
|
|
1093
|
+
this.add(ip);
|
|
1094
|
+
}
|
|
1095
|
+
// 整层重建完立刻起排:地址一次性全交给下载、绑材质逐帧一记 ⇒ 宿主不驱动这一层也长得出来
|
|
1096
|
+
this.feedImagePlaneTiles();
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* 整层重建模型槽(`importData` 与预览那一路),地址口径的要求同 `setImagePlanes`。
|
|
1100
|
+
* ⚠️ GLB 逐只**异步取、不 await**(十几只串一条队会把载入卡死),每只落地那一刻各回调一次 `onEachLoad`:
|
|
1101
|
+
* 预览每帧真画 ⇒ 不传;编辑器是按需渲染 ⇒ 传一个「标脏」的钩子,否则模型挂上来屏幕不动。
|
|
1102
|
+
* 2026-10-26 决定:取回与「挂进父元素」分两拍 —— 地址一次性全交给下载(网络 + 解析不占渲染帧),
|
|
1103
|
+
* 真正进场景图那一步交给 `threeUtils.addWork` ⇒ 全局 rAF 每拍只跑一项 = **一帧只挂一颗槽**
|
|
1104
|
+
* (挂载那一拍要重算包围盒 + 排布 + 首次把整棵子树的几何送上 GPU,十几颗挤在同一帧就是他要拆掉的那一卡)。
|
|
1105
|
+
*/
|
|
1106
|
+
setModels(datas, onEachLoad) {
|
|
1107
|
+
this._modelMountQueued.clear();
|
|
1108
|
+
for (const ms of this.models) {
|
|
1109
|
+
this.remove(ms);
|
|
1110
|
+
ms.dispose();
|
|
1111
|
+
}
|
|
1112
|
+
this.models = [];
|
|
1113
|
+
for (const d of datas) {
|
|
1114
|
+
const ms = new ModelSlot(d.name);
|
|
1115
|
+
ms.applyData(d);
|
|
1116
|
+
this.models.push(ms);
|
|
1117
|
+
this.add(ms); // 槽本体(占位小立方体)当场进图 ⇒ GLB 在路上时这一槽不凭空消失
|
|
1118
|
+
if (d.url)
|
|
1119
|
+
void ms.loadModel(d.url).then(() => this._queueModelMount(ms, onEachLoad));
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
/** 把「备好待挂」的那一槽交给全局队列;⛔ 去重闸门自备(`addWork` 按闭包引用判重,新闭包它判不出来) */
|
|
1123
|
+
_queueModelMount(ms, onEachLoad) {
|
|
1124
|
+
if (this._modelMountQueued.has(ms))
|
|
1125
|
+
return;
|
|
1126
|
+
this._modelMountQueued.add(ms);
|
|
1127
|
+
threeUtils.addWork(() => { });
|
|
1128
|
+
threeUtils.addWork(() => this._mountModelStep(ms, onEachLoad));
|
|
1129
|
+
}
|
|
1130
|
+
/** 全局队列里的那一帧:只挂这一槽,挂好才回调(`onEachLoad` = 按需渲染宿主的标脏钩子) */
|
|
1131
|
+
_mountModelStep(ms, onEachLoad) {
|
|
1132
|
+
this._modelMountQueued.delete(ms);
|
|
1133
|
+
// 槽已退场(换项目 / 删除):待挂那棵由 `ModelSlot.dispose` → `_detachModel` 当场释放,这里不挂
|
|
1134
|
+
if (!this.models.includes(ms))
|
|
1135
|
+
return;
|
|
1136
|
+
if (ms.mountStaged() && onEachLoad)
|
|
1137
|
+
onEachLoad();
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* 把各颗平面那 4 张瓦片的纹理绑进材质(10-22 晚定的口径:不走拼合器 RT)。
|
|
1141
|
+
* 2026-10-26 决定改成**逐帧**:一次把整层地址全丢给下载器(下载/解码在网络与工作线程侧,不占这一帧),
|
|
1142
|
+
* 真正写 uniform 的那一步交给 `threeUtils.addWork` —— 全局 rAF 每拍只跑一项 ⇒ 一帧只绑一颗平面,
|
|
1143
|
+
* 一整层平面的纹理上传被摊到多帧,不再挤在同一记调用里(旧写法一记 `feed` 就把所有平面全绑完)。
|
|
1144
|
+
* ⛔ 不进 `onBeforeRender` 自驱;持有渲染循环的那一方(编辑态 / 预览 / 下游)每帧调一次只是「补排漏网的平面」,
|
|
1145
|
+
* 而整层重建(`setImagePlanes`)末尾自己起排 ⇒ 只调 `importData(整串)` 的宿主不必自己驱动也会长出来。
|
|
1146
|
+
* 还没下载完/缺格的那一格绑 null ⇒ 片元那一格整格 discard,不落像素(用户 10-26 连收两条:先「不要默认图」,
|
|
1147
|
+
* 再「不是显示灰色, 而是直接 discard 丢弃」)。
|
|
1148
|
+
*/
|
|
1149
|
+
feedImagePlaneTiles() {
|
|
1150
|
+
for (const ip of this.imagePlanes)
|
|
1151
|
+
this._queueImagePlaneBind(ip);
|
|
1152
|
+
}
|
|
1153
|
+
/** 一颗平面排一帧的绑定工作:先把它那 4 条地址**一次性全部**交给下载器,再把「写材质」排进全局队列。 */
|
|
1154
|
+
_queueImagePlaneBind(ip) {
|
|
1155
|
+
const tiles = ip.tiles;
|
|
1156
|
+
const sig = (tiles ?? []).join("|");
|
|
1157
|
+
// 两道闸:已排在队里 = 不重复排;这串地址已经落定过 = 不再动它(换图会改这串 ⇒ 自然会重排)
|
|
1158
|
+
if (this._imgBindQueued.has(ip) || this._imgBindDone.get(ip) === sig)
|
|
1159
|
+
return;
|
|
1160
|
+
const zero = new Vector4(0, 0, 1, 1);
|
|
1161
|
+
// ⛔ 旧存档 16 条地址(> 满批 4)直接不收 ⇒ 整张不落像素(四格全 discard),按 10-20 口径重传即可;空 tiles 同样
|
|
1162
|
+
if (!tiles || tiles.length !== 4) {
|
|
1163
|
+
ip.setTileTextures([null, null, null, null], [zero, zero.clone(), zero.clone(), zero.clone()]);
|
|
1164
|
+
this._imgBindDone.set(ip, sig);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
for (const url of tiles)
|
|
1168
|
+
this._imgTileLoader.preload(url); // ① 一次性全部拿去加载
|
|
1169
|
+
this._imgBindQueued.add(ip);
|
|
1170
|
+
threeUtils.addWork(() => { }); // 占一格(什么都不干),把下面那一项推到下一个 tick —— 与全景上传同一手法
|
|
1171
|
+
threeUtils.addWork(() => this._bindImagePlaneStep(ip, sig));
|
|
1172
|
+
}
|
|
1173
|
+
/** 队列里的一格:只绑**这一颗**平面里已就绪的那几格;还有在途的就重新排队,⛔ 不在这里等、也⛔ 不顺手把别的平面一起做完。 */
|
|
1174
|
+
_bindImagePlaneStep(ip, sig) {
|
|
1175
|
+
this._imgBindQueued.delete(ip);
|
|
1176
|
+
if (!this.imagePlanes.includes(ip))
|
|
1177
|
+
return; // 已摘掉(删平面 / 切项目整层重建)⇒ 这一格什么都不做
|
|
1178
|
+
const tiles = ip.tiles;
|
|
1179
|
+
if ((tiles ?? []).join("|") !== sig)
|
|
1180
|
+
return; // 地址中途换了 ⇒ 下一记 feed 按新拼串重排
|
|
1181
|
+
const zero = new Vector4(0, 0, 1, 1);
|
|
1182
|
+
const texs = [null, null, null, null];
|
|
1183
|
+
const insets = [zero.clone(), zero.clone(), zero.clone(), zero.clone()];
|
|
1184
|
+
let pending = false;
|
|
1185
|
+
for (let i = 0; i < 4; i++) {
|
|
1186
|
+
const url = tiles[i];
|
|
1187
|
+
if (!url)
|
|
1188
|
+
continue;
|
|
1189
|
+
const e = this._imgTileLoader.get(url);
|
|
1190
|
+
if (e && e.tex) {
|
|
1191
|
+
texs[i] = e.tex;
|
|
1192
|
+
insets[i] = this._imgTileLoader.inset(e, i);
|
|
1193
|
+
}
|
|
1194
|
+
else if (this._imgTileLoader.isPending(url)) {
|
|
1195
|
+
pending = true; // 还在下载/解码 ⇒ 这一格先绑已到的,剩下的下一拍再看
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
ip.setTileTextures(texs, insets);
|
|
1199
|
+
if (pending) {
|
|
1200
|
+
this._queueImagePlaneBind(ip);
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
this._imgBindDone.set(ip, sig); // 全部落定(含下载失败那格:它留 null,那一格就永久 discard)
|
|
1204
|
+
}
|
|
1011
1205
|
/** 导出数据:墙点位 + 墙高 + 是否闭合 + 纹理数组(url + 矩阵)
|
|
1012
1206
|
* @param projName 项目名(可选,优先于 `setProjectName` 注入的那个)——图片地址落成 `{项目名}/xxx.jpg` */
|
|
1013
1207
|
exportData(projName) {
|
|
@@ -1030,11 +1224,29 @@ export default class MktMesh extends Mesh {
|
|
|
1030
1224
|
tiles: (t.tiles && t.tiles.length ? t.tiles.map((u) => stripAssetHost(u, pn)) : []),
|
|
1031
1225
|
matrix: t.matrix.toArray(),
|
|
1032
1226
|
})),
|
|
1227
|
+
// ⭐ 10-26 旨入库:这两层的地址与 `textures` 共用上面那一条剥域名口径(⛔ 不是另起一把尺),
|
|
1228
|
+
// 其余字段(位置/旋转/宽高/瓦片数/落点面/原始包围盒)由各自的 `toData()` 逐字存。
|
|
1229
|
+
images: this.imagePlanes.map((ip) => {
|
|
1230
|
+
const d = ip.toData();
|
|
1231
|
+
return {
|
|
1232
|
+
...d,
|
|
1233
|
+
url: d.url ? stripAssetHost(d.url, pn) : "",
|
|
1234
|
+
tiles: d.tiles.length ? d.tiles.map((u) => stripAssetHost(u, pn)) : [],
|
|
1235
|
+
};
|
|
1236
|
+
}),
|
|
1237
|
+
models: this.models.map((ms) => {
|
|
1238
|
+
const d = ms.toData();
|
|
1239
|
+
return { ...d, url: d.url ? stripAssetHost(d.url, pn) : "" };
|
|
1240
|
+
}),
|
|
1033
1241
|
};
|
|
1034
1242
|
}
|
|
1035
1243
|
/** 导入数据:重建墙体几何与纹理列表,并激活第一个纹理
|
|
1036
|
-
* @param projName 项目名(可选)——用于把落盘的 `{项目名}/x_0.jpg` 还原回真身OSS 地址
|
|
1037
|
-
|
|
1244
|
+
* @param projName 项目名(可选)——用于把落盘的 `{项目名}/x_0.jpg` 还原回真身OSS 地址
|
|
1245
|
+
* @param onModelLoad 每只 GLB 落地时回调一次(可选)——按需渲染的宿主(编辑器)拿它标脏;每帧真画的预览不传 */
|
|
1246
|
+
importData(data, projName, onModelLoad) {
|
|
1247
|
+
// ⭐ 10-08 旨的「补回域名」那颗项目名,10-26 旨之后全景/平面图/模型**三层共用同一个**(原来在 textures 的
|
|
1248
|
+
// map 里就地取,下面那两层也要用 ⇒ 提到方法开头,⛔ 不是第二处口径)。
|
|
1249
|
+
const pn = typeof projName === "string" && projName ? projName : this.projectName;
|
|
1038
1250
|
this.setWallPoints(data.wallPoints ?? [], {
|
|
1039
1251
|
closed: data.wallClosed,
|
|
1040
1252
|
height: data.wallHeight,
|
|
@@ -1049,7 +1261,6 @@ export default class MktMesh extends Mesh {
|
|
|
1049
1261
|
// (`/three-base_upload/…`)不必重存也能正常加载,不会被拼成 `https://host/https://host/…`
|
|
1050
1262
|
// · 19:53 旨后还要把 `{项目名}/x_0.jpg` **还原**成真身 OSS 地址(顶掉的上传目录要补回来),
|
|
1051
1263
|
// 否则 json 里的地址指不回原图(图片裂)。`pn` 取入参、回落 `setProjectName` 注入的那个。
|
|
1052
|
-
const pn = typeof projName === "string" && projName ? projName : this.projectName;
|
|
1053
1264
|
const urlAbs = absAssetUrl(t.url, pn);
|
|
1054
1265
|
return {
|
|
1055
1266
|
url: urlAbs,
|
|
@@ -1073,9 +1284,18 @@ export default class MktMesh extends Mesh {
|
|
|
1073
1284
|
this.panoFaceMapping[f] = imported[f];
|
|
1074
1285
|
this.rebuildPanoIndexAttribute();
|
|
1075
1286
|
}
|
|
1287
|
+
// ⭐ 10-26 旨「把图片平面和模型一起搬进库里」的另一半:这两层也从**同一份 JSON** 里读,
|
|
1288
|
+
// 地址在这一处补回域名(与上面 textures 同一条尺)⇒ 下游只调 `importData(整串)` 就全画出来。
|
|
1289
|
+
// 旧存档没有这两个键 ⇒ 空数组 = 整层清空,与「墙 + 全景」那套逐字同口径。
|
|
1290
|
+
this.setImagePlanes((data.images ?? []).map((d) => ({
|
|
1291
|
+
...d,
|
|
1292
|
+
url: d.url ? absAssetUrl(d.url, pn) : "",
|
|
1293
|
+
tiles: (d.tiles ?? []).map((u) => absAssetUrl(u, pn)),
|
|
1294
|
+
})));
|
|
1295
|
+
this.setModels((data.models ?? []).map((d) => ({ ...d, url: d.url ? absAssetUrl(d.url, pn) : "" })), onModelLoad);
|
|
1076
1296
|
}
|
|
1077
1297
|
// ───────────────────────── 释放 ─────────────────────────
|
|
1078
|
-
/** 释放 GPU 资源:瓦片缓存(**只有这里**清,皇上旨 2026-10-02
|
|
1298
|
+
/** 释放 GPU 资源:瓦片缓存(**只有这里**清,皇上旨 2026-10-02)、拼合器、平面图/模型两层、几何、材质 */
|
|
1079
1299
|
dispose() {
|
|
1080
1300
|
// ⚠️ rt 虚拟键从不进 `_texCache`(拼合槽纹理由 `_stitcher` 自己持有并释放),这里清的都是本类下载的 Texture。
|
|
1081
1301
|
// ⭐ 甲(2026-10-05):`releasePanoTexture` 除 `dispose()` 外还要 `ImageBitmap.close()` —— 位图的原生内存
|
|
@@ -1086,6 +1306,22 @@ export default class MktMesh extends Mesh {
|
|
|
1086
1306
|
this._texUploaded = new WeakSet(); // WeakSet 无 clear ⇒ 整只换掉
|
|
1087
1307
|
this._uploadQueued.clear(); // 排队项即便回调仍触发,也会因 `!this._uploadQueued.delete(tex)` 早退
|
|
1088
1308
|
this._ossSizeDepKeys.clear();
|
|
1309
|
+
// ⭐ 10-26 旨入库的两层:子对象逐个 dispose,平面那 4 张瓦片的缓存**只在这里**整批清
|
|
1310
|
+
// (与上面 `_texCache` 同一条旨 ⇒ 切工程不重建这一份、也就不会再空出一格)。
|
|
1311
|
+
for (const ip of this.imagePlanes) {
|
|
1312
|
+
this.remove(ip);
|
|
1313
|
+
ip.dispose();
|
|
1314
|
+
}
|
|
1315
|
+
this.imagePlanes = [];
|
|
1316
|
+
for (const ms of this.models) {
|
|
1317
|
+
this.remove(ms);
|
|
1318
|
+
ms.dispose();
|
|
1319
|
+
}
|
|
1320
|
+
this.models = [];
|
|
1321
|
+
this._imgTileLoader.clear();
|
|
1322
|
+
this._imgBindQueued.clear();
|
|
1323
|
+
this._imgBindDone.clear();
|
|
1324
|
+
this._modelMountQueued.clear();
|
|
1089
1325
|
this._stitcher.dispose();
|
|
1090
1326
|
this._glRenderer = null;
|
|
1091
1327
|
if (this._placeholder)
|