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.
@@ -43,8 +43,8 @@ import { Color, DataTexture, DoubleSide, Matrix4, ShaderMaterial, SRGBColorSpace
43
43
  * 现口径已统一为等距柱状线性,皇上验收「不交换 / 不翻转」那态即正确 ⇒ **方向参数整条撤除、写死**
44
44
  * (见 🎛️ 段)。本诊断开关保留,用于日后再次核对「哪块瓦片落在哪个象限」。
45
45
  *
46
- * 用法:页面 URL 带 `?panoTileDebug=1` 时,三张槽纹理**不再采样纹理**,各自输出一个纯色 ——
47
- * ⭐ 10-23 起横 3 槽(竖着不再分块):红=左列(槽0) 绿=中列(槽1) 蓝=右列(槽2)
46
+ * 用法:页面 URL 带 `?panoTileDebug=1` 时,几张槽纹理**不再采样纹理**,各自输出一个纯色 ——
47
+ * ⭐ 10-26 起横 2 槽(竖着不再分块):红=左半幅(槽0) 绿=右半幅(槽1)
48
48
  * (旧 4 象限版是 红=tiles[0] 绿=tiles[1] 蓝=tiles[2] 黄=tiles[3],已随四槽一并作废)
49
49
  * 皇上只要说出「画面上下左右各是什么颜色」,即可一次反推出真实映射。
50
50
  *
@@ -313,7 +313,7 @@ export function setMktPanoPoints(material, matrices) {
313
313
  *
314
314
  * 🧩 **第二个根因(与方向参数无关,故本次写死后依然保留)**:
315
315
  * 默认场景与旧存档的 `textures[].tiles` **只有 1 条地址**(当年上传切片功能之后才有的字段),
316
- * 喂料处回退成「各槽同为 `url`」(10-23 起是 3 槽:`[url, url, url]`)⇒ **各 sampler 装的是同一张完整等距柱状图**。于是:
316
+ * 喂料处回退成「各槽同为 `url`」(现行 2 槽:`[url, url]`)⇒ **各 sampler 装的是同一张完整等距柱状图**。于是:
317
317
  * ① 各槽同图 ⇒ 选哪一格都一样(当时看到的「交换零效果」症状,写死后该症状无从出现);
318
318
  * ② 更要命的是仍按「瓦片内 UV」`localU/localV` 去采整图 ⇒ 整图被按 1/2 拉伸铺满整个球面(旧版是 1/4):
319
319
  * 墙面仰角 +10° 采到原图 **−70°(脚底/三脚架)** —— 这才是「全景映射在模型上的位置不太对」的另一半,
@@ -321,7 +321,7 @@ export function setMktPanoPoints(material, matrices) {
321
321
  * 修法 = `uPanoTiled`(单全景标量 / 融合每点数组):0 时把瓦片内 UV **还原成整图 UV**
322
322
  * `panoU = u`、`panoV = v`(口径写死后这就是恒等式:横向 `0.5(2u)=u` / `0.5+0.5(2u−1)=u`;
323
323
  * 纵向 10-06 晚起 localV 本就等于 v ⇒ 两个方向都退化成恒等式)。
324
- * ⚠️ 判据与回退式**必须同源**(`mktPanoTiledFlag` = 「tiled 形态 且 tiles 有任意 1~9 条」,10-23 旨只支持 3×3=9 张、不足涂灰),且每个回退喂料点都要写标记,
324
+ * ⚠️ 判据与回退式**必须同源**(`mktPanoTiledFlag` = 「tiled 形态 且 tiles 有任意 1~18 条」,现行只支持 6×3=18 张、不足涂灰),且每个回退喂料点都要写标记,
325
325
  * 漏一处该对象就悄悄退回 1/2 拉伸。默认值取 1(已切片的真实上传数据行为不变)。
326
326
  * 护栏:`test/panoVMapping.test.js`。
327
327
  * 🔄 2026-09-22 追加(皇上口谕):本标记现由**用户可选的「全景纹理形态」`PanoTexMode`**(tiled/whole)与数据
@@ -395,43 +395,44 @@ const vertexShader = /* glsl */ `
395
395
  */
396
396
  // ⭐ 下面这一组「切片 / 拼合槽」几何常量**必须排在 `fragmentShader` 之前**:单全景那份片元是模块级
397
397
  // 常量字符串,里面要用 `${RT_STITCH_SLOT_COUNT}` 声明 sampler 数组长度(TS 的块级作用域禁止先用后声明)。
398
- /** ⭐ 9 瓦片拼合(2026-10-23 皇上旨:一张全景切 **横 3 × 竖 3 = 9 片**,每 **横 1 × 竖 3 = 3 片**离屏拼成
399
- * 1 张 **4096×6144** 槽纹理,**左/中/右 3 槽**组成完整全景;上一版是 10-06 晚旨的 8×4 = 32 片 / 左右 2 槽
400
- * 4096²,再上一版 8×8 = 64 片 / 4 槽 2×2 象限,最早 09-29 旨的 4×4 = 16 片)的「槽虚拟地址」前缀。
398
+ /** ⭐ 18 瓦片拼合(2026-10-26 需求:一张全景切 **横 6 × 竖 3 = 18 片**,每 **横 3 × 竖 3 = 9 片**离屏拼成
399
+ * 1 张 **6144×6144** 槽纹理,**左/右 2 槽**组成完整全景;上一版是 10-23 的 3×3 = 9 片 / 按列 3 槽 4096×6144,
400
+ * 再上一版 10-06 晚的 8×4 = 32 片 / 左右 2 槽 4096²,最早 09-29 的 4×4 = 16 片)的「槽虚拟地址」前缀。
401
401
  * 虚拟键不是真 URL,纹理由 `PanoTileStitcher` 用 shader 逐格画进 `WebGLRenderTarget` 提供,
402
402
  * 消费方(基类 `_ensurePanoTiles` / 编辑器 `_peekTileAt`)见前缀即走拼合器,⛔ 不得当 URL 下载。
403
403
  * ⚠️ 键协议 `rt:<batch>#<slot>` 与 `uPanoTiled` 这套对外接口**一字未改** —— 变的只有「每批几条地址」
404
- * 和「槽怎么分」;`slot` 取值 0..2,消费方一律用 `RT_STITCH_SLOT_COUNT` 取,不许写死。
405
- * ⚠️ 旧存档不兼容(与 10-06 同一口径):存档里 32 条瓦片地址会因张数超限被判废 ⇒ **老项目需重新上传**。 */
404
+ * 和「槽怎么分」;`slot` 取值 0..1,消费方一律用 `RT_STITCH_SLOT_COUNT` 取,不许写死。
405
+ * ⚠️ 旧存档不兼容(与 10-06 / 10-23 同一口径):存档里 9 条(或历代 16/32/64 条)瓦片地址会因张数不符被判废
406
+ * ⇒ **老项目需重新上传**(本次已明确不兼容,用户会重传图片)。 */
406
407
  export const RT_STITCH_KEY_PREFIX = "rt:";
407
- /** 原图**横向**(经度方向 u)切几片(旨「横 3」) */
408
- export const RT_STITCH_COLS = 3;
409
- /** 原图**纵向**(纬度方向 v)切几片(旨「竖 3」)⇒ 一槽在纵向上装满整幅 3 片,横向才分左/中/右 */
408
+ /** 原图**横向**(经度方向 u)切几片(需求「横 6」) */
409
+ export const RT_STITCH_COLS = 6;
410
+ /** 原图**纵向**(纬度方向 v)切几片(需求「竖 3」)⇒ 一槽在纵向上装满整幅 3 片,横向才分左/右 */
410
411
  export const RT_STITCH_ROWS = 3;
411
- /** 一个批次(= 一个全景点)的瓦片总数 = 3 × 3 = 9;`tiles` 存档数组的硬上限 */
412
+ /** 一个批次(= 一个全景点)的瓦片总数 = 6 × 3 = 18;`tiles` 存档数组的硬上限 */
412
413
  export const RT_STITCH_TILES_PER_BATCH = RT_STITCH_COLS * RT_STITCH_ROWS;
413
- /** 一个拼合槽**横向**几格(旨「竖直排列 3 个瓦片」⇒ 一槽就是原图的一整列,横 1 格)*/
414
- export const RT_STITCH_SLOT_CELL_COLS = 1;
415
- /** 一个拼合槽**纵向**几格(旨「竖直排列 3 个瓦片」⇒ 竖 3 格)
416
- * ⚠️ 10-23 起横竖**不再同数**:旧字段 `RT_STITCH_SLOT_GRID`(方形假设)已整条作废,
414
+ /** 一个拼合槽**横向**几格(需求「每个 RT 上面 3×3=9」⇒ 一槽 = 原图左半幅/右半幅 = 横 3 格)*/
415
+ export const RT_STITCH_SLOT_CELL_COLS = 3;
416
+ /** 一个拼合槽**纵向**几格(同上 ⇒ 竖 3 格)
417
+ * ⚠️ 横竖**不再同数**是 10-23 起的口径(旧字段 `RT_STITCH_SLOT_GRID`「方形假设」已整条作废),
417
418
  * 格数、格位、quad 摆位一律分轴取(`rtStitchCellPos` / `PanoTileStitcher._stitchCell`)。 */
418
419
  export const RT_STITCH_SLOT_CELL_ROWS = 3;
419
- /** 一个拼合槽的格数 = 1 × 3 = 3(画满 3 格 = 这一槽齐活) */
420
+ /** 一个拼合槽的格数 = 3 × 3 = 9(画满 9 格 = 这一槽齐活) */
420
421
  export const RT_STITCH_CELLS_PER_SLOT = RT_STITCH_SLOT_CELL_COLS * RT_STITCH_SLOT_CELL_ROWS;
421
- /** 拼合槽个数 = **左/中/右 3 槽**(旨「全景的 RT 由原来的 2 个分成 3 个」)⇒ 一个全景点占 **3 个 sampler**。
422
- * ⛔ 别以为 9 片就要 9 个 sampler:皇上本机 `MAX_TEXTURE_IMAGE_UNITS` 实测 **16** 是硬顶,
423
- * 3 槽 × 同屏 8 景点 = 24 会连编都编不过 ⇒ 同屏景点数由 `MKT_PANO_MAX_SLOTS` 收到 **5**(5×3=15 ≤ 16)。
424
- * ⭐ 分块口径:槽 `s` = 原图**第 s 列**(列 s、行 0-2)⇒ 每槽覆盖 **横向 120° × 纵向 180°**,
425
- * 与「一槽一列、竖 3 格」同一条口径(槽纹理尺寸见下 `RT_STITCH_SLOT_W/H`)。 */
426
- export const RT_STITCH_SLOT_COUNT = 3;
427
- /** 单槽像素尺寸 **4096×6144**(皇上 2026-10-23 旨「每个 RT 的尺寸是 4096 * 6144 竖直排列 3 个瓦片」):
428
- * 3 槽 = 有效分辨率 **12288×6144**,与 3×3 切片的原图(12288×6144 ⇒ 每片 4096×2048)**逐像素 1:1**;
429
- * 上一版 2 槽 4096² 是把 71.6 Mpx 的原图**缩小**塞进 33.5 Mpx ⇒ 本次是升分辨率,⛔ 不是降质。
430
- * ⚠️ 每格 = 4096×2048(横竖**不等** ⇒ 摆格必须分轴,见 `RT_STITCH_SLOT_CELL_COLS/ROWS`)。
431
- * ⚠️ 两条代价(臣已量过并报给皇上拍板):单全景 RT 显存 128MB → **302MB**(×2.25,mipmap 再 +1/3);
432
- * sampler 3 槽 × 8 景点 = 24 超硬顶 16 ⇒ 同屏景点 `MKT_PANO_MAX_SLOTS` 8 → **5**。
422
+ /** 拼合槽个数 = **左/右 2 槽**(需求「一个全景是 2 个 RT,每个 RT 上面 3×3=9」)⇒ 一个全景点占 **2 个 sampler**。
423
+ * ⛔ 别以为 18 片就要 18 个 sampler:本机 `MAX_TEXTURE_IMAGE_UNITS` 实测 **16** 是硬顶,
424
+ * 2 槽 × 同屏 8 景点 = 16 正好用满 ⇒ `MKT_PANO_MAX_SLOTS` 回到 **8**(上一版 3 槽只能坐 5 点)。
425
+ * ⭐ 分块口径:槽 `s` = 原图**第 `3s`~`3s+2` 列**(行 0-2)⇒ 每槽覆盖 **横向 180° × 纵向 180°**,
426
+ * 与「一槽横 3 格 × 竖 3 格」同一条口径(槽纹理尺寸见下 `RT_STITCH_SLOT_W/H`)。 */
427
+ export const RT_STITCH_SLOT_COUNT = 2;
428
+ /** 单槽像素尺寸 **6144×6144**(需求 2026-10-26「2 个 RT,每个 RT 上面 3×3=9」):
429
+ * 2 槽 = 有效分辨率 **12288×6144**,与 6×3 切片的原图(12288×6144 ⇒ 每片 2048×2048)**逐像素 1:1**;
430
+ * 单全景 RT 总量 2×6144² = 75.5 Mpx 与上一版 3×(4096×6144) **一字不差** ⇒ 片数翻倍而画质、显存都不变。
431
+ * ⚠️ 每格 = 2048×2048(横竖**等**了,但摆格仍分轴取,见 `RT_STITCH_SLOT_CELL_COLS/ROWS`)。
432
+ * ⚠️ 代价(与上一版同档):单全景常驻显存 ≈ 384MB(含 mipmap);同屏点数因每点只占 2 sampler 回到 8 ⇒
433
+ * 8 点常驻 ≈ 3GB,那是「同屏点数」这一档带来的,⛔ 不许为此缩 RT。
433
434
  * ⚠️ 红线(「千万不要降低质量」):这两个数与「原图切几片」无关,⛔ 不许当成省显存的旋钮去缩。 */
434
- export const RT_STITCH_SLOT_W = 4096;
435
+ export const RT_STITCH_SLOT_W = 6144;
435
436
  export const RT_STITCH_SLOT_H = 6144;
436
437
  export const floorVertexShader = /* glsl */ `
437
438
  #include <common>
@@ -495,14 +496,14 @@ const panoSlotBranches = (emit) => {
495
496
  }
496
497
  return out.join("\n ");
497
498
  };
498
- /** 🔬 归属诊断用的每槽色相(`?panoTile=1` 那条直通分支;条数必须 ≥ `RT_STITCH_SLOT_COUNT`) */
499
- const PANO_SLOT_DEBUG_COLORS = ["vec3(1.0, 0.0, 0.0)", "vec3(0.0, 1.0, 0.0)", "vec3(0.0, 0.0, 1.0)"];
499
+ /** 🔬 归属诊断用的每槽色相(`?panoTile=1` 那条直通分支;条数必须 ≥ `RT_STITCH_SLOT_COUNT`(2)) */
500
+ const PANO_SLOT_DEBUG_COLORS = ["vec3(1.0, 0.0, 0.0)", "vec3(0.0, 1.0, 0.0)"];
500
501
  const fragmentShader = /* glsl */ `
501
502
  // 对数深度缓冲(与顶点着色器配套,保证 MktMesh 深度与场景一致)
502
503
  #include <logdepthbuf_pars_fragment>
503
504
 
504
- // 一个全景点 = 3 张**拼合槽纹理**(原图切 3×3=9 片,每「横 1 × 竖 3」= 3 片拼成一张槽,10-23 皇上旨)。
505
- // panoramas[0..2] = [左列, 中列, 右列] 三个 RT;片元按全景 UV 的 **u** 挑对应槽采样(v 恒全高)。
505
+ // 一个全景点 = 2 张**拼合槽纹理**(原图切 6×3=18 片,每「横 3 × 竖 3」= 9 片拼成一张槽,2026-10-26 需求)。
506
+ // panoramas[0..1] = [左半幅, 右半幅] 两个 RT;片元按全景 UV 的 **u** 挑对应槽采样(v 恒全高)。
506
507
  // ⚠️ ANGLE(D3D) 禁止动态索引 sampler 数组,故槽选择用「常量下标 if 链」(见 panoSlotBranches)。
507
508
  uniform sampler2D panoramas[${RT_STITCH_SLOT_COUNT}];
508
509
  uniform mat4 textureMatrix; // 纹理世界矩阵:位置(第4列 xyz)=投影中心,旋转(左上3x3)=纹理朝向
@@ -511,7 +512,7 @@ const fragmentShader = /* glsl */ `
511
512
  // 原 uniform float uColorSpace; + 片元里那段 sRGB→linear 的 pow 分支已整条撤除,勿加回(原因见下方采样处)。
512
513
  // 全景上下方向**已写死**(等距柱状线性 v + 不交换 + 不翻转,2026-09-22 皇上定论,理由见文件头 🎛️ 段):
513
514
  // 曾有 uPanoSwapRow / uPanoFlipTile / uPanoVertOff 三个可调 uniform,现已整条撤除,勿加回。
514
- // ⚠️ uPanoTiled = panoramas[0..2] 装的是「拼合好的左/中/右三张槽纹理」(1) 还是「同一张完整等距柱状图」(0)。
515
+ // ⚠️ uPanoTiled = panoramas[0..1] 装的是「拼合好的左/右两张槽纹理」(1) 还是「同一张完整等距柱状图」(0)。
515
516
  // 由「用户选的纹理形态 PanoTexMode + 该点有没有切过片」共同决定(见下方 mktPanoTiledFlag)。
516
517
  // 0 时必须改用**全图 UV** 取样:拿瓦片内的 localU/localV 去采整图 = 把整图的左一档拉伸到整个
517
518
  // 球面(墙面仰角 +10° 采到原图 −70° 的地板)—— 2026-09-22「全景位置不对」的第二真因,见文件头 🧩 段。
@@ -550,9 +551,10 @@ const fragmentShader = /* glsl */ `
550
551
  float lat = asin(clamp(dir.y, -1.0, 1.0));
551
552
  float v = 0.5 + lat * 0.3183098861837907; // 1/PI
552
553
 
553
- // 左/中/右三列 -> 拼合槽下标 [左列=0, 中列=1, 右列=2](每槽 = 3×3 网格里「横 1 × 竖 3」= 3 片,见 RT_STITCH_SLOT_TILES)
554
- // ⭐ 2026-10-23 旨「全景的 RT 由原来的 2 个分成 3 个、每个 4096×6144 竖直排列 3 个瓦片」:
555
- // 槽只随 **u** 三等分走,v 方向一槽装下整幅上下 180°(槽纹理尺寸见 RT_STITCH_SLOT_W/H)。
554
+ // 左/右两半幅 -> 拼合槽下标 [左半幅=0, 右半幅=1](每槽 = 6×3 网格里「横 3 × 竖 3」= 9 片,见 RT_STITCH_SLOT_TILES)
555
+ // ⭐ 2026-10-26 需求「一个全景 2 个 RT,每个 RT 上面 3×3=9 片」:槽仍**只随 u 等分**(这里 N=2),
556
+ // 只是每一槽从「原图一整列」变成「原图左/右半幅(3 列 × 3 行)」⇒ RT 里横向有 3 格、纵向 3 格。
557
+ // ⛔ 采样式不用改:槽内 localU 一整张 RT 宽度铺满 3 格,格子边界由拼合器摆格时算好。
556
558
  int tileIdx = int(clamp(floor(u * ${RT_STITCH_SLOT_COUNT}.0), 0.0, ${RT_STITCH_SLOT_COUNT - 1}.0));
557
559
  // 槽内局部 UV(连续、无 fract 边界跳变):横向按槽数等分;
558
560
  // ⛔ 纵向**不再切**(槽就是全高)⇒ localV 恒等于 v。写成 nv / nv-1 = 纵向分辨率白丢一档。
@@ -638,7 +640,7 @@ export function isPanoTexMode(v) {
638
640
  export const MISSING_TILE_GRAY = "#767676";
639
641
  /** 接缝余量(**原图像素**,每片向四边各多切这么多;贴原图边那侧裁到剩多少算多少)。
640
642
  *
641
- * 10-06 槽内改成 4×4 = 16 格 ⇒ 一张槽内部的接缝从旧版的 1 条十字变成 **3 横 3 竖**,这一条是随之而来的画质护栏:
643
+ * 10-26 每槽装 3×3 = 9 格 ⇒ 一张槽内部的接缝是 **2 横 2 竖**(上一版每槽一竖列时只有 2 横 0 竖),这一条是随之而来的画质护栏:
642
644
  * ① 上传时每片**向四边多切 M 像素**(`rtStitchTileRects`);
643
645
  * ② 拼合时**只采中间那块**(`rtStitchTileInset` 算出的 UV 内缩窗口),多切的那一圈**不画进格子**。
644
646
  *
@@ -705,7 +707,7 @@ export function slotTilesOf(grid) {
705
707
  }
706
708
  return slots;
707
709
  }
708
- /** 全景的标准布局(3×3 网格 / 3 槽 / 4096×6144 RT)—— 一槽 = 原图一整列 */
710
+ /** 全景的标准布局(6×3 网格 / 2 槽 / 6144×6144 RT)—— 一槽 = 原图左/右半幅 */
709
711
  export const PANO_TILE_LAYOUT = { ...PANO_TILE_GRID, rtW: RT_STITCH_SLOT_W, rtH: RT_STITCH_SLOT_H };
710
712
  /**
711
713
  * 图片平面的布局:2×2 网格 / 1 槽,RT 宽高比 = 原图宽高比,长边 4096(10-21 旨把 16 片改成 4 片,
@@ -719,7 +721,7 @@ export function imageTileLayout(natW, natH) {
719
721
  const rtH = natH > natW ? LONG : Math.round(LONG * natH / natW);
720
722
  return { ...IMAGE_TILE_GRID, rtW, rtH };
721
723
  }
722
- /** 9 瓦片批次的标识 = 第一条**非空**瓦片地址(同批上传 URL 前缀一致,足以唯一;
724
+ /** 18 瓦片批次的标识 = 第一条**非空**瓦片地址(同批上传 URL 前缀一致,足以唯一;
723
725
  * 09-29 晚旨「不足张数缺的地方涂灰」后批次里允许有空串占位 ⇒ 取首个非空,防首张缺失时键为空串互撞) */
724
726
  export function rtStitchBatchOf(tiles) {
725
727
  return tiles.find((u) => u) || "";
@@ -728,7 +730,7 @@ export function rtStitchBatchOf(tiles) {
728
730
  export function rtStitchKey(batch, slot) {
729
731
  return `${RT_STITCH_KEY_PREFIX}${batch}#${slot}`;
730
732
  }
731
- /** 是否拼合槽(`rt:<batch>#<slot>`)的虚拟键 —— 16 / 32 / 64 / 现行 9 片**同一个前缀**,键本身不含片数信息 */
733
+ /** 是否拼合槽(`rt:<batch>#<slot>`)的虚拟键 —— 16 / 32 / 64 / 9 片 / 现行 18 片**同一个前缀**,键本身不含片数信息 */
732
734
  export function isRtStitchKey(key) {
733
735
  return key.startsWith(RT_STITCH_KEY_PREFIX);
734
736
  }
@@ -742,15 +744,15 @@ export function rtStitchKeyParts(key) {
742
744
  return null;
743
745
  return { batch: body.slice(0, i), slot: Number(body.slice(i + 1)) };
744
746
  }
745
- /** 槽 slot 用哪 3 张瓦片(原图 **3 列 × 3 行 行优先**下标 `r*3+c`;**槽内格序 = 横 1 × 竖 3、图像左上起**):
746
- * 槽 0 = **左列** → `[0,3,6]`;槽 1 = **中列** → `[1,4,7]`;槽 2 = **右列** → `[2,5,8]`。
747
- * ⭐ 只按 **u** 分槽(皇上旨 2026-10-23「每个 RT 竖直排列 3 个瓦片」⇒ 一槽 = 原图一整列 = 横 120° × 全高 180°),
748
- * ⚠️ 表是**从上面那几个网格常数生成**的(⛔ 手抄 9 个数):正确性由护栏 `test/mktmesh.test.js` BE 组
747
+ /** 槽 slot 用哪 9 张瓦片(原图 **6 列 × 3 行 行优先**下标 `r*6+c`;**槽内格序 = 横 3 × 竖 3、图像左上起**):
748
+ * 槽 0 = **左半幅**(列 0-2)→ `[0,1,2, 6,7,8, 12,13,14]`;槽 1 = **右半幅**(列 3-5)→ `[3,4,5, 9,10,11, 15,16,17]`。
749
+ * ⭐ 只按 **u** 分槽(需求 2026-10-26「一个全景是 2 个 RT,每个 RT 上面 3×3=9」⇒ 一槽 = 原图半幅 = 横 180° × 全高 180°),
750
+ * ⚠️ 表是**从上面那几个网格常数生成**的(⛔ 手抄 18 个数):正确性由护栏 `test/mktmesh.test.js` BE 组
749
751
  * 对着手写死的字面量逐个数断言 —— 改了网格常数而护栏没跟着改,当场红。 */
750
752
  export const RT_STITCH_SLOT_TILES = slotTilesOf(PANO_TILE_GRID);
751
753
  /** 槽内第 cell 格在**图像方向**的位置 `[列, 行]`(0 起,与 `RT_STITCH_SLOT_TILES` 的格序同一口径)。
752
754
  * ⚠️ 这是「图像方向」(行 0 = 图像最上),GL 的 y 向上 ⇒ 画进 RT 时 y 要翻(拼合器里那句 `1 - (2*r+1)/n`)。
753
- * `slotCellCols` 默认全景那档(1 = 一槽就是一竖列);图片平面传 2。 */
755
+ * `slotCellCols` 默认全景那档(3 = 一槽就是原图半幅的三列);图片平面传 2。 */
754
756
  export function rtStitchCellPos(cell, slotCellCols = PANO_TILE_GRID.slotCellCols) {
755
757
  return [cell % slotCellCols, Math.floor(cell / slotCellCols)];
756
758
  }
@@ -761,12 +763,12 @@ export function rtStitchCellPos(cell, slotCellCols = PANO_TILE_GRID.slotCellCols
761
763
  function gridLine(i, n, steps) {
762
764
  return Math.round((i * n) / steps);
763
765
  }
764
- /** 9 片瓦片的切图矩形(行优先、图像左上起,下标 `i = r*3+c` 与 `RT_STITCH_SLOT_TILES` 同源;
765
- * ⭐ 10-23 旨「横 3 × 竖 3 = 9」:`r` 走 `RT_STITCH_ROWS`(3)、`c` 走 `RT_STITCH_COLS`(3)。
766
+ /** 18 片瓦片的切图矩形(行优先、图像左上起,下标 `i = r*6+c` 与 `RT_STITCH_SLOT_TILES` 同源;
767
+ * ⭐ 10-26 需求「横 6 × 竖 3 = 18」:`r` 走 `RT_STITCH_ROWS`(3)、`c` 走 `RT_STITCH_COLS`(6)。
766
768
  *
767
769
  * ⭐ **上传侧(`panoramaUpload`)与拼合侧(`PanoTileStitcher`)必须共用这一份几何**:
768
770
  * 上传多切多少、拼合就内缩多少;两边各算一次就是「缝没治掉、反而把画面挤歪」。
769
- * 拼合侧拿不到原图尺寸(它只有 9 个 URL 和 9 张已解码纹理),所以它用 `rtStitchTileInset`
771
+ * 拼合侧拿不到原图尺寸(它只有 18 个 URL 和 18 张已解码纹理),所以它用 `rtStitchTileInset`
770
772
  * —— 那条只需「下标 + 该瓦片自己的像素尺寸」,两者等价的前提是「多切量 = 常数 M、只有贴边那几片被裁」,
771
773
  * 而「贴不贴边」纯由下标决定 ⇒ 护栏 BE 组用同一批 (W,H) 把两条算路对表钉死。 */
772
774
  export function rtStitchTileRects(w, h, grid = PANO_TILE_GRID) {
@@ -786,7 +788,7 @@ export function rtStitchTileRects(w, h, grid = PANO_TILE_GRID) {
786
788
  }
787
789
  return out;
788
790
  }
789
- /** 这片瓦片有没有贴原图边界(3×3 ⇒ 9 片里四周那 8 片至少贴一条边,只有正中心那片四边都不贴;贴边的**那一侧**多切量被裁成 0)。
791
+ /** 这片瓦片有没有贴原图边界(6×3 ⇒ 18 片里外圈那 14 片至少贴一条边,只有中间那 4 片四边都不贴;贴边的**那一侧**多切量被裁成 0)。
790
792
  * 拼合侧靠它 + 瓦片自己的像素尺寸还原内缩量 ⇒ 不必知道原图多大。
791
793
  * ⚠️ 下标拆分必须按 **列数 COLS**(`i = r*cols+c`),⛔ 不是 ROWS。 */
792
794
  export function rtStitchTileEdgeClip(i, grid = PANO_TILE_GRID) {
@@ -818,13 +820,13 @@ export function rtStitchTileInset(i, tw, th, grid = PANO_TILE_GRID) {
818
820
  return [u0, v0, u1, v1];
819
821
  }
820
822
  /**
821
- * 「这 `RT_STITCH_SLOT_COUNT`(3) 个纹理槽到底喂哪几个地址」。⚠️ **必须**与 `mktPanoTiledFlag` 同源、同一时机调用:
823
+ * 「这 `RT_STITCH_SLOT_COUNT`(2) 个纹理槽到底喂哪几个地址」。⚠️ **必须**与 `mktPanoTiledFlag` 同源、同一时机调用:
822
824
  * 只改标记不改地址(或反之)= 用整图 UV 去采一张瓦片 / 用瓦片内 UV 去采整图,画面立刻错乱。
823
825
  * 非 tiled 态(或该点压根没切过片)一律回退成「各槽同为一张整图」。
824
- * ⭐ 2026-10-23 旨「只支持 9 张(3×3)」:只要有瓦片(1~9 条)一律走拼合器(rt: 虚拟键),
825
- * 不足 9 张的位置由拼合器**涂灰**(一眼看出数据不全);8×4=32 张、8×8=64 张与 4×4=16 张那几版**都不做兼容**
826
- * (皇上「无须兼容正常使用,有异常我会重传」⇒ 旧存档的 64 条地址超张数被判废、重传即可)。
827
- * ⚠️ 返回长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写 3 个 rtStitchKey —— 槽数再变会漏改)。
826
+ * ⭐ 2026-10-26 需求「只支持 18 张(6×3)」:只要有瓦片(1~18 条)一律走拼合器(rt: 虚拟键),
827
+ * 不足 18 张的位置由拼合器**涂灰**(一眼看出数据不全);3×3=9 张、8×4=32 张、8×8=64 张与 4×4=16 张那几版**都不做兼容**
828
+ * (「无须兼容正常使用,有异常我会重传」⇒ 旧存档的 9 / 32 / 64 条地址张数不符被判废、重传即可)。
829
+ * ⚠️ 返回长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写 2 个 rtStitchKey —— 槽数再变会漏改)。
828
830
  */
829
831
  export function mktPanoTexUrls(url, tiles, mode = "tiled") {
830
832
  if (mode === "tiled" && tiles && tiles.length > 0) {
@@ -834,8 +836,8 @@ export function mktPanoTexUrls(url, tiles, mode = "tiled") {
834
836
  return Array.from({ length: RT_STITCH_SLOT_COUNT }, () => url);
835
837
  }
836
838
  /**
837
- * 「这 3 个纹理槽是不是真瓦片」的判据 —— **必须**与喂纹理处的回退式(`mktPanoTexUrls`)同源:
838
- * `tiles` 有 **任意 1~9 条**地址(不足 9 = 数据不全,缺的格子拼合时涂灰)**且**当前形态选的是 tiled = 是瓦片;
839
+ * 「这 2 个纹理槽是不是真瓦片」的判据 —— **必须**与喂纹理处的回退式(`mktPanoTexUrls`)同源:
840
+ * `tiles` 有 **任意 1~18 条**地址(不足 18 = 数据不全,缺的格子拼合时涂灰)**且**当前形态选的是 tiled = 是瓦片;
839
841
  * 否则各槽同为一张整图(旧数据 / 种子图 / 用户切整片)。
840
842
  */
841
843
  export function mktPanoTiledFlag(tiles, mode = "tiled") {
@@ -872,7 +874,7 @@ export function createMktShaderMaterial() {
872
874
  // 一个全景点恒占 **3 个 sampler**(2026-10-23 旨「全景的 RT 由原来的 2 个分成 3 个」):
873
875
  // panoramas[0..2] = 3 张**离屏拼合槽纹理**([左列, 中列, 右列],每槽由 `RT_STITCH_CELLS_PER_SLOT`
874
876
  // 片瓦片竖着拼成),片元按全景 UV 的 **u** 三等分挑槽采样(v 恒全高)。
875
- // ⛔ 长度由 `RT_STITCH_SLOT_COUNT` 派生(不许手写 3 张)—— 槽数再变这里会漏改。
877
+ // ⛔ 长度由 `RT_STITCH_SLOT_COUNT` 派生(不许手写 2 张)—— 槽数再变这里会漏改。
876
878
  panoramas: {
877
879
  value: Array.from({ length: RT_STITCH_SLOT_COUNT }, () => createPlaceholderTexture()),
878
880
  },