three-base 2.0.190 → 2.0.192

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.
@@ -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[];
@@ -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 格就绪位 + 已绑纹理的版本号。
@@ -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
 
@@ -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
  /**
@@ -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
- const obj = await threeLoader.loadModel(url);
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();
@@ -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) return true;
209
- const obj = await threeLoader.loadModel(url);
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
  }
@@ -122,6 +122,9 @@ export default class InstancedMeshBase extends MeshBase {
122
122
  instanceCollect[uuid] = instancedMesh;
123
123
  this.add(instancedMesh);
124
124
  });
125
+ // ⭐ 2026-10-28「每个模型对象逐帧显示出来」:这一组 InstancedMesh 是**一个对象**的内容 ⇒ 一次放行
126
+ // (一帧一个对象,⛔ 不是一个对象里的几十块零件排几十帧,那会变成零件逐块拼装)
127
+ this.revealSoon(this.children.slice());
125
128
  // 加载完成事件
126
129
  await new Promise((resolve) => setTimeout(resolve, 30));
127
130
  this.loaded = true;
@@ -124,6 +124,9 @@ export default class InstancedMeshBase extends MeshBase {
124
124
  instanceCollect[uuid] = instancedMesh;
125
125
  this.add(instancedMesh);
126
126
  });
127
+ // ⭐ 2026-10-28「每个模型对象逐帧显示出来」:这一组 InstancedMesh 是**一个对象**的内容 ⇒ 一次放行
128
+ // (一帧一个对象,⛔ 不是一个对象里的几十块零件排几十帧,那会变成零件逐块拼装)
129
+ this.revealSoon(this.children.slice());
127
130
  // 加载完成事件
128
131
  await new Promise((resolve) => setTimeout(resolve, 30));
129
132
  this.loaded = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-base",
3
- "version": "2.0.190",
3
+ "version": "2.0.192",
4
4
  "description": "A Three.js extension library providing common 3D components, animations, materials, and utilities",
5
5
  "author": "xiaosihan",
6
6
  "publishConfig": {