three-base 2.0.172 → 2.0.175
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/FPScontrol.d.ts +15 -24
- package/FPScontrol.js +63 -87
- package/FPScontrol.ts +64 -88
- package/Ground1/Ground1.d.ts +4 -2
- package/Ground1/Ground1.js +77 -57
- package/Ground1/Ground1.ts +77 -57
- package/Mirror.js +0 -4
- package/Mirror.ts +0 -4
- package/MktMesh/MktMesh.d.ts +3 -2
- package/MktMesh/MktMesh.js +27 -8
- package/MktMesh/MktMesh.ts +24 -8
- package/MktMesh/mktMappingShader.d.ts +6 -5
- package/MktMesh/mktMappingShader.js +26 -19
- package/MktMesh/mktMappingShader.ts +28 -19
- package/MktMesh/mktShader.d.ts +127 -58
- package/MktMesh/mktShader.js +192 -117
- package/MktMesh/mktShader.ts +251 -127
- package/MktMesh/panoSlotRoster.d.ts +2 -2
- package/MktMesh/panoSlotRoster.js +9 -7
- package/MktMesh/panoSlotRoster.ts +9 -7
- package/MktMesh/panoTileStitcher.d.ts +20 -13
- package/MktMesh/panoTileStitcher.js +97 -76
- package/MktMesh/panoTileStitcher.ts +119 -90
- package/PipeMesh/PipeJoint.d.ts +22 -0
- package/PipeMesh/PipeJoint.js +33 -0
- package/PipeMesh/PipeJoint.ts +38 -0
- package/PipeMesh/PipeMesh.d.ts +94 -22
- package/PipeMesh/PipeMesh.js +192 -53
- package/PipeMesh/PipeMesh.ts +223 -76
- package/PipeMesh/PipeStraight.d.ts +19 -0
- package/PipeMesh/PipeStraight.js +34 -0
- package/PipeMesh/PipeStraight.ts +45 -0
- package/PipeMesh/axisSnap.d.ts +52 -0
- package/PipeMesh/axisSnap.js +52 -0
- package/PipeMesh/axisSnap.ts +93 -0
- package/PipeMesh/directRoute.d.ts +85 -0
- package/PipeMesh/directRoute.js +126 -0
- package/PipeMesh/directRoute.ts +190 -0
- package/PipeMesh/index.d.ts +6 -0
- package/PipeMesh/index.js +6 -0
- package/PipeMesh/index.ts +7 -1
- package/PipeMesh/pipeMaterial.d.ts +16 -0
- package/PipeMesh/pipeMaterial.js +23 -0
- package/PipeMesh/pipeMaterial.ts +26 -0
- package/ThreeBase.d.ts +1 -39
- package/ThreeBase.js +38 -147
- package/ThreeBase.ts +1176 -1302
- package/instanced/InstancedBatch.d.ts +14 -1
- package/instanced/InstancedBatch.js +53 -3
- package/instanced/InstancedBatch.ts +62 -3
- package/instanced/InstancedChild.d.ts +24 -0
- package/instanced/InstancedChild.js +40 -0
- package/instanced/InstancedChild.ts +41 -0
- package/instanced/InstancedGroup.d.ts +48 -0
- package/instanced/InstancedGroup.js +45 -1
- package/instanced/InstancedGroup.ts +85 -0
- package/package.json +2 -7
- package/pass/debugDepthState.d.ts +16 -8
- package/pass/debugDepthState.js +4 -0
- package/pass/debugDepthState.ts +27 -9
- package/pass/glow_pass.js +11 -94
- package/pass/glow_pass.ts +11 -97
- package/pass/ssao_pass.d.ts +9 -23
- package/pass/ssao_pass.js +41 -128
- package/pass/ssao_pass.ts +36 -166
- package/pass/stroke_pass.js +3 -0
- package/pass/stroke_pass.ts +4 -0
- package/shaders/debugDepthCompareShader.d.ts +1 -1
- package/shaders/debugDepthCompareShader.js +1 -1
- package/shaders/debugDepthCompareShader.ts +1 -1
- package/shaders/debugDepthJudgeShader.d.ts +1 -1
- package/shaders/debugDepthJudgeShader.js +1 -1
- package/shaders/debugDepthJudgeShader.ts +1 -1
- package/PipeMesh/PathGeometry.d.ts +0 -44
- package/PipeMesh/PathGeometry.js +0 -307
- package/PipeMesh/PathGeometry.ts +0 -343
- package/PipeMesh/PathPoint.d.ts +0 -17
- package/PipeMesh/PathPoint.js +0 -31
- package/PipeMesh/PathPoint.ts +0 -36
- package/PipeMesh/PathPointList.d.ts +0 -31
- package/PipeMesh/PathPointList.js +0 -209
- package/PipeMesh/PathPointList.ts +0 -227
- package/PipeMesh/PathTubeGeometry.d.ts +0 -25
- package/PipeMesh/PathTubeGeometry.js +0 -130
- package/PipeMesh/PathTubeGeometry.ts +0 -146
- package/WaterFlow/FloatingDuck.d.ts +0 -88
- package/WaterFlow/FloatingDuck.js +0 -307
- package/WaterFlow/FloatingDuck.ts +0 -445
- package/WaterFlow/GPUFluidSimulation.d.ts +0 -56
- package/WaterFlow/GPUFluidSimulation.js +0 -785
- package/WaterFlow/GPUFluidSimulation.ts +0 -827
- package/WaterFlow/ReservoirSystem.d.ts +0 -106
- package/WaterFlow/ReservoirSystem.js +0 -621
- package/WaterFlow/ReservoirSystem.ts +0 -754
- package/WaterFlow/SPHField.d.ts +0 -35
- package/WaterFlow/SPHField.js +0 -204
- package/WaterFlow/SPHField.ts +0 -228
- package/WaterFlow/ScreenSpaceFluidPass.d.ts +0 -69
- package/WaterFlow/ScreenSpaceFluidPass.js +0 -736
- package/WaterFlow/ScreenSpaceFluidPass.ts +0 -789
- package/WaterFlow/ShallowWaterSurface.d.ts +0 -45
- package/WaterFlow/ShallowWaterSurface.js +0 -441
- package/WaterFlow/ShallowWaterSurface.ts +0 -510
- package/WaterFlow/WaterFlow.d.ts +0 -87
- package/WaterFlow/WaterFlow.js +0 -171
- package/WaterFlow/WaterFlow.ts +0 -219
- package/WaterFlow/types.d.ts +0 -161
- package/WaterFlow/types.js +0 -1
- package/WaterFlow/types.ts +0 -189
- package/WaterFlow/waterFlowPass.d.ts +0 -3
- package/WaterFlow/waterFlowPass.js +0 -20
- package/WaterFlow/waterFlowPass.ts +0 -40
- package/shaders/debugExcludedShader.d.ts +0 -15
- package/shaders/debugExcludedShader.js +0 -48
- package/shaders/debugExcludedShader.ts +0 -50
|
@@ -27,6 +27,13 @@ export default class InstancedBatch {
|
|
|
27
27
|
private _currentClip?;
|
|
28
28
|
/** 首例实例是否声明碰撞(构造时快照,buildMeshes 用它给 InstancedMesh 打标记) */
|
|
29
29
|
private _sampleFpsCollision;
|
|
30
|
+
/**
|
|
31
|
+
* 首例实例所在类是否声明 `static panoIndexAttribute`(构造时快照,与 _sampleFpsCollision 同一套路):
|
|
32
|
+
* true ⇒ buildMeshes 给本 batch 克隆一份渲染用几何并挂上逐实例 `aPanoIndex`(全景序号直取着色器读它)。
|
|
33
|
+
*/
|
|
34
|
+
private _samplePanoIndexAttr;
|
|
35
|
+
/** 本 batch 自己挂上去的逐实例 `aPanoIndex` 缓冲(与 _meshes 顺序对齐;未启用则为空数组) */
|
|
36
|
+
private _panoAttrs;
|
|
30
37
|
/** 当前正在播放的 AnimationAction(切换时先 stop 旧的) */
|
|
31
38
|
private _currentAction?;
|
|
32
39
|
/** 非骨骼动画:需每帧烘焙节点变换的几何列表(与 _meshes 顺序对齐) */
|
|
@@ -43,7 +50,7 @@ export default class InstancedBatch {
|
|
|
43
50
|
/** 根据当前首批实例的 fpsCollision 标记,把真实 InstancedMesh 的 userData.fpsCollision 同步过去,
|
|
44
51
|
* 并在开启时通知所有 FPScontrol 重建碰撞列表。碰撞以整个 batch 为单位开关。 */
|
|
45
52
|
private _applyCollisionFlag;
|
|
46
|
-
/** 把单个实例的 matrix + color
|
|
53
|
+
/** 把单个实例的 matrix + color +(若启用)全景序号写入所有 submesh 的对应槽位 */
|
|
47
54
|
private writeInstance;
|
|
48
55
|
/** 事件路由:给定 InstancedMesh 与 instanceId,返回对应 InstancedChild */
|
|
49
56
|
resolveInstance(mesh: InstancedMesh, instanceId: number): InstancedChild | null;
|
|
@@ -53,5 +60,11 @@ export default class InstancedBatch {
|
|
|
53
60
|
hasCursor(): boolean;
|
|
54
61
|
/** 拾取用 InstancedMesh 列表 */
|
|
55
62
|
get pickables(): InstancedMesh[];
|
|
63
|
+
/** 只读快照(拓扑图面板用):本 batch 当前持有的逻辑实例数 */
|
|
64
|
+
get childCount(): number;
|
|
65
|
+
/** 只读快照:实例缓冲容量(不够时按 step 扩容,从不缩容) */
|
|
66
|
+
get capacity(): number;
|
|
67
|
+
/** 只读快照:本 batch 的逻辑实例列表(只给引用,调用方不得增删) */
|
|
68
|
+
get childrenView(): readonly InstancedChild[];
|
|
56
69
|
dispose(): void;
|
|
57
70
|
}
|
|
@@ -111,6 +111,13 @@ export default class InstancedBatch {
|
|
|
111
111
|
_currentClip;
|
|
112
112
|
/** 首例实例是否声明碰撞(构造时快照,buildMeshes 用它给 InstancedMesh 打标记) */
|
|
113
113
|
_sampleFpsCollision = false;
|
|
114
|
+
/**
|
|
115
|
+
* 首例实例所在类是否声明 `static panoIndexAttribute`(构造时快照,与 _sampleFpsCollision 同一套路):
|
|
116
|
+
* true ⇒ buildMeshes 给本 batch 克隆一份渲染用几何并挂上逐实例 `aPanoIndex`(全景序号直取着色器读它)。
|
|
117
|
+
*/
|
|
118
|
+
_samplePanoIndexAttr = false;
|
|
119
|
+
/** 本 batch 自己挂上去的逐实例 `aPanoIndex` 缓冲(与 _meshes 顺序对齐;未启用则为空数组) */
|
|
120
|
+
_panoAttrs = [];
|
|
114
121
|
/** 当前正在播放的 AnimationAction(切换时先 stop 旧的) */
|
|
115
122
|
_currentAction;
|
|
116
123
|
/** 非骨骼动画:需每帧烘焙节点变换的几何列表(与 _meshes 顺序对齐) */
|
|
@@ -123,6 +130,7 @@ export default class InstancedBatch {
|
|
|
123
130
|
// 因 key 已含动画名,相同 glb+动画的实例都进同一个常驻 batch
|
|
124
131
|
this._clipName = sample.getEffectiveAnimation();
|
|
125
132
|
this._sampleFpsCollision = !!sample.fpsCollision;
|
|
133
|
+
this._samplePanoIndexAttr = !!sample.constructor.panoIndexAttribute;
|
|
126
134
|
this._loading = loadTemplate(sample).then((t) => {
|
|
127
135
|
const ctor = sample.constructor;
|
|
128
136
|
// GLB 模板就绪后,交给子类按需修正材质(如瓶子 metalness 修正)
|
|
@@ -140,9 +148,22 @@ export default class InstancedBatch {
|
|
|
140
148
|
return;
|
|
141
149
|
}
|
|
142
150
|
for (const sub of this.template.submeshes) {
|
|
143
|
-
|
|
151
|
+
let geom = sub.skinned && this.template.sharedGeometry
|
|
144
152
|
? this.template.sharedGeometry
|
|
145
153
|
: sub.geometry;
|
|
154
|
+
// 逐实例全景序号:本 batch 用**自己克隆的一份几何**挂 `aPanoIndex`(InstancedBufferAttribute
|
|
155
|
+
// 只能挂在 geometry 上,而 static geometry 跨 batch 共享 ⇒ 挂原几何会被别的 batch 覆盖,
|
|
156
|
+
// 详见 InstancedChild.panoIndexAttribute 的注释)。
|
|
157
|
+
// ⚠️ 骨骼蒙皮那条路不能克隆:sharedGeometry 每帧由 bakeSkinning 从 sourceGeometry 烤出来,
|
|
158
|
+
// 克隆一份就再也收不到烘焙结果(实例全部定格在绑定姿态)⇒ 蒙皮批次不加此属性。
|
|
159
|
+
if (this._samplePanoIndexAttr && !sub.skinned) {
|
|
160
|
+
const instGeom = geom.clone();
|
|
161
|
+
const panoAttr = new InstancedBufferAttribute(new Float32Array(this._capacity).fill(-1), 1);
|
|
162
|
+
panoAttr.setUsage(DynamicDrawUsage);
|
|
163
|
+
instGeom.setAttribute("aPanoIndex", panoAttr);
|
|
164
|
+
this._panoAttrs.push(panoAttr);
|
|
165
|
+
geom = instGeom;
|
|
166
|
+
}
|
|
146
167
|
const im = new InstancedMesh(geom, sub.material, this._capacity);
|
|
147
168
|
im.frustumCulled = false;
|
|
148
169
|
im.instanceMatrix.setUsage(DynamicDrawUsage);
|
|
@@ -250,7 +271,8 @@ export default class InstancedBatch {
|
|
|
250
271
|
// 容量按 100 为一阶阶梯增长、只增不降:1 个对象→100 矩阵,101 个→200 矩阵,201 个→300 ...
|
|
251
272
|
// 避免每超出即整段 realloc,也避免从 200 回降到 100 造成反复分配/收缩。
|
|
252
273
|
const newCap = Math.ceil(n / 100) * 100;
|
|
253
|
-
for (
|
|
274
|
+
for (let k = 0; k < this._meshes.length; k++) {
|
|
275
|
+
const im = this._meshes[k];
|
|
254
276
|
const imMat = new InstancedBufferAttribute(new Float32Array(newCap * 16), 16);
|
|
255
277
|
const imCol = new InstancedBufferAttribute(new Float32Array(newCap * 3).fill(1), 3);
|
|
256
278
|
for (let i = 0; i < im.count; i++) {
|
|
@@ -261,6 +283,14 @@ export default class InstancedBatch {
|
|
|
261
283
|
}
|
|
262
284
|
im.instanceMatrix = imMat;
|
|
263
285
|
im.instanceColor = imCol;
|
|
286
|
+
// 逐实例全景序号缓冲跟着一起扩容(与上面两份实例缓冲同步换)。
|
|
287
|
+
// 不必搬旧值:唯一的调用点 ensureCapacity → compactWrite() 紧接着全量重写每个实例。
|
|
288
|
+
if (this._panoAttrs[k]) {
|
|
289
|
+
const pano = new InstancedBufferAttribute(new Float32Array(newCap).fill(-1), 1);
|
|
290
|
+
pano.setUsage(DynamicDrawUsage);
|
|
291
|
+
im.geometry.setAttribute("aPanoIndex", pano);
|
|
292
|
+
this._panoAttrs[k] = pano;
|
|
293
|
+
}
|
|
264
294
|
}
|
|
265
295
|
this._capacity = newCap;
|
|
266
296
|
}
|
|
@@ -282,7 +312,7 @@ export default class InstancedBatch {
|
|
|
282
312
|
FPScontrol.markAllCollidersDirty();
|
|
283
313
|
}
|
|
284
314
|
}
|
|
285
|
-
/** 把单个实例的 matrix + color
|
|
315
|
+
/** 把单个实例的 matrix + color +(若启用)全景序号写入所有 submesh 的对应槽位 */
|
|
286
316
|
writeInstance(i, child) {
|
|
287
317
|
for (let k = 0; k < this._meshes.length; k++) {
|
|
288
318
|
const im = this._meshes[k];
|
|
@@ -299,6 +329,11 @@ export default class InstancedBatch {
|
|
|
299
329
|
im.setColorAt(i, child.color);
|
|
300
330
|
im.instanceColor.needsUpdate = true;
|
|
301
331
|
}
|
|
332
|
+
const pano = this._panoAttrs[k];
|
|
333
|
+
if (pano) {
|
|
334
|
+
pano.setX(i, child.panoIndex);
|
|
335
|
+
pano.needsUpdate = true;
|
|
336
|
+
}
|
|
302
337
|
}
|
|
303
338
|
}
|
|
304
339
|
/** 事件路由:给定 InstancedMesh 与 instanceId,返回对应 InstancedChild */
|
|
@@ -331,6 +366,18 @@ export default class InstancedBatch {
|
|
|
331
366
|
get pickables() {
|
|
332
367
|
return this._meshes;
|
|
333
368
|
}
|
|
369
|
+
/** 只读快照(拓扑图面板用):本 batch 当前持有的逻辑实例数 */
|
|
370
|
+
get childCount() {
|
|
371
|
+
return this._children.length;
|
|
372
|
+
}
|
|
373
|
+
/** 只读快照:实例缓冲容量(不够时按 step 扩容,从不缩容) */
|
|
374
|
+
get capacity() {
|
|
375
|
+
return this._capacity;
|
|
376
|
+
}
|
|
377
|
+
/** 只读快照:本 batch 的逻辑实例列表(只给引用,调用方不得增删) */
|
|
378
|
+
get childrenView() {
|
|
379
|
+
return this._children;
|
|
380
|
+
}
|
|
334
381
|
dispose() {
|
|
335
382
|
for (const im of this._meshes) {
|
|
336
383
|
// 只释放 InstancedMesh 自带的实例缓冲(instanceMatrix / instanceColor),
|
|
@@ -341,6 +388,9 @@ export default class InstancedBatch {
|
|
|
341
388
|
}
|
|
342
389
|
this._meshes = [];
|
|
343
390
|
this._nodeBake = [];
|
|
391
|
+
// 逐实例全景序号缓冲随 batch 一起散手(克隆的那份几何按本类既有规矩不 dispose:
|
|
392
|
+
// 它的 position/normal 数组与共享 static 几何同源,dispose 会把那份共享缓冲的 GPU 绑定一并摘掉)。
|
|
393
|
+
this._panoAttrs = [];
|
|
344
394
|
if (this._mixer) {
|
|
345
395
|
this._mixer.stopAllAction();
|
|
346
396
|
}
|
|
@@ -140,6 +140,13 @@ export default class InstancedBatch {
|
|
|
140
140
|
private _currentClip?: string;
|
|
141
141
|
/** 首例实例是否声明碰撞(构造时快照,buildMeshes 用它给 InstancedMesh 打标记) */
|
|
142
142
|
private _sampleFpsCollision: boolean = false;
|
|
143
|
+
/**
|
|
144
|
+
* 首例实例所在类是否声明 `static panoIndexAttribute`(构造时快照,与 _sampleFpsCollision 同一套路):
|
|
145
|
+
* true ⇒ buildMeshes 给本 batch 克隆一份渲染用几何并挂上逐实例 `aPanoIndex`(全景序号直取着色器读它)。
|
|
146
|
+
*/
|
|
147
|
+
private _samplePanoIndexAttr: boolean = false;
|
|
148
|
+
/** 本 batch 自己挂上去的逐实例 `aPanoIndex` 缓冲(与 _meshes 顺序对齐;未启用则为空数组) */
|
|
149
|
+
private _panoAttrs: InstancedBufferAttribute[] = [];
|
|
143
150
|
/** 当前正在播放的 AnimationAction(切换时先 stop 旧的) */
|
|
144
151
|
private _currentAction?: AnimationAction;
|
|
145
152
|
/** 非骨骼动画:需每帧烘焙节点变换的几何列表(与 _meshes 顺序对齐) */
|
|
@@ -153,6 +160,7 @@ export default class InstancedBatch {
|
|
|
153
160
|
// 因 key 已含动画名,相同 glb+动画的实例都进同一个常驻 batch
|
|
154
161
|
this._clipName = sample.getEffectiveAnimation();
|
|
155
162
|
this._sampleFpsCollision = !!sample.fpsCollision;
|
|
163
|
+
this._samplePanoIndexAttr = !!(sample.constructor as typeof InstancedChild).panoIndexAttribute;
|
|
156
164
|
this._loading = loadTemplate(sample).then((t) => {
|
|
157
165
|
const ctor = sample.constructor as typeof InstancedChild;
|
|
158
166
|
// GLB 模板就绪后,交给子类按需修正材质(如瓶子 metalness 修正)
|
|
@@ -171,10 +179,26 @@ export default class InstancedBatch {
|
|
|
171
179
|
return;
|
|
172
180
|
}
|
|
173
181
|
for (const sub of this.template.submeshes) {
|
|
174
|
-
|
|
182
|
+
let geom =
|
|
175
183
|
sub.skinned && this.template.sharedGeometry
|
|
176
184
|
? this.template.sharedGeometry
|
|
177
185
|
: sub.geometry;
|
|
186
|
+
// 逐实例全景序号:本 batch 用**自己克隆的一份几何**挂 `aPanoIndex`(InstancedBufferAttribute
|
|
187
|
+
// 只能挂在 geometry 上,而 static geometry 跨 batch 共享 ⇒ 挂原几何会被别的 batch 覆盖,
|
|
188
|
+
// 详见 InstancedChild.panoIndexAttribute 的注释)。
|
|
189
|
+
// ⚠️ 骨骼蒙皮那条路不能克隆:sharedGeometry 每帧由 bakeSkinning 从 sourceGeometry 烤出来,
|
|
190
|
+
// 克隆一份就再也收不到烘焙结果(实例全部定格在绑定姿态)⇒ 蒙皮批次不加此属性。
|
|
191
|
+
if (this._samplePanoIndexAttr && !sub.skinned) {
|
|
192
|
+
const instGeom = geom.clone();
|
|
193
|
+
const panoAttr = new InstancedBufferAttribute(
|
|
194
|
+
new Float32Array(this._capacity).fill(-1),
|
|
195
|
+
1
|
|
196
|
+
);
|
|
197
|
+
panoAttr.setUsage(DynamicDrawUsage);
|
|
198
|
+
instGeom.setAttribute("aPanoIndex", panoAttr);
|
|
199
|
+
this._panoAttrs.push(panoAttr);
|
|
200
|
+
geom = instGeom;
|
|
201
|
+
}
|
|
178
202
|
const im = new InstancedMesh(geom, sub.material, this._capacity);
|
|
179
203
|
im.frustumCulled = false;
|
|
180
204
|
im.instanceMatrix.setUsage(DynamicDrawUsage);
|
|
@@ -295,7 +319,8 @@ export default class InstancedBatch {
|
|
|
295
319
|
// 容量按 100 为一阶阶梯增长、只增不降:1 个对象→100 矩阵,101 个→200 矩阵,201 个→300 ...
|
|
296
320
|
// 避免每超出即整段 realloc,也避免从 200 回降到 100 造成反复分配/收缩。
|
|
297
321
|
const newCap = Math.ceil(n / 100) * 100;
|
|
298
|
-
for (
|
|
322
|
+
for (let k = 0; k < this._meshes.length; k++) {
|
|
323
|
+
const im = this._meshes[k];
|
|
299
324
|
const imMat = new InstancedBufferAttribute(new Float32Array(newCap * 16), 16);
|
|
300
325
|
const imCol = new InstancedBufferAttribute(
|
|
301
326
|
new Float32Array(newCap * 3).fill(1),
|
|
@@ -309,6 +334,17 @@ export default class InstancedBatch {
|
|
|
309
334
|
}
|
|
310
335
|
im.instanceMatrix = imMat;
|
|
311
336
|
im.instanceColor = imCol;
|
|
337
|
+
// 逐实例全景序号缓冲跟着一起扩容(与上面两份实例缓冲同步换)。
|
|
338
|
+
// 不必搬旧值:唯一的调用点 ensureCapacity → compactWrite() 紧接着全量重写每个实例。
|
|
339
|
+
if (this._panoAttrs[k]) {
|
|
340
|
+
const pano = new InstancedBufferAttribute(
|
|
341
|
+
new Float32Array(newCap).fill(-1),
|
|
342
|
+
1
|
|
343
|
+
);
|
|
344
|
+
pano.setUsage(DynamicDrawUsage);
|
|
345
|
+
im.geometry.setAttribute("aPanoIndex", pano);
|
|
346
|
+
this._panoAttrs[k] = pano;
|
|
347
|
+
}
|
|
312
348
|
}
|
|
313
349
|
this._capacity = newCap;
|
|
314
350
|
}
|
|
@@ -333,7 +369,7 @@ export default class InstancedBatch {
|
|
|
333
369
|
}
|
|
334
370
|
}
|
|
335
371
|
|
|
336
|
-
/** 把单个实例的 matrix + color
|
|
372
|
+
/** 把单个实例的 matrix + color +(若启用)全景序号写入所有 submesh 的对应槽位 */
|
|
337
373
|
private writeInstance(i: number, child: InstancedChild): void {
|
|
338
374
|
for (let k = 0; k < this._meshes.length; k++) {
|
|
339
375
|
const im = this._meshes[k];
|
|
@@ -350,6 +386,11 @@ export default class InstancedBatch {
|
|
|
350
386
|
im.setColorAt(i, child.color);
|
|
351
387
|
im.instanceColor.needsUpdate = true;
|
|
352
388
|
}
|
|
389
|
+
const pano = this._panoAttrs[k];
|
|
390
|
+
if (pano) {
|
|
391
|
+
pano.setX(i, child.panoIndex);
|
|
392
|
+
pano.needsUpdate = true;
|
|
393
|
+
}
|
|
353
394
|
}
|
|
354
395
|
}
|
|
355
396
|
|
|
@@ -389,6 +430,21 @@ export default class InstancedBatch {
|
|
|
389
430
|
return this._meshes;
|
|
390
431
|
}
|
|
391
432
|
|
|
433
|
+
/** 只读快照(拓扑图面板用):本 batch 当前持有的逻辑实例数 */
|
|
434
|
+
get childCount(): number {
|
|
435
|
+
return this._children.length;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** 只读快照:实例缓冲容量(不够时按 step 扩容,从不缩容) */
|
|
439
|
+
get capacity(): number {
|
|
440
|
+
return this._capacity;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** 只读快照:本 batch 的逻辑实例列表(只给引用,调用方不得增删) */
|
|
444
|
+
get childrenView(): readonly InstancedChild[] {
|
|
445
|
+
return this._children;
|
|
446
|
+
}
|
|
447
|
+
|
|
392
448
|
dispose(): void {
|
|
393
449
|
for (const im of this._meshes) {
|
|
394
450
|
// 只释放 InstancedMesh 自带的实例缓冲(instanceMatrix / instanceColor),
|
|
@@ -399,6 +455,9 @@ export default class InstancedBatch {
|
|
|
399
455
|
}
|
|
400
456
|
this._meshes = [];
|
|
401
457
|
this._nodeBake = [];
|
|
458
|
+
// 逐实例全景序号缓冲随 batch 一起散手(克隆的那份几何按本类既有规矩不 dispose:
|
|
459
|
+
// 它的 position/normal 数组与共享 static 几何同源,dispose 会把那份共享缓冲的 GPU 绑定一并摘掉)。
|
|
460
|
+
this._panoAttrs = [];
|
|
402
461
|
if (this._mixer) {
|
|
403
462
|
this._mixer.stopAllAction();
|
|
404
463
|
}
|
|
@@ -66,10 +66,26 @@ export default class InstancedChild extends Object3D {
|
|
|
66
66
|
static instanceUrl?: string;
|
|
67
67
|
/** GLB 模式:要播放的动画剪辑名;不同动画 = 不同 batch(彼此独立) */
|
|
68
68
|
static animation?: string;
|
|
69
|
+
/**
|
|
70
|
+
* 逐实例全景序号开关(内置几何模式):声明 true 的类,其 batch 会给渲染用的几何挂上一份
|
|
71
|
+
* 名为 `aPanoIndex` 的 InstancedBufferAttribute,每帧把每个实例的 `panoIndex` 写进去 ——
|
|
72
|
+
* 「全景序号直取」投射着色器(MktMesh/mktMappingShader)就按它逐实例挑那张全景。
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ 为什么属性挂在**克隆出来的一份几何**上而不是 `static geometry` 本身(见 InstancedBatch):
|
|
75
|
+
* three 的逐实例自定义属性只能挂在 geometry 上,而 `static geometry` 是**跨 batch 共享**的
|
|
76
|
+
* (主编辑器与预览弹窗各有一份 InstancedMesh、实例序号各自独立排)⇒ 挂在原几何上等于两边
|
|
77
|
+
* 往同一块缓冲写各自的表,后写的把先写的覆盖掉,两边显示的图都会错。
|
|
78
|
+
*/
|
|
79
|
+
static panoIndexAttribute: boolean;
|
|
69
80
|
/** GLB 模式:模板(含材质)加载完成后回调,用于按需修正材质(如瓷器瓶 metalness=1 改 0) */
|
|
70
81
|
static prepareTemplate?: (template: InstancedTemplate) => void;
|
|
71
82
|
/** 实例颜色,写入 InstancedMesh.instanceColor */
|
|
72
83
|
color: Color;
|
|
84
|
+
/**
|
|
85
|
+
* 逐实例全景序号(映射着色器的 `aPanoIndex`):-1 = 未指派 ⇒ 着色器回落 `uFaceMissingColor` 深灰。
|
|
86
|
+
* 仅当本类声明 `static panoIndexAttribute = true` 时才会被写进实例缓冲(见该静态字段的注释)。
|
|
87
|
+
*/
|
|
88
|
+
panoIndex: number;
|
|
73
89
|
/**
|
|
74
90
|
* 脏标记:true 时本帧由 InstancedGroup 把本实例的 matrix/color 写回实例缓冲,
|
|
75
91
|
* 写完后立即置 false —— 避免没有改动时重复写入真正参与渲染的 InstancedMesh。
|
|
@@ -96,6 +112,12 @@ export default class InstancedChild extends Object3D {
|
|
|
96
112
|
* 可在运行时于控制台切换:InstancedChild.AUTO_MARK_DIRTY = true / false。
|
|
97
113
|
*/
|
|
98
114
|
static AUTO_MARK_DIRTY: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* 累计创建过的逻辑实例数(只增不减):构造函数里 +1,`dispose()` 不回退。
|
|
117
|
+
* 与 `instancedChildRegistry.size`(当前存活)配对看:已销毁 = 本值 - 存活数。
|
|
118
|
+
* 注册表是 Set,实例销毁就掉出去,看不到历史 ⇒ 「一共创建了多少个」只能靠这个计数器统计。
|
|
119
|
+
*/
|
|
120
|
+
static createdTotal: number;
|
|
99
121
|
/**
|
|
100
122
|
* 请求切换到的动画剪辑名(由所属 batch 每帧应用)。
|
|
101
123
|
* 实例化是"整批共享一个 mixer",动画是 batch 级而非实例级;
|
|
@@ -140,6 +162,8 @@ export default class InstancedChild extends Object3D {
|
|
|
140
162
|
static syncSelectProxy(proxy: Group): void;
|
|
141
163
|
/** 设置实例颜色(自动置 needUpdate) */
|
|
142
164
|
setColor(c: Color | number | string): this;
|
|
165
|
+
/** 设置逐实例全景序号(自动置 needUpdate,与 setColor 同一条脏标记路) */
|
|
166
|
+
setPanoIndex(v: number): this;
|
|
143
167
|
/**
|
|
144
168
|
* 请求切换本实例的动画剪辑。batch 按「glb + 当前生效动画」分桶(每个动画一个常驻 InstancedBatch),
|
|
145
169
|
* 调用 playClip 只改变本实例的生效动画,下一帧 getBatchKey 随之变化,实例便自动从旧动画 batch
|
|
@@ -48,10 +48,26 @@ export default class InstancedChild extends Object3D {
|
|
|
48
48
|
static instanceUrl;
|
|
49
49
|
/** GLB 模式:要播放的动画剪辑名;不同动画 = 不同 batch(彼此独立) */
|
|
50
50
|
static animation;
|
|
51
|
+
/**
|
|
52
|
+
* 逐实例全景序号开关(内置几何模式):声明 true 的类,其 batch 会给渲染用的几何挂上一份
|
|
53
|
+
* 名为 `aPanoIndex` 的 InstancedBufferAttribute,每帧把每个实例的 `panoIndex` 写进去 ——
|
|
54
|
+
* 「全景序号直取」投射着色器(MktMesh/mktMappingShader)就按它逐实例挑那张全景。
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ 为什么属性挂在**克隆出来的一份几何**上而不是 `static geometry` 本身(见 InstancedBatch):
|
|
57
|
+
* three 的逐实例自定义属性只能挂在 geometry 上,而 `static geometry` 是**跨 batch 共享**的
|
|
58
|
+
* (主编辑器与预览弹窗各有一份 InstancedMesh、实例序号各自独立排)⇒ 挂在原几何上等于两边
|
|
59
|
+
* 往同一块缓冲写各自的表,后写的把先写的覆盖掉,两边显示的图都会错。
|
|
60
|
+
*/
|
|
61
|
+
static panoIndexAttribute = false;
|
|
51
62
|
/** GLB 模式:模板(含材质)加载完成后回调,用于按需修正材质(如瓷器瓶 metalness=1 改 0) */
|
|
52
63
|
static prepareTemplate;
|
|
53
64
|
/** 实例颜色,写入 InstancedMesh.instanceColor */
|
|
54
65
|
color = new Color(0xffffff);
|
|
66
|
+
/**
|
|
67
|
+
* 逐实例全景序号(映射着色器的 `aPanoIndex`):-1 = 未指派 ⇒ 着色器回落 `uFaceMissingColor` 深灰。
|
|
68
|
+
* 仅当本类声明 `static panoIndexAttribute = true` 时才会被写进实例缓冲(见该静态字段的注释)。
|
|
69
|
+
*/
|
|
70
|
+
panoIndex = -1;
|
|
55
71
|
/**
|
|
56
72
|
* 脏标记:true 时本帧由 InstancedGroup 把本实例的 matrix/color 写回实例缓冲,
|
|
57
73
|
* 写完后立即置 false —— 避免没有改动时重复写入真正参与渲染的 InstancedMesh。
|
|
@@ -78,6 +94,12 @@ export default class InstancedChild extends Object3D {
|
|
|
78
94
|
* 可在运行时于控制台切换:InstancedChild.AUTO_MARK_DIRTY = true / false。
|
|
79
95
|
*/
|
|
80
96
|
static AUTO_MARK_DIRTY = true;
|
|
97
|
+
/**
|
|
98
|
+
* 累计创建过的逻辑实例数(只增不减):构造函数里 +1,`dispose()` 不回退。
|
|
99
|
+
* 与 `instancedChildRegistry.size`(当前存活)配对看:已销毁 = 本值 - 存活数。
|
|
100
|
+
* 注册表是 Set,实例销毁就掉出去,看不到历史 ⇒ 「一共创建了多少个」只能靠这个计数器统计。
|
|
101
|
+
*/
|
|
102
|
+
static createdTotal = 0;
|
|
81
103
|
/**
|
|
82
104
|
* 请求切换到的动画剪辑名(由所属 batch 每帧应用)。
|
|
83
105
|
* 实例化是"整批共享一个 mixer",动画是 batch 级而非实例级;
|
|
@@ -87,10 +109,22 @@ export default class InstancedChild extends Object3D {
|
|
|
87
109
|
_cachedWorld = new Matrix4();
|
|
88
110
|
constructor(fpsCollision = false) {
|
|
89
111
|
super();
|
|
112
|
+
// 控制台取证用:three 给的默认值是 `type = "Object3D"` + `name = ""`,一整棵场景树里根本看不出哪个是逻辑实例、
|
|
113
|
+
// 是哪个模型来的。⇒ name = `InstancedChild-来源`(皇上旨 2026-10-12),来源 GLB 模式**只取 url 的文件名**
|
|
114
|
+
// (`./models/bottle.glb` ⇒ `bottle.glb`,整条路径在控制台里太长且没信息量),内置几何模式没有 url ⇒ 取几何类型(BoxGeometry)。
|
|
115
|
+
// ⚠️ @types/three 把 Object3D.type 声明成 readonly(只允许声明类内部赋值),故这里走一次显式类型转换。
|
|
116
|
+
this.type = "InstancedChild";
|
|
117
|
+
const ctor = this.constructor;
|
|
118
|
+
// 文件名:切掉目录(最后一截)、再丢掉可能挂着的 ?查询 / #片段
|
|
119
|
+
const urlBase = ctor.instanceUrl ? ctor.instanceUrl.split("?")[0].split("#")[0].split("/").pop() : "";
|
|
120
|
+
const src = urlBase || (ctor.geometry ? ctor.geometry.type : "noSource");
|
|
121
|
+
this.name = `${this.type}-${src}`;
|
|
90
122
|
this.fpsCollision = fpsCollision;
|
|
91
123
|
// 标记:逻辑实例节点,ThreeMouse 收集候选时跳过(不渲染、无几何,靠 InstancedMesh 命中后路由回来)
|
|
92
124
|
this.userData.__instancedChild = true;
|
|
93
125
|
instancedChildRegistry.add(this);
|
|
126
|
+
// 累计统计走基类静态字段本身(⛔ 别用 this.constructor:子类各自有一份静态槽会漏计)
|
|
127
|
+
InstancedChild.createdTotal++;
|
|
94
128
|
}
|
|
95
129
|
/** 该实例归属哪个 batch 的 key:相同 geometry+material 或相同 instanceUrl+当前生效动画 合并为一类。
|
|
96
130
|
* 因 key 含「当前生效动画」,切换动画 = 实例在「glb+旧动画」与「glb+新动画」两个常驻 batch 间迁移,而非 batch 内切 mixer。 */
|
|
@@ -207,6 +241,12 @@ export default class InstancedChild extends Object3D {
|
|
|
207
241
|
this.needUpdate = true;
|
|
208
242
|
return this;
|
|
209
243
|
}
|
|
244
|
+
/** 设置逐实例全景序号(自动置 needUpdate,与 setColor 同一条脏标记路) */
|
|
245
|
+
setPanoIndex(v) {
|
|
246
|
+
this.panoIndex = v;
|
|
247
|
+
this.needUpdate = true;
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
210
250
|
/**
|
|
211
251
|
* 请求切换本实例的动画剪辑。batch 按「glb + 当前生效动画」分桶(每个动画一个常驻 InstancedBatch),
|
|
212
252
|
* 调用 playClip 只改变本实例的生效动画,下一帧 getBatchKey 随之变化,实例便自动从旧动画 batch
|
|
@@ -78,11 +78,27 @@ export default class InstancedChild extends Object3D {
|
|
|
78
78
|
static instanceUrl?: string;
|
|
79
79
|
/** GLB 模式:要播放的动画剪辑名;不同动画 = 不同 batch(彼此独立) */
|
|
80
80
|
static animation?: string;
|
|
81
|
+
/**
|
|
82
|
+
* 逐实例全景序号开关(内置几何模式):声明 true 的类,其 batch 会给渲染用的几何挂上一份
|
|
83
|
+
* 名为 `aPanoIndex` 的 InstancedBufferAttribute,每帧把每个实例的 `panoIndex` 写进去 ——
|
|
84
|
+
* 「全景序号直取」投射着色器(MktMesh/mktMappingShader)就按它逐实例挑那张全景。
|
|
85
|
+
*
|
|
86
|
+
* ⚠️ 为什么属性挂在**克隆出来的一份几何**上而不是 `static geometry` 本身(见 InstancedBatch):
|
|
87
|
+
* three 的逐实例自定义属性只能挂在 geometry 上,而 `static geometry` 是**跨 batch 共享**的
|
|
88
|
+
* (主编辑器与预览弹窗各有一份 InstancedMesh、实例序号各自独立排)⇒ 挂在原几何上等于两边
|
|
89
|
+
* 往同一块缓冲写各自的表,后写的把先写的覆盖掉,两边显示的图都会错。
|
|
90
|
+
*/
|
|
91
|
+
static panoIndexAttribute: boolean = false;
|
|
81
92
|
/** GLB 模式:模板(含材质)加载完成后回调,用于按需修正材质(如瓷器瓶 metalness=1 改 0) */
|
|
82
93
|
static prepareTemplate?: (template: InstancedTemplate) => void;
|
|
83
94
|
|
|
84
95
|
/** 实例颜色,写入 InstancedMesh.instanceColor */
|
|
85
96
|
color: Color = new Color(0xffffff);
|
|
97
|
+
/**
|
|
98
|
+
* 逐实例全景序号(映射着色器的 `aPanoIndex`):-1 = 未指派 ⇒ 着色器回落 `uFaceMissingColor` 深灰。
|
|
99
|
+
* 仅当本类声明 `static panoIndexAttribute = true` 时才会被写进实例缓冲(见该静态字段的注释)。
|
|
100
|
+
*/
|
|
101
|
+
panoIndex: number = -1;
|
|
86
102
|
/**
|
|
87
103
|
* 脏标记:true 时本帧由 InstancedGroup 把本实例的 matrix/color 写回实例缓冲,
|
|
88
104
|
* 写完后立即置 false —— 避免没有改动时重复写入真正参与渲染的 InstancedMesh。
|
|
@@ -109,6 +125,12 @@ export default class InstancedChild extends Object3D {
|
|
|
109
125
|
* 可在运行时于控制台切换:InstancedChild.AUTO_MARK_DIRTY = true / false。
|
|
110
126
|
*/
|
|
111
127
|
static AUTO_MARK_DIRTY: boolean = true;
|
|
128
|
+
/**
|
|
129
|
+
* 累计创建过的逻辑实例数(只增不减):构造函数里 +1,`dispose()` 不回退。
|
|
130
|
+
* 与 `instancedChildRegistry.size`(当前存活)配对看:已销毁 = 本值 - 存活数。
|
|
131
|
+
* 注册表是 Set,实例销毁就掉出去,看不到历史 ⇒ 「一共创建了多少个」只能靠这个计数器统计。
|
|
132
|
+
*/
|
|
133
|
+
static createdTotal: number = 0;
|
|
112
134
|
/**
|
|
113
135
|
* 请求切换到的动画剪辑名(由所属 batch 每帧应用)。
|
|
114
136
|
* 实例化是"整批共享一个 mixer",动画是 batch 级而非实例级;
|
|
@@ -120,10 +142,22 @@ export default class InstancedChild extends Object3D {
|
|
|
120
142
|
|
|
121
143
|
constructor(fpsCollision: boolean = false) {
|
|
122
144
|
super();
|
|
145
|
+
// 控制台取证用:three 给的默认值是 `type = "Object3D"` + `name = ""`,一整棵场景树里根本看不出哪个是逻辑实例、
|
|
146
|
+
// 是哪个模型来的。⇒ name = `InstancedChild-来源`(皇上旨 2026-10-12),来源 GLB 模式**只取 url 的文件名**
|
|
147
|
+
// (`./models/bottle.glb` ⇒ `bottle.glb`,整条路径在控制台里太长且没信息量),内置几何模式没有 url ⇒ 取几何类型(BoxGeometry)。
|
|
148
|
+
// ⚠️ @types/three 把 Object3D.type 声明成 readonly(只允许声明类内部赋值),故这里走一次显式类型转换。
|
|
149
|
+
(this as { type: string }).type = "InstancedChild";
|
|
150
|
+
const ctor = this.constructor as typeof InstancedChild;
|
|
151
|
+
// 文件名:切掉目录(最后一截)、再丢掉可能挂着的 ?查询 / #片段
|
|
152
|
+
const urlBase = ctor.instanceUrl ? ctor.instanceUrl.split("?")[0].split("#")[0].split("/").pop() : "";
|
|
153
|
+
const src = urlBase || (ctor.geometry ? ctor.geometry.type : "noSource");
|
|
154
|
+
this.name = `${this.type}-${src}`;
|
|
123
155
|
this.fpsCollision = fpsCollision;
|
|
124
156
|
// 标记:逻辑实例节点,ThreeMouse 收集候选时跳过(不渲染、无几何,靠 InstancedMesh 命中后路由回来)
|
|
125
157
|
this.userData.__instancedChild = true;
|
|
126
158
|
instancedChildRegistry.add(this);
|
|
159
|
+
// 累计统计走基类静态字段本身(⛔ 别用 this.constructor:子类各自有一份静态槽会漏计)
|
|
160
|
+
InstancedChild.createdTotal++;
|
|
127
161
|
}
|
|
128
162
|
|
|
129
163
|
/** 该实例归属哪个 batch 的 key:相同 geometry+material 或相同 instanceUrl+当前生效动画 合并为一类。
|
|
@@ -265,6 +299,13 @@ export default class InstancedChild extends Object3D {
|
|
|
265
299
|
return this;
|
|
266
300
|
}
|
|
267
301
|
|
|
302
|
+
/** 设置逐实例全景序号(自动置 needUpdate,与 setColor 同一条脏标记路) */
|
|
303
|
+
setPanoIndex(v: number): this {
|
|
304
|
+
this.panoIndex = v;
|
|
305
|
+
this.needUpdate = true;
|
|
306
|
+
return this;
|
|
307
|
+
}
|
|
308
|
+
|
|
268
309
|
/**
|
|
269
310
|
* 请求切换本实例的动画剪辑。batch 按「glb + 当前生效动画」分桶(每个动画一个常驻 InstancedBatch),
|
|
270
311
|
* 调用 playClip 只改变本实例的生效动画,下一帧 getBatchKey 随之变化,实例便自动从旧动画 batch
|
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
import { Object3D } from "three";
|
|
2
2
|
import InstancedChild from "./InstancedChild";
|
|
3
3
|
import InstancedBatch from "./InstancedBatch";
|
|
4
|
+
/** 一个 batch 的合并情况快照(详见 `InstancedGroup.snapshot`) */
|
|
5
|
+
export interface InstancedBatchSnapshot {
|
|
6
|
+
/** 分桶键:`geo:<geometryUuid>/<materialUuid>` 或 `url:<glb>:anim:<剪辑名>` */
|
|
7
|
+
key: string;
|
|
8
|
+
/** 本桶里的逻辑实例数(InstancedChild) */
|
|
9
|
+
childCount: number;
|
|
10
|
+
/** 真实 InstancedMesh 个数 = 本桶一帧的 draw call 数(GLB 多 submesh ⇒ 大于 1) */
|
|
11
|
+
drawCalls: number;
|
|
12
|
+
/** 实例缓冲容量(只增不减) */
|
|
13
|
+
capacity: number;
|
|
14
|
+
/** GLB 模板是否已就绪;false ⇒ 实例已登记但还没画出来 */
|
|
15
|
+
ready: boolean;
|
|
16
|
+
/** 每个 InstancedMesh 实际写入的实例数(与 drawCalls 一一对应) */
|
|
17
|
+
counts: number[];
|
|
18
|
+
/** 子实例的可读标签(name 优先,其次构造器名) */
|
|
19
|
+
children: string[];
|
|
20
|
+
/** 首个 InstancedMesh 的几何类型名(如 `BoxGeometry`);模板未就绪时为空串 */
|
|
21
|
+
geomType: string;
|
|
22
|
+
/** 首个 InstancedMesh 的材质类型名(多材质为 `多材质`);模板未就绪时为空串 */
|
|
23
|
+
matType: string;
|
|
24
|
+
}
|
|
25
|
+
/** 一个 InstancedGroup 的合并总览 */
|
|
26
|
+
export interface InstancedGroupSnapshot {
|
|
27
|
+
/** 本组归属 tag;undefined = ThreeBase 默认组(只收未声明归属的实例) */
|
|
28
|
+
owner?: string;
|
|
29
|
+
batches: InstancedBatchSnapshot[];
|
|
30
|
+
/** 已收进各 batch 的实例总数 */
|
|
31
|
+
merged: number;
|
|
32
|
+
/** 全局注册表里的 InstancedChild 总数(含归属其他组的) */
|
|
33
|
+
registryTotal: number;
|
|
34
|
+
/**
|
|
35
|
+
* 累计创建过的 InstancedChild 总数(只增不减,dispose 不回退)。
|
|
36
|
+
* 与 `registryTotal` 配对即得「已销毁 = createdTotal - registryTotal」。
|
|
37
|
+
*/
|
|
38
|
+
createdTotal: number;
|
|
39
|
+
/** 归属本组却没进任何 batch 的实例数:正常恒为 0,非 0 说明分桶/收集有漏 */
|
|
40
|
+
leaked: number;
|
|
41
|
+
}
|
|
4
42
|
/**
|
|
5
43
|
* 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
|
|
6
44
|
* 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
|
|
@@ -29,6 +67,16 @@ export default class InstancedGroup extends Object3D {
|
|
|
29
67
|
resolveInstance(mesh: Object3D, instanceId: number | undefined): InstancedChild | null;
|
|
30
68
|
/** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
|
|
31
69
|
collectPickable(out: Object3D[]): void;
|
|
70
|
+
/**
|
|
71
|
+
* 只读快照:本组当前的合并情况,供调试面板画拓扑图。
|
|
72
|
+
*
|
|
73
|
+
* 归属判定(哪些注册表实例该由本组收集)刻意留在这里而不是让调用方自己判 ——
|
|
74
|
+
* 规则与 `_accepts` 必须同源,否则面板报的「未合并数」会与实际渲染不符。
|
|
75
|
+
*
|
|
76
|
+
* ⚠️ 子实例标签取 `name`,其次取构造器名:业务子类若没设 `name`,生产构建(压缩后)
|
|
77
|
+
* 构造器名会变成短标识符。要在拓扑图里认出实例,请给实例设 `name`(同 [[InstancedChild]] 的控制台可读命名约定)。
|
|
78
|
+
*/
|
|
79
|
+
snapshot(): InstancedGroupSnapshot;
|
|
32
80
|
/**
|
|
33
81
|
* 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
|
|
34
82
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Clock, Object3D } from "three";
|
|
2
|
-
import { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
|
|
2
|
+
import InstancedChild, { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
|
|
3
3
|
import InstancedBatch from "./InstancedBatch";
|
|
4
4
|
const PICK_EVENT_TYPES = [
|
|
5
5
|
"mouseenter",
|
|
@@ -107,6 +107,50 @@ export default class InstancedGroup extends Object3D {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* 只读快照:本组当前的合并情况,供调试面板画拓扑图。
|
|
112
|
+
*
|
|
113
|
+
* 归属判定(哪些注册表实例该由本组收集)刻意留在这里而不是让调用方自己判 ——
|
|
114
|
+
* 规则与 `_accepts` 必须同源,否则面板报的「未合并数」会与实际渲染不符。
|
|
115
|
+
*
|
|
116
|
+
* ⚠️ 子实例标签取 `name`,其次取构造器名:业务子类若没设 `name`,生产构建(压缩后)
|
|
117
|
+
* 构造器名会变成短标识符。要在拓扑图里认出实例,请给实例设 `name`(同 [[InstancedChild]] 的控制台可读命名约定)。
|
|
118
|
+
*/
|
|
119
|
+
snapshot() {
|
|
120
|
+
const batches = [];
|
|
121
|
+
let merged = 0;
|
|
122
|
+
for (const batch of this.batches.values()) {
|
|
123
|
+
const childCount = batch.childCount;
|
|
124
|
+
merged += childCount;
|
|
125
|
+
const first = batch.pickables[0];
|
|
126
|
+
const mat = first?.material;
|
|
127
|
+
batches.push({
|
|
128
|
+
key: batch.key,
|
|
129
|
+
childCount,
|
|
130
|
+
drawCalls: batch.pickables.length,
|
|
131
|
+
capacity: batch.capacity,
|
|
132
|
+
ready: batch.ready,
|
|
133
|
+
counts: batch.pickables.map((im) => im.count),
|
|
134
|
+
children: batch.childrenView.map((c) => c.name || c.constructor.name),
|
|
135
|
+
geomType: first?.geometry.type ?? "",
|
|
136
|
+
matType: Array.isArray(mat) ? "多材质" : mat?.type ?? "",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
let accepted = 0;
|
|
140
|
+
for (const child of instancedChildRegistry) {
|
|
141
|
+
if (this._accepts(child)) {
|
|
142
|
+
accepted++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
owner: this.owner,
|
|
147
|
+
batches,
|
|
148
|
+
merged,
|
|
149
|
+
registryTotal: instancedChildRegistry.size,
|
|
150
|
+
createdTotal: InstancedChild.createdTotal,
|
|
151
|
+
leaked: accepted - merged,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
110
154
|
/**
|
|
111
155
|
* 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
|
|
112
156
|
*
|