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.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
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
|
*
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
SRGBColorSpace,
|
|
40
40
|
Texture,
|
|
41
41
|
Vector3,
|
|
42
|
+
Vector4,
|
|
42
43
|
} from "three";
|
|
43
44
|
import type { WebGLRenderer } from "three";
|
|
44
45
|
import { createPlaceholderTexture, isPanoTexMode, mktPanoTiledFlag, mktPanoTexUrls, isRtStitchKey, rtStitchKeyParts, RT_STITCH_KEY_PREFIX, RT_STITCH_SLOT_COUNT, type PanoTexMode } from "./mktShader";
|
|
@@ -64,6 +65,12 @@ import { subdivideCapTriangles, WALL_CAP_MAX_EDGE_M } from "./wallCapTessellate"
|
|
|
64
65
|
// ⭐ 皇上旨 2026-10-08「json 里面的图片的地址,把域名去掉,只保留相对的根路径」:
|
|
65
66
|
// 导出时剥域名(`exportData`)、导入时补回来(`importData`)——**各只有这一处**,⛔ 不许在别处另剥/另补。
|
|
66
67
|
import { stripAssetHost, absAssetUrl } from "./assetUrl";
|
|
68
|
+
// ⭐ 皇上旨 2026-10-26「按这个方案,把图片平面和模型一起搬进库里」:这两层(含每帧喂瓦片那条编排)
|
|
69
|
+
// 原先各住在本站两个渲染器里一份 ⇒ 下游直接用包里的 `MktMesh` 时,存档里的 `images`/`models` 两键没人读。
|
|
70
|
+
// 入库之后它们挂在 `MktMesh` **自己的子树**下,与墙同一棵变换 ⇒ 预览那三根 transform 滑杆搬得动整份展品。
|
|
71
|
+
import { ImagePlane, type ImagePlaneData } from "./imagePlane";
|
|
72
|
+
import { ModelSlot, type ModelSlotData } from "./modelSlot";
|
|
73
|
+
import { ImagePlaneTileLoader } from "./imageTileLoader";
|
|
67
74
|
|
|
68
75
|
export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
69
76
|
/** 纹理列表(每个纹理的 url + 世界矩阵) */
|
|
@@ -197,6 +204,33 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
197
204
|
// 复用临时对象(避免每帧 new,降低 GC 压力)
|
|
198
205
|
private _tmpV3: Vector3 = new Vector3();
|
|
199
206
|
|
|
207
|
+
/**
|
|
208
|
+
* 图片平面层(⭐ 10-26 旨入库):对象挂在**本 MktMesh 的子树**里,⛔ 不是场景根
|
|
209
|
+
* ⇒ 移动/旋转/缩放这个展品时,墙 + 全景 + 平面图 + 模型一起走。
|
|
210
|
+
*/
|
|
211
|
+
imagePlanes: ImagePlane[] = [];
|
|
212
|
+
/** 模型槽层(同一棵子树,口径同上) */
|
|
213
|
+
models: ModelSlot[] = [];
|
|
214
|
+
/**
|
|
215
|
+
* 那 4 张瓦片的下载/解码缓存。⭐ 与全景 `_texCache` 同一条旨(10-02「纹理都缓存在对象里面,执行 dispose 时才销毁」)
|
|
216
|
+
* ⇒ 切工程**不**清它,回来那一圈直接命中缓存、不会再空一格;只随本对象 `dispose()` 释放。
|
|
217
|
+
*/
|
|
218
|
+
private _imgTileLoader = new ImagePlaneTileLoader();
|
|
219
|
+
/**
|
|
220
|
+
* 逐帧绑定用的两张账(2026-10-26 定的方案:地址一次性全部丢给下载,绑材质改成「一帧一颗平面」)。
|
|
221
|
+
* 全局 rAF 每拍只跑一项 `addWork`,而 `addWork` 按闭包引用判重 ⇒ 每次现造的新闭包它判不出来,
|
|
222
|
+
* ⛔ 去重闸门必须像全景上传那张 `_uploadQueued` 一样自备。
|
|
223
|
+
*/
|
|
224
|
+
private _imgBindQueued = new Set<ImagePlane>();
|
|
225
|
+
/** 平面 → 「它 4 条地址全部落定(就绪或失败)时的那串地址」⇒ 地址不换就不再重排;上传换图会改这串 ⇒ 自动重排 */
|
|
226
|
+
private _imgBindDone = new Map<ImagePlane, string>();
|
|
227
|
+
/**
|
|
228
|
+
* 模型槽「GLB 已取回、等这一帧把它挂进父元素」的在册表 —— 与上面图片绑定同一手法(用户 10-26 需求:
|
|
229
|
+
* 一次性全部拿去加载,用 `threeUtils.addWork` 逐帧加到场景里)。整层十几个槽同时到货时,
|
|
230
|
+
* 一帧只有一颗进 `_contentRoot`(进图那一拍要重算包围盒 + 排布 + 首次把几何送上 GPU)。
|
|
231
|
+
*/
|
|
232
|
+
private _modelMountQueued = new Set<ModelSlot>();
|
|
233
|
+
|
|
200
234
|
constructor(opts: { wallHeight?: number; wallClosed?: boolean } = {}) {
|
|
201
235
|
// 发布/预览态墙:**固定**用「全景序号直取」着色器(皇上旨 2026-09-29:mktmesh 就只有按照 attribute
|
|
202
236
|
// 配置来显示全景这一种固定的 shader——每帧由 feedFaceMapping 按 panoFaceMapping 把被引用的全景
|
|
@@ -1035,6 +1069,167 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
1035
1069
|
this.projectName = typeof name === "string" ? name : "";
|
|
1036
1070
|
}
|
|
1037
1071
|
|
|
1072
|
+
// ───────────── 平面图 / 模型两层(⭐ 皇上旨 2026-10-26「把图片平面和模型一起搬进库里」) ─────────────
|
|
1073
|
+
|
|
1074
|
+
/** 单颗图片平面入层(编辑态双击落点用):进数组 + 挂进本对象子树 + 起排它自己的逐帧绑定 */
|
|
1075
|
+
addImagePlane(ip: ImagePlane): ImagePlane {
|
|
1076
|
+
this.imagePlanes.push(ip);
|
|
1077
|
+
this.add(ip);
|
|
1078
|
+
this._queueImagePlaneBind(ip);
|
|
1079
|
+
return ip;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/** 摘掉一颗并释放它自己的几何/材质;那 4 张瓦片纹理归 `_imgTileLoader` 管 ⇒ ⛔ 不在这里 dispose */
|
|
1083
|
+
removeImagePlane(ip: ImagePlane): void {
|
|
1084
|
+
const i = this.imagePlanes.indexOf(ip);
|
|
1085
|
+
if (i >= 0) this.imagePlanes.splice(i, 1);
|
|
1086
|
+
this._imgBindQueued.delete(ip);
|
|
1087
|
+
this._imgBindDone.delete(ip);
|
|
1088
|
+
this.remove(ip);
|
|
1089
|
+
ip.dispose();
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/** 单颗模型槽入层(编辑态双击落点、以及「复制模型」用) */
|
|
1093
|
+
addModel(ms: ModelSlot): ModelSlot {
|
|
1094
|
+
this.models.push(ms);
|
|
1095
|
+
this.add(ms);
|
|
1096
|
+
return ms;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/** 摘掉一槽并释放(子树里的几何/材质/贴图由 `ModelSlot.dispose` 自己收尾;排在后头的挂载帧靠「不在册」早退) */
|
|
1100
|
+
removeModel(ms: ModelSlot): void {
|
|
1101
|
+
const i = this.models.indexOf(ms);
|
|
1102
|
+
if (i >= 0) this.models.splice(i, 1);
|
|
1103
|
+
this._modelMountQueued.delete(ms);
|
|
1104
|
+
this.remove(ms);
|
|
1105
|
+
ms.dispose();
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* 整层重建图片平面(`importData` 与预览那一路)。
|
|
1110
|
+
* ⚠️ 进来的地址必须是**已补回域名**的绝对形态:补域名只有 `importData` 那一处(与 `textures` 同一把 `assetUrl` 尺),
|
|
1111
|
+
* ⛔ 本方法里不许再补一遍,也⛔ 不许在调用方各自补。
|
|
1112
|
+
*/
|
|
1113
|
+
setImagePlanes(datas: ImagePlaneData[]): void {
|
|
1114
|
+
this._imgBindQueued.clear();
|
|
1115
|
+
this._imgBindDone.clear();
|
|
1116
|
+
for (const ip of this.imagePlanes) {
|
|
1117
|
+
this.remove(ip);
|
|
1118
|
+
ip.dispose();
|
|
1119
|
+
}
|
|
1120
|
+
this.imagePlanes = [];
|
|
1121
|
+
for (const d of datas) {
|
|
1122
|
+
const ip = new ImagePlane(d.name);
|
|
1123
|
+
ip.applyData(d);
|
|
1124
|
+
this.imagePlanes.push(ip);
|
|
1125
|
+
this.add(ip);
|
|
1126
|
+
}
|
|
1127
|
+
// 整层重建完立刻起排:地址一次性全交给下载、绑材质逐帧一记 ⇒ 宿主不驱动这一层也长得出来
|
|
1128
|
+
this.feedImagePlaneTiles();
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* 整层重建模型槽(`importData` 与预览那一路),地址口径的要求同 `setImagePlanes`。
|
|
1133
|
+
* ⚠️ GLB 逐只**异步取、不 await**(十几只串一条队会把载入卡死),每只落地那一刻各回调一次 `onEachLoad`:
|
|
1134
|
+
* 预览每帧真画 ⇒ 不传;编辑器是按需渲染 ⇒ 传一个「标脏」的钩子,否则模型挂上来屏幕不动。
|
|
1135
|
+
* 2026-10-26 决定:取回与「挂进父元素」分两拍 —— 地址一次性全交给下载(网络 + 解析不占渲染帧),
|
|
1136
|
+
* 真正进场景图那一步交给 `threeUtils.addWork` ⇒ 全局 rAF 每拍只跑一项 = **一帧只挂一颗槽**
|
|
1137
|
+
* (挂载那一拍要重算包围盒 + 排布 + 首次把整棵子树的几何送上 GPU,十几颗挤在同一帧就是他要拆掉的那一卡)。
|
|
1138
|
+
*/
|
|
1139
|
+
setModels(datas: ModelSlotData[], onEachLoad?: () => void): void {
|
|
1140
|
+
this._modelMountQueued.clear();
|
|
1141
|
+
for (const ms of this.models) {
|
|
1142
|
+
this.remove(ms);
|
|
1143
|
+
ms.dispose();
|
|
1144
|
+
}
|
|
1145
|
+
this.models = [];
|
|
1146
|
+
for (const d of datas) {
|
|
1147
|
+
const ms = new ModelSlot(d.name);
|
|
1148
|
+
ms.applyData(d);
|
|
1149
|
+
this.models.push(ms);
|
|
1150
|
+
this.add(ms); // 槽本体(占位小立方体)当场进图 ⇒ GLB 在路上时这一槽不凭空消失
|
|
1151
|
+
if (d.url) void ms.loadModel(d.url).then(() => this._queueModelMount(ms, onEachLoad));
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/** 把「备好待挂」的那一槽交给全局队列;⛔ 去重闸门自备(`addWork` 按闭包引用判重,新闭包它判不出来) */
|
|
1156
|
+
private _queueModelMount(ms: ModelSlot, onEachLoad?: () => void): void {
|
|
1157
|
+
if (this._modelMountQueued.has(ms)) return;
|
|
1158
|
+
this._modelMountQueued.add(ms);
|
|
1159
|
+
threeUtils.addWork(() => {});
|
|
1160
|
+
threeUtils.addWork(() => this._mountModelStep(ms, onEachLoad));
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/** 全局队列里的那一帧:只挂这一槽,挂好才回调(`onEachLoad` = 按需渲染宿主的标脏钩子) */
|
|
1164
|
+
private _mountModelStep(ms: ModelSlot, onEachLoad?: () => void): void {
|
|
1165
|
+
this._modelMountQueued.delete(ms);
|
|
1166
|
+
// 槽已退场(换项目 / 删除):待挂那棵由 `ModelSlot.dispose` → `_detachModel` 当场释放,这里不挂
|
|
1167
|
+
if (!this.models.includes(ms)) return;
|
|
1168
|
+
if (ms.mountStaged() && onEachLoad) onEachLoad();
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* 把各颗平面那 4 张瓦片的纹理绑进材质(10-22 晚定的口径:不走拼合器 RT)。
|
|
1173
|
+
* 2026-10-26 决定改成**逐帧**:一次把整层地址全丢给下载器(下载/解码在网络与工作线程侧,不占这一帧),
|
|
1174
|
+
* 真正写 uniform 的那一步交给 `threeUtils.addWork` —— 全局 rAF 每拍只跑一项 ⇒ 一帧只绑一颗平面,
|
|
1175
|
+
* 一整层平面的纹理上传被摊到多帧,不再挤在同一记调用里(旧写法一记 `feed` 就把所有平面全绑完)。
|
|
1176
|
+
* ⛔ 不进 `onBeforeRender` 自驱;持有渲染循环的那一方(编辑态 / 预览 / 下游)每帧调一次只是「补排漏网的平面」,
|
|
1177
|
+
* 而整层重建(`setImagePlanes`)末尾自己起排 ⇒ 只调 `importData(整串)` 的宿主不必自己驱动也会长出来。
|
|
1178
|
+
* 还没下载完/缺格的那一格绑 null ⇒ 片元那一格整格 discard,不落像素(用户 10-26 连收两条:先「不要默认图」,
|
|
1179
|
+
* 再「不是显示灰色, 而是直接 discard 丢弃」)。
|
|
1180
|
+
*/
|
|
1181
|
+
feedImagePlaneTiles(): void {
|
|
1182
|
+
for (const ip of this.imagePlanes) this._queueImagePlaneBind(ip);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/** 一颗平面排一帧的绑定工作:先把它那 4 条地址**一次性全部**交给下载器,再把「写材质」排进全局队列。 */
|
|
1186
|
+
private _queueImagePlaneBind(ip: ImagePlane): void {
|
|
1187
|
+
const tiles = ip.tiles;
|
|
1188
|
+
const sig = (tiles ?? []).join("|");
|
|
1189
|
+
// 两道闸:已排在队里 = 不重复排;这串地址已经落定过 = 不再动它(换图会改这串 ⇒ 自然会重排)
|
|
1190
|
+
if (this._imgBindQueued.has(ip) || this._imgBindDone.get(ip) === sig) return;
|
|
1191
|
+
const zero = new Vector4(0, 0, 1, 1);
|
|
1192
|
+
// ⛔ 旧存档 16 条地址(> 满批 4)直接不收 ⇒ 整张不落像素(四格全 discard),按 10-20 口径重传即可;空 tiles 同样
|
|
1193
|
+
if (!tiles || tiles.length !== 4) {
|
|
1194
|
+
ip.setTileTextures([null, null, null, null], [zero, zero.clone(), zero.clone(), zero.clone()]);
|
|
1195
|
+
this._imgBindDone.set(ip, sig);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
for (const url of tiles) this._imgTileLoader.preload(url); // ① 一次性全部拿去加载
|
|
1199
|
+
this._imgBindQueued.add(ip);
|
|
1200
|
+
threeUtils.addWork(() => {}); // 占一格(什么都不干),把下面那一项推到下一个 tick —— 与全景上传同一手法
|
|
1201
|
+
threeUtils.addWork(() => this._bindImagePlaneStep(ip, sig));
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/** 队列里的一格:只绑**这一颗**平面里已就绪的那几格;还有在途的就重新排队,⛔ 不在这里等、也⛔ 不顺手把别的平面一起做完。 */
|
|
1205
|
+
private _bindImagePlaneStep(ip: ImagePlane, sig: string): void {
|
|
1206
|
+
this._imgBindQueued.delete(ip);
|
|
1207
|
+
if (!this.imagePlanes.includes(ip)) return; // 已摘掉(删平面 / 切项目整层重建)⇒ 这一格什么都不做
|
|
1208
|
+
const tiles = ip.tiles;
|
|
1209
|
+
if ((tiles ?? []).join("|") !== sig) return; // 地址中途换了 ⇒ 下一记 feed 按新拼串重排
|
|
1210
|
+
const zero = new Vector4(0, 0, 1, 1);
|
|
1211
|
+
const texs: (Texture | null)[] = [null, null, null, null];
|
|
1212
|
+
const insets: Vector4[] = [zero.clone(), zero.clone(), zero.clone(), zero.clone()];
|
|
1213
|
+
let pending = false;
|
|
1214
|
+
for (let i = 0; i < 4; i++) {
|
|
1215
|
+
const url = tiles[i];
|
|
1216
|
+
if (!url) continue;
|
|
1217
|
+
const e = this._imgTileLoader.get(url);
|
|
1218
|
+
if (e && e.tex) {
|
|
1219
|
+
texs[i] = e.tex;
|
|
1220
|
+
insets[i] = this._imgTileLoader.inset(e, i);
|
|
1221
|
+
} else if (this._imgTileLoader.isPending(url)) {
|
|
1222
|
+
pending = true; // 还在下载/解码 ⇒ 这一格先绑已到的,剩下的下一拍再看
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
ip.setTileTextures(texs, insets);
|
|
1226
|
+
if (pending) {
|
|
1227
|
+
this._queueImagePlaneBind(ip);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
this._imgBindDone.set(ip, sig); // 全部落定(含下载失败那格:它留 null,那一格就永久 discard)
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1038
1233
|
/** 导出数据:墙点位 + 墙高 + 是否闭合 + 纹理数组(url + 矩阵)
|
|
1039
1234
|
* @param projName 项目名(可选,优先于 `setProjectName` 注入的那个)——图片地址落成 `{项目名}/xxx.jpg` */
|
|
1040
1235
|
exportData(projName?: string): MktMeshData {
|
|
@@ -1057,12 +1252,30 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
1057
1252
|
tiles: (t.tiles && t.tiles.length ? t.tiles.map((u) => stripAssetHost(u, pn)) : []) as string[],
|
|
1058
1253
|
matrix: t.matrix.toArray(),
|
|
1059
1254
|
})),
|
|
1255
|
+
// ⭐ 10-26 旨入库:这两层的地址与 `textures` 共用上面那一条剥域名口径(⛔ 不是另起一把尺),
|
|
1256
|
+
// 其余字段(位置/旋转/宽高/瓦片数/落点面/原始包围盒)由各自的 `toData()` 逐字存。
|
|
1257
|
+
images: this.imagePlanes.map((ip) => {
|
|
1258
|
+
const d = ip.toData();
|
|
1259
|
+
return {
|
|
1260
|
+
...d,
|
|
1261
|
+
url: d.url ? stripAssetHost(d.url, pn) : "",
|
|
1262
|
+
tiles: d.tiles.length ? d.tiles.map((u) => stripAssetHost(u, pn)) : [],
|
|
1263
|
+
};
|
|
1264
|
+
}),
|
|
1265
|
+
models: this.models.map((ms) => {
|
|
1266
|
+
const d = ms.toData();
|
|
1267
|
+
return { ...d, url: d.url ? stripAssetHost(d.url, pn) : "" };
|
|
1268
|
+
}),
|
|
1060
1269
|
};
|
|
1061
1270
|
}
|
|
1062
1271
|
|
|
1063
1272
|
/** 导入数据:重建墙体几何与纹理列表,并激活第一个纹理
|
|
1064
|
-
* @param projName 项目名(可选)——用于把落盘的 `{项目名}/x_0.jpg` 还原回真身OSS 地址
|
|
1065
|
-
|
|
1273
|
+
* @param projName 项目名(可选)——用于把落盘的 `{项目名}/x_0.jpg` 还原回真身OSS 地址
|
|
1274
|
+
* @param onModelLoad 每只 GLB 落地时回调一次(可选)——按需渲染的宿主(编辑器)拿它标脏;每帧真画的预览不传 */
|
|
1275
|
+
importData(data: MktMeshData, projName?: string, onModelLoad?: () => void): void {
|
|
1276
|
+
// ⭐ 10-08 旨的「补回域名」那颗项目名,10-26 旨之后全景/平面图/模型**三层共用同一个**(原来在 textures 的
|
|
1277
|
+
// map 里就地取,下面那两层也要用 ⇒ 提到方法开头,⛔ 不是第二处口径)。
|
|
1278
|
+
const pn = typeof projName === "string" && projName ? projName : this.projectName;
|
|
1066
1279
|
this.setWallPoints(data.wallPoints ?? [], {
|
|
1067
1280
|
closed: data.wallClosed,
|
|
1068
1281
|
height: data.wallHeight,
|
|
@@ -1078,7 +1291,6 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
1078
1291
|
// (`/three-base_upload/…`)不必重存也能正常加载,不会被拼成 `https://host/https://host/…`
|
|
1079
1292
|
// · 19:53 旨后还要把 `{项目名}/x_0.jpg` **还原**成真身 OSS 地址(顶掉的上传目录要补回来),
|
|
1080
1293
|
// 否则 json 里的地址指不回原图(图片裂)。`pn` 取入参、回落 `setProjectName` 注入的那个。
|
|
1081
|
-
const pn = typeof projName === "string" && projName ? projName : this.projectName;
|
|
1082
1294
|
const urlAbs = absAssetUrl(t.url as string, pn);
|
|
1083
1295
|
return {
|
|
1084
1296
|
url: urlAbs,
|
|
@@ -1102,11 +1314,25 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
1102
1314
|
for (let f = 0; f < restore; f++) this.panoFaceMapping[f] = imported[f];
|
|
1103
1315
|
this.rebuildPanoIndexAttribute();
|
|
1104
1316
|
}
|
|
1317
|
+
// ⭐ 10-26 旨「把图片平面和模型一起搬进库里」的另一半:这两层也从**同一份 JSON** 里读,
|
|
1318
|
+
// 地址在这一处补回域名(与上面 textures 同一条尺)⇒ 下游只调 `importData(整串)` 就全画出来。
|
|
1319
|
+
// 旧存档没有这两个键 ⇒ 空数组 = 整层清空,与「墙 + 全景」那套逐字同口径。
|
|
1320
|
+
this.setImagePlanes(
|
|
1321
|
+
(data.images ?? []).map((d) => ({
|
|
1322
|
+
...d,
|
|
1323
|
+
url: d.url ? absAssetUrl(d.url, pn) : "",
|
|
1324
|
+
tiles: (d.tiles ?? []).map((u) => absAssetUrl(u, pn)),
|
|
1325
|
+
}))
|
|
1326
|
+
);
|
|
1327
|
+
this.setModels(
|
|
1328
|
+
(data.models ?? []).map((d) => ({ ...d, url: d.url ? absAssetUrl(d.url, pn) : "" })),
|
|
1329
|
+
onModelLoad
|
|
1330
|
+
);
|
|
1105
1331
|
}
|
|
1106
1332
|
|
|
1107
1333
|
// ───────────────────────── 释放 ─────────────────────────
|
|
1108
1334
|
|
|
1109
|
-
/** 释放 GPU 资源:瓦片缓存(**只有这里**清,皇上旨 2026-10-02
|
|
1335
|
+
/** 释放 GPU 资源:瓦片缓存(**只有这里**清,皇上旨 2026-10-02)、拼合器、平面图/模型两层、几何、材质 */
|
|
1110
1336
|
dispose(): this {
|
|
1111
1337
|
// ⚠️ rt 虚拟键从不进 `_texCache`(拼合槽纹理由 `_stitcher` 自己持有并释放),这里清的都是本类下载的 Texture。
|
|
1112
1338
|
// ⭐ 甲(2026-10-05):`releasePanoTexture` 除 `dispose()` 外还要 `ImageBitmap.close()` —— 位图的原生内存
|
|
@@ -1116,6 +1342,22 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
|
|
|
1116
1342
|
this._texUploaded = new WeakSet(); // WeakSet 无 clear ⇒ 整只换掉
|
|
1117
1343
|
this._uploadQueued.clear(); // 排队项即便回调仍触发,也会因 `!this._uploadQueued.delete(tex)` 早退
|
|
1118
1344
|
this._ossSizeDepKeys.clear();
|
|
1345
|
+
// ⭐ 10-26 旨入库的两层:子对象逐个 dispose,平面那 4 张瓦片的缓存**只在这里**整批清
|
|
1346
|
+
// (与上面 `_texCache` 同一条旨 ⇒ 切工程不重建这一份、也就不会再空出一格)。
|
|
1347
|
+
for (const ip of this.imagePlanes) {
|
|
1348
|
+
this.remove(ip);
|
|
1349
|
+
ip.dispose();
|
|
1350
|
+
}
|
|
1351
|
+
this.imagePlanes = [];
|
|
1352
|
+
for (const ms of this.models) {
|
|
1353
|
+
this.remove(ms);
|
|
1354
|
+
ms.dispose();
|
|
1355
|
+
}
|
|
1356
|
+
this.models = [];
|
|
1357
|
+
this._imgTileLoader.clear();
|
|
1358
|
+
this._imgBindQueued.clear();
|
|
1359
|
+
this._imgBindDone.clear();
|
|
1360
|
+
this._modelMountQueued.clear();
|
|
1119
1361
|
this._stitcher.dispose();
|
|
1120
1362
|
this._glRenderer = null;
|
|
1121
1363
|
if (this._placeholder) this._placeholder.dispose();
|
package/MktMesh/imagePlane.d.ts
CHANGED
|
@@ -46,8 +46,11 @@ export declare class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
46
46
|
get url(): string;
|
|
47
47
|
get naturalW(): number | undefined;
|
|
48
48
|
get naturalH(): number | undefined;
|
|
49
|
-
/** 把上传切好的 4 张瓦片纹理 + 每片 inset 绑到材质(缺图那片传 null ⇒
|
|
49
|
+
/** 把上传切好的 4 张瓦片纹理 + 每片 inset 绑到材质(缺图那片传 null ⇒ 那一片整格 discard,不落像素)。uTiles 用持久对象引用 ⇒ 改 value 实时生效 */
|
|
50
50
|
setTileTextures(texs: (Texture | null)[], insets: Vector4[]): void;
|
|
51
|
+
/** 只读「此刻材质里绑了什么」的戳记:4 格就绪位 + 已绑纹理的版本号。
|
|
52
|
+
* 按需渲染的宿主拿它比对画面:逐帧绑定只写 uniform 值,⛔ 不动 `material.version`、也不动几何 ⇒ 场景指纹看不见这一变。 */
|
|
53
|
+
tilesBindingStamp(): string;
|
|
51
54
|
highlight(): void;
|
|
52
55
|
restoreColor(): void;
|
|
53
56
|
toData(): ImagePlaneData;
|
package/MktMesh/imagePlane.js
CHANGED
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
* - 几何 PlaneGeometry(width, height),原点在平面中心;position.y 由调用者设定(通常 height/2 让底部落地)。
|
|
12
12
|
* - ⭐ 2026-10-22 晚旨:纹理**不走拼合器 RT**,直接把上传切好的 4 张瓦片纹理绑到材质,片元按 UV 象限选片 +
|
|
13
13
|
* 用每片 `rtStitchTileInset` 窗口采样(与上传切片共用同一份几何 ⇒ 接缝不回潮)。材质用 onBeforeCompile 打补丁,
|
|
14
|
-
* 注入 4 个 sampler + 每片 inset +
|
|
14
|
+
* 注入 4 个 sampler + 每片 inset + 就绪标志;缺失那片整格 `discard`(一片像素都不落),逐张就绪逐张显影。
|
|
15
|
+
* (⭐ 2026-10-26 用户先要默认图、当晚收回改要灰,同一晚再收:「平面图的图片还没有加载出来的时候, 不是显示灰色,
|
|
16
|
+
* 而是直接 discrad 丢弃」⇒ 默认图与缺图灰两条都已删,没就绪就是透明没有。)
|
|
15
17
|
* - UV 标准 [0,1]²(PlaneGeometry 自带),4 张瓦片按 2×2 象限铺满 ⇒ 不需要投影 shader,也不需要离屏 RT。
|
|
16
18
|
*/
|
|
17
19
|
import { Color, DataTexture, FrontSide, Mesh, MeshBasicMaterial, PlaneGeometry, RGBAFormat, Vector4, } from "three";
|
|
@@ -35,21 +37,25 @@ const ZERO_INSET = new Vector4(0, 0, 1, 1);
|
|
|
35
37
|
*/
|
|
36
38
|
export const IMAGE_PLANE_EDGE_CLIP_UV = 0.004;
|
|
37
39
|
const EDGE_MAX_UV = 1 - IMAGE_PLANE_EDGE_CLIP_UV;
|
|
38
|
-
/** 片元打补丁:4 张瓦片按 UV
|
|
40
|
+
/** 片元打补丁:4 张瓦片按 UV 象限选片;⭐ 2026-10-26 用户决定「图片还没有加载出来的时候, 不是显示灰色, 而是直接 discrad 丢弃」
|
|
41
|
+
* ⇒ 这一格没就绪就 `discard`(一片像素都不落,透过去看到的是平面背后的东西),⛔ 不铺灰、⛔ 不画默认图。
|
|
42
|
+
* 索引用字面量 0..3 ⇒ GLSL ES 1.00 合法。 */
|
|
39
43
|
const IMG_PLANE_MAP_FRAG = `
|
|
40
44
|
if ( vUv.x < ${IMAGE_PLANE_EDGE_CLIP_UV} || vUv.x > ${EDGE_MAX_UV} || vUv.y < ${IMAGE_PLANE_EDGE_CLIP_UV} || vUv.y > ${EDGE_MAX_UV} ) discard;
|
|
41
|
-
vec4 imgTexel
|
|
45
|
+
vec4 imgTexel;
|
|
46
|
+
bool ready = false;
|
|
42
47
|
vec2 cell = floor(vUv * 2.0);
|
|
43
48
|
vec2 local = fract(vUv * 2.0);
|
|
44
49
|
if (cell.y >= 0.5 && cell.x < 0.5) {
|
|
45
|
-
if (uValid[0] > 0.5) { vec4 ins = uInset[0]; imgTexel = texture2D(uTiles[0], mix(ins.xy, ins.zw, local)); }
|
|
50
|
+
if (uValid[0] > 0.5) { vec4 ins = uInset[0]; imgTexel = texture2D(uTiles[0], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
46
51
|
} else if (cell.y >= 0.5 && cell.x >= 0.5) {
|
|
47
|
-
if (uValid[1] > 0.5) { vec4 ins = uInset[1]; imgTexel = texture2D(uTiles[1], mix(ins.xy, ins.zw, local)); }
|
|
52
|
+
if (uValid[1] > 0.5) { vec4 ins = uInset[1]; imgTexel = texture2D(uTiles[1], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
48
53
|
} else if (cell.y < 0.5 && cell.x < 0.5) {
|
|
49
|
-
if (uValid[2] > 0.5) { vec4 ins = uInset[2]; imgTexel = texture2D(uTiles[2], mix(ins.xy, ins.zw, local)); }
|
|
54
|
+
if (uValid[2] > 0.5) { vec4 ins = uInset[2]; imgTexel = texture2D(uTiles[2], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
50
55
|
} else {
|
|
51
|
-
if (uValid[3] > 0.5) { vec4 ins = uInset[3]; imgTexel = texture2D(uTiles[3], mix(ins.xy, ins.zw, local)); }
|
|
56
|
+
if (uValid[3] > 0.5) { vec4 ins = uInset[3]; imgTexel = texture2D(uTiles[3], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
52
57
|
}
|
|
58
|
+
if (!ready) discard;
|
|
53
59
|
diffuseColor *= imgTexel;
|
|
54
60
|
`;
|
|
55
61
|
const DEFAULT_WIDTH = 2;
|
|
@@ -78,7 +84,7 @@ export class ImagePlane extends Mesh {
|
|
|
78
84
|
});
|
|
79
85
|
// ⭐ 2026-10-22 晚旨:图片平面**不用拼合器 RT**,直接把上传切好的 4 张瓦片纹理绑到材质,
|
|
80
86
|
// 片元按 UV 象限选片 + 用每片 `rtStitchTileInset` 窗口采样(与上传切片共用同一份几何 ⇒ 接缝不回潮)。
|
|
81
|
-
// 材质打补丁(onBeforeCompile)注入 4 个 sampler + 每片 inset +
|
|
87
|
+
// 材质打补丁(onBeforeCompile)注入 4 个 sampler + 每片 inset + 就绪标志;缺失那片整格 discard。
|
|
82
88
|
mat.defines = { USE_UV: "" };
|
|
83
89
|
const uTiles = { value: [PLACEHOLDER_TEX, PLACEHOLDER_TEX, PLACEHOLDER_TEX, PLACEHOLDER_TEX] };
|
|
84
90
|
const uInset = { value: [ZERO_INSET.clone(), ZERO_INSET.clone(), ZERO_INSET.clone(), ZERO_INSET.clone()] };
|
|
@@ -113,7 +119,7 @@ export class ImagePlane extends Mesh {
|
|
|
113
119
|
get url() { return this._data.url; }
|
|
114
120
|
get naturalW() { return this._data.naturalW; }
|
|
115
121
|
get naturalH() { return this._data.naturalH; }
|
|
116
|
-
/** 把上传切好的 4 张瓦片纹理 + 每片 inset 绑到材质(缺图那片传 null ⇒
|
|
122
|
+
/** 把上传切好的 4 张瓦片纹理 + 每片 inset 绑到材质(缺图那片传 null ⇒ 那一片整格 discard,不落像素)。uTiles 用持久对象引用 ⇒ 改 value 实时生效 */
|
|
117
123
|
setTileTextures(texs, insets) {
|
|
118
124
|
for (let i = 0; i < 4; i++) {
|
|
119
125
|
const t = texs[i] || null;
|
|
@@ -123,6 +129,16 @@ export class ImagePlane extends Mesh {
|
|
|
123
129
|
this._uValid.value[i] = t ? 1 : 0;
|
|
124
130
|
}
|
|
125
131
|
}
|
|
132
|
+
/** 只读「此刻材质里绑了什么」的戳记:4 格就绪位 + 已绑纹理的版本号。
|
|
133
|
+
* 按需渲染的宿主拿它比对画面:逐帧绑定只写 uniform 值,⛔ 不动 `material.version`、也不动几何 ⇒ 场景指纹看不见这一变。 */
|
|
134
|
+
tilesBindingStamp() {
|
|
135
|
+
let s = "";
|
|
136
|
+
for (let i = 0; i < 4; i++) {
|
|
137
|
+
const t = this._uTiles.value[i];
|
|
138
|
+
s += this._uValid.value[i] ? `1@${t ? t.version : -1},` : "0,";
|
|
139
|
+
}
|
|
140
|
+
return s;
|
|
141
|
+
}
|
|
126
142
|
highlight() {
|
|
127
143
|
this.material.color.set(HIGHLIGHT_COLOR);
|
|
128
144
|
}
|
package/MktMesh/imagePlane.ts
CHANGED
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
* - 几何 PlaneGeometry(width, height),原点在平面中心;position.y 由调用者设定(通常 height/2 让底部落地)。
|
|
12
12
|
* - ⭐ 2026-10-22 晚旨:纹理**不走拼合器 RT**,直接把上传切好的 4 张瓦片纹理绑到材质,片元按 UV 象限选片 +
|
|
13
13
|
* 用每片 `rtStitchTileInset` 窗口采样(与上传切片共用同一份几何 ⇒ 接缝不回潮)。材质用 onBeforeCompile 打补丁,
|
|
14
|
-
* 注入 4 个 sampler + 每片 inset +
|
|
14
|
+
* 注入 4 个 sampler + 每片 inset + 就绪标志;缺失那片整格 `discard`(一片像素都不落),逐张就绪逐张显影。
|
|
15
|
+
* (⭐ 2026-10-26 用户先要默认图、当晚收回改要灰,同一晚再收:「平面图的图片还没有加载出来的时候, 不是显示灰色,
|
|
16
|
+
* 而是直接 discrad 丢弃」⇒ 默认图与缺图灰两条都已删,没就绪就是透明没有。)
|
|
15
17
|
* - UV 标准 [0,1]²(PlaneGeometry 自带),4 张瓦片按 2×2 象限铺满 ⇒ 不需要投影 shader,也不需要离屏 RT。
|
|
16
18
|
*/
|
|
17
19
|
import {
|
|
@@ -48,21 +50,25 @@ const ZERO_INSET = new Vector4(0, 0, 1, 1);
|
|
|
48
50
|
export const IMAGE_PLANE_EDGE_CLIP_UV = 0.004;
|
|
49
51
|
const EDGE_MAX_UV = 1 - IMAGE_PLANE_EDGE_CLIP_UV;
|
|
50
52
|
|
|
51
|
-
/** 片元打补丁:4 张瓦片按 UV
|
|
53
|
+
/** 片元打补丁:4 张瓦片按 UV 象限选片;⭐ 2026-10-26 用户决定「图片还没有加载出来的时候, 不是显示灰色, 而是直接 discrad 丢弃」
|
|
54
|
+
* ⇒ 这一格没就绪就 `discard`(一片像素都不落,透过去看到的是平面背后的东西),⛔ 不铺灰、⛔ 不画默认图。
|
|
55
|
+
* 索引用字面量 0..3 ⇒ GLSL ES 1.00 合法。 */
|
|
52
56
|
const IMG_PLANE_MAP_FRAG = `
|
|
53
57
|
if ( vUv.x < ${IMAGE_PLANE_EDGE_CLIP_UV} || vUv.x > ${EDGE_MAX_UV} || vUv.y < ${IMAGE_PLANE_EDGE_CLIP_UV} || vUv.y > ${EDGE_MAX_UV} ) discard;
|
|
54
|
-
vec4 imgTexel
|
|
58
|
+
vec4 imgTexel;
|
|
59
|
+
bool ready = false;
|
|
55
60
|
vec2 cell = floor(vUv * 2.0);
|
|
56
61
|
vec2 local = fract(vUv * 2.0);
|
|
57
62
|
if (cell.y >= 0.5 && cell.x < 0.5) {
|
|
58
|
-
if (uValid[0] > 0.5) { vec4 ins = uInset[0]; imgTexel = texture2D(uTiles[0], mix(ins.xy, ins.zw, local)); }
|
|
63
|
+
if (uValid[0] > 0.5) { vec4 ins = uInset[0]; imgTexel = texture2D(uTiles[0], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
59
64
|
} else if (cell.y >= 0.5 && cell.x >= 0.5) {
|
|
60
|
-
if (uValid[1] > 0.5) { vec4 ins = uInset[1]; imgTexel = texture2D(uTiles[1], mix(ins.xy, ins.zw, local)); }
|
|
65
|
+
if (uValid[1] > 0.5) { vec4 ins = uInset[1]; imgTexel = texture2D(uTiles[1], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
61
66
|
} else if (cell.y < 0.5 && cell.x < 0.5) {
|
|
62
|
-
if (uValid[2] > 0.5) { vec4 ins = uInset[2]; imgTexel = texture2D(uTiles[2], mix(ins.xy, ins.zw, local)); }
|
|
67
|
+
if (uValid[2] > 0.5) { vec4 ins = uInset[2]; imgTexel = texture2D(uTiles[2], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
63
68
|
} else {
|
|
64
|
-
if (uValid[3] > 0.5) { vec4 ins = uInset[3]; imgTexel = texture2D(uTiles[3], mix(ins.xy, ins.zw, local)); }
|
|
69
|
+
if (uValid[3] > 0.5) { vec4 ins = uInset[3]; imgTexel = texture2D(uTiles[3], mix(ins.xy, ins.zw, local)); ready = true; }
|
|
65
70
|
}
|
|
71
|
+
if (!ready) discard;
|
|
66
72
|
diffuseColor *= imgTexel;
|
|
67
73
|
`;
|
|
68
74
|
|
|
@@ -111,7 +117,7 @@ export class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
111
117
|
});
|
|
112
118
|
// ⭐ 2026-10-22 晚旨:图片平面**不用拼合器 RT**,直接把上传切好的 4 张瓦片纹理绑到材质,
|
|
113
119
|
// 片元按 UV 象限选片 + 用每片 `rtStitchTileInset` 窗口采样(与上传切片共用同一份几何 ⇒ 接缝不回潮)。
|
|
114
|
-
// 材质打补丁(onBeforeCompile)注入 4 个 sampler + 每片 inset +
|
|
120
|
+
// 材质打补丁(onBeforeCompile)注入 4 个 sampler + 每片 inset + 就绪标志;缺失那片整格 discard。
|
|
115
121
|
mat.defines = { USE_UV: "" };
|
|
116
122
|
const uTiles = { value: [PLACEHOLDER_TEX, PLACEHOLDER_TEX, PLACEHOLDER_TEX, PLACEHOLDER_TEX] as Texture[] };
|
|
117
123
|
const uInset = { value: [ZERO_INSET.clone(), ZERO_INSET.clone(), ZERO_INSET.clone(), ZERO_INSET.clone()] };
|
|
@@ -154,7 +160,7 @@ export class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
154
160
|
get naturalW(): number | undefined { return this._data.naturalW; }
|
|
155
161
|
get naturalH(): number | undefined { return this._data.naturalH; }
|
|
156
162
|
|
|
157
|
-
/** 把上传切好的 4 张瓦片纹理 + 每片 inset 绑到材质(缺图那片传 null ⇒
|
|
163
|
+
/** 把上传切好的 4 张瓦片纹理 + 每片 inset 绑到材质(缺图那片传 null ⇒ 那一片整格 discard,不落像素)。uTiles 用持久对象引用 ⇒ 改 value 实时生效 */
|
|
158
164
|
setTileTextures(texs: (Texture | null)[], insets: Vector4[]): void {
|
|
159
165
|
for (let i = 0; i < 4; i++) {
|
|
160
166
|
const t = texs[i] || null;
|
|
@@ -164,6 +170,17 @@ export class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
164
170
|
}
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
/** 只读「此刻材质里绑了什么」的戳记:4 格就绪位 + 已绑纹理的版本号。
|
|
174
|
+
* 按需渲染的宿主拿它比对画面:逐帧绑定只写 uniform 值,⛔ 不动 `material.version`、也不动几何 ⇒ 场景指纹看不见这一变。 */
|
|
175
|
+
tilesBindingStamp(): string {
|
|
176
|
+
let s = "";
|
|
177
|
+
for (let i = 0; i < 4; i++) {
|
|
178
|
+
const t = this._uTiles.value[i];
|
|
179
|
+
s += this._uValid.value[i] ? `1@${t ? t.version : -1},` : "0,";
|
|
180
|
+
}
|
|
181
|
+
return s;
|
|
182
|
+
}
|
|
183
|
+
|
|
167
184
|
highlight(): void {
|
|
168
185
|
this.material.color.set(HIGHLIGHT_COLOR);
|
|
169
186
|
}
|
|
@@ -11,6 +11,8 @@ export declare class ImagePlaneTileLoader {
|
|
|
11
11
|
preload(url: string): void;
|
|
12
12
|
/** 取已就绪的瓦片(未就绪返回 null) */
|
|
13
13
|
get(url: string): TileEntry | null;
|
|
14
|
+
/** 这张是否还在下载/解码途中。既不在缓存又不在途 ⇒ 要么没发起、要么已经失败(调用方据此决定「再排一帧等」还是「就此收工」) */
|
|
15
|
+
isPending(url: string): boolean;
|
|
14
16
|
/** 第 i 片(行优先、图像左上起)的采样窗口;瓦片未就绪时返回整张 [0,0,1,1] */
|
|
15
17
|
inset(entry: TileEntry, i: number): Vector4;
|
|
16
18
|
/** 释放本 loader 持有的全部瓦片纹理(项目重置 / 卸载场景时调用;⛔ 不关共享占位纹理) */
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 与全景同一套上下翻约定);本类只补 `colorSpace = SRGBColorSpace`(硬件采样即解 sRGB→linear,与旧 RT 方案逐字节同口径)。
|
|
10
10
|
* - inset 用库的 `rtStitchTileInset(i, tw, th, IMAGE_TILE_GRID)`:上传多切 8px、采样只取中间干净块 ⇒ 接缝不回潮,
|
|
11
11
|
* 与上传切片 `rtStitchTileRects` 共用同一份几何(⛔ 不许在这里另算一份)。
|
|
12
|
-
* - 逐张就绪、逐张显影:未就绪那张 `get()` 返回 null ⇒ 调方把那片传 null
|
|
12
|
+
* - 逐张就绪、逐张显影:未就绪那张 `get()` 返回 null ⇒ 调方把那片传 null(材质那一格整格 discard,不落像素),下一帧就绪后即亮。
|
|
13
13
|
*/
|
|
14
14
|
import { SRGBColorSpace, Vector4 } from "three";
|
|
15
15
|
import { loadPanoTexture, releasePanoTexture } from "./panoBitmapTexture";
|
|
@@ -39,6 +39,10 @@ export class ImagePlaneTileLoader {
|
|
|
39
39
|
get(url) {
|
|
40
40
|
return this.cache.get(url) || null;
|
|
41
41
|
}
|
|
42
|
+
/** 这张是否还在下载/解码途中。既不在缓存又不在途 ⇒ 要么没发起、要么已经失败(调用方据此决定「再排一帧等」还是「就此收工」) */
|
|
43
|
+
isPending(url) {
|
|
44
|
+
return this.inflight.has(url);
|
|
45
|
+
}
|
|
42
46
|
/** 第 i 片(行优先、图像左上起)的采样窗口;瓦片未就绪时返回整张 [0,0,1,1] */
|
|
43
47
|
inset(entry, i) {
|
|
44
48
|
if (!entry.w || !entry.h)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 与全景同一套上下翻约定);本类只补 `colorSpace = SRGBColorSpace`(硬件采样即解 sRGB→linear,与旧 RT 方案逐字节同口径)。
|
|
10
10
|
* - inset 用库的 `rtStitchTileInset(i, tw, th, IMAGE_TILE_GRID)`:上传多切 8px、采样只取中间干净块 ⇒ 接缝不回潮,
|
|
11
11
|
* 与上传切片 `rtStitchTileRects` 共用同一份几何(⛔ 不许在这里另算一份)。
|
|
12
|
-
* - 逐张就绪、逐张显影:未就绪那张 `get()` 返回 null ⇒ 调方把那片传 null
|
|
12
|
+
* - 逐张就绪、逐张显影:未就绪那张 `get()` 返回 null ⇒ 调方把那片传 null(材质那一格整格 discard,不落像素),下一帧就绪后即亮。
|
|
13
13
|
*/
|
|
14
14
|
import { SRGBColorSpace, Texture, Vector4 } from "three";
|
|
15
15
|
import { loadPanoTexture, releasePanoTexture } from "./panoBitmapTexture";
|
|
@@ -48,6 +48,11 @@ export class ImagePlaneTileLoader {
|
|
|
48
48
|
return this.cache.get(url) || null;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/** 这张是否还在下载/解码途中。既不在缓存又不在途 ⇒ 要么没发起、要么已经失败(调用方据此决定「再排一帧等」还是「就此收工」) */
|
|
52
|
+
isPending(url: string): boolean {
|
|
53
|
+
return this.inflight.has(url);
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
/** 第 i 片(行优先、图像左上起)的采样窗口;瓦片未就绪时返回整张 [0,0,1,1] */
|
|
52
57
|
inset(entry: TileEntry, i: number): Vector4 {
|
|
53
58
|
if (!entry.w || !entry.h) return new Vector4(0, 0, 1, 1);
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { ShaderMaterial } from "three";
|
|
2
2
|
/**
|
|
3
3
|
* ⭐ 融合材质的全景点数**上限**:每点占 **3** 张拼合槽纹理(皇上旨 2026-10-23「全景的 RT 由原来的 2 个分成 3 个、
|
|
4
|
-
* 每个
|
|
5
|
-
* 本机 `MAX_TEXTURE_IMAGE_UNITS` 实测 **16
|
|
6
|
-
* (
|
|
4
|
+
* 每个 RT 上面 3×3=9 片」⇒ 原图切 6×3=18 片,按**半幅**分 2 槽)⇒ 点数 × 2 必须 ≤ sampler 硬顶。
|
|
5
|
+
* 本机 `MAX_TEXTURE_IMAGE_UNITS` 实测 **16**:每点 2 槽 ⇒ 取 ⌊16/2⌋ = **8** 点(10-23 那版每点 3 槽只能坐 5 点)
|
|
6
|
+
* (8 × 2 = 16 正好用满)。2026-10-26 需求回到「2 个 RT / 每 RT 3×3」⇒ **同屏全景点数 5 → 8**。
|
|
7
7
|
*
|
|
8
8
|
* ⭐⭐ 2026-10-09 皇上旨「MktMesh 只有映射一种,blend/selected 归编辑器」⇒ 该常量从 `mktShader.ts`
|
|
9
9
|
* **搬到这里**(映射材质的所在地)。它同时是库与编辑器那份 blend 的**共同上限**,两边必须同值。
|
|
10
10
|
*/
|
|
11
|
-
export declare const MKT_PANO_MAX_SLOTS =
|
|
11
|
+
export declare const MKT_PANO_MAX_SLOTS = 8;
|
|
12
12
|
/**
|
|
13
13
|
* 创建「全景序号直取」着色器材质(皇上旨 2026-09-29「三模式三 shader」之二):
|
|
14
14
|
* 编辑器「映射设置模式」的墙材质 + **基类 MktMesh(发布/预览)的固定墙材质**
|
|
15
15
|
* (皇上旨:mktmesh 就只有按照 attribute 配置来显示全景这一种固定的 shader)。
|
|
16
|
-
* uniform 形状与 blend 工厂同档(panoramas[
|
|
16
|
+
* uniform 形状与 blend 工厂同档(panoramas[16]/textureMatrices[8]/weights[8]/uPanoTiled[8])⇒
|
|
17
17
|
* 喂料复用 feedBlendProjection 的槽位指派路(feedFaceMapping),零新喂料路。
|
|
18
18
|
* uFloorCoreRadius/uFloorFadeRange/uFloorOpacity 三根本 shader 不读,但喂料统一入口会写 ⇒ JS 侧留着(静默忽略)。
|
|
19
19
|
*/
|