three-base 2.0.181 → 2.0.188
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 +36 -6
- package/MktMesh/MktMesh.js +107 -27
- package/MktMesh/MktMesh.ts +103 -26
- package/MktMesh/imagePlane.d.ts +4 -1
- package/MktMesh/imagePlane.js +25 -10
- package/MktMesh/imagePlane.ts +26 -10
- 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/ThreeBase.js +5 -4
- package/ThreeBase.ts +5 -4
- package/package.json +1 -1
package/MktMesh/mktShader.ts
CHANGED
|
@@ -44,8 +44,8 @@ import { Color, DataTexture, DoubleSide, Matrix4, ShaderMaterial, SRGBColorSpace
|
|
|
44
44
|
* 现口径已统一为等距柱状线性,皇上验收「不交换 / 不翻转」那态即正确 ⇒ **方向参数整条撤除、写死**
|
|
45
45
|
* (见 🎛️ 段)。本诊断开关保留,用于日后再次核对「哪块瓦片落在哪个象限」。
|
|
46
46
|
*
|
|
47
|
-
* 用法:页面 URL 带 `?panoTileDebug=1`
|
|
48
|
-
* ⭐ 10-
|
|
47
|
+
* 用法:页面 URL 带 `?panoTileDebug=1` 时,几张槽纹理**不再采样纹理**,各自输出一个纯色 ——
|
|
48
|
+
* ⭐ 10-26 起横 2 槽(竖着不再分块):红=左半幅(槽0) 绿=右半幅(槽1)
|
|
49
49
|
* (旧 4 象限版是 红=tiles[0] 绿=tiles[1] 蓝=tiles[2] 黄=tiles[3],已随四槽一并作废)
|
|
50
50
|
* 皇上只要说出「画面上下左右各是什么颜色」,即可一次反推出真实映射。
|
|
51
51
|
*
|
|
@@ -325,7 +325,7 @@ export function setMktPanoPoints(
|
|
|
325
325
|
*
|
|
326
326
|
* 🧩 **第二个根因(与方向参数无关,故本次写死后依然保留)**:
|
|
327
327
|
* 默认场景与旧存档的 `textures[].tiles` **只有 1 条地址**(当年上传切片功能之后才有的字段),
|
|
328
|
-
* 喂料处回退成「各槽同为 `url
|
|
328
|
+
* 喂料处回退成「各槽同为 `url`」(现行 2 槽:`[url, url]`)⇒ **各 sampler 装的是同一张完整等距柱状图**。于是:
|
|
329
329
|
* ① 各槽同图 ⇒ 选哪一格都一样(当时看到的「交换零效果」症状,写死后该症状无从出现);
|
|
330
330
|
* ② 更要命的是仍按「瓦片内 UV」`localU/localV` 去采整图 ⇒ 整图被按 1/2 拉伸铺满整个球面(旧版是 1/4):
|
|
331
331
|
* 墙面仰角 +10° 采到原图 **−70°(脚底/三脚架)** —— 这才是「全景映射在模型上的位置不太对」的另一半,
|
|
@@ -333,7 +333,7 @@ export function setMktPanoPoints(
|
|
|
333
333
|
* 修法 = `uPanoTiled`(单全景标量 / 融合每点数组):0 时把瓦片内 UV **还原成整图 UV**
|
|
334
334
|
* `panoU = u`、`panoV = v`(口径写死后这就是恒等式:横向 `0.5(2u)=u` / `0.5+0.5(2u−1)=u`;
|
|
335
335
|
* 纵向 10-06 晚起 localV 本就等于 v ⇒ 两个方向都退化成恒等式)。
|
|
336
|
-
* ⚠️ 判据与回退式**必须同源**(`mktPanoTiledFlag` = 「tiled 形态 且 tiles 有任意 1~
|
|
336
|
+
* ⚠️ 判据与回退式**必须同源**(`mktPanoTiledFlag` = 「tiled 形态 且 tiles 有任意 1~18 条」,现行只支持 6×3=18 张、不足涂灰),且每个回退喂料点都要写标记,
|
|
337
337
|
* 漏一处该对象就悄悄退回 1/2 拉伸。默认值取 1(已切片的真实上传数据行为不变)。
|
|
338
338
|
* 护栏:`test/panoVMapping.test.js`。
|
|
339
339
|
* 🔄 2026-09-22 追加(皇上口谕):本标记现由**用户可选的「全景纹理形态」`PanoTexMode`**(tiled/whole)与数据
|
|
@@ -409,51 +409,52 @@ const vertexShader = /* glsl */ `
|
|
|
409
409
|
*/
|
|
410
410
|
// ⭐ 下面这一组「切片 / 拼合槽」几何常量**必须排在 `fragmentShader` 之前**:单全景那份片元是模块级
|
|
411
411
|
// 常量字符串,里面要用 `${RT_STITCH_SLOT_COUNT}` 声明 sampler 数组长度(TS 的块级作用域禁止先用后声明)。
|
|
412
|
-
/** ⭐
|
|
413
|
-
* 1 张 **
|
|
414
|
-
*
|
|
412
|
+
/** ⭐ 18 瓦片拼合(2026-10-26 需求:一张全景切 **横 6 × 竖 3 = 18 片**,每 **横 3 × 竖 3 = 9 片**离屏拼成
|
|
413
|
+
* 1 张 **6144×6144** 槽纹理,**左/右 2 槽**组成完整全景;上一版是 10-23 的 3×3 = 9 片 / 按列 3 槽 4096×6144,
|
|
414
|
+
* 再上一版 10-06 晚的 8×4 = 32 片 / 左右 2 槽 4096²,最早 09-29 的 4×4 = 16 片)的「槽虚拟地址」前缀。
|
|
415
415
|
* 虚拟键不是真 URL,纹理由 `PanoTileStitcher` 用 shader 逐格画进 `WebGLRenderTarget` 提供,
|
|
416
416
|
* 消费方(基类 `_ensurePanoTiles` / 编辑器 `_peekTileAt`)见前缀即走拼合器,⛔ 不得当 URL 下载。
|
|
417
417
|
* ⚠️ 键协议 `rt:<batch>#<slot>` 与 `uPanoTiled` 这套对外接口**一字未改** —— 变的只有「每批几条地址」
|
|
418
|
-
* 和「槽怎么分」;`slot` 取值 0..
|
|
419
|
-
* ⚠️ 旧存档不兼容(与 10-06 同一口径):存档里
|
|
418
|
+
* 和「槽怎么分」;`slot` 取值 0..1,消费方一律用 `RT_STITCH_SLOT_COUNT` 取,不许写死。
|
|
419
|
+
* ⚠️ 旧存档不兼容(与 10-06 / 10-23 同一口径):存档里 9 条(或历代 16/32/64 条)瓦片地址会因张数不符被判废
|
|
420
|
+
* ⇒ **老项目需重新上传**(本次已明确不兼容,用户会重传图片)。 */
|
|
420
421
|
export const RT_STITCH_KEY_PREFIX = "rt:";
|
|
421
422
|
|
|
422
|
-
/** 原图**横向**(经度方向 u
|
|
423
|
-
export const RT_STITCH_COLS =
|
|
423
|
+
/** 原图**横向**(经度方向 u)切几片(需求「横 6」) */
|
|
424
|
+
export const RT_STITCH_COLS = 6;
|
|
424
425
|
|
|
425
|
-
/** 原图**纵向**(纬度方向 v
|
|
426
|
+
/** 原图**纵向**(纬度方向 v)切几片(需求「竖 3」)⇒ 一槽在纵向上装满整幅 3 片,横向才分左/右 */
|
|
426
427
|
export const RT_STITCH_ROWS = 3;
|
|
427
428
|
|
|
428
|
-
/** 一个批次(= 一个全景点)的瓦片总数 =
|
|
429
|
+
/** 一个批次(= 一个全景点)的瓦片总数 = 6 × 3 = 18;`tiles` 存档数组的硬上限 */
|
|
429
430
|
export const RT_STITCH_TILES_PER_BATCH = RT_STITCH_COLS * RT_STITCH_ROWS;
|
|
430
431
|
|
|
431
|
-
/**
|
|
432
|
-
export const RT_STITCH_SLOT_CELL_COLS =
|
|
432
|
+
/** 一个拼合槽**横向**几格(需求「每个 RT 上面 3×3=9」⇒ 一槽 = 原图左半幅/右半幅 = 横 3 格)*/
|
|
433
|
+
export const RT_STITCH_SLOT_CELL_COLS = 3;
|
|
433
434
|
|
|
434
|
-
/**
|
|
435
|
-
* ⚠️ 10-23
|
|
435
|
+
/** 一个拼合槽**纵向**几格(同上 ⇒ 竖 3 格)
|
|
436
|
+
* ⚠️ 横竖**不再同数**是 10-23 起的口径(旧字段 `RT_STITCH_SLOT_GRID`「方形假设」已整条作废),
|
|
436
437
|
* 格数、格位、quad 摆位一律分轴取(`rtStitchCellPos` / `PanoTileStitcher._stitchCell`)。 */
|
|
437
438
|
export const RT_STITCH_SLOT_CELL_ROWS = 3;
|
|
438
439
|
|
|
439
|
-
/** 一个拼合槽的格数 =
|
|
440
|
+
/** 一个拼合槽的格数 = 3 × 3 = 9(画满 9 格 = 这一槽齐活) */
|
|
440
441
|
export const RT_STITCH_CELLS_PER_SLOT = RT_STITCH_SLOT_CELL_COLS * RT_STITCH_SLOT_CELL_ROWS;
|
|
441
442
|
|
|
442
|
-
/** 拼合槽个数 =
|
|
443
|
-
* ⛔ 别以为
|
|
444
|
-
*
|
|
445
|
-
* ⭐ 分块口径:槽 `s` = 原图**第
|
|
446
|
-
*
|
|
447
|
-
export const RT_STITCH_SLOT_COUNT =
|
|
448
|
-
|
|
449
|
-
/** 单槽像素尺寸 **
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
* ⚠️ 每格 =
|
|
453
|
-
* ⚠️
|
|
454
|
-
*
|
|
443
|
+
/** 拼合槽个数 = **左/右 2 槽**(需求「一个全景是 2 个 RT,每个 RT 上面 3×3=9」)⇒ 一个全景点占 **2 个 sampler**。
|
|
444
|
+
* ⛔ 别以为 18 片就要 18 个 sampler:本机 `MAX_TEXTURE_IMAGE_UNITS` 实测 **16** 是硬顶,
|
|
445
|
+
* 2 槽 × 同屏 8 景点 = 16 正好用满 ⇒ `MKT_PANO_MAX_SLOTS` 回到 **8**(上一版 3 槽只能坐 5 点)。
|
|
446
|
+
* ⭐ 分块口径:槽 `s` = 原图**第 `3s`~`3s+2` 列**(行 0-2)⇒ 每槽覆盖 **横向 180° × 纵向 180°**,
|
|
447
|
+
* 与「一槽横 3 格 × 竖 3 格」同一条口径(槽纹理尺寸见下 `RT_STITCH_SLOT_W/H`)。 */
|
|
448
|
+
export const RT_STITCH_SLOT_COUNT = 2;
|
|
449
|
+
|
|
450
|
+
/** 单槽像素尺寸 **6144×6144**(需求 2026-10-26「2 个 RT,每个 RT 上面 3×3=9」):
|
|
451
|
+
* 2 槽 = 有效分辨率 **12288×6144**,与 6×3 切片的原图(12288×6144 ⇒ 每片 2048×2048)**逐像素 1:1**;
|
|
452
|
+
* 单全景 RT 总量 2×6144² = 75.5 Mpx 与上一版 3×(4096×6144) **一字不差** ⇒ 片数翻倍而画质、显存都不变。
|
|
453
|
+
* ⚠️ 每格 = 2048×2048(横竖**等**了,但摆格仍分轴取,见 `RT_STITCH_SLOT_CELL_COLS/ROWS`)。
|
|
454
|
+
* ⚠️ 代价(与上一版同档):单全景常驻显存 ≈ 384MB(含 mipmap);同屏点数因每点只占 2 sampler 回到 8 ⇒
|
|
455
|
+
* 8 点常驻 ≈ 3GB,那是「同屏点数」这一档带来的,⛔ 不许为此缩 RT。
|
|
455
456
|
* ⚠️ 红线(「千万不要降低质量」):这两个数与「原图切几片」无关,⛔ 不许当成省显存的旋钮去缩。 */
|
|
456
|
-
export const RT_STITCH_SLOT_W =
|
|
457
|
+
export const RT_STITCH_SLOT_W = 6144;
|
|
457
458
|
export const RT_STITCH_SLOT_H = 6144;
|
|
458
459
|
|
|
459
460
|
export const floorVertexShader = /* glsl */ `
|
|
@@ -522,15 +523,15 @@ const panoSlotBranches = (emit: (slot: number) => string): string => {
|
|
|
522
523
|
return out.join("\n ");
|
|
523
524
|
};
|
|
524
525
|
|
|
525
|
-
/** 🔬 归属诊断用的每槽色相(`?panoTile=1` 那条直通分支;条数必须 ≥ `RT_STITCH_SLOT_COUNT
|
|
526
|
-
const PANO_SLOT_DEBUG_COLORS = ["vec3(1.0, 0.0, 0.0)", "vec3(0.0, 1.0, 0.0)"
|
|
526
|
+
/** 🔬 归属诊断用的每槽色相(`?panoTile=1` 那条直通分支;条数必须 ≥ `RT_STITCH_SLOT_COUNT`(2)) */
|
|
527
|
+
const PANO_SLOT_DEBUG_COLORS = ["vec3(1.0, 0.0, 0.0)", "vec3(0.0, 1.0, 0.0)"];
|
|
527
528
|
|
|
528
529
|
const fragmentShader = /* glsl */ `
|
|
529
530
|
// 对数深度缓冲(与顶点着色器配套,保证 MktMesh 深度与场景一致)
|
|
530
531
|
#include <logdepthbuf_pars_fragment>
|
|
531
532
|
|
|
532
|
-
// 一个全景点 =
|
|
533
|
-
// panoramas[0..
|
|
533
|
+
// 一个全景点 = 2 张**拼合槽纹理**(原图切 6×3=18 片,每「横 3 × 竖 3」= 9 片拼成一张槽,2026-10-26 需求)。
|
|
534
|
+
// panoramas[0..1] = [左半幅, 右半幅] 两个 RT;片元按全景 UV 的 **u** 挑对应槽采样(v 恒全高)。
|
|
534
535
|
// ⚠️ ANGLE(D3D) 禁止动态索引 sampler 数组,故槽选择用「常量下标 if 链」(见 panoSlotBranches)。
|
|
535
536
|
uniform sampler2D panoramas[${RT_STITCH_SLOT_COUNT}];
|
|
536
537
|
uniform mat4 textureMatrix; // 纹理世界矩阵:位置(第4列 xyz)=投影中心,旋转(左上3x3)=纹理朝向
|
|
@@ -539,7 +540,7 @@ const fragmentShader = /* glsl */ `
|
|
|
539
540
|
// 原 uniform float uColorSpace; + 片元里那段 sRGB→linear 的 pow 分支已整条撤除,勿加回(原因见下方采样处)。
|
|
540
541
|
// 全景上下方向**已写死**(等距柱状线性 v + 不交换 + 不翻转,2026-09-22 皇上定论,理由见文件头 🎛️ 段):
|
|
541
542
|
// 曾有 uPanoSwapRow / uPanoFlipTile / uPanoVertOff 三个可调 uniform,现已整条撤除,勿加回。
|
|
542
|
-
// ⚠️ uPanoTiled = panoramas[0..
|
|
543
|
+
// ⚠️ uPanoTiled = panoramas[0..1] 装的是「拼合好的左/右两张槽纹理」(1) 还是「同一张完整等距柱状图」(0)。
|
|
543
544
|
// 由「用户选的纹理形态 PanoTexMode + 该点有没有切过片」共同决定(见下方 mktPanoTiledFlag)。
|
|
544
545
|
// 0 时必须改用**全图 UV** 取样:拿瓦片内的 localU/localV 去采整图 = 把整图的左一档拉伸到整个
|
|
545
546
|
// 球面(墙面仰角 +10° 采到原图 −70° 的地板)—— 2026-09-22「全景位置不对」的第二真因,见文件头 🧩 段。
|
|
@@ -578,9 +579,10 @@ const fragmentShader = /* glsl */ `
|
|
|
578
579
|
float lat = asin(clamp(dir.y, -1.0, 1.0));
|
|
579
580
|
float v = 0.5 + lat * 0.3183098861837907; // 1/PI
|
|
580
581
|
|
|
581
|
-
//
|
|
582
|
-
// ⭐ 2026-10-
|
|
583
|
-
//
|
|
582
|
+
// 左/右两半幅 -> 拼合槽下标 [左半幅=0, 右半幅=1](每槽 = 6×3 网格里「横 3 × 竖 3」= 9 片,见 RT_STITCH_SLOT_TILES)
|
|
583
|
+
// ⭐ 2026-10-26 需求「一个全景 2 个 RT,每个 RT 上面 3×3=9 片」:槽仍**只随 u 等分**(这里 N=2),
|
|
584
|
+
// 只是每一槽从「原图一整列」变成「原图左/右半幅(3 列 × 3 行)」⇒ RT 里横向有 3 格、纵向 3 格。
|
|
585
|
+
// ⛔ 采样式不用改:槽内 localU 一整张 RT 宽度铺满 3 格,格子边界由拼合器摆格时算好。
|
|
584
586
|
int tileIdx = int(clamp(floor(u * ${RT_STITCH_SLOT_COUNT}.0), 0.0, ${RT_STITCH_SLOT_COUNT - 1}.0));
|
|
585
587
|
// 槽内局部 UV(连续、无 fract 边界跳变):横向按槽数等分;
|
|
586
588
|
// ⛔ 纵向**不再切**(槽就是全高)⇒ localV 恒等于 v。写成 nv / nv-1 = 纵向分辨率白丢一档。
|
|
@@ -661,27 +663,27 @@ export function createPlaceholderTexture(): DataTexture {
|
|
|
661
663
|
/**
|
|
662
664
|
* 全景纹理形态(皇上旨 2026-09-22「全景球可以切成 4 片和一整片,两者效果很不一样,做个功能可以切换」)。
|
|
663
665
|
*
|
|
664
|
-
* "tiled" =
|
|
665
|
-
* 10-23
|
|
666
|
+
* "tiled" = 2 个 sampler 各装一张**离屏拼合好的槽纹理**(原图 **6×3 = 18 片**,每「横 3 × 竖 3」= 9 片拼一张;
|
|
667
|
+
* 2026-10-26 需求;再早是 10-23 的 3×3 = 9 片 / 3 槽、10-06 晚的 8×4 = 32 片 / 2 槽、同日的 8×8 = 64 片 / 4 槽、09-29 的 4×4 = 16 片),
|
|
666
668
|
* `uPanoTiled` 写 1;
|
|
667
|
-
* "whole" =
|
|
669
|
+
* "whole" = 2 个 sampler 都装**同一张完整等距柱状原图**,`uPanoTiled` 写 0(改用全图 UV)。
|
|
668
670
|
*
|
|
669
671
|
* ⚠️ 两种形态在**几何上应当逐像素一致**(`panoU/panoV` 的还原式就是恒等式,见文件头 🧩 段与
|
|
670
672
|
* `test/panoVMapping.test.js` 的等价性数值证明)。所以线上若看着「很不一样」,差异只可能来自下面两条,
|
|
671
673
|
* 与本切换功能无关、也不会被它修掉:
|
|
672
674
|
* ① **分辨率**:`x-oss-process=image/resize,m_lfit,w_<ossSize>` 是**按 URL** 生效的,且 2026-09-23 皇上定
|
|
673
|
-
* 「**切片的瓦片不走该尺寸档,默认用原图**」(每片本就只有原图的 1/
|
|
675
|
+
* 「**切片的瓦片不走该尺寸档,默认用原图**」(每片本就只有原图的 1/6 宽 × 1/3 高,再按档缩是二次损失)⇒
|
|
674
676
|
* 判据 = `mktPanoTiledFlag(...)===1`(喂地址与写 `uPanoTiled` 同源,见 `resolveOssUrl` 两处)。
|
|
675
677
|
* 于是 tiled 态 = 拼合槽纹理**逐片取自原始分辨率**瓦片(合计像素≈整张原图,10-06 那版仍是 8192×4096 等效),
|
|
676
678
|
* whole 态 = 整图按档(w_1024 ⇒ 等效 1024×512)。
|
|
677
679
|
* ⚠️ 两档清晰度差**不是固定的倍数**,而取决于「原图宽 ÷ 所选档位」;又因 `m_lfit` 只缩不放,
|
|
678
680
|
* 原图宽 ≤ 2×档位时 whole 态其实也没被缩,两态清晰度自然接近 —— 别把这当成「切换坏了」。
|
|
679
|
-
* ② **瓦片边界**(10-06 晚切 32 片起这一条分两层,10-
|
|
680
|
-
* · **拼合槽之间**(u=1/
|
|
681
|
+
* ② **瓦片边界**(10-06 晚切 32 片起这一条分两层,10-26 回到「2 槽 + 每槽 3×3」后两层口径又换一档):
|
|
682
|
+
* · **拼合槽之间**(u=1/2 那**一道**竖;⭐ v 方向不再有横缝 —— 每槽就是全高):2 张槽是 2 张独立纹理,
|
|
681
683
|
* 各自 mip + ClampToEdge ⇒ 仍可能有细亮/细暗线,这一条**与 09-29 那版一字未变**,
|
|
682
684
|
* `RT_STITCH_SEAM_MARGIN_PX` 管不到它(它治的是「同一张纹理内」的格子边界);
|
|
683
685
|
* whole 态是同一张贴图,天然无此缝。
|
|
684
|
-
* · **拼合槽内部**(每张
|
|
686
|
+
* · **拼合槽内部**(每张 6144×6144 里 3×3 片 ⇒ 2 道横缝 + 2 道竖缝;上一版 4096×6144 只有 2 横 0 竖):
|
|
685
687
|
* 靠「上传多切 `RT_STITCH_SEAM_MARGIN_PX` 像素 + 拼合只采中间那块」消掉,见该常量的说明。
|
|
686
688
|
*
|
|
687
689
|
* 旧数据 / 种子图没有 `tiles`(或只有 1 条)⇒ 两种形态都只能退回整图,`whole` 与 `tiled` 表现相同。
|
|
@@ -700,7 +702,7 @@ export const MISSING_TILE_GRAY = "#767676";
|
|
|
700
702
|
|
|
701
703
|
/** 接缝余量(**原图像素**,每片向四边各多切这么多;贴原图边那侧裁到剩多少算多少)。
|
|
702
704
|
*
|
|
703
|
-
* 10-
|
|
705
|
+
* 10-26 每槽装 3×3 = 9 格 ⇒ 一张槽内部的接缝是 **2 横 2 竖**(上一版每槽一竖列时只有 2 横 0 竖),这一条是随之而来的画质护栏:
|
|
704
706
|
* ① 上传时每片**向四边多切 M 像素**(`rtStitchTileRects`);
|
|
705
707
|
* ② 拼合时**只采中间那块**(`rtStitchTileInset` 算出的 UV 内缩窗口),多切的那一圈**不画进格子**。
|
|
706
708
|
*
|
|
@@ -715,12 +717,12 @@ export const RT_STITCH_SEAM_MARGIN_PX = 8;
|
|
|
715
717
|
|
|
716
718
|
/**
|
|
717
719
|
* 「一张图切成几张瓦片、瓦片怎么分组进拼合槽」的**网格描述** —— 皇上旨 2026-10-20「上传的这个图切成
|
|
718
|
-
* 4*4=16 片吧,加载的时候,也是一帧载入一片」⇒ 拼合器从此要同时服务两种网格(全景
|
|
720
|
+
* 4*4=16 片吧,加载的时候,也是一帧载入一片」⇒ 拼合器从此要同时服务两种网格(全景 6×3 / 图片平面那一份),
|
|
719
721
|
* 于是这一组参数必须能**当数据传**,⛔ 不许在拼合器里再写死一遍全景那套数字(那就是两份实现)。
|
|
720
722
|
* ⭐ 同一条参数化让 10-21 旨「平面切成 2*2=4 份,不用切成 16 份了」只需要改 `IMAGE_TILE_GRID` 一处。
|
|
721
723
|
*
|
|
722
724
|
* ⚠️ 只描述「格子怎么排」,不含槽纹理像素尺寸(那是 GL 资源,由拼合器按消费方给的 layout 建)。
|
|
723
|
-
* ⚠️ `slotCellCols` / `slotCellRows` = **每槽横几格 / 竖几格**(10-23
|
|
725
|
+
* ⚠️ `slotCellCols` / `slotCellRows` = **每槽横几格 / 竖几格**(10-23 起两个数独立,现行全景是每槽 3×3 = 原图左/右半幅;
|
|
724
726
|
* 旧的单一 `slotGrid`「横竖同数」假设已整条作废 ⇒ 格数 = `slotCellCols * slotCellRows`)。
|
|
725
727
|
*/
|
|
726
728
|
export interface TileGridLayout {
|
|
@@ -800,7 +802,7 @@ export function slotTilesOf(grid: TileGridLayout): number[][] {
|
|
|
800
802
|
/** 拼合器内部使用的完整布局 = 网格描述 + RT 像素尺寸(GL 资源参数,消费方建组时传入)。 */
|
|
801
803
|
export type TileLayout = TileGridLayout & { rtW: number; rtH: number };
|
|
802
804
|
|
|
803
|
-
/** 全景的标准布局(
|
|
805
|
+
/** 全景的标准布局(6×3 网格 / 2 槽 / 6144×6144 RT)—— 一槽 = 原图左/右半幅 */
|
|
804
806
|
export const PANO_TILE_LAYOUT: TileLayout = { ...PANO_TILE_GRID, rtW: RT_STITCH_SLOT_W, rtH: RT_STITCH_SLOT_H };
|
|
805
807
|
|
|
806
808
|
/**
|
|
@@ -816,7 +818,7 @@ export function imageTileLayout(natW: number, natH: number): TileLayout {
|
|
|
816
818
|
return { ...IMAGE_TILE_GRID, rtW, rtH };
|
|
817
819
|
}
|
|
818
820
|
|
|
819
|
-
/**
|
|
821
|
+
/** 18 瓦片批次的标识 = 第一条**非空**瓦片地址(同批上传 URL 前缀一致,足以唯一;
|
|
820
822
|
* 09-29 晚旨「不足张数缺的地方涂灰」后批次里允许有空串占位 ⇒ 取首个非空,防首张缺失时键为空串互撞) */
|
|
821
823
|
export function rtStitchBatchOf(tiles: string[]): string {
|
|
822
824
|
return tiles.find((u) => u) || "";
|
|
@@ -827,7 +829,7 @@ export function rtStitchKey(batch: string, slot: number): string {
|
|
|
827
829
|
return `${RT_STITCH_KEY_PREFIX}${batch}#${slot}`;
|
|
828
830
|
}
|
|
829
831
|
|
|
830
|
-
/** 是否拼合槽(`rt:<batch>#<slot>`)的虚拟键 —— 16 / 32 / 64 / 现行
|
|
832
|
+
/** 是否拼合槽(`rt:<batch>#<slot>`)的虚拟键 —— 16 / 32 / 64 / 9 片 / 现行 18 片**同一个前缀**,键本身不含片数信息 */
|
|
831
833
|
export function isRtStitchKey(key: string): boolean {
|
|
832
834
|
return key.startsWith(RT_STITCH_KEY_PREFIX);
|
|
833
835
|
}
|
|
@@ -841,16 +843,16 @@ export function rtStitchKeyParts(key: string): { batch: string; slot: number } |
|
|
|
841
843
|
return { batch: body.slice(0, i), slot: Number(body.slice(i + 1)) };
|
|
842
844
|
}
|
|
843
845
|
|
|
844
|
-
/** 槽 slot 用哪
|
|
845
|
-
* 槽 0 =
|
|
846
|
-
* ⭐ 只按 **u**
|
|
847
|
-
* ⚠️ 表是**从上面那几个网格常数生成**的(⛔ 手抄
|
|
846
|
+
/** 槽 slot 用哪 9 张瓦片(原图 **6 列 × 3 行 行优先**下标 `r*6+c`;**槽内格序 = 横 3 × 竖 3、图像左上起**):
|
|
847
|
+
* 槽 0 = **左半幅**(列 0-2)→ `[0,1,2, 6,7,8, 12,13,14]`;槽 1 = **右半幅**(列 3-5)→ `[3,4,5, 9,10,11, 15,16,17]`。
|
|
848
|
+
* ⭐ 只按 **u** 分槽(需求 2026-10-26「一个全景是 2 个 RT,每个 RT 上面 3×3=9」⇒ 一槽 = 原图半幅 = 横 180° × 全高 180°),
|
|
849
|
+
* ⚠️ 表是**从上面那几个网格常数生成**的(⛔ 手抄 18 个数):正确性由护栏 `test/mktmesh.test.js` BE 组
|
|
848
850
|
* 对着手写死的字面量逐个数断言 —— 改了网格常数而护栏没跟着改,当场红。 */
|
|
849
851
|
export const RT_STITCH_SLOT_TILES: ReadonlyArray<ReadonlyArray<number>> = slotTilesOf(PANO_TILE_GRID);
|
|
850
852
|
|
|
851
853
|
/** 槽内第 cell 格在**图像方向**的位置 `[列, 行]`(0 起,与 `RT_STITCH_SLOT_TILES` 的格序同一口径)。
|
|
852
854
|
* ⚠️ 这是「图像方向」(行 0 = 图像最上),GL 的 y 向上 ⇒ 画进 RT 时 y 要翻(拼合器里那句 `1 - (2*r+1)/n`)。
|
|
853
|
-
* `slotCellCols` 默认全景那档(
|
|
855
|
+
* `slotCellCols` 默认全景那档(3 = 一槽就是原图半幅的三列);图片平面传 2。 */
|
|
854
856
|
export function rtStitchCellPos(cell: number, slotCellCols: number = PANO_TILE_GRID.slotCellCols): [number, number] {
|
|
855
857
|
return [cell % slotCellCols, Math.floor(cell / slotCellCols)];
|
|
856
858
|
}
|
|
@@ -876,12 +878,12 @@ function gridLine(i: number, n: number, steps: number): number {
|
|
|
876
878
|
return Math.round((i * n) / steps);
|
|
877
879
|
}
|
|
878
880
|
|
|
879
|
-
/**
|
|
880
|
-
* ⭐ 10-
|
|
881
|
+
/** 18 片瓦片的切图矩形(行优先、图像左上起,下标 `i = r*6+c` 与 `RT_STITCH_SLOT_TILES` 同源;
|
|
882
|
+
* ⭐ 10-26 需求「横 6 × 竖 3 = 18」:`r` 走 `RT_STITCH_ROWS`(3)、`c` 走 `RT_STITCH_COLS`(6)。
|
|
881
883
|
*
|
|
882
884
|
* ⭐ **上传侧(`panoramaUpload`)与拼合侧(`PanoTileStitcher`)必须共用这一份几何**:
|
|
883
885
|
* 上传多切多少、拼合就内缩多少;两边各算一次就是「缝没治掉、反而把画面挤歪」。
|
|
884
|
-
* 拼合侧拿不到原图尺寸(它只有
|
|
886
|
+
* 拼合侧拿不到原图尺寸(它只有 18 个 URL 和 18 张已解码纹理),所以它用 `rtStitchTileInset`
|
|
885
887
|
* —— 那条只需「下标 + 该瓦片自己的像素尺寸」,两者等价的前提是「多切量 = 常数 M、只有贴边那几片被裁」,
|
|
886
888
|
* 而「贴不贴边」纯由下标决定 ⇒ 护栏 BE 组用同一批 (W,H) 把两条算路对表钉死。 */
|
|
887
889
|
export function rtStitchTileRects(w: number, h: number, grid: TileGridLayout = PANO_TILE_GRID): RtStitchTileRect[] {
|
|
@@ -902,7 +904,7 @@ export function rtStitchTileRects(w: number, h: number, grid: TileGridLayout = P
|
|
|
902
904
|
return out;
|
|
903
905
|
}
|
|
904
906
|
|
|
905
|
-
/** 这片瓦片有没有贴原图边界(
|
|
907
|
+
/** 这片瓦片有没有贴原图边界(6×3 ⇒ 18 片里外圈那 14 片至少贴一条边,只有中间那 4 片四边都不贴;贴边的**那一侧**多切量被裁成 0)。
|
|
906
908
|
* 拼合侧靠它 + 瓦片自己的像素尺寸还原内缩量 ⇒ 不必知道原图多大。
|
|
907
909
|
* ⚠️ 下标拆分必须按 **列数 COLS**(`i = r*cols+c`),⛔ 不是 ROWS。 */
|
|
908
910
|
export function rtStitchTileEdgeClip(
|
|
@@ -944,13 +946,13 @@ export function rtStitchTileInset(
|
|
|
944
946
|
}
|
|
945
947
|
|
|
946
948
|
/**
|
|
947
|
-
* 「这 `RT_STITCH_SLOT_COUNT`(
|
|
949
|
+
* 「这 `RT_STITCH_SLOT_COUNT`(2) 个纹理槽到底喂哪几个地址」。⚠️ **必须**与 `mktPanoTiledFlag` 同源、同一时机调用:
|
|
948
950
|
* 只改标记不改地址(或反之)= 用整图 UV 去采一张瓦片 / 用瓦片内 UV 去采整图,画面立刻错乱。
|
|
949
951
|
* 非 tiled 态(或该点压根没切过片)一律回退成「各槽同为一张整图」。
|
|
950
|
-
* ⭐ 2026-10-
|
|
951
|
-
* 不足 9
|
|
952
|
-
*
|
|
953
|
-
* ⚠️ 返回长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写
|
|
952
|
+
* ⭐ 2026-10-26 需求「只支持 18 张(6×3)」:只要有瓦片(1~18 条)一律走拼合器(rt: 虚拟键),
|
|
953
|
+
* 不足 18 张的位置由拼合器**涂灰**(一眼看出数据不全);3×3=9 张、8×4=32 张、8×8=64 张与 4×4=16 张那几版**都不做兼容**
|
|
954
|
+
* (「无须兼容正常使用,有异常我会重传」⇒ 旧存档的 9 / 32 / 64 条地址张数不符被判废、重传即可)。
|
|
955
|
+
* ⚠️ 返回长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写 2 个 rtStitchKey —— 槽数再变会漏改)。
|
|
954
956
|
*/
|
|
955
957
|
export function mktPanoTexUrls(url: string, tiles: string[] | undefined, mode: PanoTexMode = "tiled"): string[] {
|
|
956
958
|
if (mode === "tiled" && tiles && tiles.length > 0) {
|
|
@@ -961,8 +963,8 @@ export function mktPanoTexUrls(url: string, tiles: string[] | undefined, mode: P
|
|
|
961
963
|
}
|
|
962
964
|
|
|
963
965
|
/**
|
|
964
|
-
* 「这
|
|
965
|
-
* `tiles` 有 **任意 1~
|
|
966
|
+
* 「这 2 个纹理槽是不是真瓦片」的判据 —— **必须**与喂纹理处的回退式(`mktPanoTexUrls`)同源:
|
|
967
|
+
* `tiles` 有 **任意 1~18 条**地址(不足 18 = 数据不全,缺的格子拼合时涂灰)**且**当前形态选的是 tiled = 是瓦片;
|
|
966
968
|
* 否则各槽同为一张整图(旧数据 / 种子图 / 用户切整片)。
|
|
967
969
|
*/
|
|
968
970
|
export function mktPanoTiledFlag(tiles: string[] | undefined, mode: PanoTexMode = "tiled"): 0 | 1 {
|
|
@@ -1001,7 +1003,7 @@ export function createMktShaderMaterial(): ShaderMaterial {
|
|
|
1001
1003
|
// 一个全景点恒占 **3 个 sampler**(2026-10-23 旨「全景的 RT 由原来的 2 个分成 3 个」):
|
|
1002
1004
|
// panoramas[0..2] = 3 张**离屏拼合槽纹理**([左列, 中列, 右列],每槽由 `RT_STITCH_CELLS_PER_SLOT`
|
|
1003
1005
|
// 片瓦片竖着拼成),片元按全景 UV 的 **u** 三等分挑槽采样(v 恒全高)。
|
|
1004
|
-
// ⛔ 长度由 `RT_STITCH_SLOT_COUNT` 派生(不许手写
|
|
1006
|
+
// ⛔ 长度由 `RT_STITCH_SLOT_COUNT` 派生(不许手写 2 张)—— 槽数再变这里会漏改。
|
|
1005
1007
|
panoramas: {
|
|
1006
1008
|
value: Array.from({ length: RT_STITCH_SLOT_COUNT }, () => createPlaceholderTexture()),
|
|
1007
1009
|
},
|
package/MktMesh/modelSlot.d.ts
CHANGED
|
@@ -59,6 +59,11 @@ export declare class ModelSlot extends Group {
|
|
|
59
59
|
private _size;
|
|
60
60
|
private _surface;
|
|
61
61
|
private _naturalSize?;
|
|
62
|
+
/**
|
|
63
|
+
* **取回但还没挂进场景**的那棵 GLB(整层重建那一路:下载/解析不占渲染帧,挂载交给库的逐帧队列,
|
|
64
|
+
* 一帧只有一颗进 `_contentRoot`)。⛔ 攥在这里的东西不能没人认领 —— 换地址 / 删除都当场释放(见 `_detachModel`)。
|
|
65
|
+
*/
|
|
66
|
+
private _staged;
|
|
62
67
|
/** 载入令牌:换地址/删除后迟到的回调靠它作废(否则旧模型会挂进新槽) */
|
|
63
68
|
private _loadToken;
|
|
64
69
|
private _disposed;
|
|
@@ -85,8 +90,18 @@ export declare class ModelSlot extends Group {
|
|
|
85
90
|
/** 改落点面(地面/墙面):重摆占位方块的落位 + 重算高亮框(用户上传的模型位置不动,见 10-22 旨) */
|
|
86
91
|
setSurface(surface: "floor" | "wall"): void;
|
|
87
92
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
93
|
+
* 取回 GLB 并**备好待挂**(`url` 必须是可直接 fetch 的绝对地址;空串 = 退回占位小立方体,也返回 true)。
|
|
94
|
+
* ⛔ 这一步不碰场景图:挂载由 `mountStaged()` 做(整层重建时库把它交给 `threeUtils.addWork`,一帧一颗)。
|
|
95
|
+
* @returns 是否备好了东西(空地址 / 载入失败 / 这次结果已作废 ⇒ false;空地址那一档例外,见下)
|
|
96
|
+
*/
|
|
97
|
+
loadModel(url: string): Promise<boolean>;
|
|
98
|
+
/** 把备好待挂的那棵挂进内容根并重排。没有待挂的东西(空地址 / 取回失败 / 已经挂过)⇒ false */
|
|
99
|
+
mountStaged(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* 载入并挂上 GLB —— 「取回 + 当场挂好」的合成,一颗一颗来的那两路(上传 / 复制)用它就够:
|
|
102
|
+
* 一次动作只涉及一槽,本就已经是「一帧一颗」,⛔ 不必再排队。整层重建走 `MktMesh.setModels`
|
|
103
|
+
* (那一路是 `loadModel` + 逐帧 `mountStaged`)。
|
|
104
|
+
* @returns 是否真的挂上了(地址无效/载入失败/这次结果已作废 ⇒ false;空地址 = 退回占位,返回 true)
|
|
90
105
|
*/
|
|
91
106
|
setModelUrl(url: string): Promise<boolean>;
|
|
92
107
|
/**
|
|
@@ -107,7 +122,10 @@ export declare class ModelSlot extends Group {
|
|
|
107
122
|
private _ensureBox;
|
|
108
123
|
/** 把 `_boxFit` 写进框(尺寸 = 包围盒外扩 `BOX_FIT_SCALE`,中心 = 本槽局部系下的盒心) */
|
|
109
124
|
private _applyBoxFit;
|
|
110
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* 摘掉 GLB(几何/材质/贴图一起释放,否则换一只模型就漏一份显存)。
|
|
127
|
+
* ⚠️ 「备好待挂」那一棵也在这一刀里放掉:槽换了地址 / 被删除之后,排在后头的挂载帧⛔ 不许再把没人认领的东西挂上来。
|
|
128
|
+
*/
|
|
111
129
|
private _detachModel;
|
|
112
130
|
/**
|
|
113
131
|
* 内容排布的唯一出口:GLB 等比归一 + 占位方块显隐 + 高亮框贴合(⛔ 不再挪模型的位置,见 `_layoutNeutral`)。
|
package/MktMesh/modelSlot.js
CHANGED
|
@@ -100,6 +100,11 @@ export class ModelSlot extends Group {
|
|
|
100
100
|
_size = MODEL_SLOT_DEFAULT_SIZE;
|
|
101
101
|
_surface = "floor";
|
|
102
102
|
_naturalSize;
|
|
103
|
+
/**
|
|
104
|
+
* **取回但还没挂进场景**的那棵 GLB(整层重建那一路:下载/解析不占渲染帧,挂载交给库的逐帧队列,
|
|
105
|
+
* 一帧只有一颗进 `_contentRoot`)。⛔ 攥在这里的东西不能没人认领 —— 换地址 / 删除都当场释放(见 `_detachModel`)。
|
|
106
|
+
*/
|
|
107
|
+
_staged = null;
|
|
103
108
|
/** 载入令牌:换地址/删除后迟到的回调靠它作废(否则旧模型会挂进新槽) */
|
|
104
109
|
_loadToken = 0;
|
|
105
110
|
_disposed = false;
|
|
@@ -149,10 +154,11 @@ export class ModelSlot extends Group {
|
|
|
149
154
|
this._layout();
|
|
150
155
|
}
|
|
151
156
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
157
|
+
* 取回 GLB 并**备好待挂**(`url` 必须是可直接 fetch 的绝对地址;空串 = 退回占位小立方体,也返回 true)。
|
|
158
|
+
* ⛔ 这一步不碰场景图:挂载由 `mountStaged()` 做(整层重建时库把它交给 `threeUtils.addWork`,一帧一颗)。
|
|
159
|
+
* @returns 是否备好了东西(空地址 / 载入失败 / 这次结果已作废 ⇒ false;空地址那一档例外,见下)
|
|
154
160
|
*/
|
|
155
|
-
async
|
|
161
|
+
async loadModel(url) {
|
|
156
162
|
this._url = url;
|
|
157
163
|
const token = ++this._loadToken;
|
|
158
164
|
this._detachModel();
|
|
@@ -172,11 +178,33 @@ export class ModelSlot extends Group {
|
|
|
172
178
|
this._layout();
|
|
173
179
|
return false;
|
|
174
180
|
}
|
|
181
|
+
this._staged = obj;
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
/** 把备好待挂的那棵挂进内容根并重排。没有待挂的东西(空地址 / 取回失败 / 已经挂过)⇒ false */
|
|
185
|
+
mountStaged() {
|
|
186
|
+
const obj = this._staged;
|
|
187
|
+
if (!obj)
|
|
188
|
+
return false;
|
|
189
|
+
this._staged = null;
|
|
175
190
|
this._model = obj;
|
|
176
191
|
this._contentRoot.add(obj);
|
|
177
192
|
this._layout();
|
|
178
193
|
return true;
|
|
179
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* 载入并挂上 GLB —— 「取回 + 当场挂好」的合成,一颗一颗来的那两路(上传 / 复制)用它就够:
|
|
197
|
+
* 一次动作只涉及一槽,本就已经是「一帧一颗」,⛔ 不必再排队。整层重建走 `MktMesh.setModels`
|
|
198
|
+
* (那一路是 `loadModel` + 逐帧 `mountStaged`)。
|
|
199
|
+
* @returns 是否真的挂上了(地址无效/载入失败/这次结果已作废 ⇒ false;空地址 = 退回占位,返回 true)
|
|
200
|
+
*/
|
|
201
|
+
async setModelUrl(url) {
|
|
202
|
+
if (!(await this.loadModel(url)))
|
|
203
|
+
return false;
|
|
204
|
+
if (this._staged)
|
|
205
|
+
this.mountStaged();
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
180
208
|
/**
|
|
181
209
|
* 把存档里的数据写进来(位置 / 旋转 / 尺寸 / 落点面 / 地址)。
|
|
182
210
|
* ⚠️ 刻意**不**在这里发起 GLB 载入:载入是异步的,落地那一刻必须由调用方(编辑器导入 / 预览)
|
|
@@ -257,8 +285,15 @@ export class ModelSlot extends Group {
|
|
|
257
285
|
this._box.setSize(BOX_FIT_SIZE);
|
|
258
286
|
this._box.position.copy(this._boxFit.center);
|
|
259
287
|
}
|
|
260
|
-
/**
|
|
288
|
+
/**
|
|
289
|
+
* 摘掉 GLB(几何/材质/贴图一起释放,否则换一只模型就漏一份显存)。
|
|
290
|
+
* ⚠️ 「备好待挂」那一棵也在这一刀里放掉:槽换了地址 / 被删除之后,排在后头的挂载帧⛔ 不许再把没人认领的东西挂上来。
|
|
291
|
+
*/
|
|
261
292
|
_detachModel() {
|
|
293
|
+
if (this._staged) {
|
|
294
|
+
disposeSubtree(this._staged);
|
|
295
|
+
this._staged = null;
|
|
296
|
+
}
|
|
262
297
|
if (!this._model)
|
|
263
298
|
return;
|
|
264
299
|
this._contentRoot.remove(this._model);
|
package/MktMesh/modelSlot.ts
CHANGED
|
@@ -130,6 +130,11 @@ export class ModelSlot extends Group {
|
|
|
130
130
|
private _size = MODEL_SLOT_DEFAULT_SIZE;
|
|
131
131
|
private _surface: "floor" | "wall" = "floor";
|
|
132
132
|
private _naturalSize?: { x: number; y: number; z: number };
|
|
133
|
+
/**
|
|
134
|
+
* **取回但还没挂进场景**的那棵 GLB(整层重建那一路:下载/解析不占渲染帧,挂载交给库的逐帧队列,
|
|
135
|
+
* 一帧只有一颗进 `_contentRoot`)。⛔ 攥在这里的东西不能没人认领 —— 换地址 / 删除都当场释放(见 `_detachModel`)。
|
|
136
|
+
*/
|
|
137
|
+
private _staged: Object3D | null = null;
|
|
133
138
|
/** 载入令牌:换地址/删除后迟到的回调靠它作废(否则旧模型会挂进新槽) */
|
|
134
139
|
private _loadToken = 0;
|
|
135
140
|
private _disposed = false;
|
|
@@ -189,10 +194,11 @@ export class ModelSlot extends Group {
|
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
197
|
+
* 取回 GLB 并**备好待挂**(`url` 必须是可直接 fetch 的绝对地址;空串 = 退回占位小立方体,也返回 true)。
|
|
198
|
+
* ⛔ 这一步不碰场景图:挂载由 `mountStaged()` 做(整层重建时库把它交给 `threeUtils.addWork`,一帧一颗)。
|
|
199
|
+
* @returns 是否备好了东西(空地址 / 载入失败 / 这次结果已作废 ⇒ false;空地址那一档例外,见下)
|
|
194
200
|
*/
|
|
195
|
-
async
|
|
201
|
+
async loadModel(url: string): Promise<boolean> {
|
|
196
202
|
this._url = url;
|
|
197
203
|
const token = ++this._loadToken;
|
|
198
204
|
this._detachModel();
|
|
@@ -210,12 +216,33 @@ export class ModelSlot extends Group {
|
|
|
210
216
|
this._layout();
|
|
211
217
|
return false;
|
|
212
218
|
}
|
|
219
|
+
this._staged = obj;
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** 把备好待挂的那棵挂进内容根并重排。没有待挂的东西(空地址 / 取回失败 / 已经挂过)⇒ false */
|
|
224
|
+
mountStaged(): boolean {
|
|
225
|
+
const obj = this._staged;
|
|
226
|
+
if (!obj) return false;
|
|
227
|
+
this._staged = null;
|
|
213
228
|
this._model = obj;
|
|
214
229
|
this._contentRoot.add(obj);
|
|
215
230
|
this._layout();
|
|
216
231
|
return true;
|
|
217
232
|
}
|
|
218
233
|
|
|
234
|
+
/**
|
|
235
|
+
* 载入并挂上 GLB —— 「取回 + 当场挂好」的合成,一颗一颗来的那两路(上传 / 复制)用它就够:
|
|
236
|
+
* 一次动作只涉及一槽,本就已经是「一帧一颗」,⛔ 不必再排队。整层重建走 `MktMesh.setModels`
|
|
237
|
+
* (那一路是 `loadModel` + 逐帧 `mountStaged`)。
|
|
238
|
+
* @returns 是否真的挂上了(地址无效/载入失败/这次结果已作废 ⇒ false;空地址 = 退回占位,返回 true)
|
|
239
|
+
*/
|
|
240
|
+
async setModelUrl(url: string): Promise<boolean> {
|
|
241
|
+
if (!(await this.loadModel(url))) return false;
|
|
242
|
+
if (this._staged) this.mountStaged();
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
219
246
|
/**
|
|
220
247
|
* 把存档里的数据写进来(位置 / 旋转 / 尺寸 / 落点面 / 地址)。
|
|
221
248
|
* ⚠️ 刻意**不**在这里发起 GLB 载入:载入是异步的,落地那一刻必须由调用方(编辑器导入 / 预览)
|
|
@@ -299,8 +326,15 @@ export class ModelSlot extends Group {
|
|
|
299
326
|
this._box.position.copy(this._boxFit.center);
|
|
300
327
|
}
|
|
301
328
|
|
|
302
|
-
/**
|
|
329
|
+
/**
|
|
330
|
+
* 摘掉 GLB(几何/材质/贴图一起释放,否则换一只模型就漏一份显存)。
|
|
331
|
+
* ⚠️ 「备好待挂」那一棵也在这一刀里放掉:槽换了地址 / 被删除之后,排在后头的挂载帧⛔ 不许再把没人认领的东西挂上来。
|
|
332
|
+
*/
|
|
303
333
|
private _detachModel(): void {
|
|
334
|
+
if (this._staged) {
|
|
335
|
+
disposeSubtree(this._staged);
|
|
336
|
+
this._staged = null;
|
|
337
|
+
}
|
|
304
338
|
if (!this._model) return;
|
|
305
339
|
this._contentRoot.remove(this._model);
|
|
306
340
|
disposeSubtree(this._model);
|
|
@@ -11,8 +11,8 @@ export interface PanoRosterCand<T> {
|
|
|
11
11
|
* 算出下一帧的槽位名册(**不修改传进来的 roster**,返回新数组)。
|
|
12
12
|
* @param roster 当前名册(0 号槽 = 上一帧的中心),长度可以小于 maxSlots(内部按 undefined 补齐)
|
|
13
13
|
* @param dists 本帧全部全景点及其到**锚点**的距离(锚点 = 调用方那把尺子的端点;⭐ 10-07 第二十一道旨后 = 控制器中心点),**数组顺序 = 全景列表顺序**(下标就是旨意里说的「几号全景」)
|
|
14
|
-
* @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-
|
|
15
|
-
*
|
|
14
|
+
* @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-26 起是 **8**:8 点 × 每个全景
|
|
15
|
+
* 左/右 2 张 RT = 16 sampler,正好用满物理上限。⛔ 本函数不许假设它恒等于 4 或 8)
|
|
16
16
|
* @param margin 抢中心的死区(米,= `PANO_SLOT_SWAP_MARGIN_M`)
|
|
17
17
|
* @returns 长度恒 = maxSlots 的新名册:**0 号槽 = 中心**,其余槽 = 列表里按序号一层层往外扩的那些
|
|
18
18
|
* (同一层先取离锚点更近那侧)。⭐ 10-07 旨「尽可能填满」⇒ 越界的序号不占格、外扩继续往有货那侧走,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* ⇒ 收窄上面那句「如果没有目标全景…就留空」的适用边界:**留空只发生在「项目里的全景总数不够一格一人」时**;
|
|
14
14
|
* 序号越界的那个位置根本不是一张全景 ⇒ 跳过它、继续往还有货的那一侧扩,槽位不许白留给「不存在的序号」。
|
|
15
15
|
* 同一道旨还要求:新加的全景点**不必等中心换人**才进 shader ⇒ 中心没换人时在册的人照旧一格不动,空格当场补满。
|
|
16
|
-
* ⚠️ 上限仍是 `MKT_PANO_MAX_SLOTS`(=
|
|
16
|
+
* ⚠️ 上限仍是 `MKT_PANO_MAX_SLOTS`(=8):8 点 × 左右 2 张拼合槽 = 16 个 sampler,那是 GLSL 物理闸
|
|
17
17
|
* (皇上机器 maxTextures=16)⇒ 本函数不越那道闸,只是把那 5 格**尽量用满**。
|
|
18
18
|
* ⭐ 皇上旨 2026-10-07 第二十一道旨(verbatim):「判断使用那些全景球的判断条件, 从距离相机的距离 改成
|
|
19
19
|
* 判断距离控制器的中心点的距离来判断」⇒ **本函数的尺子另一端换了人**:函数体一个字不改(它只吃调用方算好的
|
|
@@ -66,8 +66,8 @@ export const PANO_SLOT_SWAP_MARGIN_M = 0.5;
|
|
|
66
66
|
* 算出下一帧的槽位名册(**不修改传进来的 roster**,返回新数组)。
|
|
67
67
|
* @param roster 当前名册(0 号槽 = 上一帧的中心),长度可以小于 maxSlots(内部按 undefined 补齐)
|
|
68
68
|
* @param dists 本帧全部全景点及其到**锚点**的距离(锚点 = 调用方那把尺子的端点;⭐ 10-07 第二十一道旨后 = 控制器中心点),**数组顺序 = 全景列表顺序**(下标就是旨意里说的「几号全景」)
|
|
69
|
-
* @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-
|
|
70
|
-
*
|
|
69
|
+
* @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-26 起是 **8**:8 点 × 每个全景
|
|
70
|
+
* 左/右 2 张 RT = 16 sampler,正好用满物理上限。⛔ 本函数不许假设它恒等于 4 或 8)
|
|
71
71
|
* @param margin 抢中心的死区(米,= `PANO_SLOT_SWAP_MARGIN_M`)
|
|
72
72
|
* @returns 长度恒 = maxSlots 的新名册:**0 号槽 = 中心**,其余槽 = 列表里按序号一层层往外扩的那些
|
|
73
73
|
* (同一层先取离锚点更近那侧)。⭐ 10-07 旨「尽可能填满」⇒ 越界的序号不占格、外扩继续往有货那侧走,
|
|
@@ -97,7 +97,7 @@ export function panoSlotRosterStep(roster, dists, maxSlots, margin) {
|
|
|
97
97
|
// 直到凑够 maxSlots。⭐ 2026-10-06 晚皇上旨「一次能够展示 8 个全景纹理」⇒ maxSlots 由 4 变 8,
|
|
98
98
|
// 此处从「只扩一层」改成「一层层扩下去」。扩的**顺序**沿用当日那句「更近那侧再往外一张」:
|
|
99
99
|
// k=2 那一轮先取更近那侧 ⇒ 前 4 张与 4 槽时代**逐字相同**(中心、-1、+1、更近那侧的 ±2),
|
|
100
|
-
//
|
|
100
|
+
// 多出来的几张继续 ±3、±4…
|
|
101
101
|
// ⭐ 2026-10-07 旨「尽可能得填满 8 个全景位置」⇒ 越界的那个**序号**直接跳过、**不占一格**,
|
|
102
102
|
// 外扩继续往还有货的那一侧走(旧写法把 `undefined` 也塞进清单去占槽 ⇒ 10 个点、中心贴列表首端时
|
|
103
103
|
// 只填 5 格,剩下 5 张全景明明存在却进不了 shader)。清单里只装真实存在的全景
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* ⇒ 收窄上面那句「如果没有目标全景…就留空」的适用边界:**留空只发生在「项目里的全景总数不够一格一人」时**;
|
|
14
14
|
* 序号越界的那个位置根本不是一张全景 ⇒ 跳过它、继续往还有货的那一侧扩,槽位不许白留给「不存在的序号」。
|
|
15
15
|
* 同一道旨还要求:新加的全景点**不必等中心换人**才进 shader ⇒ 中心没换人时在册的人照旧一格不动,空格当场补满。
|
|
16
|
-
* ⚠️ 上限仍是 `MKT_PANO_MAX_SLOTS`(=
|
|
16
|
+
* ⚠️ 上限仍是 `MKT_PANO_MAX_SLOTS`(=8):8 点 × 左右 2 张拼合槽 = 16 个 sampler,那是 GLSL 物理闸
|
|
17
17
|
* (皇上机器 maxTextures=16)⇒ 本函数不越那道闸,只是把那 5 格**尽量用满**。
|
|
18
18
|
* ⭐ 皇上旨 2026-10-07 第二十一道旨(verbatim):「判断使用那些全景球的判断条件, 从距离相机的距离 改成
|
|
19
19
|
* 判断距离控制器的中心点的距离来判断」⇒ **本函数的尺子另一端换了人**:函数体一个字不改(它只吃调用方算好的
|
|
@@ -74,8 +74,8 @@ export interface PanoRosterCand<T> {
|
|
|
74
74
|
* 算出下一帧的槽位名册(**不修改传进来的 roster**,返回新数组)。
|
|
75
75
|
* @param roster 当前名册(0 号槽 = 上一帧的中心),长度可以小于 maxSlots(内部按 undefined 补齐)
|
|
76
76
|
* @param dists 本帧全部全景点及其到**锚点**的距离(锚点 = 调用方那把尺子的端点;⭐ 10-07 第二十一道旨后 = 控制器中心点),**数组顺序 = 全景列表顺序**(下标就是旨意里说的「几号全景」)
|
|
77
|
-
* @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-
|
|
78
|
-
*
|
|
77
|
+
* @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-26 起是 **8**:8 点 × 每个全景
|
|
78
|
+
* 左/右 2 张 RT = 16 sampler,正好用满物理上限。⛔ 本函数不许假设它恒等于 4 或 8)
|
|
79
79
|
* @param margin 抢中心的死区(米,= `PANO_SLOT_SWAP_MARGIN_M`)
|
|
80
80
|
* @returns 长度恒 = maxSlots 的新名册:**0 号槽 = 中心**,其余槽 = 列表里按序号一层层往外扩的那些
|
|
81
81
|
* (同一层先取离锚点更近那侧)。⭐ 10-07 旨「尽可能填满」⇒ 越界的序号不占格、外扩继续往有货那侧走,
|
|
@@ -104,7 +104,7 @@ export function panoSlotRosterStep<T>(
|
|
|
104
104
|
// 直到凑够 maxSlots。⭐ 2026-10-06 晚皇上旨「一次能够展示 8 个全景纹理」⇒ maxSlots 由 4 变 8,
|
|
105
105
|
// 此处从「只扩一层」改成「一层层扩下去」。扩的**顺序**沿用当日那句「更近那侧再往外一张」:
|
|
106
106
|
// k=2 那一轮先取更近那侧 ⇒ 前 4 张与 4 槽时代**逐字相同**(中心、-1、+1、更近那侧的 ±2),
|
|
107
|
-
//
|
|
107
|
+
// 多出来的几张继续 ±3、±4…
|
|
108
108
|
// ⭐ 2026-10-07 旨「尽可能得填满 8 个全景位置」⇒ 越界的那个**序号**直接跳过、**不占一格**,
|
|
109
109
|
// 外扩继续往还有货的那一侧走(旧写法把 `undefined` 也塞进清单去占槽 ⇒ 10 个点、中心贴列表首端时
|
|
110
110
|
// 只填 5 格,剩下 5 张全景明明存在却进不了 shader)。清单里只装真实存在的全景
|