three-base 2.0.142 → 2.0.145

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.
Files changed (227) hide show
  1. package/DirectionalLight.d.ts +6 -6
  2. package/DirectionalLight.js +68 -68
  3. package/EventMap.d.ts +103 -103
  4. package/EventMap.js +17 -17
  5. package/EventMesh.d.ts +18 -18
  6. package/EventMesh.js +59 -59
  7. package/ExtrudeMesh.d.ts +112 -112
  8. package/ExtrudeMesh.js +342 -342
  9. package/FPScontrol.d.ts +511 -511
  10. package/FPScontrol.js +1799 -1799
  11. package/FPScontrol.ts +1855 -1855
  12. package/Fire/Fire.d.ts +28 -28
  13. package/Fire/Fire.js +80 -80
  14. package/Fire/FireGeometry.d.ts +9 -9
  15. package/Fire/FireGeometry.js +40 -40
  16. package/Fire/FireMaterial.d.ts +28 -28
  17. package/Fire/FireMaterial.js +32 -32
  18. package/Fire/index.d.ts +2 -2
  19. package/Fire/index.js +2 -2
  20. package/GLTFDracoLoader.d.ts +71 -71
  21. package/GLTFDracoLoader.js +266 -266
  22. package/GLTFDracoLoader.ts +1 -1
  23. package/Grass/Grass.d.ts +8 -8
  24. package/Grass/Grass.js +38 -38
  25. package/Grass/index.d.ts +2 -2
  26. package/Grass/index.js +2 -2
  27. package/Ground1/Ground1.d.ts +10 -10
  28. package/Ground1/Ground1.js +71 -71
  29. package/Ground1/index.d.ts +1 -1
  30. package/Ground1/index.js +1 -1
  31. package/MeshBase.d.ts +36 -36
  32. package/MeshBase.js +247 -247
  33. package/Mirror.d.ts +25 -25
  34. package/Mirror.js +143 -143
  35. package/MktMesh/MktMesh.d.ts +301 -291
  36. package/MktMesh/MktMesh.js +1064 -1045
  37. package/MktMesh/MktMesh.ts +32 -11
  38. package/MktMesh/assetUrl.d.ts +59 -35
  39. package/MktMesh/assetUrl.js +154 -69
  40. package/MktMesh/assetUrl.ts +155 -69
  41. package/MktMesh/mktMappingShader.d.ts +12 -12
  42. package/MktMesh/mktMappingShader.js +138 -138
  43. package/MktMesh/mktMappingShader.ts +139 -139
  44. package/MktMesh/mktShader.d.ts +263 -263
  45. package/MktMesh/mktShader.js +1236 -1236
  46. package/MktMesh/mktShader.ts +1344 -1344
  47. package/MktMesh/panoBitmapTexture.d.ts +25 -25
  48. package/MktMesh/panoBitmapTexture.js +52 -52
  49. package/MktMesh/panoBitmapTexture.ts +54 -54
  50. package/MktMesh/panoSlotBind.d.ts +29 -29
  51. package/MktMesh/panoSlotBind.js +106 -106
  52. package/MktMesh/panoSlotBind.ts +153 -153
  53. package/MktMesh/panoSlotRoster.d.ts +22 -22
  54. package/MktMesh/panoSlotRoster.js +169 -169
  55. package/MktMesh/panoSlotRoster.ts +168 -168
  56. package/MktMesh/panoTileStitcher.d.ts +119 -119
  57. package/MktMesh/panoTileStitcher.js +529 -529
  58. package/MktMesh/panoTileStitcher.ts +616 -616
  59. package/MktMesh/types.d.ts +75 -75
  60. package/MktMesh/types.js +1 -1
  61. package/MktMesh/wallCapTessellate.d.ts +33 -33
  62. package/MktMesh/wallCapTessellate.js +97 -97
  63. package/MktMesh/wallCapTessellate.ts +68 -68
  64. package/PipeMesh/PathGeometry.d.ts +44 -44
  65. package/PipeMesh/PathGeometry.js +307 -307
  66. package/PipeMesh/PathPoint.d.ts +17 -17
  67. package/PipeMesh/PathPoint.js +31 -31
  68. package/PipeMesh/PathPointList.d.ts +31 -31
  69. package/PipeMesh/PathPointList.js +209 -209
  70. package/PipeMesh/PathTubeGeometry.d.ts +25 -25
  71. package/PipeMesh/PathTubeGeometry.js +130 -130
  72. package/PipeMesh/PipeMesh.d.ts +25 -25
  73. package/PipeMesh/PipeMesh.js +63 -63
  74. package/PipeMesh/index.d.ts +1 -1
  75. package/PipeMesh/index.js +1 -1
  76. package/PlaneModal1/PlaneModal1.d.ts +34 -34
  77. package/PlaneModal1/PlaneModal1.js +109 -109
  78. package/PlaneModal1/index.d.ts +1 -1
  79. package/PlaneModal1/index.js +1 -1
  80. package/PlaneText.d.ts +27 -27
  81. package/PlaneText.js +35 -35
  82. package/PlaneWithHtml.d.ts +13 -13
  83. package/PlaneWithHtml.js +64 -64
  84. package/PointLight/PointLight.d.ts +13 -13
  85. package/PointLight/PointLight.js +64 -64
  86. package/PointLight/index.d.ts +1 -1
  87. package/PointLight/index.js +1 -1
  88. package/Sky/Sky.d.ts +9 -9
  89. package/Sky/Sky.js +22 -22
  90. package/Sky/index.d.ts +1 -1
  91. package/Sky/index.js +1 -1
  92. package/SpotLight.d.ts +6 -6
  93. package/SpotLight.js +72 -72
  94. package/SpriteModal1/SpriteModal1.d.ts +33 -33
  95. package/SpriteModal1/SpriteModal1.js +116 -116
  96. package/SpriteModal1/index.d.ts +1 -1
  97. package/SpriteModal1/index.js +1 -1
  98. package/SpriteText.d.ts +46 -46
  99. package/SpriteText.js +176 -176
  100. package/SpriteText2.d.ts +48 -48
  101. package/SpriteText2.js +223 -223
  102. package/SpriteText3.d.ts +86 -86
  103. package/SpriteText3.js +517 -517
  104. package/SpriteText3.ts +599 -599
  105. package/SpriteWithHtml.d.ts +9 -9
  106. package/SpriteWithHtml.js +44 -44
  107. package/Text3D/Text3D.d.ts +20 -20
  108. package/Text3D/Text3D.js +58 -58
  109. package/Text3D/fontController.d.ts +60 -60
  110. package/Text3D/fontController.js +166 -166
  111. package/Text3D/index.d.ts +1 -1
  112. package/Text3D/index.js +1 -1
  113. package/ThreeBase.d.ts +248 -248
  114. package/ThreeBase.js +1202 -1202
  115. package/ThreeBase.ts +1314 -1314
  116. package/ThreeMouse.d.ts +53 -54
  117. package/ThreeMouse.js +453 -453
  118. package/Transition.d.ts +45 -45
  119. package/Transition.js +209 -209
  120. package/Water/Water.d.ts +5 -5
  121. package/Water/Water.js +44 -44
  122. package/Water/index.d.ts +2 -2
  123. package/Water/index.js +2 -2
  124. package/WaterFlow/FloatingDuck.d.ts +88 -88
  125. package/WaterFlow/FloatingDuck.js +307 -307
  126. package/WaterFlow/FloatingDuck.ts +445 -445
  127. package/WaterFlow/GPUFluidSimulation.d.ts +56 -56
  128. package/WaterFlow/GPUFluidSimulation.js +781 -781
  129. package/WaterFlow/GPUFluidSimulation.ts +827 -827
  130. package/WaterFlow/ReservoirSystem.d.ts +106 -106
  131. package/WaterFlow/ReservoirSystem.js +611 -611
  132. package/WaterFlow/ReservoirSystem.ts +754 -754
  133. package/WaterFlow/SPHField.d.ts +35 -35
  134. package/WaterFlow/SPHField.js +203 -203
  135. package/WaterFlow/SPHField.ts +228 -228
  136. package/WaterFlow/ScreenSpaceFluidPass.d.ts +69 -69
  137. package/WaterFlow/ScreenSpaceFluidPass.js +728 -728
  138. package/WaterFlow/ScreenSpaceFluidPass.ts +789 -789
  139. package/WaterFlow/ShallowWaterSurface.d.ts +45 -45
  140. package/WaterFlow/ShallowWaterSurface.js +438 -438
  141. package/WaterFlow/ShallowWaterSurface.ts +510 -510
  142. package/WaterFlow/WaterFlow.d.ts +87 -87
  143. package/WaterFlow/WaterFlow.js +171 -171
  144. package/WaterFlow/WaterFlow.ts +219 -219
  145. package/WaterFlow/types.d.ts +161 -161
  146. package/WaterFlow/types.js +1 -1
  147. package/WaterFlow/types.ts +189 -189
  148. package/WaterFlow/waterFlowPass.d.ts +3 -3
  149. package/WaterFlow/waterFlowPass.js +20 -20
  150. package/WaterFlow/waterFlowPass.ts +40 -40
  151. package/collisionOBB.d.ts +42 -42
  152. package/collisionOBB.js +112 -112
  153. package/collisionOBB.ts +108 -108
  154. package/declare.d.ts +25 -25
  155. package/index.d.ts +1 -1
  156. package/index.js +1 -1
  157. package/instanced/InstancedBatch.d.ts +57 -57
  158. package/instanced/InstancedBatch.js +348 -348
  159. package/instanced/InstancedBatch.ts +406 -406
  160. package/instanced/InstancedChild.d.ts +174 -174
  161. package/instanced/InstancedChild.js +264 -264
  162. package/instanced/InstancedChild.ts +327 -327
  163. package/instanced/InstancedGroup.d.ts +43 -43
  164. package/instanced/InstancedGroup.js +126 -126
  165. package/instanced/InstancedGroup.ts +137 -137
  166. package/instanced/InstancedMeshBase.d.ts +38 -38
  167. package/instanced/InstancedMeshBase.js +282 -282
  168. package/instanced/InstancedTemplate.d.ts +46 -46
  169. package/instanced/InstancedTemplate.js +125 -125
  170. package/keyControls.d.ts +29 -30
  171. package/keyControls.js +101 -101
  172. package/lensflare/LensFlare.d.ts +39 -39
  173. package/lensflare/LensFlare.js +284 -284
  174. package/package.json +6 -1
  175. package/pass/bloom_pass.d.ts +2 -2
  176. package/pass/bloom_pass.js +174 -174
  177. package/pass/cover_pass.d.ts +2 -2
  178. package/pass/cover_pass.js +108 -108
  179. package/pass/debugDepthState.d.ts +20 -20
  180. package/pass/debugDepthState.js +9 -9
  181. package/pass/debugDepthState.ts +22 -22
  182. package/pass/depthTestBias.d.ts +12 -12
  183. package/pass/depthTestBias.js +12 -12
  184. package/pass/depthTestBias.ts +12 -12
  185. package/pass/effectUserData.d.ts +27 -27
  186. package/pass/effectUserData.js +1 -1
  187. package/pass/effectUserData.ts +46 -46
  188. package/pass/frosted_glass_pass.d.ts +2 -2
  189. package/pass/frosted_glass_pass.js +150 -150
  190. package/pass/fsQuad.d.ts +9 -9
  191. package/pass/fsQuad.js +28 -28
  192. package/pass/glow_pass.d.ts +2 -2
  193. package/pass/glow_pass.js +350 -350
  194. package/pass/screen_bloom_pass.d.ts +11 -11
  195. package/pass/screen_bloom_pass.js +136 -136
  196. package/pass/ssao_pass.d.ts +88 -88
  197. package/pass/ssao_pass.js +799 -799
  198. package/pass/ssao_pass.ts +918 -918
  199. package/pass/stroke_pass.d.ts +2 -2
  200. package/pass/stroke_pass.js +267 -267
  201. package/shaders/FXAAShaderMaterial.d.ts +3 -3
  202. package/shaders/FXAAShaderMaterial.js +20 -20
  203. package/shaders/baseDepthVisShader.d.ts +11 -11
  204. package/shaders/baseDepthVisShader.js +36 -36
  205. package/shaders/baseDepthVisShader.ts +36 -36
  206. package/shaders/baseTextureToRenderShader.d.ts +2 -2
  207. package/shaders/baseTextureToRenderShader.js +19 -19
  208. package/shaders/copyShader.d.ts +3 -3
  209. package/shaders/copyShader.js +13 -13
  210. package/shaders/debugDepthCompareShader.d.ts +14 -14
  211. package/shaders/debugDepthCompareShader.js +49 -49
  212. package/shaders/debugDepthCompareShader.ts +50 -50
  213. package/shaders/debugDepthJudgeShader.d.ts +18 -18
  214. package/shaders/debugDepthJudgeShader.js +52 -52
  215. package/shaders/debugDepthJudgeShader.ts +54 -54
  216. package/shaders/debugExcludedShader.d.ts +13 -13
  217. package/shaders/debugExcludedShader.js +46 -46
  218. package/shaders/debugExcludedShader.ts +48 -48
  219. package/spriteTextBg.d.ts +1 -1
  220. package/spriteTextBg.js +20 -20
  221. package/spriteTextBg.ts +20 -20
  222. package/threeLoader.d.ts +35 -35
  223. package/threeLoader.js +518 -518
  224. package/threeUtils.d.ts +53 -53
  225. package/threeUtils.js +329 -329
  226. package/types.d.ts +2 -2
  227. package/types.js +6 -6
@@ -1,43 +1,43 @@
1
- import { Object3D } from "three";
2
- import InstancedChild from "./InstancedChild";
3
- import InstancedBatch from "./InstancedBatch";
4
- /**
5
- * 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
6
- * 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
7
- * batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
8
- *
9
- * 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
10
- * 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
11
- */
12
- export default class InstancedGroup extends Object3D {
13
- /** batchKey -> InstancedBatch */
14
- batches: Map<string, InstancedBatch>;
15
- private _clock;
16
- /**
17
- * 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
18
- * - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
19
- * - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
20
- * 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
21
- */
22
- readonly owner?: string;
23
- constructor(owner?: string);
24
- /** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
25
- private _accepts;
26
- /** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
27
- update(): void;
28
- /** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
29
- resolveInstance(mesh: Object3D, instanceId: number | undefined): InstancedChild | null;
30
- /** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
31
- collectPickable(out: Object3D[]): void;
32
- /**
33
- * 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
34
- *
35
- * 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
36
- * 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
37
- *
38
- * 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
39
- * 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
40
- * ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
41
- */
42
- dispose(): void;
43
- }
1
+ import { Object3D } from "three";
2
+ import InstancedChild from "./InstancedChild";
3
+ import InstancedBatch from "./InstancedBatch";
4
+ /**
5
+ * 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
6
+ * 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
7
+ * batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
8
+ *
9
+ * 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
10
+ * 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
11
+ */
12
+ export default class InstancedGroup extends Object3D {
13
+ /** batchKey -> InstancedBatch */
14
+ batches: Map<string, InstancedBatch>;
15
+ private _clock;
16
+ /**
17
+ * 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
18
+ * - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
19
+ * - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
20
+ * 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
21
+ */
22
+ readonly owner?: string;
23
+ constructor(owner?: string);
24
+ /** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
25
+ private _accepts;
26
+ /** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
27
+ update(): void;
28
+ /** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
29
+ resolveInstance(mesh: Object3D, instanceId: number | undefined): InstancedChild | null;
30
+ /** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
31
+ collectPickable(out: Object3D[]): void;
32
+ /**
33
+ * 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
34
+ *
35
+ * 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
36
+ * 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
37
+ *
38
+ * 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
39
+ * 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
40
+ * ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
41
+ */
42
+ dispose(): void;
43
+ }
@@ -1,126 +1,126 @@
1
- import { Clock, Object3D } from "three";
2
- import { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
3
- import InstancedBatch from "./InstancedBatch";
4
- const PICK_EVENT_TYPES = [
5
- "mouseenter",
6
- "mouseleave",
7
- "mousemove",
8
- "click",
9
- "leftclick",
10
- "rightclick",
11
- "dbclick",
12
- "mousedown",
13
- "mouseup",
14
- ];
15
- /**
16
- * 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
17
- * 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
18
- * batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
19
- *
20
- * 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
21
- * 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
22
- */
23
- export default class InstancedGroup extends Object3D {
24
- /** batchKey -> InstancedBatch */
25
- batches = new Map();
26
- _clock = new Clock();
27
- /**
28
- * 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
29
- * - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
30
- * - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
31
- * 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
32
- */
33
- owner;
34
- constructor(owner) {
35
- super();
36
- this.owner = owner;
37
- }
38
- /** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
39
- _accepts(child) {
40
- const childOwner = instancedChildOwner.get(child);
41
- if (this.owner === undefined) {
42
- // 无归属的组(ThreeBase 默认组):只收未声明归属的实例,跳过已归属他组的
43
- return childOwner === undefined;
44
- }
45
- // 有归属的组:只收 owner 完全一致的实例
46
- return childOwner === this.owner;
47
- }
48
- /** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
49
- update() {
50
- const delta = this._clock.getDelta();
51
- // 1. 按 batchKey 收集所有活着的 InstancedChild(按 owner 归属过滤,避免多组互相污染)
52
- const groups = new Map();
53
- for (const child of instancedChildRegistry) {
54
- if (!this._accepts(child))
55
- continue;
56
- // 强制刷新世界矩阵(实例可能位于任意层级,不依赖 scene 遍历顺序)
57
- child.updateWorldMatrix(true, false);
58
- child.markDirtyIfWorldChanged();
59
- const key = child.getBatchKey();
60
- let arr = groups.get(key);
61
- if (!arr) {
62
- arr = [];
63
- groups.set(key, arr);
64
- }
65
- arr.push(child);
66
- }
67
- // 2. 创建 / 更新每个有实例的 batch(按 glb+当前动画分桶,每个动画一个常驻 batch)
68
- for (const [key, children] of groups) {
69
- let batch = this.batches.get(key);
70
- if (!batch) {
71
- batch = new InstancedBatch(key, children[0]);
72
- this.batches.set(key, batch);
73
- this.add(batch.group);
74
- }
75
- batch.sync(children, delta);
76
- }
77
- // 3. 持久化:实例已迁移走的空 batch 仍需 sync([]) 把 count 归零,避免残留上一帧实例;
78
- // 但【不销毁】,使其常驻可复用 —— 切换动作时实例在常驻 batch 间迁移,无需频繁创建/销毁 InstancedBatch。
79
- for (const [key, batch] of this.batches) {
80
- if (!groups.has(key)) {
81
- batch.sync([], delta);
82
- }
83
- }
84
- }
85
- /** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
86
- resolveInstance(mesh, instanceId) {
87
- if (instanceId === undefined) {
88
- return null;
89
- }
90
- const key = mesh.userData?.__batchKey;
91
- if (!key) {
92
- return null;
93
- }
94
- const batch = this.batches.get(key);
95
- if (!batch) {
96
- return null;
97
- }
98
- return batch.resolveInstance(mesh, instanceId);
99
- }
100
- /** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
101
- collectPickable(out) {
102
- for (const batch of this.batches.values()) {
103
- if (batch.ready && (batch.hasListener(PICK_EVENT_TYPES) || batch.hasCursor())) {
104
- for (const im of batch.pickables) {
105
- out.push(im);
106
- }
107
- }
108
- }
109
- }
110
- /**
111
- * 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
112
- *
113
- * 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
114
- * 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
115
- *
116
- * 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
117
- * 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
118
- * ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
119
- */
120
- dispose() {
121
- for (const batch of this.batches.values()) {
122
- batch.dispose();
123
- }
124
- this.batches.clear();
125
- }
126
- }
1
+ import { Clock, Object3D } from "three";
2
+ import { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
3
+ import InstancedBatch from "./InstancedBatch";
4
+ const PICK_EVENT_TYPES = [
5
+ "mouseenter",
6
+ "mouseleave",
7
+ "mousemove",
8
+ "click",
9
+ "leftclick",
10
+ "rightclick",
11
+ "dbclick",
12
+ "mousedown",
13
+ "mouseup",
14
+ ];
15
+ /**
16
+ * 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
17
+ * 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
18
+ * batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
19
+ *
20
+ * 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
21
+ * 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
22
+ */
23
+ export default class InstancedGroup extends Object3D {
24
+ /** batchKey -> InstancedBatch */
25
+ batches = new Map();
26
+ _clock = new Clock();
27
+ /**
28
+ * 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
29
+ * - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
30
+ * - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
31
+ * 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
32
+ */
33
+ owner;
34
+ constructor(owner) {
35
+ super();
36
+ this.owner = owner;
37
+ }
38
+ /** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
39
+ _accepts(child) {
40
+ const childOwner = instancedChildOwner.get(child);
41
+ if (this.owner === undefined) {
42
+ // 无归属的组(ThreeBase 默认组):只收未声明归属的实例,跳过已归属他组的
43
+ return childOwner === undefined;
44
+ }
45
+ // 有归属的组:只收 owner 完全一致的实例
46
+ return childOwner === this.owner;
47
+ }
48
+ /** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
49
+ update() {
50
+ const delta = this._clock.getDelta();
51
+ // 1. 按 batchKey 收集所有活着的 InstancedChild(按 owner 归属过滤,避免多组互相污染)
52
+ const groups = new Map();
53
+ for (const child of instancedChildRegistry) {
54
+ if (!this._accepts(child))
55
+ continue;
56
+ // 强制刷新世界矩阵(实例可能位于任意层级,不依赖 scene 遍历顺序)
57
+ child.updateWorldMatrix(true, false);
58
+ child.markDirtyIfWorldChanged();
59
+ const key = child.getBatchKey();
60
+ let arr = groups.get(key);
61
+ if (!arr) {
62
+ arr = [];
63
+ groups.set(key, arr);
64
+ }
65
+ arr.push(child);
66
+ }
67
+ // 2. 创建 / 更新每个有实例的 batch(按 glb+当前动画分桶,每个动画一个常驻 batch)
68
+ for (const [key, children] of groups) {
69
+ let batch = this.batches.get(key);
70
+ if (!batch) {
71
+ batch = new InstancedBatch(key, children[0]);
72
+ this.batches.set(key, batch);
73
+ this.add(batch.group);
74
+ }
75
+ batch.sync(children, delta);
76
+ }
77
+ // 3. 持久化:实例已迁移走的空 batch 仍需 sync([]) 把 count 归零,避免残留上一帧实例;
78
+ // 但【不销毁】,使其常驻可复用 —— 切换动作时实例在常驻 batch 间迁移,无需频繁创建/销毁 InstancedBatch。
79
+ for (const [key, batch] of this.batches) {
80
+ if (!groups.has(key)) {
81
+ batch.sync([], delta);
82
+ }
83
+ }
84
+ }
85
+ /** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
86
+ resolveInstance(mesh, instanceId) {
87
+ if (instanceId === undefined) {
88
+ return null;
89
+ }
90
+ const key = mesh.userData?.__batchKey;
91
+ if (!key) {
92
+ return null;
93
+ }
94
+ const batch = this.batches.get(key);
95
+ if (!batch) {
96
+ return null;
97
+ }
98
+ return batch.resolveInstance(mesh, instanceId);
99
+ }
100
+ /** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
101
+ collectPickable(out) {
102
+ for (const batch of this.batches.values()) {
103
+ if (batch.ready && (batch.hasListener(PICK_EVENT_TYPES) || batch.hasCursor())) {
104
+ for (const im of batch.pickables) {
105
+ out.push(im);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
112
+ *
113
+ * 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
114
+ * 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
115
+ *
116
+ * 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
117
+ * 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
118
+ * ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
119
+ */
120
+ dispose() {
121
+ for (const batch of this.batches.values()) {
122
+ batch.dispose();
123
+ }
124
+ this.batches.clear();
125
+ }
126
+ }
@@ -1,137 +1,137 @@
1
- import { Clock, Object3D } from "three";
2
- import InstancedChild, { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
3
- import InstancedBatch from "./InstancedBatch";
4
-
5
- const PICK_EVENT_TYPES = [
6
- "mouseenter",
7
- "mouseleave",
8
- "mousemove",
9
- "click",
10
- "leftclick",
11
- "rightclick",
12
- "dbclick",
13
- "mousedown",
14
- "mouseup",
15
- ];
16
-
17
- /**
18
- * 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
19
- * 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
20
- * batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
21
- *
22
- * 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
23
- * 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
24
- */
25
- export default class InstancedGroup extends Object3D {
26
- /** batchKey -> InstancedBatch */
27
- batches = new Map<string, InstancedBatch>();
28
- private _clock = new Clock();
29
-
30
- /**
31
- * 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
32
- * - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
33
- * - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
34
- * 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
35
- */
36
- readonly owner?: string;
37
-
38
- constructor(owner?: string) {
39
- super();
40
- this.owner = owner;
41
- }
42
-
43
- /** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
44
- private _accepts(child: InstancedChild): boolean {
45
- const childOwner = instancedChildOwner.get(child);
46
- if (this.owner === undefined) {
47
- // 无归属的组(ThreeBase 默认组):只收未声明归属的实例,跳过已归属他组的
48
- return childOwner === undefined;
49
- }
50
- // 有归属的组:只收 owner 完全一致的实例
51
- return childOwner === this.owner;
52
- }
53
-
54
- /** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
55
- update(): void {
56
- const delta = this._clock.getDelta();
57
-
58
- // 1. 按 batchKey 收集所有活着的 InstancedChild(按 owner 归属过滤,避免多组互相污染)
59
- const groups = new Map<string, InstancedChild[]>();
60
- for (const child of instancedChildRegistry) {
61
- if (!this._accepts(child)) continue;
62
- // 强制刷新世界矩阵(实例可能位于任意层级,不依赖 scene 遍历顺序)
63
- child.updateWorldMatrix(true, false);
64
- child.markDirtyIfWorldChanged();
65
- const key = child.getBatchKey();
66
- let arr = groups.get(key);
67
- if (!arr) {
68
- arr = [];
69
- groups.set(key, arr);
70
- }
71
- arr.push(child);
72
- }
73
-
74
- // 2. 创建 / 更新每个有实例的 batch(按 glb+当前动画分桶,每个动画一个常驻 batch)
75
- for (const [key, children] of groups) {
76
- let batch = this.batches.get(key);
77
- if (!batch) {
78
- batch = new InstancedBatch(key, children[0]);
79
- this.batches.set(key, batch);
80
- this.add(batch.group);
81
- }
82
- batch.sync(children, delta);
83
- }
84
-
85
- // 3. 持久化:实例已迁移走的空 batch 仍需 sync([]) 把 count 归零,避免残留上一帧实例;
86
- // 但【不销毁】,使其常驻可复用 —— 切换动作时实例在常驻 batch 间迁移,无需频繁创建/销毁 InstancedBatch。
87
- for (const [key, batch] of this.batches) {
88
- if (!groups.has(key)) {
89
- batch.sync([], delta);
90
- }
91
- }
92
- }
93
-
94
- /** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
95
- resolveInstance(mesh: Object3D, instanceId: number | undefined): InstancedChild | null {
96
- if (instanceId === undefined) {
97
- return null;
98
- }
99
- const key = (mesh as any).userData?.__batchKey as string | undefined;
100
- if (!key) {
101
- return null;
102
- }
103
- const batch = this.batches.get(key);
104
- if (!batch) {
105
- return null;
106
- }
107
- return batch.resolveInstance(mesh as any, instanceId);
108
- }
109
-
110
- /** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
111
- collectPickable(out: Object3D[]): void {
112
- for (const batch of this.batches.values()) {
113
- if (batch.ready && (batch.hasListener(PICK_EVENT_TYPES) || batch.hasCursor())) {
114
- for (const im of batch.pickables) {
115
- out.push(im);
116
- }
117
- }
118
- }
119
- }
120
-
121
- /**
122
- * 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
123
- *
124
- * 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
125
- * 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
126
- *
127
- * 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
128
- * 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
129
- * ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
130
- */
131
- dispose(): void {
132
- for (const batch of this.batches.values()) {
133
- batch.dispose();
134
- }
135
- this.batches.clear();
136
- }
137
- }
1
+ import { Clock, Object3D } from "three";
2
+ import InstancedChild, { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
3
+ import InstancedBatch from "./InstancedBatch";
4
+
5
+ const PICK_EVENT_TYPES = [
6
+ "mouseenter",
7
+ "mouseleave",
8
+ "mousemove",
9
+ "click",
10
+ "leftclick",
11
+ "rightclick",
12
+ "dbclick",
13
+ "mousedown",
14
+ "mouseup",
15
+ ];
16
+
17
+ /**
18
+ * 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
19
+ * 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
20
+ * batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
21
+ *
22
+ * 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
23
+ * 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
24
+ */
25
+ export default class InstancedGroup extends Object3D {
26
+ /** batchKey -> InstancedBatch */
27
+ batches = new Map<string, InstancedBatch>();
28
+ private _clock = new Clock();
29
+
30
+ /**
31
+ * 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
32
+ * - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
33
+ * - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
34
+ * 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
35
+ */
36
+ readonly owner?: string;
37
+
38
+ constructor(owner?: string) {
39
+ super();
40
+ this.owner = owner;
41
+ }
42
+
43
+ /** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
44
+ private _accepts(child: InstancedChild): boolean {
45
+ const childOwner = instancedChildOwner.get(child);
46
+ if (this.owner === undefined) {
47
+ // 无归属的组(ThreeBase 默认组):只收未声明归属的实例,跳过已归属他组的
48
+ return childOwner === undefined;
49
+ }
50
+ // 有归属的组:只收 owner 完全一致的实例
51
+ return childOwner === this.owner;
52
+ }
53
+
54
+ /** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
55
+ update(): void {
56
+ const delta = this._clock.getDelta();
57
+
58
+ // 1. 按 batchKey 收集所有活着的 InstancedChild(按 owner 归属过滤,避免多组互相污染)
59
+ const groups = new Map<string, InstancedChild[]>();
60
+ for (const child of instancedChildRegistry) {
61
+ if (!this._accepts(child)) continue;
62
+ // 强制刷新世界矩阵(实例可能位于任意层级,不依赖 scene 遍历顺序)
63
+ child.updateWorldMatrix(true, false);
64
+ child.markDirtyIfWorldChanged();
65
+ const key = child.getBatchKey();
66
+ let arr = groups.get(key);
67
+ if (!arr) {
68
+ arr = [];
69
+ groups.set(key, arr);
70
+ }
71
+ arr.push(child);
72
+ }
73
+
74
+ // 2. 创建 / 更新每个有实例的 batch(按 glb+当前动画分桶,每个动画一个常驻 batch)
75
+ for (const [key, children] of groups) {
76
+ let batch = this.batches.get(key);
77
+ if (!batch) {
78
+ batch = new InstancedBatch(key, children[0]);
79
+ this.batches.set(key, batch);
80
+ this.add(batch.group);
81
+ }
82
+ batch.sync(children, delta);
83
+ }
84
+
85
+ // 3. 持久化:实例已迁移走的空 batch 仍需 sync([]) 把 count 归零,避免残留上一帧实例;
86
+ // 但【不销毁】,使其常驻可复用 —— 切换动作时实例在常驻 batch 间迁移,无需频繁创建/销毁 InstancedBatch。
87
+ for (const [key, batch] of this.batches) {
88
+ if (!groups.has(key)) {
89
+ batch.sync([], delta);
90
+ }
91
+ }
92
+ }
93
+
94
+ /** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
95
+ resolveInstance(mesh: Object3D, instanceId: number | undefined): InstancedChild | null {
96
+ if (instanceId === undefined) {
97
+ return null;
98
+ }
99
+ const key = (mesh as any).userData?.__batchKey as string | undefined;
100
+ if (!key) {
101
+ return null;
102
+ }
103
+ const batch = this.batches.get(key);
104
+ if (!batch) {
105
+ return null;
106
+ }
107
+ return batch.resolveInstance(mesh as any, instanceId);
108
+ }
109
+
110
+ /** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
111
+ collectPickable(out: Object3D[]): void {
112
+ for (const batch of this.batches.values()) {
113
+ if (batch.ready && (batch.hasListener(PICK_EVENT_TYPES) || batch.hasCursor())) {
114
+ for (const im of batch.pickables) {
115
+ out.push(im);
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
123
+ *
124
+ * 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
125
+ * 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
126
+ *
127
+ * 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
128
+ * 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
129
+ * ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
130
+ */
131
+ dispose(): void {
132
+ for (const batch of this.batches.values()) {
133
+ batch.dispose();
134
+ }
135
+ this.batches.clear();
136
+ }
137
+ }