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,25 +1,25 @@
1
- import { Texture } from "three";
2
- /**
3
- * ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
4
- *
5
- * 为什么要换掉 three 的 `TextureLoader`:它只等 `img.onload`(字节到手),**从不解码** —— 解码是懒的,
6
- * 真正碰像素的是上传那一拍的 `texImage2D(<img>)`,于是整段 JPEG 解码被拽到主线程上同步做完。
7
- * 实测(2026-10-05,皇上线上的种子全景 11904×5952 / 解码后 270MB):
8
- * `texImage2D(未解码的 <img>)` = **1266ms 全在主线程**;
9
- * `createImageBitmap` 解码 = **768ms 在工作线程**(同期主线程跑完 40 个宏任务只花 6ms),
10
- * 解完再 `texImage2D(<ImageBitmap>)` = **366ms**(这 366 是 270MB 像素搬运,跟解码无关,只有降尺寸才能压掉)。
11
- * ⚠️ 试过 `await img.decode()` 这条路:同一张图 **90 秒没回**(超大图的异步解码在 Chrome 上不可靠)⇒ 别再走它。
12
- *
13
- * ⚠️ **上下翻这颗雷(实测,别照抄直觉)**:Chrome 对 `ImageBitmap` 源**忽略** `UNPACK_FLIP_Y_WEBGL`
14
- * (`<img>` / canvas 源认,bitmap 源不认)⇒ 直接把 `<img>` 换成 bitmap 会让全景**上下颠倒**。
15
- * 解法 = 建图时 `imageOrientation: "flipY"` 把翻转烘进位图,同时把 `texture.flipY = false`
16
- * 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
17
- */
18
- /** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
19
- * 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(皇上旨 2026-09-25),
20
- * 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。 */
21
- export declare function loadPanoTexture(url: string): Promise<Texture>;
22
- /** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
23
- * (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
24
- * 只在「这张纹理确实被丢掉」的清理路径上调用:关掉的位图再也传不上去,WebGL 上下文丢失后的重建会拿到空图。 */
25
- export declare function releasePanoTexture(tex: Texture | null | undefined): void;
1
+ import { Texture } from "three";
2
+ /**
3
+ * ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
4
+ *
5
+ * 为什么要换掉 three 的 `TextureLoader`:它只等 `img.onload`(字节到手),**从不解码** —— 解码是懒的,
6
+ * 真正碰像素的是上传那一拍的 `texImage2D(<img>)`,于是整段 JPEG 解码被拽到主线程上同步做完。
7
+ * 实测(2026-10-05,皇上线上的种子全景 11904×5952 / 解码后 270MB):
8
+ * `texImage2D(未解码的 <img>)` = **1266ms 全在主线程**;
9
+ * `createImageBitmap` 解码 = **768ms 在工作线程**(同期主线程跑完 40 个宏任务只花 6ms),
10
+ * 解完再 `texImage2D(<ImageBitmap>)` = **366ms**(这 366 是 270MB 像素搬运,跟解码无关,只有降尺寸才能压掉)。
11
+ * ⚠️ 试过 `await img.decode()` 这条路:同一张图 **90 秒没回**(超大图的异步解码在 Chrome 上不可靠)⇒ 别再走它。
12
+ *
13
+ * ⚠️ **上下翻这颗雷(实测,别照抄直觉)**:Chrome 对 `ImageBitmap` 源**忽略** `UNPACK_FLIP_Y_WEBGL`
14
+ * (`<img>` / canvas 源认,bitmap 源不认)⇒ 直接把 `<img>` 换成 bitmap 会让全景**上下颠倒**。
15
+ * 解法 = 建图时 `imageOrientation: "flipY"` 把翻转烘进位图,同时把 `texture.flipY = false`
16
+ * 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
17
+ */
18
+ /** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
19
+ * 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(皇上旨 2026-09-25),
20
+ * 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。 */
21
+ export declare function loadPanoTexture(url: string): Promise<Texture>;
22
+ /** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
23
+ * (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
24
+ * 只在「这张纹理确实被丢掉」的清理路径上调用:关掉的位图再也传不上去,WebGL 上下文丢失后的重建会拿到空图。 */
25
+ export declare function releasePanoTexture(tex: Texture | null | undefined): void;
@@ -1,52 +1,52 @@
1
- import { Texture } from "three";
2
- /**
3
- * ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
4
- *
5
- * 为什么要换掉 three 的 `TextureLoader`:它只等 `img.onload`(字节到手),**从不解码** —— 解码是懒的,
6
- * 真正碰像素的是上传那一拍的 `texImage2D(<img>)`,于是整段 JPEG 解码被拽到主线程上同步做完。
7
- * 实测(2026-10-05,皇上线上的种子全景 11904×5952 / 解码后 270MB):
8
- * `texImage2D(未解码的 <img>)` = **1266ms 全在主线程**;
9
- * `createImageBitmap` 解码 = **768ms 在工作线程**(同期主线程跑完 40 个宏任务只花 6ms),
10
- * 解完再 `texImage2D(<ImageBitmap>)` = **366ms**(这 366 是 270MB 像素搬运,跟解码无关,只有降尺寸才能压掉)。
11
- * ⚠️ 试过 `await img.decode()` 这条路:同一张图 **90 秒没回**(超大图的异步解码在 Chrome 上不可靠)⇒ 别再走它。
12
- *
13
- * ⚠️ **上下翻这颗雷(实测,别照抄直觉)**:Chrome 对 `ImageBitmap` 源**忽略** `UNPACK_FLIP_Y_WEBGL`
14
- * (`<img>` / canvas 源认,bitmap 源不认)⇒ 直接把 `<img>` 换成 bitmap 会让全景**上下颠倒**。
15
- * 解法 = 建图时 `imageOrientation: "flipY"` 把翻转烘进位图,同时把 `texture.flipY = false`
16
- * 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
17
- */
18
- /** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
19
- * 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(皇上旨 2026-09-25),
20
- * 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。 */
21
- export function loadPanoTexture(url) {
22
- return fetch(url)
23
- .then((res) => {
24
- if (!res.ok)
25
- throw new Error(`HTTP ${res.status}`);
26
- return res.blob();
27
- })
28
- .then((blob) => createImageBitmap(blob, {
29
- imageOrientation: "flipY",
30
- premultiplyAlpha: "none",
31
- colorSpaceConversion: "none",
32
- }))
33
- .then((bmp) => {
34
- const tex = new Texture(bmp);
35
- tex.flipY = false; // 翻转已烘进位图(见文件头)
36
- tex.needsUpdate = true;
37
- // `TextureLoader` 时代的 `<img>.src` 在这里没有了;「打印 shader 数据」探针回退取地址要用它。
38
- tex.userData.url = url;
39
- return tex;
40
- });
41
- }
42
- /** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
43
- * (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
44
- * 只在「这张纹理确实被丢掉」的清理路径上调用:关掉的位图再也传不上去,WebGL 上下文丢失后的重建会拿到空图。 */
45
- export function releasePanoTexture(tex) {
46
- if (!tex)
47
- return;
48
- const bmp = tex.image;
49
- tex.dispose();
50
- if (bmp && typeof bmp.close === "function")
51
- bmp.close();
52
- }
1
+ import { Texture } from "three";
2
+ /**
3
+ * ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
4
+ *
5
+ * 为什么要换掉 three 的 `TextureLoader`:它只等 `img.onload`(字节到手),**从不解码** —— 解码是懒的,
6
+ * 真正碰像素的是上传那一拍的 `texImage2D(<img>)`,于是整段 JPEG 解码被拽到主线程上同步做完。
7
+ * 实测(2026-10-05,皇上线上的种子全景 11904×5952 / 解码后 270MB):
8
+ * `texImage2D(未解码的 <img>)` = **1266ms 全在主线程**;
9
+ * `createImageBitmap` 解码 = **768ms 在工作线程**(同期主线程跑完 40 个宏任务只花 6ms),
10
+ * 解完再 `texImage2D(<ImageBitmap>)` = **366ms**(这 366 是 270MB 像素搬运,跟解码无关,只有降尺寸才能压掉)。
11
+ * ⚠️ 试过 `await img.decode()` 这条路:同一张图 **90 秒没回**(超大图的异步解码在 Chrome 上不可靠)⇒ 别再走它。
12
+ *
13
+ * ⚠️ **上下翻这颗雷(实测,别照抄直觉)**:Chrome 对 `ImageBitmap` 源**忽略** `UNPACK_FLIP_Y_WEBGL`
14
+ * (`<img>` / canvas 源认,bitmap 源不认)⇒ 直接把 `<img>` 换成 bitmap 会让全景**上下颠倒**。
15
+ * 解法 = 建图时 `imageOrientation: "flipY"` 把翻转烘进位图,同时把 `texture.flipY = false`
16
+ * 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
17
+ */
18
+ /** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
19
+ * 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(皇上旨 2026-09-25),
20
+ * 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。 */
21
+ export function loadPanoTexture(url) {
22
+ return fetch(url)
23
+ .then((res) => {
24
+ if (!res.ok)
25
+ throw new Error(`HTTP ${res.status}`);
26
+ return res.blob();
27
+ })
28
+ .then((blob) => createImageBitmap(blob, {
29
+ imageOrientation: "flipY",
30
+ premultiplyAlpha: "none",
31
+ colorSpaceConversion: "none",
32
+ }))
33
+ .then((bmp) => {
34
+ const tex = new Texture(bmp);
35
+ tex.flipY = false; // 翻转已烘进位图(见文件头)
36
+ tex.needsUpdate = true;
37
+ // `TextureLoader` 时代的 `<img>.src` 在这里没有了;「打印 shader 数据」探针回退取地址要用它。
38
+ tex.userData.url = url;
39
+ return tex;
40
+ });
41
+ }
42
+ /** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
43
+ * (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
44
+ * 只在「这张纹理确实被丢掉」的清理路径上调用:关掉的位图再也传不上去,WebGL 上下文丢失后的重建会拿到空图。 */
45
+ export function releasePanoTexture(tex) {
46
+ if (!tex)
47
+ return;
48
+ const bmp = tex.image;
49
+ tex.dispose();
50
+ if (bmp && typeof bmp.close === "function")
51
+ bmp.close();
52
+ }
@@ -1,54 +1,54 @@
1
- import { Texture } from "three";
2
-
3
- /**
4
- * ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
5
- *
6
- * 为什么要换掉 three 的 `TextureLoader`:它只等 `img.onload`(字节到手),**从不解码** —— 解码是懒的,
7
- * 真正碰像素的是上传那一拍的 `texImage2D(<img>)`,于是整段 JPEG 解码被拽到主线程上同步做完。
8
- * 实测(2026-10-05,皇上线上的种子全景 11904×5952 / 解码后 270MB):
9
- * `texImage2D(未解码的 <img>)` = **1266ms 全在主线程**;
10
- * `createImageBitmap` 解码 = **768ms 在工作线程**(同期主线程跑完 40 个宏任务只花 6ms),
11
- * 解完再 `texImage2D(<ImageBitmap>)` = **366ms**(这 366 是 270MB 像素搬运,跟解码无关,只有降尺寸才能压掉)。
12
- * ⚠️ 试过 `await img.decode()` 这条路:同一张图 **90 秒没回**(超大图的异步解码在 Chrome 上不可靠)⇒ 别再走它。
13
- *
14
- * ⚠️ **上下翻这颗雷(实测,别照抄直觉)**:Chrome 对 `ImageBitmap` 源**忽略** `UNPACK_FLIP_Y_WEBGL`
15
- * (`<img>` / canvas 源认,bitmap 源不认)⇒ 直接把 `<img>` 换成 bitmap 会让全景**上下颠倒**。
16
- * 解法 = 建图时 `imageOrientation: "flipY"` 把翻转烘进位图,同时把 `texture.flipY = false`
17
- * 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
18
- */
19
-
20
- /** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
21
- * 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(皇上旨 2026-09-25),
22
- * 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。 */
23
- export function loadPanoTexture(url: string): Promise<Texture> {
24
- return fetch(url)
25
- .then((res) => {
26
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
27
- return res.blob();
28
- })
29
- .then((blob) =>
30
- createImageBitmap(blob, {
31
- imageOrientation: "flipY",
32
- premultiplyAlpha: "none",
33
- colorSpaceConversion: "none",
34
- })
35
- )
36
- .then((bmp) => {
37
- const tex = new Texture(bmp);
38
- tex.flipY = false; // 翻转已烘进位图(见文件头)
39
- tex.needsUpdate = true;
40
- // `TextureLoader` 时代的 `<img>.src` 在这里没有了;「打印 shader 数据」探针回退取地址要用它。
41
- tex.userData.url = url;
42
- return tex;
43
- });
44
- }
45
-
46
- /** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
47
- * (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
48
- * 只在「这张纹理确实被丢掉」的清理路径上调用:关掉的位图再也传不上去,WebGL 上下文丢失后的重建会拿到空图。 */
49
- export function releasePanoTexture(tex: Texture | null | undefined): void {
50
- if (!tex) return;
51
- const bmp = tex.image as ImageBitmap | undefined;
52
- tex.dispose();
53
- if (bmp && typeof bmp.close === "function") bmp.close();
54
- }
1
+ import { Texture } from "three";
2
+
3
+ /**
4
+ * ⭐ 甲(皇上旨 2026-10-05「你试试甲」):一张全景图(整图 / 单片瓦片,现行 32 片/全景)的**异步解码**建纹理。
5
+ *
6
+ * 为什么要换掉 three 的 `TextureLoader`:它只等 `img.onload`(字节到手),**从不解码** —— 解码是懒的,
7
+ * 真正碰像素的是上传那一拍的 `texImage2D(<img>)`,于是整段 JPEG 解码被拽到主线程上同步做完。
8
+ * 实测(2026-10-05,皇上线上的种子全景 11904×5952 / 解码后 270MB):
9
+ * `texImage2D(未解码的 <img>)` = **1266ms 全在主线程**;
10
+ * `createImageBitmap` 解码 = **768ms 在工作线程**(同期主线程跑完 40 个宏任务只花 6ms),
11
+ * 解完再 `texImage2D(<ImageBitmap>)` = **366ms**(这 366 是 270MB 像素搬运,跟解码无关,只有降尺寸才能压掉)。
12
+ * ⚠️ 试过 `await img.decode()` 这条路:同一张图 **90 秒没回**(超大图的异步解码在 Chrome 上不可靠)⇒ 别再走它。
13
+ *
14
+ * ⚠️ **上下翻这颗雷(实测,别照抄直觉)**:Chrome 对 `ImageBitmap` 源**忽略** `UNPACK_FLIP_Y_WEBGL`
15
+ * (`<img>` / canvas 源认,bitmap 源不认)⇒ 直接把 `<img>` 换成 bitmap 会让全景**上下颠倒**。
16
+ * 解法 = 建图时 `imageOrientation: "flipY"` 把翻转烘进位图,同时把 `texture.flipY = false`
17
+ * 明说「已经翻过了」。1×2 像素对照实测:`<img>`+flipY=true 与 bitmap(flipY)+flipY=false 逐行一致。
18
+ */
19
+
20
+ /** 发起一张全景纹理的加载(下载 + 异步解码都在浏览器工作线程,主线程只拿结果)。
21
+ * 与 `TextureLoader.load` 同口径:**不许**把这一步排进 `threeUtils` 的全局队列(皇上旨 2026-09-25),
22
+ * 排队的是「上传那一句」,见两侧的 `_commitTileToShader`。 */
23
+ export function loadPanoTexture(url: string): Promise<Texture> {
24
+ return fetch(url)
25
+ .then((res) => {
26
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27
+ return res.blob();
28
+ })
29
+ .then((blob) =>
30
+ createImageBitmap(blob, {
31
+ imageOrientation: "flipY",
32
+ premultiplyAlpha: "none",
33
+ colorSpaceConversion: "none",
34
+ })
35
+ )
36
+ .then((bmp) => {
37
+ const tex = new Texture(bmp);
38
+ tex.flipY = false; // 翻转已烘进位图(见文件头)
39
+ tex.needsUpdate = true;
40
+ // `TextureLoader` 时代的 `<img>.src` 在这里没有了;「打印 shader 数据」探针回退取地址要用它。
41
+ tex.userData.url = url;
42
+ return tex;
43
+ });
44
+ }
45
+
46
+ /** 释放一张由 `loadPanoTexture` 建出来的纹理:`dispose()` 只退显存,**ImageBitmap 的原生内存要 `close()`**
47
+ * (不像 `<img>` 能等 GC —— 一张 270MB 的位图挂着不关,切几轮全景就把进程吃爆)。
48
+ * 只在「这张纹理确实被丢掉」的清理路径上调用:关掉的位图再也传不上去,WebGL 上下文丢失后的重建会拿到空图。 */
49
+ export function releasePanoTexture(tex: Texture | null | undefined): void {
50
+ if (!tex) return;
51
+ const bmp = tex.image as ImageBitmap | undefined;
52
+ tex.dispose();
53
+ if (bmp && typeof bmp.close === "function") bmp.close();
54
+ }
@@ -1,29 +1,29 @@
1
- import type { ShaderMaterial, Texture } from "three";
2
- /** 取一张「已解码**且**已上传 GPU」的瓦片;未就绪要**当场**自行起载 / 排上传,然后返回 null。
3
- * ⚠️ 语义硬约束(皇上旨 09-25「队列里的代码就是把加载好的纹理设进 shader 这一句」):
4
- * 本回调**不许**做同步的重活(解码、上传、绑定判断都不许在 render 里现场发生),只负责「发起 + 报告就绪」。 */
5
- export interface PanoSlotBindDeps {
6
- peek(key: string, original: boolean): Texture | null;
7
- /** 整槽口径(**只**给跨形态翻转那一次用):同一个点的 `RT_STITCH_SLOT_COUNT` 张一起要,缺的返回 null。
8
- * 编辑器实现 = `_peekSlotGen`(带 `proxy:` 过渡档墓碑分支),库实现 = 逐张 `peek`;
9
- * 「判不齐就整档不换」这个**决定**仍然只在本文件里下。 */
10
- peekSlot(tiles: string[], original: boolean): (Texture | null)[];
11
- /** 该槽从没成功绑过任何图时用来垫 sampler 的 1×1 占位(⛔ 空 sampler 会报错) */
12
- placeholder: Texture;
13
- }
14
- /** 取(必要时建)某材质的逐格地址账本;`len` = `panoramas.length`(= 点数 × `RT_STITCH_SLOT_COUNT`)。 */
15
- export declare function panoSlotTileKeys(mat: ShaderMaterial, len: number): string[];
16
- /**
17
- * 把一个「点槽」的 `RT_STITCH_SLOT_COUNT` 张瓦片换绑进 `panoramas[base .. base + RT_STITCH_SLOT_COUNT - 1]`。
18
- *
19
- * 返回 true = 本槽这一帧的图与点位**配好了**(调用方据此写投影矩阵);false = 沿用上一批(调用方矩阵也别动)。
20
- *
21
- * ⚠️ 只有一条例外必须整槽同帧:`uPanoTiled` 是**按槽一个标记**(每点一个数,不是每张一个),
22
- * 它告诉着色器「这几个槽装的是左右两张拼合槽(1) 还是一张整图(0)」。跨形态那一次(瓦片 ↔ 整片、
23
- * 或某点压根没切过片)若逐张换,中间几帧会拿整图按半幅采(或反之)⇒ 瞬间拉伸错位。
24
- * 所以「目标形态 ≠ 本槽现有形态」时走整档判齐口径。
25
- * ⚠️ 同形态内也分两条路(09-25 治本):**整槽换人**(有格正贴着另一批的真图 ⇒ 全部同帧换,判不齐一张都不动)与
26
- * **逐张补格**(本槽空着或同一批地址还缺的格 ⇒ 到一张补一张)。
27
- * 「这一格认的是哪张地址」记在 `panoSlotTileKeys`,换人不成就连账本都不改,下一帧重试。
28
- */
29
- export declare function panoSlotBind(mat: ShaderMaterial, panos: Texture[], base: number, tiles: string[], tiled: boolean, slot: number | undefined, deps: PanoSlotBindDeps): boolean;
1
+ import type { ShaderMaterial, Texture } from "three";
2
+ /** 取一张「已解码**且**已上传 GPU」的瓦片;未就绪要**当场**自行起载 / 排上传,然后返回 null。
3
+ * ⚠️ 语义硬约束(皇上旨 09-25「队列里的代码就是把加载好的纹理设进 shader 这一句」):
4
+ * 本回调**不许**做同步的重活(解码、上传、绑定判断都不许在 render 里现场发生),只负责「发起 + 报告就绪」。 */
5
+ export interface PanoSlotBindDeps {
6
+ peek(key: string, original: boolean): Texture | null;
7
+ /** 整槽口径(**只**给跨形态翻转那一次用):同一个点的 `RT_STITCH_SLOT_COUNT` 张一起要,缺的返回 null。
8
+ * 编辑器实现 = `_peekSlotGen`(带 `proxy:` 过渡档墓碑分支),库实现 = 逐张 `peek`;
9
+ * 「判不齐就整档不换」这个**决定**仍然只在本文件里下。 */
10
+ peekSlot(tiles: string[], original: boolean): (Texture | null)[];
11
+ /** 该槽从没成功绑过任何图时用来垫 sampler 的 1×1 占位(⛔ 空 sampler 会报错) */
12
+ placeholder: Texture;
13
+ }
14
+ /** 取(必要时建)某材质的逐格地址账本;`len` = `panoramas.length`(= 点数 × `RT_STITCH_SLOT_COUNT`)。 */
15
+ export declare function panoSlotTileKeys(mat: ShaderMaterial, len: number): string[];
16
+ /**
17
+ * 把一个「点槽」的 `RT_STITCH_SLOT_COUNT` 张瓦片换绑进 `panoramas[base .. base + RT_STITCH_SLOT_COUNT - 1]`。
18
+ *
19
+ * 返回 true = 本槽这一帧的图与点位**配好了**(调用方据此写投影矩阵);false = 沿用上一批(调用方矩阵也别动)。
20
+ *
21
+ * ⚠️ 只有一条例外必须整槽同帧:`uPanoTiled` 是**按槽一个标记**(每点一个数,不是每张一个),
22
+ * 它告诉着色器「这几个槽装的是左右两张拼合槽(1) 还是一张整图(0)」。跨形态那一次(瓦片 ↔ 整片、
23
+ * 或某点压根没切过片)若逐张换,中间几帧会拿整图按半幅采(或反之)⇒ 瞬间拉伸错位。
24
+ * 所以「目标形态 ≠ 本槽现有形态」时走整档判齐口径。
25
+ * ⚠️ 同形态内也分两条路(09-25 治本):**整槽换人**(有格正贴着另一批的真图 ⇒ 全部同帧换,判不齐一张都不动)与
26
+ * **逐张补格**(本槽空着或同一批地址还缺的格 ⇒ 到一张补一张)。
27
+ * 「这一格认的是哪张地址」记在 `panoSlotTileKeys`,换人不成就连账本都不改,下一帧重试。
28
+ */
29
+ export declare function panoSlotBind(mat: ShaderMaterial, panos: Texture[], base: number, tiles: string[], tiled: boolean, slot: number | undefined, deps: PanoSlotBindDeps): boolean;
@@ -1,106 +1,106 @@
1
- import { RT_STITCH_SLOT_COUNT, setMktPanoTiled } from "./mktShader";
2
- /**
3
- * 「panoramas[i] 这一格认的是哪张瓦片地址」——逐张换绑的**账本**,按材质实例分 WeakMap 键。
4
- * 放模块级 ⇒ 编辑器与库**共一本账**(两侧材质是不同实例,天然不串味);材质重建后自动从空账开始。
5
- */
6
- const slotTileKeys = new WeakMap();
7
- /** 取(必要时建)某材质的逐格地址账本;`len` = `panoramas.length`(= 点数 × `RT_STITCH_SLOT_COUNT`)。 */
8
- export function panoSlotTileKeys(mat, len) {
9
- let keys = slotTileKeys.get(mat);
10
- if (!keys) {
11
- keys = new Array(len).fill("");
12
- slotTileKeys.set(mat, keys);
13
- }
14
- return keys;
15
- }
16
- /**
17
- * 把一个「点槽」的 `RT_STITCH_SLOT_COUNT` 张瓦片换绑进 `panoramas[base .. base + RT_STITCH_SLOT_COUNT - 1]`。
18
- *
19
- * 返回 true = 本槽这一帧的图与点位**配好了**(调用方据此写投影矩阵);false = 沿用上一批(调用方矩阵也别动)。
20
- *
21
- * ⚠️ 只有一条例外必须整槽同帧:`uPanoTiled` 是**按槽一个标记**(每点一个数,不是每张一个),
22
- * 它告诉着色器「这几个槽装的是左右两张拼合槽(1) 还是一张整图(0)」。跨形态那一次(瓦片 ↔ 整片、
23
- * 或某点压根没切过片)若逐张换,中间几帧会拿整图按半幅采(或反之)⇒ 瞬间拉伸错位。
24
- * 所以「目标形态 ≠ 本槽现有形态」时走整档判齐口径。
25
- * ⚠️ 同形态内也分两条路(09-25 治本):**整槽换人**(有格正贴着另一批的真图 ⇒ 全部同帧换,判不齐一张都不动)与
26
- * **逐张补格**(本槽空着或同一批地址还缺的格 ⇒ 到一张补一张)。
27
- * 「这一格认的是哪张地址」记在 `panoSlotTileKeys`,换人不成就连账本都不改,下一帧重试。
28
- */
29
- export function panoSlotBind(mat, panos, base, tiles, tiled, slot, deps) {
30
- // 单全景材质的 uPanoTiled 是标量(写的时候忽略槽位);融合/映射材质是每点一个的数组 ⇒ 读法两样,判定同一个。
31
- const uv = mat.uniforms.uPanoTiled?.value;
32
- const curTiled = uv === undefined ? tiled
33
- : (typeof uv === "number" ? uv > 0.5 : uv[slot ?? 0] > 0.5);
34
- if (curTiled !== tiled) {
35
- // ── 跨形态:整槽判齐、一次写入、同一帧翻标记(见上面 ⚠️)──
36
- // ⭐ 09-28 旨「瓦片纹理不需要添加 oss 的参数」后,过渡缩图档与原图档下载的是**同一张图**,
37
- // 「原图没齐先上缩图」的兜底只剩双下载 ⇒ 整条删除;判不齐就沿用上一批。
38
- const ready = deps.peekSlot(tiles, tiled);
39
- // ⛔ 判齐用「本点槽数」派生(不许写 `ready[0] && ready[1] && ready[2] && ready[3]`)
40
- const all = ready.length >= RT_STITCH_SLOT_COUNT && ready.slice(0, RT_STITCH_SLOT_COUNT).every((t) => !!t);
41
- if (all) {
42
- const keys = panoSlotTileKeys(mat, panos.length);
43
- for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
44
- panos[base + k] = ready[k];
45
- keys[base + k] = tiles[k];
46
- }
47
- // ⚠️ 纹理与形态标记必须同一帧落地(分叉过一次 = 整片形态按瓦片 UV 采整图 ⇒ 半幅拉伸)
48
- setMktPanoTiled(mat, tiled ? 1 : 0, slot);
49
- return true;
50
- }
51
- // 整档还没齐:本槽沿用上一批(sampler 读到 undefined 会报错,所以压根没绑过才填占位)
52
- if (!panos[base]) {
53
- const keys = panoSlotTileKeys(mat, panos.length);
54
- for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
55
- panos[base + k] = deps.placeholder;
56
- keys[base + k] = tiles[k];
57
- }
58
- setMktPanoTiled(mat, tiled ? 1 : 0, slot); // 占位是 1×1 素色,两种采法无差别,直接把标记对准目标
59
- }
60
- return false;
61
- }
62
- // ── 同形态 ──
63
- const keys = panoSlotTileKeys(mat, panos.length);
64
- // ⭐ 换人(皇上旨 09-25「绝不让一张已显示的全景因为正在补第 3 格瓦片而整体退出」):
65
- // 只要**有任何一格正贴着别的批次的真图**,本槽这次就是「整槽换人」⇒ 整槽所有张必须同帧落地,
66
- // 判不齐就一张都不动(连账本也不动 ⇒ 下一帧照判「要换」继续重试,缺的那几张的下载/上传已由 `deps.peek` 当场发起)。
67
- // 否则旧全景的第 1 张会配新全景的投影矩阵、且权重照给 ⇒ 墙上的图一会儿这张一会儿那张(正是他报的现象)。
68
- let swapping = false;
69
- for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
70
- const cur = panos[base + k];
71
- if (cur && cur !== deps.placeholder && keys[base + k] !== tiles[k]) {
72
- swapping = true;
73
- break;
74
- }
75
- }
76
- if (swapping) {
77
- const got = [];
78
- for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
79
- // ⭐ 09-28 旨后不再退缩图档(过渡档去参 = 同一张原图,兜底只剩双下载)
80
- got.push(deps.peek(tiles[k], tiled));
81
- }
82
- if (!got.every((t) => !!t))
83
- return false; // 沿用上一批:图 + 矩阵 + 账本一概留着
84
- for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
85
- panos[base + k] = got[k];
86
- keys[base + k] = tiles[k];
87
- }
88
- return true;
89
- }
90
- // ── 逐张补格(皇上的口径):走到这里说明本槽没有任何一格贴着别的批次的真图
91
- // ⇒ 不是「首次那几帧」(整槽都是 1×1 占位),就是「同一批地址里还缺的格」。
92
- // 两种都各判各的、到一张补一张,缺的格露的是素色占位而非另一张全景 ⇒ 无需攒齐。──
93
- let bound = false;
94
- for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
95
- const i = base + k;
96
- // ⭐ 09-28 旨后不再退缩图档(过渡档去参 = 同一张原图,兜底只剩双下载)
97
- const tex = deps.peek(tiles[k], tiled);
98
- if (!tex)
99
- continue; // 还在路上(缺的这张已由 deps.peek 当场发起下载 / 排上传)
100
- keys[i] = tiles[k];
101
- if (panos[i] !== tex)
102
- panos[i] = tex;
103
- bound = true;
104
- }
105
- return bound;
106
- }
1
+ import { RT_STITCH_SLOT_COUNT, setMktPanoTiled } from "./mktShader";
2
+ /**
3
+ * 「panoramas[i] 这一格认的是哪张瓦片地址」——逐张换绑的**账本**,按材质实例分 WeakMap 键。
4
+ * 放模块级 ⇒ 编辑器与库**共一本账**(两侧材质是不同实例,天然不串味);材质重建后自动从空账开始。
5
+ */
6
+ const slotTileKeys = new WeakMap();
7
+ /** 取(必要时建)某材质的逐格地址账本;`len` = `panoramas.length`(= 点数 × `RT_STITCH_SLOT_COUNT`)。 */
8
+ export function panoSlotTileKeys(mat, len) {
9
+ let keys = slotTileKeys.get(mat);
10
+ if (!keys) {
11
+ keys = new Array(len).fill("");
12
+ slotTileKeys.set(mat, keys);
13
+ }
14
+ return keys;
15
+ }
16
+ /**
17
+ * 把一个「点槽」的 `RT_STITCH_SLOT_COUNT` 张瓦片换绑进 `panoramas[base .. base + RT_STITCH_SLOT_COUNT - 1]`。
18
+ *
19
+ * 返回 true = 本槽这一帧的图与点位**配好了**(调用方据此写投影矩阵);false = 沿用上一批(调用方矩阵也别动)。
20
+ *
21
+ * ⚠️ 只有一条例外必须整槽同帧:`uPanoTiled` 是**按槽一个标记**(每点一个数,不是每张一个),
22
+ * 它告诉着色器「这几个槽装的是左右两张拼合槽(1) 还是一张整图(0)」。跨形态那一次(瓦片 ↔ 整片、
23
+ * 或某点压根没切过片)若逐张换,中间几帧会拿整图按半幅采(或反之)⇒ 瞬间拉伸错位。
24
+ * 所以「目标形态 ≠ 本槽现有形态」时走整档判齐口径。
25
+ * ⚠️ 同形态内也分两条路(09-25 治本):**整槽换人**(有格正贴着另一批的真图 ⇒ 全部同帧换,判不齐一张都不动)与
26
+ * **逐张补格**(本槽空着或同一批地址还缺的格 ⇒ 到一张补一张)。
27
+ * 「这一格认的是哪张地址」记在 `panoSlotTileKeys`,换人不成就连账本都不改,下一帧重试。
28
+ */
29
+ export function panoSlotBind(mat, panos, base, tiles, tiled, slot, deps) {
30
+ // 单全景材质的 uPanoTiled 是标量(写的时候忽略槽位);融合/映射材质是每点一个的数组 ⇒ 读法两样,判定同一个。
31
+ const uv = mat.uniforms.uPanoTiled?.value;
32
+ const curTiled = uv === undefined ? tiled
33
+ : (typeof uv === "number" ? uv > 0.5 : uv[slot ?? 0] > 0.5);
34
+ if (curTiled !== tiled) {
35
+ // ── 跨形态:整槽判齐、一次写入、同一帧翻标记(见上面 ⚠️)──
36
+ // ⭐ 09-28 旨「瓦片纹理不需要添加 oss 的参数」后,过渡缩图档与原图档下载的是**同一张图**,
37
+ // 「原图没齐先上缩图」的兜底只剩双下载 ⇒ 整条删除;判不齐就沿用上一批。
38
+ const ready = deps.peekSlot(tiles, tiled);
39
+ // ⛔ 判齐用「本点槽数」派生(不许写 `ready[0] && ready[1] && ready[2] && ready[3]`)
40
+ const all = ready.length >= RT_STITCH_SLOT_COUNT && ready.slice(0, RT_STITCH_SLOT_COUNT).every((t) => !!t);
41
+ if (all) {
42
+ const keys = panoSlotTileKeys(mat, panos.length);
43
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
44
+ panos[base + k] = ready[k];
45
+ keys[base + k] = tiles[k];
46
+ }
47
+ // ⚠️ 纹理与形态标记必须同一帧落地(分叉过一次 = 整片形态按瓦片 UV 采整图 ⇒ 半幅拉伸)
48
+ setMktPanoTiled(mat, tiled ? 1 : 0, slot);
49
+ return true;
50
+ }
51
+ // 整档还没齐:本槽沿用上一批(sampler 读到 undefined 会报错,所以压根没绑过才填占位)
52
+ if (!panos[base]) {
53
+ const keys = panoSlotTileKeys(mat, panos.length);
54
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
55
+ panos[base + k] = deps.placeholder;
56
+ keys[base + k] = tiles[k];
57
+ }
58
+ setMktPanoTiled(mat, tiled ? 1 : 0, slot); // 占位是 1×1 素色,两种采法无差别,直接把标记对准目标
59
+ }
60
+ return false;
61
+ }
62
+ // ── 同形态 ──
63
+ const keys = panoSlotTileKeys(mat, panos.length);
64
+ // ⭐ 换人(皇上旨 09-25「绝不让一张已显示的全景因为正在补第 3 格瓦片而整体退出」):
65
+ // 只要**有任何一格正贴着别的批次的真图**,本槽这次就是「整槽换人」⇒ 整槽所有张必须同帧落地,
66
+ // 判不齐就一张都不动(连账本也不动 ⇒ 下一帧照判「要换」继续重试,缺的那几张的下载/上传已由 `deps.peek` 当场发起)。
67
+ // 否则旧全景的第 1 张会配新全景的投影矩阵、且权重照给 ⇒ 墙上的图一会儿这张一会儿那张(正是他报的现象)。
68
+ let swapping = false;
69
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
70
+ const cur = panos[base + k];
71
+ if (cur && cur !== deps.placeholder && keys[base + k] !== tiles[k]) {
72
+ swapping = true;
73
+ break;
74
+ }
75
+ }
76
+ if (swapping) {
77
+ const got = [];
78
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
79
+ // ⭐ 09-28 旨后不再退缩图档(过渡档去参 = 同一张原图,兜底只剩双下载)
80
+ got.push(deps.peek(tiles[k], tiled));
81
+ }
82
+ if (!got.every((t) => !!t))
83
+ return false; // 沿用上一批:图 + 矩阵 + 账本一概留着
84
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
85
+ panos[base + k] = got[k];
86
+ keys[base + k] = tiles[k];
87
+ }
88
+ return true;
89
+ }
90
+ // ── 逐张补格(皇上的口径):走到这里说明本槽没有任何一格贴着别的批次的真图
91
+ // ⇒ 不是「首次那几帧」(整槽都是 1×1 占位),就是「同一批地址里还缺的格」。
92
+ // 两种都各判各的、到一张补一张,缺的格露的是素色占位而非另一张全景 ⇒ 无需攒齐。──
93
+ let bound = false;
94
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) {
95
+ const i = base + k;
96
+ // ⭐ 09-28 旨后不再退缩图档(过渡档去参 = 同一张原图,兜底只剩双下载)
97
+ const tex = deps.peek(tiles[k], tiled);
98
+ if (!tex)
99
+ continue; // 还在路上(缺的这张已由 deps.peek 当场发起下载 / 排上传)
100
+ keys[i] = tiles[k];
101
+ if (panos[i] !== tex)
102
+ panos[i] = tex;
103
+ bound = true;
104
+ }
105
+ return bound;
106
+ }