three-base 2.0.190 → 2.0.193
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/EventMap.d.ts +7 -0
- package/EventMap.ts +8 -0
- package/MeshBase.d.ts +23 -0
- package/MeshBase.js +47 -1
- package/MeshBase.ts +301 -257
- package/MktMesh/MktMesh.d.ts +110 -2
- package/MktMesh/MktMesh.js +246 -5
- package/MktMesh/MktMesh.ts +1617 -1368
- package/MktMesh/glassPane.d.ts +43 -0
- package/MktMesh/glassPane.js +97 -0
- package/MktMesh/glassPane.ts +116 -0
- package/MktMesh/imagePlane.d.ts +4 -0
- package/MktMesh/imagePlane.js +8 -0
- package/MktMesh/imagePlane.ts +7 -0
- package/MktMesh/modelSlot.d.ts +8 -0
- package/MktMesh/modelSlot.js +26 -2
- package/MktMesh/modelSlot.ts +26 -2
- package/MktMesh/panoBitmapTexture.d.ts +5 -2
- package/MktMesh/panoBitmapTexture.js +14 -4
- package/MktMesh/panoBitmapTexture.ts +17 -4
- package/MktMesh/panoTileStitcher.d.ts +9 -3
- package/MktMesh/panoTileStitcher.js +23 -2
- package/MktMesh/panoTileStitcher.ts +24 -4
- package/MktMesh/types.d.ts +6 -0
- package/MktMesh/types.ts +6 -0
- package/instanced/InstancedMeshBase.js +3 -0
- package/instanced/InstancedMeshBase.ts +3 -0
- package/package.json +6 -1
- package/threeLoader.js +2 -0
- package/threeLoader.ts +2 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Mesh, MeshPhysicalMaterial, PlaneGeometry, Texture } from "three";
|
|
2
|
+
/** 玻璃面的持久化数据(进 `MktMesh.exportData` 的 `glass` 键 / `importData` 还原) */
|
|
3
|
+
export interface GlassPaneData {
|
|
4
|
+
name?: string;
|
|
5
|
+
position: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
z: number;
|
|
9
|
+
};
|
|
10
|
+
rotation: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
z: number;
|
|
14
|
+
};
|
|
15
|
+
/** 正方形边长(米):宽 = 高 = 本值,⛔ 不拆成两档 */
|
|
16
|
+
size: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class GlassPane extends Mesh<PlaneGeometry, MeshPhysicalMaterial> {
|
|
19
|
+
private _originalColor;
|
|
20
|
+
/** 反射环境纹理的身份(由 `MktMesh` 注入,本类只持有引用、⛔ 从不 dispose 它 —— 那张按地址缓存在库里) */
|
|
21
|
+
private _envTex;
|
|
22
|
+
constructor(name?: string);
|
|
23
|
+
get paneSize(): number;
|
|
24
|
+
/** 换正方形边长(宽高等比跟着走:几何只有这一把尺) */
|
|
25
|
+
setPaneSize(size: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* 绑/摘反射环境纹理(`null` = 还没有可用的环境源)。
|
|
28
|
+
* ⚠️ 判等在前:`material.needsUpdate = true` 会涨材质版本 ⇒ 程序要重编译。库里给同一批玻璃面注入的是
|
|
29
|
+
* **同一个 Texture 对象**(反射源只下载一次),逐颗重复赋同一张时必须早退,不然就是 N 次无谓重编译。
|
|
30
|
+
* ⚠️ 传进来的纹理必须已标 `EquirectangularReflectionMapping`:three 的 `WebGLCubeUVMaps` 只认那个标记才会
|
|
31
|
+
* 自动把它卷积成立方 UV(PMREM);直接给一张普通 UV 纹理(例如全景拼合槽的那两张 RT)采出来是花屏。
|
|
32
|
+
*/
|
|
33
|
+
setEnvironmentTexture(tex: Texture | null): void;
|
|
34
|
+
highlight(): void;
|
|
35
|
+
restoreColor(): void;
|
|
36
|
+
toData(): GlassPaneData;
|
|
37
|
+
applyData(d: GlassPaneData): void;
|
|
38
|
+
/** 只释放自己的几何与材质;反射纹理归 `MktMesh` 管(多颗共用一张)。 */
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|
|
41
|
+
export declare const GLASS_PANE_DEFAULTS: {
|
|
42
|
+
size: number;
|
|
43
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Description: 正方形玻璃面 —— 2026-10-28 需求「mkteditor 还需要增加一个 正方的面的 功能, 要双面显示,
|
|
3
|
+
* 环境贴图就用第一个全景, 效果用 three.js 的 webgl_materials_physical_transmission 案例, 并且要写入 json 数据和回显」。
|
|
4
|
+
*
|
|
5
|
+
* 定位:与「图片平面」(`./imagePlane.ts`)同为挂在 `MktMesh` 子树下的独立 Mesh,但材质完全不同:
|
|
6
|
+
* - 图片平面 = MeshBasicMaterial + FrontSide + 自带内容瓦片(单面可见是 2026-10-22 单独点名的,⛔ 不适用于本类);
|
|
7
|
+
* - 玻璃面 = MeshPhysicalMaterial + DoubleSide + transmission(本次点名「要双面显示」⇒ 正反两面都画、都折射)。
|
|
8
|
+
* 它自己没有内容纹理:画面 = 背后的东西被折射 + 一层反射,反射来自 `MktMesh` 给的**第一个全景**(`setEnvironmentTexture`)。
|
|
9
|
+
*
|
|
10
|
+
* 材质参数逐字对齐官方示例 `webgl_materials_physical_transmission` 的默认档
|
|
11
|
+
* (transmission 1 / ior 1.5 / thickness 0.01 / roughness 0 / metalness 0 / specularIntensity 1 /
|
|
12
|
+
* specularColor 白 / envMapIntensity 1 / color 白 / opacity 1 / transparent true / side DoubleSide)。
|
|
13
|
+
* ⛔ 示例 GUI 里那颗 `transmissionResolutionScale` 在 three 0.167 的构建里不存在(属主渲染器且该版本无此属性),
|
|
14
|
+
* 写了会直接把类型检查打红 ⇒ 本文件与宿主都不设它,折射全屏缓冲用引擎默认分辨率。
|
|
15
|
+
* ⛔ 不设 `alphaMap`:示例把它挂在那颗圆环上(切面形状用),玻璃面就是一块方板,没有alpha 轮廓要描。
|
|
16
|
+
*
|
|
17
|
+
* 形状:正方形 ⇒ 存档只有**一个**边长 `size`(米),宽恒等于高。几何仍是 PlaneGeometry(size, size),
|
|
18
|
+
* 原点在板中心,落地/贴墙那两种落点由调用者摆(与图片平面同一条口径,见编辑器 `addGlassPaneAt`)。
|
|
19
|
+
*/
|
|
20
|
+
import { Color, DoubleSide, Mesh, MeshPhysicalMaterial, PlaneGeometry } from "three";
|
|
21
|
+
const DEFAULT_SIZE = 1.5;
|
|
22
|
+
/** 选中高亮色:与图片平面/模型槽同一档(同族观感一致,只换颜色不换形态) */
|
|
23
|
+
const HIGHLIGHT_COLOR = "#ffd24a";
|
|
24
|
+
export class GlassPane extends Mesh {
|
|
25
|
+
_originalColor = "#ffffff";
|
|
26
|
+
/** 反射环境纹理的身份(由 `MktMesh` 注入,本类只持有引用、⛔ 从不 dispose 它 —— 那张按地址缓存在库里) */
|
|
27
|
+
_envTex = null;
|
|
28
|
+
constructor(name) {
|
|
29
|
+
const geo = new PlaneGeometry(DEFAULT_SIZE, DEFAULT_SIZE);
|
|
30
|
+
const mat = new MeshPhysicalMaterial({
|
|
31
|
+
color: new Color("#ffffff"),
|
|
32
|
+
// 双面(本次点名「要双面显示」):正面反面都画,也都能被射线打中
|
|
33
|
+
//(three 的射线拾取按材质 side 剔背面,见 `node_modules/three/src/objects/Mesh.js`)。
|
|
34
|
+
side: DoubleSide,
|
|
35
|
+
transparent: true,
|
|
36
|
+
opacity: 1,
|
|
37
|
+
// ↓ 以下六档 + 上面两档 = 官方示例的默认 GUI 值,一字未改
|
|
38
|
+
transmission: 1,
|
|
39
|
+
ior: 1.5,
|
|
40
|
+
thickness: 0.01,
|
|
41
|
+
roughness: 0,
|
|
42
|
+
metalness: 0,
|
|
43
|
+
specularIntensity: 1,
|
|
44
|
+
specularColor: new Color("#ffffff"),
|
|
45
|
+
envMapIntensity: 1,
|
|
46
|
+
});
|
|
47
|
+
super(geo, mat);
|
|
48
|
+
this.name = name || "玻璃面";
|
|
49
|
+
}
|
|
50
|
+
get paneSize() { return this.geometry.parameters.width; }
|
|
51
|
+
/** 换正方形边长(宽高等比跟着走:几何只有这一把尺) */
|
|
52
|
+
setPaneSize(size) {
|
|
53
|
+
this.geometry.dispose();
|
|
54
|
+
this.geometry = new PlaneGeometry(size, size);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 绑/摘反射环境纹理(`null` = 还没有可用的环境源)。
|
|
58
|
+
* ⚠️ 判等在前:`material.needsUpdate = true` 会涨材质版本 ⇒ 程序要重编译。库里给同一批玻璃面注入的是
|
|
59
|
+
* **同一个 Texture 对象**(反射源只下载一次),逐颗重复赋同一张时必须早退,不然就是 N 次无谓重编译。
|
|
60
|
+
* ⚠️ 传进来的纹理必须已标 `EquirectangularReflectionMapping`:three 的 `WebGLCubeUVMaps` 只认那个标记才会
|
|
61
|
+
* 自动把它卷积成立方 UV(PMREM);直接给一张普通 UV 纹理(例如全景拼合槽的那两张 RT)采出来是花屏。
|
|
62
|
+
*/
|
|
63
|
+
setEnvironmentTexture(tex) {
|
|
64
|
+
if (this._envTex === tex)
|
|
65
|
+
return;
|
|
66
|
+
this._envTex = tex;
|
|
67
|
+
this.material.envMap = tex;
|
|
68
|
+
this.material.needsUpdate = true;
|
|
69
|
+
}
|
|
70
|
+
highlight() {
|
|
71
|
+
this.material.color.set(HIGHLIGHT_COLOR);
|
|
72
|
+
}
|
|
73
|
+
restoreColor() {
|
|
74
|
+
this.material.color.set(this._originalColor);
|
|
75
|
+
}
|
|
76
|
+
toData() {
|
|
77
|
+
return {
|
|
78
|
+
name: this.name || undefined,
|
|
79
|
+
position: { x: this.position.x, y: this.position.y, z: this.position.z },
|
|
80
|
+
rotation: { x: this.rotation.x, y: this.rotation.y, z: this.rotation.z },
|
|
81
|
+
size: this.paneSize,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
applyData(d) {
|
|
85
|
+
if (d.name)
|
|
86
|
+
this.name = d.name;
|
|
87
|
+
this.position.set(d.position.x, d.position.y, d.position.z);
|
|
88
|
+
this.rotation.set(d.rotation.x, d.rotation.y, d.rotation.z);
|
|
89
|
+
this.setPaneSize(d.size);
|
|
90
|
+
}
|
|
91
|
+
/** 只释放自己的几何与材质;反射纹理归 `MktMesh` 管(多颗共用一张)。 */
|
|
92
|
+
dispose() {
|
|
93
|
+
this.geometry.dispose();
|
|
94
|
+
this.material.dispose();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export const GLASS_PANE_DEFAULTS = { size: DEFAULT_SIZE };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Description: 正方形玻璃面 —— 2026-10-28 需求「mkteditor 还需要增加一个 正方的面的 功能, 要双面显示,
|
|
3
|
+
* 环境贴图就用第一个全景, 效果用 three.js 的 webgl_materials_physical_transmission 案例, 并且要写入 json 数据和回显」。
|
|
4
|
+
*
|
|
5
|
+
* 定位:与「图片平面」(`./imagePlane.ts`)同为挂在 `MktMesh` 子树下的独立 Mesh,但材质完全不同:
|
|
6
|
+
* - 图片平面 = MeshBasicMaterial + FrontSide + 自带内容瓦片(单面可见是 2026-10-22 单独点名的,⛔ 不适用于本类);
|
|
7
|
+
* - 玻璃面 = MeshPhysicalMaterial + DoubleSide + transmission(本次点名「要双面显示」⇒ 正反两面都画、都折射)。
|
|
8
|
+
* 它自己没有内容纹理:画面 = 背后的东西被折射 + 一层反射,反射来自 `MktMesh` 给的**第一个全景**(`setEnvironmentTexture`)。
|
|
9
|
+
*
|
|
10
|
+
* 材质参数逐字对齐官方示例 `webgl_materials_physical_transmission` 的默认档
|
|
11
|
+
* (transmission 1 / ior 1.5 / thickness 0.01 / roughness 0 / metalness 0 / specularIntensity 1 /
|
|
12
|
+
* specularColor 白 / envMapIntensity 1 / color 白 / opacity 1 / transparent true / side DoubleSide)。
|
|
13
|
+
* ⛔ 示例 GUI 里那颗 `transmissionResolutionScale` 在 three 0.167 的构建里不存在(属主渲染器且该版本无此属性),
|
|
14
|
+
* 写了会直接把类型检查打红 ⇒ 本文件与宿主都不设它,折射全屏缓冲用引擎默认分辨率。
|
|
15
|
+
* ⛔ 不设 `alphaMap`:示例把它挂在那颗圆环上(切面形状用),玻璃面就是一块方板,没有alpha 轮廓要描。
|
|
16
|
+
*
|
|
17
|
+
* 形状:正方形 ⇒ 存档只有**一个**边长 `size`(米),宽恒等于高。几何仍是 PlaneGeometry(size, size),
|
|
18
|
+
* 原点在板中心,落地/贴墙那两种落点由调用者摆(与图片平面同一条口径,见编辑器 `addGlassPaneAt`)。
|
|
19
|
+
*/
|
|
20
|
+
import { Color, DoubleSide, Mesh, MeshPhysicalMaterial, PlaneGeometry, Texture } from "three";
|
|
21
|
+
|
|
22
|
+
/** 玻璃面的持久化数据(进 `MktMesh.exportData` 的 `glass` 键 / `importData` 还原) */
|
|
23
|
+
export interface GlassPaneData {
|
|
24
|
+
name?: string;
|
|
25
|
+
position: { x: number; y: number; z: number };
|
|
26
|
+
rotation: { x: number; y: number; z: number };
|
|
27
|
+
/** 正方形边长(米):宽 = 高 = 本值,⛔ 不拆成两档 */
|
|
28
|
+
size: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const DEFAULT_SIZE = 1.5;
|
|
32
|
+
/** 选中高亮色:与图片平面/模型槽同一档(同族观感一致,只换颜色不换形态) */
|
|
33
|
+
const HIGHLIGHT_COLOR = "#ffd24a";
|
|
34
|
+
|
|
35
|
+
export class GlassPane extends Mesh<PlaneGeometry, MeshPhysicalMaterial> {
|
|
36
|
+
private _originalColor: string = "#ffffff";
|
|
37
|
+
/** 反射环境纹理的身份(由 `MktMesh` 注入,本类只持有引用、⛔ 从不 dispose 它 —— 那张按地址缓存在库里) */
|
|
38
|
+
private _envTex: Texture | null = null;
|
|
39
|
+
|
|
40
|
+
constructor(name?: string) {
|
|
41
|
+
const geo = new PlaneGeometry(DEFAULT_SIZE, DEFAULT_SIZE);
|
|
42
|
+
const mat = new MeshPhysicalMaterial({
|
|
43
|
+
color: new Color("#ffffff"),
|
|
44
|
+
// 双面(本次点名「要双面显示」):正面反面都画,也都能被射线打中
|
|
45
|
+
//(three 的射线拾取按材质 side 剔背面,见 `node_modules/three/src/objects/Mesh.js`)。
|
|
46
|
+
side: DoubleSide,
|
|
47
|
+
transparent: true,
|
|
48
|
+
opacity: 1,
|
|
49
|
+
// ↓ 以下六档 + 上面两档 = 官方示例的默认 GUI 值,一字未改
|
|
50
|
+
transmission: 1,
|
|
51
|
+
ior: 1.5,
|
|
52
|
+
thickness: 0.01,
|
|
53
|
+
roughness: 0,
|
|
54
|
+
metalness: 0,
|
|
55
|
+
specularIntensity: 1,
|
|
56
|
+
specularColor: new Color("#ffffff"),
|
|
57
|
+
envMapIntensity: 1,
|
|
58
|
+
});
|
|
59
|
+
super(geo, mat);
|
|
60
|
+
this.name = name || "玻璃面";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get paneSize(): number { return (this.geometry as PlaneGeometry).parameters.width; }
|
|
64
|
+
|
|
65
|
+
/** 换正方形边长(宽高等比跟着走:几何只有这一把尺) */
|
|
66
|
+
setPaneSize(size: number): void {
|
|
67
|
+
this.geometry.dispose();
|
|
68
|
+
this.geometry = new PlaneGeometry(size, size);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 绑/摘反射环境纹理(`null` = 还没有可用的环境源)。
|
|
73
|
+
* ⚠️ 判等在前:`material.needsUpdate = true` 会涨材质版本 ⇒ 程序要重编译。库里给同一批玻璃面注入的是
|
|
74
|
+
* **同一个 Texture 对象**(反射源只下载一次),逐颗重复赋同一张时必须早退,不然就是 N 次无谓重编译。
|
|
75
|
+
* ⚠️ 传进来的纹理必须已标 `EquirectangularReflectionMapping`:three 的 `WebGLCubeUVMaps` 只认那个标记才会
|
|
76
|
+
* 自动把它卷积成立方 UV(PMREM);直接给一张普通 UV 纹理(例如全景拼合槽的那两张 RT)采出来是花屏。
|
|
77
|
+
*/
|
|
78
|
+
setEnvironmentTexture(tex: Texture | null): void {
|
|
79
|
+
if (this._envTex === tex) return;
|
|
80
|
+
this._envTex = tex;
|
|
81
|
+
this.material.envMap = tex;
|
|
82
|
+
this.material.needsUpdate = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
highlight(): void {
|
|
86
|
+
this.material.color.set(HIGHLIGHT_COLOR);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
restoreColor(): void {
|
|
90
|
+
this.material.color.set(this._originalColor);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
toData(): GlassPaneData {
|
|
94
|
+
return {
|
|
95
|
+
name: this.name || undefined,
|
|
96
|
+
position: { x: this.position.x, y: this.position.y, z: this.position.z },
|
|
97
|
+
rotation: { x: this.rotation.x, y: this.rotation.y, z: this.rotation.z },
|
|
98
|
+
size: this.paneSize,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
applyData(d: GlassPaneData): void {
|
|
103
|
+
if (d.name) this.name = d.name;
|
|
104
|
+
this.position.set(d.position.x, d.position.y, d.position.z);
|
|
105
|
+
this.rotation.set(d.rotation.x, d.rotation.y, d.rotation.z);
|
|
106
|
+
this.setPaneSize(d.size);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 只释放自己的几何与材质;反射纹理归 `MktMesh` 管(多颗共用一张)。 */
|
|
110
|
+
dispose(): void {
|
|
111
|
+
this.geometry.dispose();
|
|
112
|
+
this.material.dispose();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const GLASS_PANE_DEFAULTS = { size: DEFAULT_SIZE };
|
package/MktMesh/imagePlane.d.ts
CHANGED
|
@@ -37,9 +37,13 @@ export declare class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
37
37
|
private _uInset;
|
|
38
38
|
private _uValid;
|
|
39
39
|
private _data;
|
|
40
|
+
/** 内容是否已落定:4 格里至少有一格真的绑上了纹理。**只朝 true 翻一次**(之后逐张显影继续,但不再回退成「没内容」)。 */
|
|
41
|
+
private _loaded;
|
|
40
42
|
constructor(name?: string);
|
|
41
43
|
get imageWidth(): number;
|
|
42
44
|
get imageHeight(): number;
|
|
45
|
+
/** 这一颗的内容落定没有 —— 库的逐帧点亮闸只认这一条(`MktMesh.tickProgressiveReveal`)。 */
|
|
46
|
+
get loaded(): boolean;
|
|
43
47
|
setPlaneSize(w: number, h: number): void;
|
|
44
48
|
setPlaneData(d: Pick<ImagePlaneData, "url" | "tiles" | "naturalW" | "naturalH">): void;
|
|
45
49
|
get tiles(): string[];
|
package/MktMesh/imagePlane.js
CHANGED
|
@@ -70,6 +70,8 @@ export class ImagePlane extends Mesh {
|
|
|
70
70
|
url: "",
|
|
71
71
|
tiles: [],
|
|
72
72
|
};
|
|
73
|
+
/** 内容是否已落定:4 格里至少有一格真的绑上了纹理。**只朝 true 翻一次**(之后逐张显影继续,但不再回退成「没内容」)。 */
|
|
74
|
+
_loaded = false;
|
|
73
75
|
constructor(name) {
|
|
74
76
|
const geo = new PlaneGeometry(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
75
77
|
const mat = new MeshBasicMaterial({
|
|
@@ -108,6 +110,8 @@ export class ImagePlane extends Mesh {
|
|
|
108
110
|
}
|
|
109
111
|
get imageWidth() { return this.geometry.parameters.width; }
|
|
110
112
|
get imageHeight() { return this.geometry.parameters.height; }
|
|
113
|
+
/** 这一颗的内容落定没有 —— 库的逐帧点亮闸只认这一条(`MktMesh.tickProgressiveReveal`)。 */
|
|
114
|
+
get loaded() { return this._loaded; }
|
|
111
115
|
setPlaneSize(w, h) {
|
|
112
116
|
this.geometry.dispose();
|
|
113
117
|
this.geometry = new PlaneGeometry(w, h);
|
|
@@ -127,6 +131,10 @@ export class ImagePlane extends Mesh {
|
|
|
127
131
|
if (insets[i])
|
|
128
132
|
this._uInset.value[i].copy(insets[i]);
|
|
129
133
|
this._uValid.value[i] = t ? 1 : 0;
|
|
134
|
+
// 第一格就绪 = 这一颗有东西可画了 ⇒ 落定(点亮闸认的就是这一条,⛔ 不是「4 格全到齐」:
|
|
135
|
+
// 「逐张就绪逐张显影」是本文件 10-22 晚定下的口径,等到全到齐会让先到的那几格白等几帧)。
|
|
136
|
+
if (t)
|
|
137
|
+
this._loaded = true;
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
/** 只读「此刻材质里绑了什么」的戳记:4 格就绪位 + 已绑纹理的版本号。
|
package/MktMesh/imagePlane.ts
CHANGED
|
@@ -102,6 +102,8 @@ export class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
102
102
|
url: "",
|
|
103
103
|
tiles: [],
|
|
104
104
|
};
|
|
105
|
+
/** 内容是否已落定:4 格里至少有一格真的绑上了纹理。**只朝 true 翻一次**(之后逐张显影继续,但不再回退成「没内容」)。 */
|
|
106
|
+
private _loaded = false;
|
|
105
107
|
|
|
106
108
|
constructor(name?: string) {
|
|
107
109
|
const geo = new PlaneGeometry(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
@@ -145,6 +147,8 @@ export class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
145
147
|
|
|
146
148
|
get imageWidth(): number { return (this.geometry as PlaneGeometry).parameters.width; }
|
|
147
149
|
get imageHeight(): number { return (this.geometry as PlaneGeometry).parameters.height; }
|
|
150
|
+
/** 这一颗的内容落定没有 —— 库的逐帧点亮闸只认这一条(`MktMesh.tickProgressiveReveal`)。 */
|
|
151
|
+
get loaded(): boolean { return this._loaded; }
|
|
148
152
|
|
|
149
153
|
setPlaneSize(w: number, h: number): void {
|
|
150
154
|
this.geometry.dispose();
|
|
@@ -167,6 +171,9 @@ export class ImagePlane extends Mesh<PlaneGeometry, MeshBasicMaterial> {
|
|
|
167
171
|
this._uTiles.value[i] = t || PLACEHOLDER_TEX;
|
|
168
172
|
if (insets[i]) this._uInset.value[i].copy(insets[i]);
|
|
169
173
|
this._uValid.value[i] = t ? 1 : 0;
|
|
174
|
+
// 第一格就绪 = 这一颗有东西可画了 ⇒ 落定(点亮闸认的就是这一条,⛔ 不是「4 格全到齐」:
|
|
175
|
+
// 「逐张就绪逐张显影」是本文件 10-22 晚定下的口径,等到全到齐会让先到的那几格白等几帧)。
|
|
176
|
+
if (t) this._loaded = true;
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
179
|
|
package/MktMesh/modelSlot.d.ts
CHANGED
|
@@ -66,6 +66,12 @@ export declare class ModelSlot extends Group {
|
|
|
66
66
|
private _staged;
|
|
67
67
|
/** 载入令牌:换地址/删除后迟到的回调靠它作废(否则旧模型会挂进新槽) */
|
|
68
68
|
private _loadToken;
|
|
69
|
+
/**
|
|
70
|
+
* 内容是否已落定:真模型挂进了 `_contentRoot`,或这一颗压根没在等什么(空地址 / 取回失败 ⇒ 停在占位小立方体)。
|
|
71
|
+
* ⚠️ 落定**不等于**已经画进 GPU —— 顶点缓冲与首次 program 编译发生在「亮出来的那一帧」,
|
|
72
|
+
* 所以逐帧限流必须闸在点亮这一下(`MktMesh.tickProgressiveReveal`),闸在挂载那一下拦不住 GPU 侧的账。
|
|
73
|
+
*/
|
|
74
|
+
private _loaded;
|
|
69
75
|
private _disposed;
|
|
70
76
|
constructor(name?: string);
|
|
71
77
|
get size(): number;
|
|
@@ -78,6 +84,8 @@ export declare class ModelSlot extends Group {
|
|
|
78
84
|
} | undefined;
|
|
79
85
|
/** 是否已经挂上真模型(false = 仍是占位小立方体) */
|
|
80
86
|
get hasModel(): boolean;
|
|
87
|
+
/** 这一颗的内容落定没有 —— 库的逐帧点亮闸只认这一条(`MktMesh.tickProgressiveReveal`)。 */
|
|
88
|
+
get loaded(): boolean;
|
|
81
89
|
/** 改尺寸档(米,最长边):立刻重缩内容 */
|
|
82
90
|
setSize(size: number): void;
|
|
83
91
|
/**
|
package/MktMesh/modelSlot.js
CHANGED
|
@@ -107,6 +107,12 @@ export class ModelSlot extends Group {
|
|
|
107
107
|
_staged = null;
|
|
108
108
|
/** 载入令牌:换地址/删除后迟到的回调靠它作废(否则旧模型会挂进新槽) */
|
|
109
109
|
_loadToken = 0;
|
|
110
|
+
/**
|
|
111
|
+
* 内容是否已落定:真模型挂进了 `_contentRoot`,或这一颗压根没在等什么(空地址 / 取回失败 ⇒ 停在占位小立方体)。
|
|
112
|
+
* ⚠️ 落定**不等于**已经画进 GPU —— 顶点缓冲与首次 program 编译发生在「亮出来的那一帧」,
|
|
113
|
+
* 所以逐帧限流必须闸在点亮这一下(`MktMesh.tickProgressiveReveal`),闸在挂载那一下拦不住 GPU 侧的账。
|
|
114
|
+
*/
|
|
115
|
+
_loaded = false;
|
|
110
116
|
_disposed = false;
|
|
111
117
|
constructor(name) {
|
|
112
118
|
super();
|
|
@@ -126,6 +132,8 @@ export class ModelSlot extends Group {
|
|
|
126
132
|
get naturalSize() { return this._naturalSize; }
|
|
127
133
|
/** 是否已经挂上真模型(false = 仍是占位小立方体) */
|
|
128
134
|
get hasModel() { return this._model !== null; }
|
|
135
|
+
/** 这一颗的内容落定没有 —— 库的逐帧点亮闸只认这一条(`MktMesh.tickProgressiveReveal`)。 */
|
|
136
|
+
get loaded() { return this._loaded; }
|
|
129
137
|
/** 改尺寸档(米,最长边):立刻重缩内容 */
|
|
130
138
|
setSize(size) {
|
|
131
139
|
this._size = size > 0 ? size : this._size;
|
|
@@ -161,13 +169,25 @@ export class ModelSlot extends Group {
|
|
|
161
169
|
async loadModel(url) {
|
|
162
170
|
this._url = url;
|
|
163
171
|
const token = ++this._loadToken;
|
|
172
|
+
this._loaded = false;
|
|
164
173
|
this._detachModel();
|
|
165
174
|
// 先立刻退回占位小立方体:GLB 在路上那段窗口(慢网/大模型)⛔ 不许让这一槽凭空消失
|
|
166
175
|
// (取回失败那条支路本来也是停在占位上,两段时间里画面一致)
|
|
167
176
|
this._layout();
|
|
168
|
-
if (!url)
|
|
177
|
+
if (!url) {
|
|
178
|
+
// 空地址 = 这一颗压根没在等什么 ⇒ 当场落定(占位小立方体就是它的最终内容)
|
|
179
|
+
this._loaded = true;
|
|
169
180
|
return true;
|
|
170
|
-
|
|
181
|
+
}
|
|
182
|
+
// 取回失败(404 / 解析报错都从这一档走):`threeLoader.loadModel` 是会 reject 的,⚠️ 必须当「落定在占位方块」处理
|
|
183
|
+
// —— 整层重建时这一颗是压着不亮的,不落定就永远隐形(旧写法里它至少还露得出占位方块)。
|
|
184
|
+
let obj;
|
|
185
|
+
try {
|
|
186
|
+
obj = await threeLoader.loadModel(url);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
obj = undefined;
|
|
190
|
+
}
|
|
171
191
|
// 迟到回调:这一槽已经改指向别的地址、或已经删除 ⇒ 当场释放,别留一份没人认领的显存
|
|
172
192
|
if (this._disposed || token !== this._loadToken) {
|
|
173
193
|
if (obj)
|
|
@@ -175,6 +195,7 @@ export class ModelSlot extends Group {
|
|
|
175
195
|
return false;
|
|
176
196
|
}
|
|
177
197
|
if (!obj) {
|
|
198
|
+
this._loaded = true;
|
|
178
199
|
this._layout();
|
|
179
200
|
return false;
|
|
180
201
|
}
|
|
@@ -189,6 +210,7 @@ export class ModelSlot extends Group {
|
|
|
189
210
|
this._staged = null;
|
|
190
211
|
this._model = obj;
|
|
191
212
|
this._contentRoot.add(obj);
|
|
213
|
+
this._loaded = true;
|
|
192
214
|
this._layout();
|
|
193
215
|
return true;
|
|
194
216
|
}
|
|
@@ -220,6 +242,8 @@ export class ModelSlot extends Group {
|
|
|
220
242
|
this._size = d.size > 0 ? d.size : MODEL_SLOT_DEFAULT_SIZE;
|
|
221
243
|
this._naturalSize = d.naturalSize;
|
|
222
244
|
this._url = d.url || "";
|
|
245
|
+
// 落定判据跟着地址走:空地址 = 没在等什么(占位方块就是最终内容);有地址 = 等 GLB 那一拍
|
|
246
|
+
this._loaded = !this._url;
|
|
223
247
|
if (!this._url)
|
|
224
248
|
this._detachModel();
|
|
225
249
|
this._layout();
|
package/MktMesh/modelSlot.ts
CHANGED
|
@@ -137,6 +137,12 @@ export class ModelSlot extends Group {
|
|
|
137
137
|
private _staged: Object3D | null = null;
|
|
138
138
|
/** 载入令牌:换地址/删除后迟到的回调靠它作废(否则旧模型会挂进新槽) */
|
|
139
139
|
private _loadToken = 0;
|
|
140
|
+
/**
|
|
141
|
+
* 内容是否已落定:真模型挂进了 `_contentRoot`,或这一颗压根没在等什么(空地址 / 取回失败 ⇒ 停在占位小立方体)。
|
|
142
|
+
* ⚠️ 落定**不等于**已经画进 GPU —— 顶点缓冲与首次 program 编译发生在「亮出来的那一帧」,
|
|
143
|
+
* 所以逐帧限流必须闸在点亮这一下(`MktMesh.tickProgressiveReveal`),闸在挂载那一下拦不住 GPU 侧的账。
|
|
144
|
+
*/
|
|
145
|
+
private _loaded = false;
|
|
140
146
|
private _disposed = false;
|
|
141
147
|
|
|
142
148
|
constructor(name?: string) {
|
|
@@ -164,6 +170,8 @@ export class ModelSlot extends Group {
|
|
|
164
170
|
get naturalSize(): { x: number; y: number; z: number } | undefined { return this._naturalSize; }
|
|
165
171
|
/** 是否已经挂上真模型(false = 仍是占位小立方体) */
|
|
166
172
|
get hasModel(): boolean { return this._model !== null; }
|
|
173
|
+
/** 这一颗的内容落定没有 —— 库的逐帧点亮闸只认这一条(`MktMesh.tickProgressiveReveal`)。 */
|
|
174
|
+
get loaded(): boolean { return this._loaded; }
|
|
167
175
|
|
|
168
176
|
/** 改尺寸档(米,最长边):立刻重缩内容 */
|
|
169
177
|
setSize(size: number): void {
|
|
@@ -201,18 +209,31 @@ export class ModelSlot extends Group {
|
|
|
201
209
|
async loadModel(url: string): Promise<boolean> {
|
|
202
210
|
this._url = url;
|
|
203
211
|
const token = ++this._loadToken;
|
|
212
|
+
this._loaded = false;
|
|
204
213
|
this._detachModel();
|
|
205
214
|
// 先立刻退回占位小立方体:GLB 在路上那段窗口(慢网/大模型)⛔ 不许让这一槽凭空消失
|
|
206
215
|
// (取回失败那条支路本来也是停在占位上,两段时间里画面一致)
|
|
207
216
|
this._layout();
|
|
208
|
-
if (!url)
|
|
209
|
-
|
|
217
|
+
if (!url) {
|
|
218
|
+
// 空地址 = 这一颗压根没在等什么 ⇒ 当场落定(占位小立方体就是它的最终内容)
|
|
219
|
+
this._loaded = true;
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
// 取回失败(404 / 解析报错都从这一档走):`threeLoader.loadModel` 是会 reject 的,⚠️ 必须当「落定在占位方块」处理
|
|
223
|
+
// —— 整层重建时这一颗是压着不亮的,不落定就永远隐形(旧写法里它至少还露得出占位方块)。
|
|
224
|
+
let obj: Object3D | undefined;
|
|
225
|
+
try {
|
|
226
|
+
obj = await threeLoader.loadModel(url);
|
|
227
|
+
} catch {
|
|
228
|
+
obj = undefined;
|
|
229
|
+
}
|
|
210
230
|
// 迟到回调:这一槽已经改指向别的地址、或已经删除 ⇒ 当场释放,别留一份没人认领的显存
|
|
211
231
|
if (this._disposed || token !== this._loadToken) {
|
|
212
232
|
if (obj) disposeSubtree(obj);
|
|
213
233
|
return false;
|
|
214
234
|
}
|
|
215
235
|
if (!obj) {
|
|
236
|
+
this._loaded = true;
|
|
216
237
|
this._layout();
|
|
217
238
|
return false;
|
|
218
239
|
}
|
|
@@ -227,6 +248,7 @@ export class ModelSlot extends Group {
|
|
|
227
248
|
this._staged = null;
|
|
228
249
|
this._model = obj;
|
|
229
250
|
this._contentRoot.add(obj);
|
|
251
|
+
this._loaded = true;
|
|
230
252
|
this._layout();
|
|
231
253
|
return true;
|
|
232
254
|
}
|
|
@@ -257,6 +279,8 @@ export class ModelSlot extends Group {
|
|
|
257
279
|
this._size = d.size > 0 ? d.size : MODEL_SLOT_DEFAULT_SIZE;
|
|
258
280
|
this._naturalSize = d.naturalSize;
|
|
259
281
|
this._url = d.url || "";
|
|
282
|
+
// 落定判据跟着地址走:空地址 = 没在等什么(占位方块就是最终内容);有地址 = 等 GLB 那一拍
|
|
283
|
+
this._loaded = !this._url;
|
|
260
284
|
if (!this._url) this._detachModel();
|
|
261
285
|
this._layout();
|
|
262
286
|
}
|
|
@@ -16,8 +16,11 @@ import { Texture } from "three";
|
|
|
16
16
|
* 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
|
|
17
17
|
*/
|
|
18
18
|
/** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
|
|
19
|
-
* 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils`
|
|
20
|
-
* 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。
|
|
19
|
+
* 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(用户决定 2026-09-25),
|
|
20
|
+
* 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。
|
|
21
|
+
* ⭐ 这一步走的是原生 `fetch`,不经过 three 的 `Loader`,所以它**自己**向 `DefaultLoadingManager`
|
|
22
|
+
* 报一次 `itemStart` / `itemEnd`(失败再多一次 `itemError`,与 `FileLoader` 的口径一致)——
|
|
23
|
+
* 否则页面拿到的加载状态只算模型和 `TextureLoader` 那些路,全景切片的下载完全不在里面。 */
|
|
21
24
|
export declare function loadPanoTexture(url: string): Promise<Texture>;
|
|
22
25
|
/** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
|
|
23
26
|
* (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Texture } from "three";
|
|
1
|
+
import { DefaultLoadingManager, Texture } from "three";
|
|
2
2
|
/**
|
|
3
3
|
* ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
|
|
4
4
|
*
|
|
@@ -16,10 +16,14 @@ import { Texture } from "three";
|
|
|
16
16
|
* 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
|
|
17
17
|
*/
|
|
18
18
|
/** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
|
|
19
|
-
* 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils`
|
|
20
|
-
* 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。
|
|
19
|
+
* 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(用户决定 2026-09-25),
|
|
20
|
+
* 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。
|
|
21
|
+
* ⭐ 这一步走的是原生 `fetch`,不经过 three 的 `Loader`,所以它**自己**向 `DefaultLoadingManager`
|
|
22
|
+
* 报一次 `itemStart` / `itemEnd`(失败再多一次 `itemError`,与 `FileLoader` 的口径一致)——
|
|
23
|
+
* 否则页面拿到的加载状态只算模型和 `TextureLoader` 那些路,全景切片的下载完全不在里面。 */
|
|
21
24
|
export function loadPanoTexture(url) {
|
|
22
|
-
|
|
25
|
+
DefaultLoadingManager.itemStart(url);
|
|
26
|
+
const task = fetch(url)
|
|
23
27
|
.then((res) => {
|
|
24
28
|
if (!res.ok)
|
|
25
29
|
throw new Error(`HTTP ${res.status}`);
|
|
@@ -38,6 +42,12 @@ export function loadPanoTexture(url) {
|
|
|
38
42
|
tex.userData.url = url;
|
|
39
43
|
return tex;
|
|
40
44
|
});
|
|
45
|
+
// 一条 promise 只 settle 一次 ⇒ 开始/结束各报一次,两边都不会漏;reject 原样透给调用方。
|
|
46
|
+
task.then(() => DefaultLoadingManager.itemEnd(url), () => {
|
|
47
|
+
DefaultLoadingManager.itemError(url);
|
|
48
|
+
DefaultLoadingManager.itemEnd(url);
|
|
49
|
+
});
|
|
50
|
+
return task;
|
|
41
51
|
}
|
|
42
52
|
/** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
|
|
43
53
|
* (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Texture } from "three";
|
|
1
|
+
import { DefaultLoadingManager, Texture } from "three";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
|
|
@@ -18,10 +18,14 @@ import { Texture } from "three";
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
/** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
|
|
21
|
-
* 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils`
|
|
22
|
-
* 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。
|
|
21
|
+
* 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(用户决定 2026-09-25),
|
|
22
|
+
* 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。
|
|
23
|
+
* ⭐ 这一步走的是原生 `fetch`,不经过 three 的 `Loader`,所以它**自己**向 `DefaultLoadingManager`
|
|
24
|
+
* 报一次 `itemStart` / `itemEnd`(失败再多一次 `itemError`,与 `FileLoader` 的口径一致)——
|
|
25
|
+
* 否则页面拿到的加载状态只算模型和 `TextureLoader` 那些路,全景切片的下载完全不在里面。 */
|
|
23
26
|
export function loadPanoTexture(url: string): Promise<Texture> {
|
|
24
|
-
|
|
27
|
+
DefaultLoadingManager.itemStart(url);
|
|
28
|
+
const task = fetch(url)
|
|
25
29
|
.then((res) => {
|
|
26
30
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
27
31
|
return res.blob();
|
|
@@ -41,6 +45,15 @@ export function loadPanoTexture(url: string): Promise<Texture> {
|
|
|
41
45
|
tex.userData.url = url;
|
|
42
46
|
return tex;
|
|
43
47
|
});
|
|
48
|
+
// 一条 promise 只 settle 一次 ⇒ 开始/结束各报一次,两边都不会漏;reject 原样透给调用方。
|
|
49
|
+
task.then(
|
|
50
|
+
() => DefaultLoadingManager.itemEnd(url),
|
|
51
|
+
() => {
|
|
52
|
+
DefaultLoadingManager.itemError(url);
|
|
53
|
+
DefaultLoadingManager.itemEnd(url);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
return task;
|
|
44
57
|
}
|
|
45
58
|
|
|
46
59
|
/** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
|
|
@@ -7,8 +7,11 @@ import { type TileLayout } from "./mktShader";
|
|
|
7
7
|
export interface PanoStitcher {
|
|
8
8
|
/** 确保批次已在册并发起瓦片加载(幂等;同批已在册 ⇒ 什么都不做)。
|
|
9
9
|
* 新增可选 `layout` 参数:缺省 = PANO_TILE_LAYOUT(全景 8×4 / 2 槽 / 6144×6144,2026-10-27 需求);
|
|
10
|
-
* 图片平面传 `imageTileLayout(natW, natH)`(2×2 / 1 槽 / RT 宽高比=原图)。
|
|
11
|
-
|
|
10
|
+
* 图片平面传 `imageTileLayout(natW, natH)`(2×2 / 1 槽 / RT 宽高比=原图)。
|
|
11
|
+
* 新增可选 `front` 参数(2026-10-29 需求):`true` ⇒ 这一批**挪到在册顺序最前** = 每帧那三道工序
|
|
12
|
+
* 先取它的料(下载额度「每帧 2 张」先归它)。消费方每帧点一次 ⇒ 换渲染器那次整份作废后,
|
|
13
|
+
* 下一帧又把头名抢回来(⛔ 光靠「先入册」做不到,见 `MktMesh.prefetchHeadPanoramas` 的说明)。 */
|
|
14
|
+
ensure(urls: string[], layout?: TileLayout, front?: boolean): void;
|
|
12
15
|
/**
|
|
13
16
|
* 取某一拼合槽的纹理。**画过 ≥1 格就返回**(同日旨「需要先喂给 shader」)。
|
|
14
17
|
* ⭐ 2026-10-09 皇上旨「切换全景时上一个全景的内容要渲染成深灰」:**传了 `renderer` 且该槽一格都还没画时,
|
|
@@ -74,7 +77,10 @@ export declare class PanoTileStitcher implements PanoStitcher {
|
|
|
74
77
|
* ⚠️ 唯一按张数**能混进来**的是 10-06 那版同为 32 片的老档(切片顺序与槽分组两版一致 ⇒ 不会错位),
|
|
75
78
|
* 但它的瓦片是 1024²、被画进 1536² 的格子 = 1.5× 放大 ⇒ 发虚,那类老点仍要重传一次。
|
|
76
79
|
* ⚠️ 每槽数组长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写 `[null,null]` —— 槽数再变会漏改)。 */
|
|
77
|
-
ensure(urls: string[], layout?: TileLayout): void;
|
|
80
|
+
ensure(urls: string[], layout?: TileLayout, front?: boolean): void;
|
|
81
|
+
/** 把一批挪到在册顺序的**最前**(只动 Map 的顺序,⛔ 不动组里任何东西:RT、已画格子、瓦片账本原样)。
|
|
82
|
+
* 已在最前 ⇒ 一次 `keys().next()` 就返回(每帧点名走的就是这条零成本支路)。 */
|
|
83
|
+
private _toFront;
|
|
78
84
|
/** 取某一拼合槽的纹理:**画过 ≥1 格就交出去**(同日旨「需要先喂给 shader」⇒ 不再等 `cellsPerSlot` 格画满)。
|
|
79
85
|
* RT 建好时整张涂过缺失灰 ⇒ 交出去的那张里**没有未定义像素**,观感 = 该象限逐格显影。 */
|
|
80
86
|
peek(batch: string, slot: number, renderer?: WebGLRenderer | null): Texture | null;
|