three-base 2.0.142 → 2.0.166

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 (231) 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 +531 -511
  10. package/FPScontrol.js +1866 -1799
  11. package/FPScontrol.ts +1922 -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 +251 -247
  33. package/MeshBase.ts +6 -2
  34. package/Mirror.d.ts +25 -25
  35. package/Mirror.js +143 -143
  36. package/MktMesh/MktMesh.d.ts +301 -291
  37. package/MktMesh/MktMesh.js +1064 -1045
  38. package/MktMesh/MktMesh.ts +32 -11
  39. package/MktMesh/assetUrl.d.ts +59 -35
  40. package/MktMesh/assetUrl.js +165 -69
  41. package/MktMesh/assetUrl.ts +164 -69
  42. package/MktMesh/mktMappingShader.d.ts +12 -12
  43. package/MktMesh/mktMappingShader.js +138 -138
  44. package/MktMesh/mktMappingShader.ts +139 -139
  45. package/MktMesh/mktShader.d.ts +263 -263
  46. package/MktMesh/mktShader.js +1236 -1236
  47. package/MktMesh/mktShader.ts +1344 -1344
  48. package/MktMesh/panoBitmapTexture.d.ts +25 -25
  49. package/MktMesh/panoBitmapTexture.js +52 -52
  50. package/MktMesh/panoBitmapTexture.ts +54 -54
  51. package/MktMesh/panoSlotBind.d.ts +29 -29
  52. package/MktMesh/panoSlotBind.js +106 -106
  53. package/MktMesh/panoSlotBind.ts +153 -153
  54. package/MktMesh/panoSlotRoster.d.ts +22 -22
  55. package/MktMesh/panoSlotRoster.js +169 -169
  56. package/MktMesh/panoSlotRoster.ts +168 -168
  57. package/MktMesh/panoTileStitcher.d.ts +119 -119
  58. package/MktMesh/panoTileStitcher.js +529 -529
  59. package/MktMesh/panoTileStitcher.ts +616 -616
  60. package/MktMesh/types.d.ts +75 -75
  61. package/MktMesh/types.js +1 -1
  62. package/MktMesh/wallCapTessellate.d.ts +33 -33
  63. package/MktMesh/wallCapTessellate.js +97 -97
  64. package/MktMesh/wallCapTessellate.ts +68 -68
  65. package/PipeMesh/PathGeometry.d.ts +44 -44
  66. package/PipeMesh/PathGeometry.js +307 -307
  67. package/PipeMesh/PathPoint.d.ts +17 -17
  68. package/PipeMesh/PathPoint.js +31 -31
  69. package/PipeMesh/PathPointList.d.ts +31 -31
  70. package/PipeMesh/PathPointList.js +209 -209
  71. package/PipeMesh/PathTubeGeometry.d.ts +25 -25
  72. package/PipeMesh/PathTubeGeometry.js +130 -130
  73. package/PipeMesh/PipeMesh.d.ts +25 -25
  74. package/PipeMesh/PipeMesh.js +63 -63
  75. package/PipeMesh/index.d.ts +1 -1
  76. package/PipeMesh/index.js +1 -1
  77. package/PlaneModal1/PlaneModal1.d.ts +34 -34
  78. package/PlaneModal1/PlaneModal1.js +109 -109
  79. package/PlaneModal1/index.d.ts +1 -1
  80. package/PlaneModal1/index.js +1 -1
  81. package/PlaneText.d.ts +27 -27
  82. package/PlaneText.js +35 -35
  83. package/PlaneWithHtml.d.ts +13 -13
  84. package/PlaneWithHtml.js +66 -64
  85. package/PlaneWithHtml.ts +3 -1
  86. package/PointLight/PointLight.d.ts +13 -13
  87. package/PointLight/PointLight.js +64 -64
  88. package/PointLight/index.d.ts +1 -1
  89. package/PointLight/index.js +1 -1
  90. package/Sky/Sky.d.ts +9 -9
  91. package/Sky/Sky.js +22 -22
  92. package/Sky/index.d.ts +1 -1
  93. package/Sky/index.js +1 -1
  94. package/SpotLight.d.ts +6 -6
  95. package/SpotLight.js +72 -72
  96. package/SpriteModal1/SpriteModal1.d.ts +33 -33
  97. package/SpriteModal1/SpriteModal1.js +116 -116
  98. package/SpriteModal1/index.d.ts +1 -1
  99. package/SpriteModal1/index.js +1 -1
  100. package/SpriteText.d.ts +46 -46
  101. package/SpriteText.js +176 -176
  102. package/SpriteText2.d.ts +48 -48
  103. package/SpriteText2.js +223 -223
  104. package/SpriteText3.d.ts +86 -86
  105. package/SpriteText3.js +517 -517
  106. package/SpriteText3.ts +599 -599
  107. package/SpriteWithHtml.d.ts +9 -9
  108. package/SpriteWithHtml.js +46 -44
  109. package/SpriteWithHtml.ts +3 -1
  110. package/Text3D/Text3D.d.ts +20 -20
  111. package/Text3D/Text3D.js +58 -58
  112. package/Text3D/fontController.d.ts +60 -60
  113. package/Text3D/fontController.js +166 -166
  114. package/Text3D/index.d.ts +1 -1
  115. package/Text3D/index.js +1 -1
  116. package/ThreeBase.d.ts +248 -248
  117. package/ThreeBase.js +1202 -1202
  118. package/ThreeBase.ts +1314 -1314
  119. package/ThreeMouse.d.ts +53 -54
  120. package/ThreeMouse.js +453 -453
  121. package/Transition.d.ts +45 -45
  122. package/Transition.js +209 -209
  123. package/Water/Water.d.ts +5 -5
  124. package/Water/Water.js +44 -44
  125. package/Water/index.d.ts +2 -2
  126. package/Water/index.js +2 -2
  127. package/WaterFlow/FloatingDuck.d.ts +88 -88
  128. package/WaterFlow/FloatingDuck.js +307 -307
  129. package/WaterFlow/FloatingDuck.ts +445 -445
  130. package/WaterFlow/GPUFluidSimulation.d.ts +56 -56
  131. package/WaterFlow/GPUFluidSimulation.js +781 -781
  132. package/WaterFlow/GPUFluidSimulation.ts +827 -827
  133. package/WaterFlow/ReservoirSystem.d.ts +106 -106
  134. package/WaterFlow/ReservoirSystem.js +611 -611
  135. package/WaterFlow/ReservoirSystem.ts +754 -754
  136. package/WaterFlow/SPHField.d.ts +35 -35
  137. package/WaterFlow/SPHField.js +203 -203
  138. package/WaterFlow/SPHField.ts +228 -228
  139. package/WaterFlow/ScreenSpaceFluidPass.d.ts +69 -69
  140. package/WaterFlow/ScreenSpaceFluidPass.js +728 -728
  141. package/WaterFlow/ScreenSpaceFluidPass.ts +789 -789
  142. package/WaterFlow/ShallowWaterSurface.d.ts +45 -45
  143. package/WaterFlow/ShallowWaterSurface.js +438 -438
  144. package/WaterFlow/ShallowWaterSurface.ts +510 -510
  145. package/WaterFlow/WaterFlow.d.ts +87 -87
  146. package/WaterFlow/WaterFlow.js +171 -171
  147. package/WaterFlow/WaterFlow.ts +219 -219
  148. package/WaterFlow/types.d.ts +161 -161
  149. package/WaterFlow/types.js +1 -1
  150. package/WaterFlow/types.ts +189 -189
  151. package/WaterFlow/waterFlowPass.d.ts +3 -3
  152. package/WaterFlow/waterFlowPass.js +20 -20
  153. package/WaterFlow/waterFlowPass.ts +40 -40
  154. package/collisionOBB.d.ts +42 -42
  155. package/collisionOBB.js +112 -112
  156. package/collisionOBB.ts +108 -108
  157. package/declare.d.ts +25 -25
  158. package/index.d.ts +1 -1
  159. package/index.js +1 -1
  160. package/instanced/InstancedBatch.d.ts +57 -57
  161. package/instanced/InstancedBatch.js +348 -348
  162. package/instanced/InstancedBatch.ts +406 -406
  163. package/instanced/InstancedChild.d.ts +174 -174
  164. package/instanced/InstancedChild.js +264 -264
  165. package/instanced/InstancedChild.ts +327 -327
  166. package/instanced/InstancedGroup.d.ts +43 -43
  167. package/instanced/InstancedGroup.js +126 -126
  168. package/instanced/InstancedGroup.ts +137 -137
  169. package/instanced/InstancedMeshBase.d.ts +38 -38
  170. package/instanced/InstancedMeshBase.js +282 -282
  171. package/instanced/InstancedTemplate.d.ts +46 -46
  172. package/instanced/InstancedTemplate.js +125 -125
  173. package/keyControls.d.ts +29 -30
  174. package/keyControls.js +101 -101
  175. package/lensflare/LensFlare.d.ts +39 -39
  176. package/lensflare/LensFlare.js +284 -284
  177. package/package.json +6 -1
  178. package/pass/bloom_pass.d.ts +2 -2
  179. package/pass/bloom_pass.js +174 -174
  180. package/pass/cover_pass.d.ts +2 -2
  181. package/pass/cover_pass.js +108 -108
  182. package/pass/debugDepthState.d.ts +20 -20
  183. package/pass/debugDepthState.js +9 -9
  184. package/pass/debugDepthState.ts +22 -22
  185. package/pass/depthTestBias.d.ts +12 -12
  186. package/pass/depthTestBias.js +12 -12
  187. package/pass/depthTestBias.ts +12 -12
  188. package/pass/effectUserData.d.ts +27 -27
  189. package/pass/effectUserData.js +1 -1
  190. package/pass/effectUserData.ts +46 -46
  191. package/pass/frosted_glass_pass.d.ts +2 -2
  192. package/pass/frosted_glass_pass.js +150 -150
  193. package/pass/fsQuad.d.ts +9 -9
  194. package/pass/fsQuad.js +28 -28
  195. package/pass/glow_pass.d.ts +2 -2
  196. package/pass/glow_pass.js +350 -350
  197. package/pass/screen_bloom_pass.d.ts +11 -11
  198. package/pass/screen_bloom_pass.js +136 -136
  199. package/pass/ssao_pass.d.ts +88 -88
  200. package/pass/ssao_pass.js +799 -799
  201. package/pass/ssao_pass.ts +918 -918
  202. package/pass/stroke_pass.d.ts +2 -2
  203. package/pass/stroke_pass.js +267 -267
  204. package/shaders/FXAAShaderMaterial.d.ts +3 -3
  205. package/shaders/FXAAShaderMaterial.js +20 -20
  206. package/shaders/baseDepthVisShader.d.ts +11 -11
  207. package/shaders/baseDepthVisShader.js +36 -36
  208. package/shaders/baseDepthVisShader.ts +36 -36
  209. package/shaders/baseTextureToRenderShader.d.ts +2 -2
  210. package/shaders/baseTextureToRenderShader.js +19 -19
  211. package/shaders/copyShader.d.ts +3 -3
  212. package/shaders/copyShader.js +13 -13
  213. package/shaders/debugDepthCompareShader.d.ts +14 -14
  214. package/shaders/debugDepthCompareShader.js +49 -49
  215. package/shaders/debugDepthCompareShader.ts +50 -50
  216. package/shaders/debugDepthJudgeShader.d.ts +18 -18
  217. package/shaders/debugDepthJudgeShader.js +52 -52
  218. package/shaders/debugDepthJudgeShader.ts +54 -54
  219. package/shaders/debugExcludedShader.d.ts +13 -13
  220. package/shaders/debugExcludedShader.js +46 -46
  221. package/shaders/debugExcludedShader.ts +48 -48
  222. package/spriteTextBg.d.ts +1 -1
  223. package/spriteTextBg.js +20 -20
  224. package/spriteTextBg.ts +20 -20
  225. package/threeLoader.d.ts +35 -35
  226. package/threeLoader.js +518 -518
  227. package/threeUtils.d.ts +71 -53
  228. package/threeUtils.js +363 -329
  229. package/threeUtils.ts +35 -0
  230. package/types.d.ts +2 -2
  231. package/types.js +6 -6
@@ -1,168 +1,168 @@
1
- /*
2
- * ⭐ 槽位名册纯函数:**「中心 + 序号邻居」**定 shader 那 N 格装哪 N 张全景(皇上旨 2026-10-02 晚;
3
- * 2026-10-06 晚旨「一次展示 8 个全景」⇒ N 从 4 变 8,靠「每个全景只占左右 2 张 RT」腾出 sampler)。
4
- *
5
- * 旨意原文(verbatim):「我想要把按距离筛选的逻辑, 改一改, 首先, 按照 全景到相机的距离选中第一个,
6
- * 然后按照这个全景的序号, 前后各添加一个全景, 举个例子, 通过计算 5号 全景距离相机最近,
7
- * 那就选出一个 5号全景填入 shader 然后再, 5号前后是 4号 和 6号, 也分别填入 shader,
8
- * 最后再判断一下, 如果相机距离4号的距离 比 相机距离6号的距离近 就再追加一个[它外侧那张]全景, 反之就追加一个 7号 全景,
9
- * 如果没有 目标全景, 比如 1号全景往前 没有 -1 -2 号全景, shader 里面就留空」
10
- * ⭐ 同日追问三择(verbatim):「补 3 号(顺着往外扩一张)」/「中心没换人就维持整组」/
11
- * 探针第一行「打成『中心#5 + 邻居 4/6/7』」。
12
- * ⭐ 皇上旨 2026-10-07(第二十道,verbatim):「shader 是可以支持最多传入8个全景的, 所以尽可能得填满8个全景位置」
13
- * ⇒ 收窄上面那句「如果没有目标全景…就留空」的适用边界:**留空只发生在「项目里的全景总数不够一格一人」时**;
14
- * 序号越界的那个位置根本不是一张全景 ⇒ 跳过它、继续往还有货的那一侧扩,槽位不许白留给「不存在的序号」。
15
- * 同一道旨还要求:新加的全景点**不必等中心换人**才进 shader ⇒ 中心没换人时在册的人照旧一格不动,空格当场补满。
16
- * ⚠️ 上限仍是 `MKT_PANO_MAX_SLOTS`(=8):8 点 × 左右 2 张拼合槽 = 16 个 sampler,那是 GLSL 物理闸
17
- * (皇上机器 maxTextures=16)⇒ 本函数不越那道闸,只是把那 8 格**尽量用满**。
18
- * ⭐ 皇上旨 2026-10-07 第二十一道旨(verbatim):「判断使用那些全景球的判断条件, 从距离相机的距离 改成
19
- * 判断距离控制器的中心点的距离来判断」⇒ **本函数的尺子另一端换了人**:函数体一个字不改(它只吃调用方算好的
20
- * `d`),改的是**调用方**——编辑态与预览的锚点 = 轨道控制器注视点 `controls.target`(画面正中央那个点),
21
- * 发布页那一路没有控制器 ⇒ 回落相机位置。下面所有「相机」二字请按「锚点(= 控制器中心点,未注入时才是相机)」读;
22
- * 10-02 那道旨的原文里「距离相机最近」是**历史引文**,逐字保留。
23
- * ⚠️ 同一把尺还喂着 `camW`(混合权重)⇒ 权重跟着换锚点;而面上「取哪一张」的 `score = 顶点距离 − 夹角`
24
- * 里**根本没有相机量**(09-28 15:07 七修亲自撤掉 camD 项),不受本道旨影响。
25
- *
26
- * 一句话口径:
27
- * 1) **中心** = 到**锚点**(调用方给的水平距离 XZ;锚点 = 控制器中心点,见上方 10-07 那道旨)最近那张;
28
- * ⚠️ 带死区 `margin`:别人必须比在册中心**近出 margin 米以上**
29
- * 才抢走中心 ⇒ 锚点在等距带抖动时整组一动不动(09-26 那条「其他 3 个依然不动」的稳定诉求搬到这里来用)。
30
- * 2) 中心定了之后按**列表序号**(= 面板列表顺序 = 日志里那个 `#i`)取邻居:`中心-1`、`中心+1` 各一张。
31
- * 3) 再往外一层层扩(`中心±2`、`中心±3`…)直到凑够槽数;**同一层里先取离锚点更近那一侧**
32
- * (平手归序号小的那侧)。⭐ 2026-10-06 晚旨「一次展示 8 个」⇒ 槽数 4 → 8,这里从「只扩一层」
33
- * 改成一层层扩下去;前 4 张的顺序与 4 槽时代**逐字相同**(中心、-1、+1、更近那侧的 ±2)。
34
- * 4) ⭐ 2026-10-07 皇上旨「shader 是可以支持最多传入 8 个全景的, 所以尽可能得填满 8 个全景位置」
35
- * ⇒ **改写 10-02 那句「没有目标全景就留空」的适用边界**:越界的那个**序号**直接跳过(它本来就不是一个全景),
36
- * 外扩**继续往还有货的那一侧**走 ⇒ 只要列表里还有没用过的全景,槽就一直被填,直到凑够 `maxSlots`。
37
- * ⛔ 只有「整个列表都被请进来了还不够一格一人的时候」(= 项目里的全景总数 < 槽数)才留空 ——
38
- * 那是物理不够,不是策略留白。旧行为(中心贴列表两端 ⇒ 明明还有 5 张没用上却空着 3 格)作废。
39
- * 5) ⭐ 中心没换人 ⇒ **在册的人一格都不动**(10-02 当日亲择,照旧);但 10-07 那道旨补了后半句:
40
- * **空格要当场补上** ⇒ 新增的全景点不必等「中心换人」才进 shader(否则他一张一张加点时永远填不满)。
41
- *
42
- * ⚠️ 与历代口径的关系(别搞混):
43
- * - 09-25「槽位与相机完全无关」→ 09-26「名册增量换人(一进一出)」→ 09-27「在册者按相机距离落槽、取最近 4 个」
44
- * ⇒ 本函数把 09-27 那半条**作废**:不再取「最近 4 个」,改取「最近那一张的序号邻居」,槽号也不再按距离排。
45
- * - 09-26 的「一进一出 + 0.5m 死区」精神保留,但对象从「谁挤掉谁」变成「谁能抢走中心」。
46
- *
47
- * ⚠️ 为什么 0 号槽必须是中心:立方体(`FurnitureObject`)那份 `BoxGeometry` **没有** `aPanoIndex` attribute ⇒
48
- * 序号映射 shader 里 `vPanoIndex` 恒 0 ⇒ 立方体显示的就是名册第 0 格那一张(皇上旨 10-02「模型也按距离相机最近的方式
49
- * 设置全景参数」的落点)。中心坐别的格 = 立方体显示错图。
50
- *
51
- * ⚠️ 为什么单独成文件:本函数零依赖(不碰 three / MobX / DOM),`test/mktmesh.test.js` 的 **AP 组**会用 esbuild
52
- * 把**这份源码原文**转译后真跑一遍(中心/邻居/外扩/越界不占格/死区/删点/幂等/空格回填 各断言一次)。
53
- * ⇒ 改这里的算法,那条数字测试立刻知道;⛔ 别把逻辑搬回渲染器里,那样就只能靠正则钉、证不了行为。
54
- *
55
- * ⭐ 皇上旨 2026-10-03「库侧也一并改成中心 ± 邻居」⇒ 本文件从 `src/views/MktMeshEdit/` **搬进库里**
56
- * (`three-base/MktMesh/`):编辑态(`mktMeshEditRenderer`)与预览/发布态(`MktMesh.updateTexture` +
57
- * `feedFaceMapping`)**共用这一份算法和下面那一个死区常数**,⛔ 不许在任一侧再抄一份 0.5 或另写一套挑图。
58
- */
59
-
60
- /** ⭐ 槽位「中心死区」(米):离**锚点**(控制器中心点;发布页未注入时 = 相机位置)水平距离最近的那张是中心;挑战者必须比现任中心**近出这么多**才抢得走。
61
- * 没有死区 ⇒ 锚点停在两点的等距带附近每帧换中心 ⇒ 换中心 = 整组邻居重排 = 一整组瓦片重绑(正是 09-24/25 报的「切全景卡一下」)。
62
- * 定义在此是因为编辑态与库侧共用(全项目**只此一处**,两侧都 import 它)。 */
63
- export const PANO_SLOT_SWAP_MARGIN_M = 0.5;
64
-
65
- /** 一个候选:`t` 是全景点对象(存引用不存下标 —— 删点会让下标整体前移,引用不会认错人),`d` 是它到**锚点**的水平距离(锚点由调用方决定,本函数不认识它) */
66
- export interface PanoRosterCand<T> {
67
- t: T;
68
- d: number;
69
- }
70
-
71
- /**
72
- * 算出下一帧的槽位名册(**不修改传进来的 roster**,返回新数组)。
73
- * @param roster 当前名册(0 号槽 = 上一帧的中心),长度可以小于 maxSlots(内部按 undefined 补齐)
74
- * @param dists 本帧全部全景点及其到**锚点**的距离(锚点 = 调用方那把尺子的端点;⭐ 10-07 第二十一道旨后 = 控制器中心点),**数组顺序 = 全景列表顺序**(下标就是旨意里说的「几号全景」)
75
- * @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-06 晚旨后是 **8**:8 点 × 每个全景
76
- * 左右 2 张 RT = 16 sampler 仍是 GLSL 物理上限。⛔ 本函数不许假设它恒等于 4)
77
- * @param margin 抢中心的死区(米,= `PANO_SLOT_SWAP_MARGIN_M`)
78
- * @returns 长度恒 = maxSlots 的新名册:**0 号槽 = 中心**,其余槽 = 列表里按序号一层层往外扩的那些
79
- * (同一层先取离锚点更近那侧)。⭐ 10-07 旨「尽可能填满」⇒ 越界的序号不占格、外扩继续往有货那侧走,
80
- * 所以**填几格 = min(项目全景总数, maxSlots)**;只有列表里的人不够时才留空(= `undefined`)。
81
- * 中心没换人 ⇒ 在册的一格都不动,只把**空格**补上。
82
- */
83
- export function panoSlotRosterStep<T>(
84
- roster: (T | undefined)[],
85
- dists: PanoRosterCand<T>[],
86
- maxSlots: number,
87
- margin: number
88
- ): (T | undefined)[] {
89
- const next = roster.slice(0, maxSlots);
90
- while (next.length < maxSlots) next.push(undefined);
91
- const alive = new Set(dists.map((n) => n.t));
92
- // ① 已被删掉的点先腾槽(名册里躺着一个不存在的点 = 那格的矩阵与权重都是旧账)
93
- for (let s = 0; s < maxSlots; s++) if (next[s] && !alive.has(next[s] as T)) next[s] = undefined;
94
- if (!dists.length) return next.fill(undefined);
95
- // ② 在册中心 = 0 号槽那位;别人必须比它近出 margin 以上才换中心
96
- const incumbent = next[0] !== undefined && alive.has(next[0] as T) ? (next[0] as T) : undefined;
97
- let nearest = 0;
98
- for (let i = 1; i < dists.length; i++) if (dists[i].d < dists[nearest].d) nearest = i;
99
- const incIdx = incumbent === undefined ? -1 : dists.findIndex((n) => n.t === (incumbent as T));
100
- const centerIdx = incIdx < 0 || dists[nearest].d < dists[incIdx].d - margin ? nearest : incIdx;
101
- // ③ 目标清单 = 中心、列表前一张、后一张,然后**按「离锚点更近那一侧优先」一层层往外扩**,
102
- // 直到凑够 maxSlots。⭐ 2026-10-06 晚皇上旨「一次能够展示 8 个全景纹理」⇒ maxSlots 由 4 变 8,
103
- // 此处从「只扩一层」改成「一层层扩下去」。扩的**顺序**沿用当日那句「更近那侧再往外一张」:
104
- // k=2 那一轮先取更近那侧 ⇒ 前 4 张与 4 槽时代**逐字相同**(中心、-1、+1、更近那侧的 ±2),
105
- // 多出来的 4 张继续 ±3、±4…
106
- // ⭐ 2026-10-07 旨「尽可能得填满 8 个全景位置」⇒ 越界的那个**序号**直接跳过、**不占一格**,
107
- // 外扩继续往还有货的那一侧走(旧写法把 `undefined` 也塞进清单去占槽 ⇒ 10 个点、中心贴列表首端时
108
- // 只填 5 格,剩下 5 张全景明明存在却进不了 shader)。清单里只装真实存在的全景
109
- // ⇒ 填几格 = **min(项目全景总数, maxSlots)**,只有人不够时才留空(⛔ 不复制一张来凑数)。
110
- const at = (i: number): T | undefined => (i >= 0 && i < dists.length ? dists[i].t : undefined);
111
- const lo = centerIdx - 1;
112
- const hi = centerIdx + 1;
113
- const dLo = lo >= 0 && lo < dists.length ? dists[lo].d : Infinity;
114
- const dHi = hi >= 0 && hi < dists.length ? dists[hi].d : Infinity;
115
- const nearSign = dLo <= dHi ? -1 : 1; // 更近那侧:-1 = 往前(序号更小),+1 = 往后;平手归序号小的那侧
116
- const targets: T[] = [];
117
- const push = (i: number): void => {
118
- if (targets.length >= maxSlots) return;
119
- const t = at(i);
120
- if (t !== undefined) targets.push(t);
121
- };
122
- push(centerIdx);
123
- push(lo);
124
- push(hi);
125
- // k 超过列表长度 ⇒ 两侧都再也请不到人,循环自然停(⛔ 别写成「直到填满」的无界循环)
126
- for (let k = 2; targets.length < maxSlots && k <= dists.length; k++) {
127
- push(centerIdx + nearSign * k);
128
- push(centerIdx - nearSign * k);
129
- }
130
- const used = new Set<T>();
131
- used.add(targets[0]);
132
- // ④ ⭐ 中心没换人(10-02 当日亲择「维持整组」)⇒ 在册的人**一格都不动**,只把空格补上
133
- // (10-07 那道旨的后半句):他一张一张加全景时,新增那张立刻进得了 shader,不用等中心换人;
134
- // 删掉一张留下的那格也当场补。已有格子一字节都不重绑 ⇒ 没有任何换人/重传代价。
135
- if (incIdx >= 0 && centerIdx === incIdx) {
136
- const kept = next.slice();
137
- kept[0] = targets[0];
138
- for (const x of kept) if (x !== undefined) used.add(x);
139
- for (const t of targets) {
140
- if (used.has(t)) continue;
141
- const hole = kept.indexOf(undefined);
142
- if (hole < 1) break; // -1 = 没有空格;0 = 0 号槽恒归中心(这里不可能发生,兜住)
143
- kept[hole] = t;
144
- used.add(t);
145
- }
146
- return kept;
147
- }
148
- // ⑤ 中心换人 ⇒ 整组按**新中心**的序号清单重算,但「仍在清单里的人留在原格」
149
- // ⇒ 中心挪一格时只有必要的格重绑瓦片。0 号槽恒 = 中心(立方体 `vPanoIndex` 恒 0,它显示的就是这一格)。
150
- const placed: (T | undefined)[] = new Array(maxSlots).fill(undefined);
151
- placed[0] = targets[0];
152
- const keepable = (x: T | undefined): x is T => x !== undefined && targets.indexOf(x) >= 0 && !used.has(x);
153
- for (let s = 1; s < maxSlots; s++) {
154
- if (keepable(next[s])) {
155
- placed[s] = next[s];
156
- used.add(next[s] as T);
157
- }
158
- }
159
- for (let s = 1; s < maxSlots; s++) {
160
- if (placed[s] !== undefined) continue;
161
- const fresh = targets.find((x) => !used.has(x));
162
- // 清单里的人全请完了还空 ⇒ 项目里的全景本来就不够一格一人(总数 < maxSlots),这一格留空
163
- if (fresh === undefined) continue;
164
- placed[s] = fresh;
165
- used.add(fresh);
166
- }
167
- return placed;
168
- }
1
+ /*
2
+ * ⭐ 槽位名册纯函数:**「中心 + 序号邻居」**定 shader 那 N 格装哪 N 张全景(皇上旨 2026-10-02 晚;
3
+ * 2026-10-06 晚旨「一次展示 8 个全景」⇒ N 从 4 变 8,靠「每个全景只占左右 2 张 RT」腾出 sampler)。
4
+ *
5
+ * 旨意原文(verbatim):「我想要把按距离筛选的逻辑, 改一改, 首先, 按照 全景到相机的距离选中第一个,
6
+ * 然后按照这个全景的序号, 前后各添加一个全景, 举个例子, 通过计算 5号 全景距离相机最近,
7
+ * 那就选出一个 5号全景填入 shader 然后再, 5号前后是 4号 和 6号, 也分别填入 shader,
8
+ * 最后再判断一下, 如果相机距离4号的距离 比 相机距离6号的距离近 就再追加一个[它外侧那张]全景, 反之就追加一个 7号 全景,
9
+ * 如果没有 目标全景, 比如 1号全景往前 没有 -1 -2 号全景, shader 里面就留空」
10
+ * ⭐ 同日追问三择(verbatim):「补 3 号(顺着往外扩一张)」/「中心没换人就维持整组」/
11
+ * 探针第一行「打成『中心#5 + 邻居 4/6/7』」。
12
+ * ⭐ 皇上旨 2026-10-07(第二十道,verbatim):「shader 是可以支持最多传入8个全景的, 所以尽可能得填满8个全景位置」
13
+ * ⇒ 收窄上面那句「如果没有目标全景…就留空」的适用边界:**留空只发生在「项目里的全景总数不够一格一人」时**;
14
+ * 序号越界的那个位置根本不是一张全景 ⇒ 跳过它、继续往还有货的那一侧扩,槽位不许白留给「不存在的序号」。
15
+ * 同一道旨还要求:新加的全景点**不必等中心换人**才进 shader ⇒ 中心没换人时在册的人照旧一格不动,空格当场补满。
16
+ * ⚠️ 上限仍是 `MKT_PANO_MAX_SLOTS`(=8):8 点 × 左右 2 张拼合槽 = 16 个 sampler,那是 GLSL 物理闸
17
+ * (皇上机器 maxTextures=16)⇒ 本函数不越那道闸,只是把那 8 格**尽量用满**。
18
+ * ⭐ 皇上旨 2026-10-07 第二十一道旨(verbatim):「判断使用那些全景球的判断条件, 从距离相机的距离 改成
19
+ * 判断距离控制器的中心点的距离来判断」⇒ **本函数的尺子另一端换了人**:函数体一个字不改(它只吃调用方算好的
20
+ * `d`),改的是**调用方**——编辑态与预览的锚点 = 轨道控制器注视点 `controls.target`(画面正中央那个点),
21
+ * 发布页那一路没有控制器 ⇒ 回落相机位置。下面所有「相机」二字请按「锚点(= 控制器中心点,未注入时才是相机)」读;
22
+ * 10-02 那道旨的原文里「距离相机最近」是**历史引文**,逐字保留。
23
+ * ⚠️ 同一把尺还喂着 `camW`(混合权重)⇒ 权重跟着换锚点;而面上「取哪一张」的 `score = 顶点距离 − 夹角`
24
+ * 里**根本没有相机量**(09-28 15:07 七修亲自撤掉 camD 项),不受本道旨影响。
25
+ *
26
+ * 一句话口径:
27
+ * 1) **中心** = 到**锚点**(调用方给的水平距离 XZ;锚点 = 控制器中心点,见上方 10-07 那道旨)最近那张;
28
+ * ⚠️ 带死区 `margin`:别人必须比在册中心**近出 margin 米以上**
29
+ * 才抢走中心 ⇒ 锚点在等距带抖动时整组一动不动(09-26 那条「其他 3 个依然不动」的稳定诉求搬到这里来用)。
30
+ * 2) 中心定了之后按**列表序号**(= 面板列表顺序 = 日志里那个 `#i`)取邻居:`中心-1`、`中心+1` 各一张。
31
+ * 3) 再往外一层层扩(`中心±2`、`中心±3`…)直到凑够槽数;**同一层里先取离锚点更近那一侧**
32
+ * (平手归序号小的那侧)。⭐ 2026-10-06 晚旨「一次展示 8 个」⇒ 槽数 4 → 8,这里从「只扩一层」
33
+ * 改成一层层扩下去;前 4 张的顺序与 4 槽时代**逐字相同**(中心、-1、+1、更近那侧的 ±2)。
34
+ * 4) ⭐ 2026-10-07 皇上旨「shader 是可以支持最多传入 8 个全景的, 所以尽可能得填满 8 个全景位置」
35
+ * ⇒ **改写 10-02 那句「没有目标全景就留空」的适用边界**:越界的那个**序号**直接跳过(它本来就不是一个全景),
36
+ * 外扩**继续往还有货的那一侧**走 ⇒ 只要列表里还有没用过的全景,槽就一直被填,直到凑够 `maxSlots`。
37
+ * ⛔ 只有「整个列表都被请进来了还不够一格一人的时候」(= 项目里的全景总数 < 槽数)才留空 ——
38
+ * 那是物理不够,不是策略留白。旧行为(中心贴列表两端 ⇒ 明明还有 5 张没用上却空着 3 格)作废。
39
+ * 5) ⭐ 中心没换人 ⇒ **在册的人一格都不动**(10-02 当日亲择,照旧);但 10-07 那道旨补了后半句:
40
+ * **空格要当场补上** ⇒ 新增的全景点不必等「中心换人」才进 shader(否则他一张一张加点时永远填不满)。
41
+ *
42
+ * ⚠️ 与历代口径的关系(别搞混):
43
+ * - 09-25「槽位与相机完全无关」→ 09-26「名册增量换人(一进一出)」→ 09-27「在册者按相机距离落槽、取最近 4 个」
44
+ * ⇒ 本函数把 09-27 那半条**作废**:不再取「最近 4 个」,改取「最近那一张的序号邻居」,槽号也不再按距离排。
45
+ * - 09-26 的「一进一出 + 0.5m 死区」精神保留,但对象从「谁挤掉谁」变成「谁能抢走中心」。
46
+ *
47
+ * ⚠️ 为什么 0 号槽必须是中心:立方体(`FurnitureObject`)那份 `BoxGeometry` **没有** `aPanoIndex` attribute ⇒
48
+ * 序号映射 shader 里 `vPanoIndex` 恒 0 ⇒ 立方体显示的就是名册第 0 格那一张(皇上旨 10-02「模型也按距离相机最近的方式
49
+ * 设置全景参数」的落点)。中心坐别的格 = 立方体显示错图。
50
+ *
51
+ * ⚠️ 为什么单独成文件:本函数零依赖(不碰 three / MobX / DOM),`test/mktmesh.test.js` 的 **AP 组**会用 esbuild
52
+ * 把**这份源码原文**转译后真跑一遍(中心/邻居/外扩/越界不占格/死区/删点/幂等/空格回填 各断言一次)。
53
+ * ⇒ 改这里的算法,那条数字测试立刻知道;⛔ 别把逻辑搬回渲染器里,那样就只能靠正则钉、证不了行为。
54
+ *
55
+ * ⭐ 皇上旨 2026-10-03「库侧也一并改成中心 ± 邻居」⇒ 本文件从 `src/views/MktMeshEdit/` **搬进库里**
56
+ * (`three-base/MktMesh/`):编辑态(`mktMeshEditRenderer`)与预览/发布态(`MktMesh.updateTexture` +
57
+ * `feedFaceMapping`)**共用这一份算法和下面那一个死区常数**,⛔ 不许在任一侧再抄一份 0.5 或另写一套挑图。
58
+ */
59
+
60
+ /** ⭐ 槽位「中心死区」(米):离**锚点**(控制器中心点;发布页未注入时 = 相机位置)水平距离最近的那张是中心;挑战者必须比现任中心**近出这么多**才抢得走。
61
+ * 没有死区 ⇒ 锚点停在两点的等距带附近每帧换中心 ⇒ 换中心 = 整组邻居重排 = 一整组瓦片重绑(正是 09-24/25 报的「切全景卡一下」)。
62
+ * 定义在此是因为编辑态与库侧共用(全项目**只此一处**,两侧都 import 它)。 */
63
+ export const PANO_SLOT_SWAP_MARGIN_M = 0.5;
64
+
65
+ /** 一个候选:`t` 是全景点对象(存引用不存下标 —— 删点会让下标整体前移,引用不会认错人),`d` 是它到**锚点**的水平距离(锚点由调用方决定,本函数不认识它) */
66
+ export interface PanoRosterCand<T> {
67
+ t: T;
68
+ d: number;
69
+ }
70
+
71
+ /**
72
+ * 算出下一帧的槽位名册(**不修改传进来的 roster**,返回新数组)。
73
+ * @param roster 当前名册(0 号槽 = 上一帧的中心),长度可以小于 maxSlots(内部按 undefined 补齐)
74
+ * @param dists 本帧全部全景点及其到**锚点**的距离(锚点 = 调用方那把尺子的端点;⭐ 10-07 第二十一道旨后 = 控制器中心点),**数组顺序 = 全景列表顺序**(下标就是旨意里说的「几号全景」)
75
+ * @param maxSlots shader 的槽位上限(= `MKT_PANO_MAX_SLOTS`;⭐ 2026-10-06 晚旨后是 **8**:8 点 × 每个全景
76
+ * 左右 2 张 RT = 16 sampler 仍是 GLSL 物理上限。⛔ 本函数不许假设它恒等于 4)
77
+ * @param margin 抢中心的死区(米,= `PANO_SLOT_SWAP_MARGIN_M`)
78
+ * @returns 长度恒 = maxSlots 的新名册:**0 号槽 = 中心**,其余槽 = 列表里按序号一层层往外扩的那些
79
+ * (同一层先取离锚点更近那侧)。⭐ 10-07 旨「尽可能填满」⇒ 越界的序号不占格、外扩继续往有货那侧走,
80
+ * 所以**填几格 = min(项目全景总数, maxSlots)**;只有列表里的人不够时才留空(= `undefined`)。
81
+ * 中心没换人 ⇒ 在册的一格都不动,只把**空格**补上。
82
+ */
83
+ export function panoSlotRosterStep<T>(
84
+ roster: (T | undefined)[],
85
+ dists: PanoRosterCand<T>[],
86
+ maxSlots: number,
87
+ margin: number
88
+ ): (T | undefined)[] {
89
+ const next = roster.slice(0, maxSlots);
90
+ while (next.length < maxSlots) next.push(undefined);
91
+ const alive = new Set(dists.map((n) => n.t));
92
+ // ① 已被删掉的点先腾槽(名册里躺着一个不存在的点 = 那格的矩阵与权重都是旧账)
93
+ for (let s = 0; s < maxSlots; s++) if (next[s] && !alive.has(next[s] as T)) next[s] = undefined;
94
+ if (!dists.length) return next.fill(undefined);
95
+ // ② 在册中心 = 0 号槽那位;别人必须比它近出 margin 以上才换中心
96
+ const incumbent = next[0] !== undefined && alive.has(next[0] as T) ? (next[0] as T) : undefined;
97
+ let nearest = 0;
98
+ for (let i = 1; i < dists.length; i++) if (dists[i].d < dists[nearest].d) nearest = i;
99
+ const incIdx = incumbent === undefined ? -1 : dists.findIndex((n) => n.t === (incumbent as T));
100
+ const centerIdx = incIdx < 0 || dists[nearest].d < dists[incIdx].d - margin ? nearest : incIdx;
101
+ // ③ 目标清单 = 中心、列表前一张、后一张,然后**按「离锚点更近那一侧优先」一层层往外扩**,
102
+ // 直到凑够 maxSlots。⭐ 2026-10-06 晚皇上旨「一次能够展示 8 个全景纹理」⇒ maxSlots 由 4 变 8,
103
+ // 此处从「只扩一层」改成「一层层扩下去」。扩的**顺序**沿用当日那句「更近那侧再往外一张」:
104
+ // k=2 那一轮先取更近那侧 ⇒ 前 4 张与 4 槽时代**逐字相同**(中心、-1、+1、更近那侧的 ±2),
105
+ // 多出来的 4 张继续 ±3、±4…
106
+ // ⭐ 2026-10-07 旨「尽可能得填满 8 个全景位置」⇒ 越界的那个**序号**直接跳过、**不占一格**,
107
+ // 外扩继续往还有货的那一侧走(旧写法把 `undefined` 也塞进清单去占槽 ⇒ 10 个点、中心贴列表首端时
108
+ // 只填 5 格,剩下 5 张全景明明存在却进不了 shader)。清单里只装真实存在的全景
109
+ // ⇒ 填几格 = **min(项目全景总数, maxSlots)**,只有人不够时才留空(⛔ 不复制一张来凑数)。
110
+ const at = (i: number): T | undefined => (i >= 0 && i < dists.length ? dists[i].t : undefined);
111
+ const lo = centerIdx - 1;
112
+ const hi = centerIdx + 1;
113
+ const dLo = lo >= 0 && lo < dists.length ? dists[lo].d : Infinity;
114
+ const dHi = hi >= 0 && hi < dists.length ? dists[hi].d : Infinity;
115
+ const nearSign = dLo <= dHi ? -1 : 1; // 更近那侧:-1 = 往前(序号更小),+1 = 往后;平手归序号小的那侧
116
+ const targets: T[] = [];
117
+ const push = (i: number): void => {
118
+ if (targets.length >= maxSlots) return;
119
+ const t = at(i);
120
+ if (t !== undefined) targets.push(t);
121
+ };
122
+ push(centerIdx);
123
+ push(lo);
124
+ push(hi);
125
+ // k 超过列表长度 ⇒ 两侧都再也请不到人,循环自然停(⛔ 别写成「直到填满」的无界循环)
126
+ for (let k = 2; targets.length < maxSlots && k <= dists.length; k++) {
127
+ push(centerIdx + nearSign * k);
128
+ push(centerIdx - nearSign * k);
129
+ }
130
+ const used = new Set<T>();
131
+ used.add(targets[0]);
132
+ // ④ ⭐ 中心没换人(10-02 当日亲择「维持整组」)⇒ 在册的人**一格都不动**,只把空格补上
133
+ // (10-07 那道旨的后半句):他一张一张加全景时,新增那张立刻进得了 shader,不用等中心换人;
134
+ // 删掉一张留下的那格也当场补。已有格子一字节都不重绑 ⇒ 没有任何换人/重传代价。
135
+ if (incIdx >= 0 && centerIdx === incIdx) {
136
+ const kept = next.slice();
137
+ kept[0] = targets[0];
138
+ for (const x of kept) if (x !== undefined) used.add(x);
139
+ for (const t of targets) {
140
+ if (used.has(t)) continue;
141
+ const hole = kept.indexOf(undefined);
142
+ if (hole < 1) break; // -1 = 没有空格;0 = 0 号槽恒归中心(这里不可能发生,兜住)
143
+ kept[hole] = t;
144
+ used.add(t);
145
+ }
146
+ return kept;
147
+ }
148
+ // ⑤ 中心换人 ⇒ 整组按**新中心**的序号清单重算,但「仍在清单里的人留在原格」
149
+ // ⇒ 中心挪一格时只有必要的格重绑瓦片。0 号槽恒 = 中心(立方体 `vPanoIndex` 恒 0,它显示的就是这一格)。
150
+ const placed: (T | undefined)[] = new Array(maxSlots).fill(undefined);
151
+ placed[0] = targets[0];
152
+ const keepable = (x: T | undefined): x is T => x !== undefined && targets.indexOf(x) >= 0 && !used.has(x);
153
+ for (let s = 1; s < maxSlots; s++) {
154
+ if (keepable(next[s])) {
155
+ placed[s] = next[s];
156
+ used.add(next[s] as T);
157
+ }
158
+ }
159
+ for (let s = 1; s < maxSlots; s++) {
160
+ if (placed[s] !== undefined) continue;
161
+ const fresh = targets.find((x) => !used.has(x));
162
+ // 清单里的人全请完了还空 ⇒ 项目里的全景本来就不够一格一人(总数 < maxSlots),这一格留空
163
+ if (fresh === undefined) continue;
164
+ placed[s] = fresh;
165
+ used.add(fresh);
166
+ }
167
+ return placed;
168
+ }
@@ -1,119 +1,119 @@
1
- import { Texture, type WebGLRenderer } from "three";
2
- /**
3
- * 拼合器对外接口(消费方 `MktMesh` / 编辑器渲染器 / 预览只认这一个类)。
4
- * 10-06 之前它是「两份实现共用一份接口」里的 canvas 那一份 ⇒ 那份已奉旨删除,接口留在原地当文档。
5
- */
6
- export interface PanoStitcher {
7
- /** 确保批次已在册并发起瓦片加载(幂等;同批已在册 ⇒ 什么都不做) */
8
- ensure(urls: string[]): void;
9
- /** 取某一拼合槽的纹理。**画过 ≥1 格就返回**(同日旨「需要先喂给 shader」);没画过 / 没这组返回 null。 */
10
- peek(batch: string, slot: number): Texture | null;
11
- /** 每帧驱动(消费方每帧只调一次)。`renderer` = 调用方当前的 WebGLRenderer,还没捕获到就传 null
12
- * ⇒ 这一帧什么都不画(⛔ 不打假读数,也⛔ 不退回任何 CPU 拼合路径)。 */
13
- pump(renderer: WebGLRenderer | null): void;
14
- /** 这张槽纹理是否**已经在 GPU 上**(RT 版恒 true):消费方据此跳过「每 tick 一张」预上传闸。
15
- * ⚠️ 返回 true 的纹理绝不能喂给 `renderer.initTexture` —— 它不是普通纹理,是 three 自己管的渲染目标附件。 */
16
- resident(tex: Texture): boolean;
17
- /** 该批次是否已在册(诊断/测试用) */
18
- has(batch: string): boolean;
19
- /** 该批次已画进 RT 的格数(0~32)⇒ 编辑器按需渲染的「指纹」用它判断拼合还在推进 */
20
- cellsDrawn(batch: string): number;
21
- /** 全局推进计数(画一格 / 传一张 / 建组 / 拆组都 +1)⇒ 上面那个指纹的单调哨兵,无需知道批次名 */
22
- progressStamp(): number;
23
- /** 释放一整组(含它的 GPU 资源) */
24
- disposeGroup(batch: string): void;
25
- /** 释放全部 */
26
- dispose(): void;
27
- }
28
- export declare class PanoTileStitcher implements PanoStitcher {
29
- /** 批次组(键 = 32 张瓦片的第一张**非空**地址,与消费方同一个 `rtStitchBatchOf`) */
30
- private _groups;
31
- /** 瓦片纹理(url → `{tex, uploaded}`)。画进自己那一格就立刻释放 ⇒ 只在册期间占显存/内存。 */
32
- private _texs;
33
- /** 仍在册的组还需要的地址(迟到的加载回调据此判断「这条已经没人要了,立刻释放」) */
34
- private _wanted;
35
- /** 加载失败退避(url → 最早可重试时间戳)/ 失败计数 / 死链 */
36
- private _retryAt;
37
- private _fails;
38
- private _dead;
39
- /** 建 RT 时用的渲染器(换 renderer ⇒ 在册组全清,见文件头) */
40
- private _renderer;
41
- /** 推进计数(`progressStamp`)*/
42
- private _stamp;
43
- private _scene;
44
- private _camera;
45
- private _mat;
46
- /** 几何是 2×2(铺满正交相机的 [-1,1]²),每格只把它缩到 1/4 再挪过去 ⇒ 一格 = 槽的 1/16 */
47
- private _quad;
48
- /** 缺格那一路 `uMap` 也要有个合法纹理可绑(1×1,从不真采样) */
49
- private _blank;
50
- /** 本拼合器发出去的槽纹理 = RT 附件(`resident()` 认它);⛔ 不许喂给 `renderer.initTexture` */
51
- private _rtTextures;
52
- constructor();
53
- /** 确保批次已建组、加载已发起。幂等:同批已在册 ⇒ 直接返回(一片瓦片终身只下载、只解码一次)。
54
- * ⭐ 09-29 晚旨「不足的涂灰」+ 10-06 晚旨「8×4 = 32 片」:1~32 条皆可入组,内部补空串占位到 32;
55
- * 空串 / 死链的格子拼合时涂灰(一眼看出该全景数据不全)。
56
- * ⛔ 旧版 16 条(4×4)与 64 条(8×8)的数据**都不做兼容**(皇上「无须兼容正常使用, 有异常我会重传」):
57
- * 64 条会在上一行被判超限直接丢弃;真喂进来的短数据只占前几格 + 其余涂灰,那是**故意**让数据不全看得见。
58
- * ⚠️ 每槽数组长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写 `[null,null,null,null]` —— 槽数再变会漏改)。 */
59
- ensure(urls: string[]): void;
60
- /** 取某一拼合槽的纹理:**画过 ≥1 格就交出去**(同日旨「需要先喂给 shader」⇒ 不再等 16 格画满)。
61
- * RT 建好时整张涂过缺失灰 ⇒ 交出去的那张里**没有未定义像素**,观感 = 该象限逐格显影。 */
62
- peek(batch: string, slot: number): Texture | null;
63
- /** ⭐ 这条路交出去的槽纹理本来就是 GL 渲染目标的附件 —— 像素已经在显存里,没有「上传」这一步。
64
- * 消费方(`MktMesh._peekTileAt` / 编辑器 `_peekTileAt`)据此**跳过**「每 tick 一张 `initTexture`」那道闸。 */
65
- resident(tex: Texture): boolean;
66
- has(batch: string): boolean;
67
- cellsDrawn(batch: string): number;
68
- progressStamp(): number;
69
- /** 每帧驱动(三道工序,顺序固定:补下载 → 画已进 GPU 的 → 再传一张新的)。 */
70
- pump(renderer: WebGLRenderer | null): void;
71
- /** 释放整组(2 张 RT + 该组还没画掉的瓦片纹理) */
72
- disposeGroup(batch: string): void;
73
- /** 释放全部(持有者 `dispose()` 时调用);共享的那套 quad/材质/空白纹理一并退掉 */
74
- dispose(): void;
75
- private _now;
76
- /** 工序 1:按「槽 0 格 0 → 槽 3 格 15」的顺序补齐还没发起的下载,受三道闸约束(每帧 2 张 /
77
- * 下载中 ≤8 / 已解码待上传 ≤8)⇒ 一片瓦片终身只 `fetch` 一次(在册条目不重复发)。 */
78
- private _pumpDownloads;
79
- /** 工序 2:把所有「已进 GPU 且还没画」的格子画进各自的 RT —— 一帧画多少格都行(旨②)。
80
- * ⛔ 准入只看一件事:`uploaded`。没上传过的纹理当场画 = `texImage2D` 藏进 `render()` 里
81
- * = 每帧一张新纹理那道闸形同虚设(这正是本轮要治的卡顿本体)。 */
82
- private _drawPass;
83
- /** 工序 3:每帧至多把**一张**新纹理传进 GPU(旨②),这一趟只上传、不画。
84
- * 按「槽 0 格 0 → …」的顺序挑第一张已解码没上传的 ⇒ RT 从左上角起逐格显影,观感可预期。 */
85
- private _uploadPass;
86
- /** 上传一片瓦片的纹理(`texImage2D` + mipmap 才是那一下贵的,所以单独成工序)。
87
- * ⚠️ 成败都记 `uploaded`:不记 ⇒ 那一格永远进不了 `_drawPass` ⇒ 该槽永远画不满、每帧白烧一个预算,
88
- * 比「那一帧没错峰」严重得多;记了最坏也只是退回 three 在绑定那一帧自己上传的老路径。
89
- * (与 `MktMesh._uploadTileTexture` 同一口径,皇上旨 2026-09-25「队列里只放上传那一句」。) */
90
- private _uploadTile;
91
- /** 画一格:把那片瓦片的**中间那块**铺进该槽第 `cell` 格(16 格里的一格)。
92
- * ⚠️⚠️ `autoClear` 必须关掉:three 每次 `render()` 默认清屏,而这里**同一张 RT 要连画 16 帧**,
93
- * 清一次就把前面画好的格子抹了 ⇒ 槽纹理只剩最后一格。清屏交给我们自己(见 `_clearRt`)。 */
94
- private _stitchCell;
95
- /** 把整张 RT 涂成缺失灰:RT 建好后的第一件事 ⇒ 保证「画不满也先喂给 shader」时**没有未定义像素**。
96
- * 走的是与缺格同一条 shader 路径(同一个 sRGB→linear→硬件编码回 sRGB)⇒ 灰度字节与涂灰格子完全相同。 */
97
- private _clearRt;
98
- /** 唯一碰 `renderer` 状态的那一句:存现场 → 关掉自动清屏 → 画 → 还原。 */
99
- private _renderQuad;
100
- /** 发起一片瓦片的下载 + 异步解码(`loadPanoTexture` ⇒ 解码在工作线程,主线程只拿结果)。
101
- * 幂等;失败记 3s 退避重发,累计 3 次判死链 ⇒ 那一格涂灰,一眼看出该全景数据不全。 */
102
- private _loadTile;
103
- /** 加载失败:摘掉「加载中」占位;3 次判死链,否则 3s 退避由 `_pumpRetries` 重发 */
104
- private _failTile;
105
- /** 到点的失败重发(每帧最多 2 个,防风暴) */
106
- private _pumpRetries;
107
- /** 换 renderer:RT 是旧上下文的私有资源,留着既画不了也释放不掉 ⇒ 在册组整份作废,
108
- * 靠消费方每帧照旧的 `ensure()` 重建(瓦片走浏览器 HTTP 缓存,不重新编码)。 */
109
- private _switchRenderer;
110
- /** 懒建某槽的渲染目标:4096×4096 RGBA8 + sRGB + mipmap,并**立刻整张涂缺失灰**
111
- * (⛔ 未定义像素不许进 shader:`peek()` 现在画过 1 格就交出去)。 */
112
- private _ensureRt;
113
- /** 释放一片瓦片的纹理 + ImageBitmap(它只属于一格,画完即弃 ⇒ 显存峰值 = 一格) */
114
- private _dropTile;
115
- /** 整组拼完:该组已无在册地址(瓦片逐片释放过了),清 `_wanted` 让迟到回调自己收尾 */
116
- private _releaseGroupWanted;
117
- /** 释放一组的全部 GPU 资源:2 张 RT + 还没画掉的瓦片纹理 */
118
- private _dropGroupGpu;
119
- }
1
+ import { Texture, type WebGLRenderer } from "three";
2
+ /**
3
+ * 拼合器对外接口(消费方 `MktMesh` / 编辑器渲染器 / 预览只认这一个类)。
4
+ * 10-06 之前它是「两份实现共用一份接口」里的 canvas 那一份 ⇒ 那份已奉旨删除,接口留在原地当文档。
5
+ */
6
+ export interface PanoStitcher {
7
+ /** 确保批次已在册并发起瓦片加载(幂等;同批已在册 ⇒ 什么都不做) */
8
+ ensure(urls: string[]): void;
9
+ /** 取某一拼合槽的纹理。**画过 ≥1 格就返回**(同日旨「需要先喂给 shader」);没画过 / 没这组返回 null。 */
10
+ peek(batch: string, slot: number): Texture | null;
11
+ /** 每帧驱动(消费方每帧只调一次)。`renderer` = 调用方当前的 WebGLRenderer,还没捕获到就传 null
12
+ * ⇒ 这一帧什么都不画(⛔ 不打假读数,也⛔ 不退回任何 CPU 拼合路径)。 */
13
+ pump(renderer: WebGLRenderer | null): void;
14
+ /** 这张槽纹理是否**已经在 GPU 上**(RT 版恒 true):消费方据此跳过「每 tick 一张」预上传闸。
15
+ * ⚠️ 返回 true 的纹理绝不能喂给 `renderer.initTexture` —— 它不是普通纹理,是 three 自己管的渲染目标附件。 */
16
+ resident(tex: Texture): boolean;
17
+ /** 该批次是否已在册(诊断/测试用) */
18
+ has(batch: string): boolean;
19
+ /** 该批次已画进 RT 的格数(0~32)⇒ 编辑器按需渲染的「指纹」用它判断拼合还在推进 */
20
+ cellsDrawn(batch: string): number;
21
+ /** 全局推进计数(画一格 / 传一张 / 建组 / 拆组都 +1)⇒ 上面那个指纹的单调哨兵,无需知道批次名 */
22
+ progressStamp(): number;
23
+ /** 释放一整组(含它的 GPU 资源) */
24
+ disposeGroup(batch: string): void;
25
+ /** 释放全部 */
26
+ dispose(): void;
27
+ }
28
+ export declare class PanoTileStitcher implements PanoStitcher {
29
+ /** 批次组(键 = 32 张瓦片的第一张**非空**地址,与消费方同一个 `rtStitchBatchOf`) */
30
+ private _groups;
31
+ /** 瓦片纹理(url → `{tex, uploaded}`)。画进自己那一格就立刻释放 ⇒ 只在册期间占显存/内存。 */
32
+ private _texs;
33
+ /** 仍在册的组还需要的地址(迟到的加载回调据此判断「这条已经没人要了,立刻释放」) */
34
+ private _wanted;
35
+ /** 加载失败退避(url → 最早可重试时间戳)/ 失败计数 / 死链 */
36
+ private _retryAt;
37
+ private _fails;
38
+ private _dead;
39
+ /** 建 RT 时用的渲染器(换 renderer ⇒ 在册组全清,见文件头) */
40
+ private _renderer;
41
+ /** 推进计数(`progressStamp`)*/
42
+ private _stamp;
43
+ private _scene;
44
+ private _camera;
45
+ private _mat;
46
+ /** 几何是 2×2(铺满正交相机的 [-1,1]²),每格只把它缩到 1/4 再挪过去 ⇒ 一格 = 槽的 1/16 */
47
+ private _quad;
48
+ /** 缺格那一路 `uMap` 也要有个合法纹理可绑(1×1,从不真采样) */
49
+ private _blank;
50
+ /** 本拼合器发出去的槽纹理 = RT 附件(`resident()` 认它);⛔ 不许喂给 `renderer.initTexture` */
51
+ private _rtTextures;
52
+ constructor();
53
+ /** 确保批次已建组、加载已发起。幂等:同批已在册 ⇒ 直接返回(一片瓦片终身只下载、只解码一次)。
54
+ * ⭐ 09-29 晚旨「不足的涂灰」+ 10-06 晚旨「8×4 = 32 片」:1~32 条皆可入组,内部补空串占位到 32;
55
+ * 空串 / 死链的格子拼合时涂灰(一眼看出该全景数据不全)。
56
+ * ⛔ 旧版 16 条(4×4)与 64 条(8×8)的数据**都不做兼容**(皇上「无须兼容正常使用, 有异常我会重传」):
57
+ * 64 条会在上一行被判超限直接丢弃;真喂进来的短数据只占前几格 + 其余涂灰,那是**故意**让数据不全看得见。
58
+ * ⚠️ 每槽数组长度由 `RT_STITCH_SLOT_COUNT` 派生(⛔ 不许手写 `[null,null,null,null]` —— 槽数再变会漏改)。 */
59
+ ensure(urls: string[]): void;
60
+ /** 取某一拼合槽的纹理:**画过 ≥1 格就交出去**(同日旨「需要先喂给 shader」⇒ 不再等 16 格画满)。
61
+ * RT 建好时整张涂过缺失灰 ⇒ 交出去的那张里**没有未定义像素**,观感 = 该象限逐格显影。 */
62
+ peek(batch: string, slot: number): Texture | null;
63
+ /** ⭐ 这条路交出去的槽纹理本来就是 GL 渲染目标的附件 —— 像素已经在显存里,没有「上传」这一步。
64
+ * 消费方(`MktMesh._peekTileAt` / 编辑器 `_peekTileAt`)据此**跳过**「每 tick 一张 `initTexture`」那道闸。 */
65
+ resident(tex: Texture): boolean;
66
+ has(batch: string): boolean;
67
+ cellsDrawn(batch: string): number;
68
+ progressStamp(): number;
69
+ /** 每帧驱动(三道工序,顺序固定:补下载 → 画已进 GPU 的 → 再传一张新的)。 */
70
+ pump(renderer: WebGLRenderer | null): void;
71
+ /** 释放整组(2 张 RT + 该组还没画掉的瓦片纹理) */
72
+ disposeGroup(batch: string): void;
73
+ /** 释放全部(持有者 `dispose()` 时调用);共享的那套 quad/材质/空白纹理一并退掉 */
74
+ dispose(): void;
75
+ private _now;
76
+ /** 工序 1:按「槽 0 格 0 → 槽 3 格 15」的顺序补齐还没发起的下载,受三道闸约束(每帧 2 张 /
77
+ * 下载中 ≤8 / 已解码待上传 ≤8)⇒ 一片瓦片终身只 `fetch` 一次(在册条目不重复发)。 */
78
+ private _pumpDownloads;
79
+ /** 工序 2:把所有「已进 GPU 且还没画」的格子画进各自的 RT —— 一帧画多少格都行(旨②)。
80
+ * ⛔ 准入只看一件事:`uploaded`。没上传过的纹理当场画 = `texImage2D` 藏进 `render()` 里
81
+ * = 每帧一张新纹理那道闸形同虚设(这正是本轮要治的卡顿本体)。 */
82
+ private _drawPass;
83
+ /** 工序 3:每帧至多把**一张**新纹理传进 GPU(旨②),这一趟只上传、不画。
84
+ * 按「槽 0 格 0 → …」的顺序挑第一张已解码没上传的 ⇒ RT 从左上角起逐格显影,观感可预期。 */
85
+ private _uploadPass;
86
+ /** 上传一片瓦片的纹理(`texImage2D` + mipmap 才是那一下贵的,所以单独成工序)。
87
+ * ⚠️ 成败都记 `uploaded`:不记 ⇒ 那一格永远进不了 `_drawPass` ⇒ 该槽永远画不满、每帧白烧一个预算,
88
+ * 比「那一帧没错峰」严重得多;记了最坏也只是退回 three 在绑定那一帧自己上传的老路径。
89
+ * (与 `MktMesh._uploadTileTexture` 同一口径,皇上旨 2026-09-25「队列里只放上传那一句」。) */
90
+ private _uploadTile;
91
+ /** 画一格:把那片瓦片的**中间那块**铺进该槽第 `cell` 格(16 格里的一格)。
92
+ * ⚠️⚠️ `autoClear` 必须关掉:three 每次 `render()` 默认清屏,而这里**同一张 RT 要连画 16 帧**,
93
+ * 清一次就把前面画好的格子抹了 ⇒ 槽纹理只剩最后一格。清屏交给我们自己(见 `_clearRt`)。 */
94
+ private _stitchCell;
95
+ /** 把整张 RT 涂成缺失灰:RT 建好后的第一件事 ⇒ 保证「画不满也先喂给 shader」时**没有未定义像素**。
96
+ * 走的是与缺格同一条 shader 路径(同一个 sRGB→linear→硬件编码回 sRGB)⇒ 灰度字节与涂灰格子完全相同。 */
97
+ private _clearRt;
98
+ /** 唯一碰 `renderer` 状态的那一句:存现场 → 关掉自动清屏 → 画 → 还原。 */
99
+ private _renderQuad;
100
+ /** 发起一片瓦片的下载 + 异步解码(`loadPanoTexture` ⇒ 解码在工作线程,主线程只拿结果)。
101
+ * 幂等;失败记 3s 退避重发,累计 3 次判死链 ⇒ 那一格涂灰,一眼看出该全景数据不全。 */
102
+ private _loadTile;
103
+ /** 加载失败:摘掉「加载中」占位;3 次判死链,否则 3s 退避由 `_pumpRetries` 重发 */
104
+ private _failTile;
105
+ /** 到点的失败重发(每帧最多 2 个,防风暴) */
106
+ private _pumpRetries;
107
+ /** 换 renderer:RT 是旧上下文的私有资源,留着既画不了也释放不掉 ⇒ 在册组整份作废,
108
+ * 靠消费方每帧照旧的 `ensure()` 重建(瓦片走浏览器 HTTP 缓存,不重新编码)。 */
109
+ private _switchRenderer;
110
+ /** 懒建某槽的渲染目标:4096×4096 RGBA8 + sRGB + mipmap,并**立刻整张涂缺失灰**
111
+ * (⛔ 未定义像素不许进 shader:`peek()` 现在画过 1 格就交出去)。 */
112
+ private _ensureRt;
113
+ /** 释放一片瓦片的纹理 + ImageBitmap(它只属于一格,画完即弃 ⇒ 显存峰值 = 一格) */
114
+ private _dropTile;
115
+ /** 整组拼完:该组已无在册地址(瓦片逐片释放过了),清 `_wanted` 让迟到回调自己收尾 */
116
+ private _releaseGroupWanted;
117
+ /** 释放一组的全部 GPU 资源:2 张 RT + 还没画掉的瓦片纹理 */
118
+ private _dropGroupGpu;
119
+ }