three-base 2.0.137 → 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 (273) hide show
  1. package/DirectionalLight.d.ts +2 -2
  2. package/DirectionalLight.js +1 -1
  3. package/DirectionalLight.ts +70 -0
  4. package/EventMap.d.ts +103 -0
  5. package/EventMap.js +17 -0
  6. package/EventMap.ts +139 -0
  7. package/EventMesh.ts +60 -0
  8. package/ExtrudeMesh.js +1 -25
  9. package/ExtrudeMesh.ts +553 -0
  10. package/FPScontrol.d.ts +448 -15
  11. package/FPScontrol.js +1448 -266
  12. package/FPScontrol.ts +1855 -0
  13. package/Fire/Fire.d.ts +4 -4
  14. package/Fire/Fire.js +8 -7
  15. package/Fire/Fire.ts +89 -0
  16. package/Fire/{FireGromtry.d.ts → FireGeometry.d.ts} +2 -2
  17. package/Fire/{FireGromtry.js → FireGeometry.js} +1 -1
  18. package/Fire/FireGeometry.ts +40 -0
  19. package/Fire/FireMaterial.d.ts +3 -3
  20. package/Fire/FireMaterial.js +1 -1
  21. package/Fire/FireMaterial.ts +89 -0
  22. package/Fire/index.d.ts +1 -0
  23. package/Fire/index.js +1 -0
  24. package/Fire/index.ts +2 -0
  25. package/GLTFDracoLoader.d.ts +71 -0
  26. package/GLTFDracoLoader.js +266 -0
  27. package/GLTFDracoLoader.ts +430 -0
  28. package/Grass/Grass.d.ts +2 -2
  29. package/Grass/Grass.ts +38 -0
  30. package/Grass/index.d.ts +1 -0
  31. package/Grass/index.js +1 -0
  32. package/Grass/index.ts +2 -0
  33. package/Ground1/Ground1.d.ts +2 -3
  34. package/Ground1/Ground1.js +64 -60
  35. package/Ground1/Ground1.ts +114 -0
  36. package/Ground1/index.ts +1 -0
  37. package/MeshBase.d.ts +1 -0
  38. package/MeshBase.js +37 -13
  39. package/MeshBase.ts +253 -0
  40. package/Mirror.d.ts +3 -3
  41. package/Mirror.js +7 -3
  42. package/Mirror.ts +203 -0
  43. package/MktMesh/MktMesh.d.ts +301 -0
  44. package/MktMesh/MktMesh.js +1064 -0
  45. package/MktMesh/MktMesh.ts +1096 -0
  46. package/MktMesh/assetUrl.d.ts +59 -0
  47. package/MktMesh/assetUrl.js +154 -0
  48. package/MktMesh/assetUrl.ts +155 -0
  49. package/MktMesh/mktMappingShader.d.ts +12 -0
  50. package/MktMesh/mktMappingShader.js +139 -0
  51. package/MktMesh/mktMappingShader.ts +139 -0
  52. package/MktMesh/mktShader.d.ts +263 -0
  53. package/MktMesh/mktShader.js +1248 -0
  54. package/MktMesh/mktShader.ts +1344 -0
  55. package/MktMesh/panoBitmapTexture.d.ts +25 -0
  56. package/MktMesh/panoBitmapTexture.js +52 -0
  57. package/MktMesh/panoBitmapTexture.ts +54 -0
  58. package/MktMesh/panoSlotBind.d.ts +29 -0
  59. package/MktMesh/panoSlotBind.js +106 -0
  60. package/MktMesh/panoSlotBind.ts +153 -0
  61. package/MktMesh/panoSlotRoster.d.ts +22 -0
  62. package/MktMesh/panoSlotRoster.js +169 -0
  63. package/MktMesh/panoSlotRoster.ts +168 -0
  64. package/MktMesh/panoTileStitcher.d.ts +119 -0
  65. package/MktMesh/panoTileStitcher.js +533 -0
  66. package/MktMesh/panoTileStitcher.ts +616 -0
  67. package/MktMesh/types.d.ts +75 -0
  68. package/MktMesh/types.js +1 -0
  69. package/MktMesh/types.ts +87 -0
  70. package/MktMesh/wallCapTessellate.d.ts +33 -0
  71. package/MktMesh/wallCapTessellate.js +97 -0
  72. package/MktMesh/wallCapTessellate.ts +68 -0
  73. package/PipeMesh/PathGeometry.js +9 -2
  74. package/PipeMesh/PathGeometry.ts +343 -0
  75. package/PipeMesh/PathPoint.ts +36 -0
  76. package/PipeMesh/PathPointList.js +1 -0
  77. package/PipeMesh/PathPointList.ts +227 -0
  78. package/PipeMesh/PathTubeGeometry.ts +146 -0
  79. package/PipeMesh/PipeMesh.d.ts +1 -1
  80. package/PipeMesh/PipeMesh.js +1 -1
  81. package/PipeMesh/PipeMesh.ts +76 -0
  82. package/PipeMesh/index.ts +1 -0
  83. package/PlaneModal1/PlaneModal1.d.ts +16 -13
  84. package/PlaneModal1/PlaneModal1.js +4 -0
  85. package/PlaneModal1/PlaneModal1.ts +138 -0
  86. package/PlaneModal1/index.ts +1 -0
  87. package/PlaneText.d.ts +2 -14
  88. package/PlaneText.js +7 -2
  89. package/PlaneText.ts +55 -0
  90. package/PlaneWithHtml.d.ts +4 -2
  91. package/PlaneWithHtml.js +2 -1
  92. package/PlaneWithHtml.ts +70 -0
  93. package/PointLight/PointLight.d.ts +3 -4
  94. package/PointLight/PointLight.js +1 -7
  95. package/PointLight/PointLight.ts +67 -0
  96. package/PointLight/index.ts +1 -0
  97. package/README.md +110 -79
  98. package/Sky/Sky.ts +22 -0
  99. package/Sky/index.ts +1 -0
  100. package/SpotLight.d.ts +2 -2
  101. package/SpotLight.js +1 -1
  102. package/SpotLight.ts +74 -0
  103. package/SpriteModal1/SpriteModal1.d.ts +16 -13
  104. package/SpriteModal1/SpriteModal1.js +8 -2
  105. package/SpriteModal1/SpriteModal1.ts +146 -0
  106. package/SpriteModal1/index.ts +1 -0
  107. package/SpriteText.d.ts +14 -13
  108. package/SpriteText.js +22 -9
  109. package/SpriteText.ts +205 -0
  110. package/SpriteText2.d.ts +9 -8
  111. package/SpriteText2.js +57 -32
  112. package/SpriteText2.ts +258 -0
  113. package/SpriteText3.d.ts +86 -0
  114. package/SpriteText3.js +519 -0
  115. package/SpriteText3.ts +599 -0
  116. package/SpriteWithHtml.d.ts +4 -2
  117. package/SpriteWithHtml.ts +50 -0
  118. package/Text3D/Text3D.js +17 -21
  119. package/Text3D/Text3D.ts +77 -0
  120. package/Text3D/fontController.d.ts +10 -9
  121. package/Text3D/fontController.js +17 -50
  122. package/Text3D/fontController.ts +209 -0
  123. package/Text3D/index.ts +1 -0
  124. package/ThreeBase.d.ts +168 -31
  125. package/ThreeBase.js +663 -117
  126. package/ThreeBase.ts +1314 -0
  127. package/ThreeMouse.d.ts +20 -7
  128. package/ThreeMouse.js +78 -32
  129. package/ThreeMouse.ts +472 -0
  130. package/Transition.d.ts +12 -7
  131. package/Transition.js +26 -27
  132. package/Transition.ts +242 -0
  133. package/Water/Water.js +21 -3
  134. package/Water/Water.ts +45 -0
  135. package/Water/index.d.ts +2 -0
  136. package/Water/index.js +2 -0
  137. package/Water/index.ts +2 -0
  138. package/WaterFlow/FloatingDuck.d.ts +88 -0
  139. package/WaterFlow/FloatingDuck.js +307 -0
  140. package/WaterFlow/FloatingDuck.ts +445 -0
  141. package/WaterFlow/GPUFluidSimulation.d.ts +56 -0
  142. package/WaterFlow/GPUFluidSimulation.js +785 -0
  143. package/WaterFlow/GPUFluidSimulation.ts +827 -0
  144. package/WaterFlow/ReservoirSystem.d.ts +106 -0
  145. package/WaterFlow/ReservoirSystem.js +621 -0
  146. package/WaterFlow/ReservoirSystem.ts +754 -0
  147. package/WaterFlow/SPHField.d.ts +35 -0
  148. package/WaterFlow/SPHField.js +204 -0
  149. package/WaterFlow/SPHField.ts +228 -0
  150. package/WaterFlow/ScreenSpaceFluidPass.d.ts +69 -0
  151. package/WaterFlow/ScreenSpaceFluidPass.js +736 -0
  152. package/WaterFlow/ScreenSpaceFluidPass.ts +789 -0
  153. package/WaterFlow/ShallowWaterSurface.d.ts +45 -0
  154. package/WaterFlow/ShallowWaterSurface.js +441 -0
  155. package/WaterFlow/ShallowWaterSurface.ts +510 -0
  156. package/WaterFlow/WaterFlow.d.ts +87 -0
  157. package/WaterFlow/WaterFlow.js +171 -0
  158. package/WaterFlow/WaterFlow.ts +219 -0
  159. package/WaterFlow/types.d.ts +161 -0
  160. package/WaterFlow/types.js +1 -0
  161. package/WaterFlow/types.ts +189 -0
  162. package/WaterFlow/waterFlowPass.d.ts +3 -0
  163. package/WaterFlow/waterFlowPass.js +20 -0
  164. package/WaterFlow/waterFlowPass.ts +40 -0
  165. package/collisionOBB.d.ts +42 -0
  166. package/collisionOBB.js +112 -0
  167. package/collisionOBB.ts +108 -0
  168. package/declare.d.ts +25 -167
  169. package/index.js +0 -1
  170. package/index.ts +1 -0
  171. package/instanced/InstancedBatch.d.ts +57 -0
  172. package/instanced/InstancedBatch.js +348 -0
  173. package/instanced/InstancedBatch.ts +406 -0
  174. package/instanced/InstancedChild.d.ts +174 -0
  175. package/instanced/InstancedChild.js +264 -0
  176. package/instanced/InstancedChild.ts +327 -0
  177. package/instanced/InstancedGroup.d.ts +43 -0
  178. package/instanced/InstancedGroup.js +126 -0
  179. package/instanced/InstancedGroup.ts +137 -0
  180. package/{InstancedMeshBase.d.ts → instanced/InstancedMeshBase.d.ts} +6 -7
  181. package/{InstancedMeshBase.js → instanced/InstancedMeshBase.js} +36 -47
  182. package/instanced/InstancedMeshBase.ts +292 -0
  183. package/instanced/InstancedTemplate.d.ts +46 -0
  184. package/instanced/InstancedTemplate.js +125 -0
  185. package/instanced/InstancedTemplate.ts +188 -0
  186. package/keyControls.d.ts +29 -0
  187. package/{keyContorls.js → keyControls.js} +10 -7
  188. package/keyControls.ts +107 -0
  189. package/lensflare/LensFlare.d.ts +39 -0
  190. package/lensflare/LensFlare.js +373 -0
  191. package/lensflare/LensFlare.ts +448 -0
  192. package/lensflare/lensflare0.png +0 -0
  193. package/lensflare/lensflare3.png +0 -0
  194. package/lensflare/lensflare_custom.png +0 -0
  195. package/lensflare/lensflare_hex_ghost.png +0 -0
  196. package/lensflare/lensflare_hex_main.png +0 -0
  197. package/package.json +104 -25
  198. package/pass/bloom_pass.d.ts +1 -1
  199. package/pass/bloom_pass.js +127 -40
  200. package/pass/bloom_pass.ts +243 -0
  201. package/pass/cover_pass.d.ts +1 -1
  202. package/pass/cover_pass.js +55 -32
  203. package/pass/cover_pass.ts +182 -0
  204. package/pass/debugDepthState.d.ts +20 -0
  205. package/pass/debugDepthState.js +9 -0
  206. package/pass/debugDepthState.ts +22 -0
  207. package/pass/depthTestBias.d.ts +12 -0
  208. package/pass/depthTestBias.js +12 -0
  209. package/pass/depthTestBias.ts +12 -0
  210. package/pass/effectUserData.d.ts +27 -0
  211. package/pass/effectUserData.js +1 -0
  212. package/pass/effectUserData.ts +46 -0
  213. package/pass/frosted_glass_pass.d.ts +1 -1
  214. package/pass/frosted_glass_pass.js +45 -64
  215. package/pass/frosted_glass_pass.ts +288 -0
  216. package/pass/fsQuad.d.ts +2 -2
  217. package/pass/fsQuad.ts +34 -0
  218. package/pass/glow_pass.d.ts +1 -1
  219. package/pass/glow_pass.js +288 -72
  220. package/pass/glow_pass.ts +654 -0
  221. package/pass/screen_bloom_pass.d.ts +11 -0
  222. package/pass/screen_bloom_pass.js +235 -0
  223. package/pass/screen_bloom_pass.ts +263 -0
  224. package/pass/ssao_pass.d.ts +78 -3
  225. package/pass/ssao_pass.js +642 -206
  226. package/pass/ssao_pass.ts +918 -0
  227. package/pass/stroke_pass.d.ts +1 -1
  228. package/pass/stroke_pass.js +187 -116
  229. package/pass/stroke_pass.ts +638 -0
  230. package/shaders/FXAAShaderMaterial.js +5 -48
  231. package/shaders/FXAAShaderMaterial.ts +201 -0
  232. package/shaders/baseDepthVisShader.d.ts +11 -0
  233. package/shaders/baseDepthVisShader.js +36 -0
  234. package/shaders/baseDepthVisShader.ts +36 -0
  235. package/shaders/baseTextureToRenderShader.js +46 -51
  236. package/shaders/baseTextureToRenderShader.ts +232 -0
  237. package/shaders/copyShader.js +2 -3
  238. package/shaders/copyShader.ts +32 -0
  239. package/shaders/debugDepthCompareShader.d.ts +14 -0
  240. package/shaders/debugDepthCompareShader.js +49 -0
  241. package/shaders/debugDepthCompareShader.ts +50 -0
  242. package/shaders/debugDepthJudgeShader.d.ts +18 -0
  243. package/shaders/debugDepthJudgeShader.js +52 -0
  244. package/shaders/debugDepthJudgeShader.ts +54 -0
  245. package/shaders/debugExcludedShader.d.ts +13 -0
  246. package/shaders/debugExcludedShader.js +46 -0
  247. package/shaders/debugExcludedShader.ts +48 -0
  248. package/spriteTextBg.d.ts +1 -0
  249. package/spriteTextBg.js +20 -0
  250. package/spriteTextBg.ts +20 -0
  251. package/threeLoader.d.ts +8 -7
  252. package/threeLoader.js +58 -67
  253. package/threeLoader.ts +525 -0
  254. package/threeUtils.d.ts +2 -1
  255. package/threeUtils.js +24 -25
  256. package/threeUtils.ts +373 -0
  257. package/types.d.ts +2 -0
  258. package/types.js +6 -0
  259. package/types.ts +8 -0
  260. package/ApngTexture.d.ts +0 -13
  261. package/ApngTexture.js +0 -61
  262. package/ApngTextureManage.d.ts +0 -18
  263. package/ApngTextureManage.js +0 -87
  264. package/KeyFrameAnimation.d.ts +0 -25
  265. package/KeyFrameAnimation.js +0 -78
  266. package/keyContorls.d.ts +0 -25
  267. package/lib/draco/draco_decoder.js +0 -34
  268. package/lib/draco/draco_decoder.wasm +0 -0
  269. package/lib/draco/draco_wasm_wrapper.js +0 -117
  270. package/pass/emissive_ao_pass.d.ts +0 -13
  271. package/pass/emissive_ao_pass.js +0 -370
  272. package/shaders.d.ts +0 -5
  273. package/shaders.js +0 -93
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 全景图片地址的「相对 ⇄ 绝对」互转 —— **零 import 纯函数**(护栏可用 esbuild 直接转译真跑)。
3
+ *
4
+ * ⭐ 皇上旨 2026-10-08「json 里面的图片的地址,把域名去掉,只保留相对的根路径」;
5
+ * 同日 18:46 补充「图片路径要相对路径,**前面不要有 `/`**」⇒ 落盘形态最终定为
6
+ * **纯相对路径**(`three-base_upload/panorama/xxx_0.jpg`,⛔ 无前导 `/`),换来两件事:
7
+ * ① 换域名 / 换环境部署时,已有存档与下载的 zip 不用改一字就能用;
8
+ * ② 导出的 json 里不再写死机器名,人眼可读、可 diff。
9
+ * 为什么连前导 `/` 都不留:站点部署在子路径 `/three-base/` 下,相对路径随 `<base href>`
10
+ * 一起解析,换部署位置仍指到同一张图;根相对 `/xxx` 会被钉死在域名根,放到子目录就 404。
11
+ *
12
+ * ⛔ 代价必须在同一处偿还:落盘时剥了域名,**加载前就必须补回来**。
13
+ * 补的动作由 `MktMesh.importData()` 一处统一做(⛔ 不许在拼合器 / 预览 / UI 各补一份 ——
14
+ * 那就是老坑「旁路即失控」的重演)。同理,剥的动作只在 `MktMesh.exportData()` 一处。
15
+ *
16
+ * 🔒 两个函数必须满足两条硬性质(护栏 BR 组逐条真跑):
17
+ * - **幂等**:`strip(strip(x)) === strip(x)`、`abs(abs(x)) === abs(x)`
18
+ * ⇒ 旧存档本来存的绝对地址绝不会被反复剥/补成一串 `https://host/https://host/...`
19
+ * - **互逆**:`strip(abs(rel)) === rel`(相对 → 绝对 → 相对,回到原样)
20
+ * ⚠️ **向后兼容**:`absAssetUrl` 同时认「纯相对」与「旧存档的根相对」两种形态
21
+ * (`/a/b.jpg` 也补成绝对)⇒ 上午发布前存下的那些存档**不必重存**也能正常加载。
22
+ *
23
+ * ⚠️ 本模块刻意**不 import 任何东西**(连 three 都不):它是地址字符串规则,不是渲染代码,
24
+ * 保持零依赖才能被测试单独转译真跑。
25
+ */
26
+ /** 资源公网域名(⛔ 与 `panoramaUpload.OSS_PUBLIC_HOST` 必须同值,护栏 BR ② 钉着两边一致) */
27
+ export declare const MKT_ASSET_HOST = "https://www.3dkeshihua.com";
28
+ /**
29
+ * 剥掉本站域名 ⇒ **纯相对路径**(⭐ 皇上旨10-08 18:46「图片路径要相对路径,前面不要有 "/"」)。
30
+ * @param projName 项目名。给了 ⇒ 落盘成 `{项目名}/文件名.jpg`(19:53 旨);没给 ⇒ 落成 `文件名.jpg`。
31
+ * - 已是相对 / 根相对 / blob / data / 别的域名 ⇒ **原样返回**(幂等,也保证旧存档安全)
32
+ * - `https://www.3dkeshihua.com/a/b.jpg` ⇒ `a/b.jpg`(⛔ 不带前导 `/`)
33
+ * - `https://www.3dkeshihua.com/three-base_upload/panorama/x_0.jpg` ⇒ `x_0.jpg` 或 `{proj}/x_0.jpg`
34
+ * - host 后面直接跟 `?` `#` 也认(OSS 地址可能带参),或直接到尾 ⇒ `""`(空串:无路径可留)
35
+ *
36
+ * ⚠️ 为何要「不带 `/`」:站点部署在子路径 `/three-base/` 下,相对路径能随
37
+ * `<base href>` 一起解析,换部署位置仍指到同一张图;而根相对 `/xxx` 会被钉死在域名根,
38
+ * 放到子目录就404。
39
+ * ⚠️ 为何要顶掉上传目录前缀:落盘地址是给「换环境部署 / 交给别人」用的,
40
+ * `three-base_upload/panorama` 是本项目的 OSS 内部目录,跟内容无关;顶成项目名后
41
+ * 「一个项目一套目录」的语义直接体现在地址里。
42
+ */
43
+ export declare function stripAssetHost(url: string, projName?: string): string;
44
+ /**
45
+ * 补回域名 ⇒ 绝对地址(加载前的唯一落点)。
46
+ * - 已是绝对 / blob / data ⇒ **原样返回**(幂等⇒ 旧存档进 `importData` 不会被二次拼接)
47
+ * - `a/b.jpg` ⇒ `https://www.3dkeshihua.com/a/b.jpg`
48
+ * - `/a/b.jpg`(旧存档的根相对形态)⇒ 同样认,补成绝对(**旧存档不必重存也能用**)
49
+ * - `{项目名}/x_0.jpg`(19:53 旨落盘形态)⇒ 还原成真身
50
+ * `https://host/three-base_upload/panorama/x_0.jpg`
51
+ * ⚠️ 落盘时顶掉了上传目录前缀,这里必须还原,否则 json 里的地址永远指不回原图。
52
+ *
53
+ * ⛔ 认错的后果(所以判据要严):把「项目名段」误当成真目录,地址会指向**不存在的图**(图片裂开);
54
+ * 漏认则地址多一段目录名,同样指不到图。两种都只影响显示、不会崩,所以宁可漏认不可误认 ——
55
+ * 判据 =「首段命中已知站点目录 ⇒ 一律不还原」+「首段等于传入项目名 ⇒ 才还原」。
56
+ *
57
+ * @param projName 项目名。**传了就以它为准**(最可靠);没传则只认「首段是已知站点目录之外的名字」这一条。
58
+ */
59
+ export declare function absAssetUrl(url: string, projName?: string): string;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * 全景图片地址的「相对 ⇄ 绝对」互转 —— **零 import 纯函数**(护栏可用 esbuild 直接转译真跑)。
3
+ *
4
+ * ⭐ 皇上旨 2026-10-08「json 里面的图片的地址,把域名去掉,只保留相对的根路径」;
5
+ * 同日 18:46 补充「图片路径要相对路径,**前面不要有 `/`**」⇒ 落盘形态最终定为
6
+ * **纯相对路径**(`three-base_upload/panorama/xxx_0.jpg`,⛔ 无前导 `/`),换来两件事:
7
+ * ① 换域名 / 换环境部署时,已有存档与下载的 zip 不用改一字就能用;
8
+ * ② 导出的 json 里不再写死机器名,人眼可读、可 diff。
9
+ * 为什么连前导 `/` 都不留:站点部署在子路径 `/three-base/` 下,相对路径随 `<base href>`
10
+ * 一起解析,换部署位置仍指到同一张图;根相对 `/xxx` 会被钉死在域名根,放到子目录就 404。
11
+ *
12
+ * ⛔ 代价必须在同一处偿还:落盘时剥了域名,**加载前就必须补回来**。
13
+ * 补的动作由 `MktMesh.importData()` 一处统一做(⛔ 不许在拼合器 / 预览 / UI 各补一份 ——
14
+ * 那就是老坑「旁路即失控」的重演)。同理,剥的动作只在 `MktMesh.exportData()` 一处。
15
+ *
16
+ * 🔒 两个函数必须满足两条硬性质(护栏 BR 组逐条真跑):
17
+ * - **幂等**:`strip(strip(x)) === strip(x)`、`abs(abs(x)) === abs(x)`
18
+ * ⇒ 旧存档本来存的绝对地址绝不会被反复剥/补成一串 `https://host/https://host/...`
19
+ * - **互逆**:`strip(abs(rel)) === rel`(相对 → 绝对 → 相对,回到原样)
20
+ * ⚠️ **向后兼容**:`absAssetUrl` 同时认「纯相对」与「旧存档的根相对」两种形态
21
+ * (`/a/b.jpg` 也补成绝对)⇒ 上午发布前存下的那些存档**不必重存**也能正常加载。
22
+ *
23
+ * ⚠️ 本模块刻意**不 import 任何东西**(连 three 都不):它是地址字符串规则,不是渲染代码,
24
+ * 保持零依赖才能被测试单独转译真跑。
25
+ */
26
+ /** 资源公网域名(⛔ 与 `panoramaUpload.OSS_PUBLIC_HOST` 必须同值,护栏 BR ② 钉着两边一致) */
27
+ export const MKT_ASSET_HOST = "https://www.3dkeshihua.com";
28
+ /** http 明文版 host:历史数据里可能存过这个形态,一并认,避免出现「剥不掉」的漏网地址 */
29
+ const MKT_ASSET_HOST_HTTP = "http://www.3dkeshihua.com";
30
+ /**
31
+ * 这些前缀的地址**不参与**剥/补 —— 它们压根不是远端资源:
32
+ * `blob:` 是本地内存里的 File/ObjectURL,`data:` 是内联字节,补域名只会把它们弄坏。
33
+ */
34
+ function isLocalUrl(url) {
35
+ return url.startsWith("blob:") || url.startsWith("data:");
36
+ }
37
+ /** 是不是「路径式地址」(相对 `three-base_upload/xxx` 或根相对 `/three-base_upload/xxx`;`//host/x` 是协议相对 = 绝对,不算) */
38
+ function isPathLike(url) {
39
+ return !url.startsWith("//") && (url.startsWith("/") || !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url));
40
+ }
41
+ /**
42
+ * OSS 上传目录前缀(`three-base_upload/panorama/…`)⇒ 落盘时整段丢掉,只留**文件名**。
43
+ * ⭐ 皇上旨10-08 19:53「导出时图片地址改成 `{项目名称}/xxx.jpg`」⇒ 这一段要被**项目名**顶替。
44
+ * 取不到项目名时(库内直接调 `exportData()` 而没传)**仍按老口径剥成纯相对**(不退回带`/` 的形态)。
45
+ */
46
+ /** OSS 上传目录(`three-base_upload/panorama/`)——⛔ **不带尾斜杠**(拼接时自己补 `/`,否则会拼出双斜杠) */
47
+ const UPLOAD_DIR_PREFIX = "three-base_upload";
48
+ /** 上传目录的**完整**两级路径(还原时用) */
49
+ const UPLOAD_FULL_PREFIX = `${UPLOAD_DIR_PREFIX}/panorama`;
50
+ /**
51
+ * 本桶上「不是资源目录」的一级目录名—— 命中它们就说明是**站点自身路径**,不是项目名。
52
+ *(`three-base/` = 站点根,`three-base_upload/` = 资源上传根;两者都是固定目录,与项目名无关。)
53
+ */
54
+ const KNOWN_TOP_DIRS = [UPLOAD_DIR_PREFIX, "three-base"];
55
+ /** 首段看起来像「项目名」(= 不是已知站点目录)—— `strip` 与 `abs` 两侧**必须用同一把尺**,否则闭环对不上 */
56
+ function segs0IsProjectDir(seg0) {
57
+ return seg0.length > 0 && !KNOWN_TOP_DIRS.includes(seg0);
58
+ }
59
+ /**
60
+ * 文件名安全化(与 `projectZipLayout.safeFileSegment` **逐字同口径**,两处必须同步 ——
61
+ * 一个存 json 里的地址、一个存zip 里的目录名,规则不一致会让「按 json 里的地址去 zip 找图」扑空)。
62
+ * Windows 与URL 都待见的字符 → 下划线;空名回落。
63
+ */
64
+ function safeSeg(name) {
65
+ const s = (name || "").replace(/[\\/:*?"<>|]+/g, "_").trim();
66
+ return s.length > 0 ? s : "";
67
+ }
68
+ /**
69
+ * 剥掉本站域名 ⇒ **纯相对路径**(⭐ 皇上旨10-08 18:46「图片路径要相对路径,前面不要有 "/"」)。
70
+ * @param projName 项目名。给了 ⇒ 落盘成 `{项目名}/文件名.jpg`(19:53 旨);没给 ⇒ 落成 `文件名.jpg`。
71
+ * - 已是相对 / 根相对 / blob / data / 别的域名 ⇒ **原样返回**(幂等,也保证旧存档安全)
72
+ * - `https://www.3dkeshihua.com/a/b.jpg` ⇒ `a/b.jpg`(⛔ 不带前导 `/`)
73
+ * - `https://www.3dkeshihua.com/three-base_upload/panorama/x_0.jpg` ⇒ `x_0.jpg` 或 `{proj}/x_0.jpg`
74
+ * - host 后面直接跟 `?` `#` 也认(OSS 地址可能带参),或直接到尾 ⇒ `""`(空串:无路径可留)
75
+ *
76
+ * ⚠️ 为何要「不带 `/`」:站点部署在子路径 `/three-base/` 下,相对路径能随
77
+ * `<base href>` 一起解析,换部署位置仍指到同一张图;而根相对 `/xxx` 会被钉死在域名根,
78
+ * 放到子目录就404。
79
+ * ⚠️ 为何要顶掉上传目录前缀:落盘地址是给「换环境部署 / 交给别人」用的,
80
+ * `three-base_upload/panorama` 是本项目的 OSS 内部目录,跟内容无关;顶成项目名后
81
+ * 「一个项目一套目录」的语义直接体现在地址里。
82
+ */
83
+ export function stripAssetHost(url, projName) {
84
+ if (typeof url !== "string" || url === "")
85
+ return url;
86
+ if (isLocalUrl(url) || isPathLike(url))
87
+ return url;
88
+ for (const host of [MKT_ASSET_HOST, MKT_ASSET_HOST_HTTP]) {
89
+ if (!url.startsWith(host))
90
+ continue;
91
+ const rest = url.slice(host.length);
92
+ // host 是前缀但后面不是 path/query/fragment 的边界 ⇒ 不是本站资源(例如`...3dkeshihua.com.evil.com`)
93
+ if (rest !== "" && !/^[/?#]/.test(rest))
94
+ return url;
95
+ // 去掉前导 `/`:得到真正的相对路径(`/a/b.jpg` → `a/b.jpg`;`/` → `""`)
96
+ let path = rest.replace(/^\/+/, "");
97
+ // 上传目录前缀整段丢掉,只留文件名(query / fragment 原样跟在后面)
98
+ if (path === UPLOAD_DIR_PREFIX || path.startsWith(`${UPLOAD_DIR_PREFIX}/`)) {
99
+ const tail = path.slice(UPLOAD_DIR_PREFIX.length + 1); // 越过 `three-base_upload/` ⇒ `panorama/x_0.jpg`
100
+ const cut = tail.search(/[/?#]/); // 截到第一个分隔符(`/` `?` `#`)
101
+ // ⚠️ slice(cut) 会把那个分隔符一起带上(`panorama/x.jpg` → `/x.jpg`)⇒ 要 slice(cut+1)
102
+ const file = cut < 0 ? tail : tail.slice(cut + 1); // 文件名(含后面的 ?/#)
103
+ const dir = safeSeg(projName ?? "");
104
+ path = dir ? `${dir}/${file}` : file;
105
+ return path;
106
+ }
107
+ // ⭐ 幂等对称:`{项目名}/x.jpg` 这种**已顶替过**的形态(还原回绝对后再剥)要认出来,
108
+ // 否则项目名段会被原样留着、形态跟首轮落盘的不一致 ⇒ 导出→导入→导出闭环对不上。
109
+ // 判据 = 首段既不是已知站点目录、也不是上传目录(与 `absAssetUrl` 的还原判据镜像)。
110
+ const seg0 = path.split("/")[0];
111
+ if (segs0IsProjectDir(seg0)) {
112
+ const tail = path.slice(seg0.length + 1); // 越过 `{项目名}/`
113
+ const dir = safeSeg(projName ?? "");
114
+ // 传了项目名且首段与它一致 ⇒ 剥掉首段还原成裸文件名(无项目名时的落盘形态)
115
+ if (dir && seg0 === dir)
116
+ return tail;
117
+ // 没传项目名 ⇒ 首段无从确认是不是项目名,⛔ 原样留着(宁可多一段也不猜错)
118
+ return path;
119
+ }
120
+ return path;
121
+ }
122
+ return url;
123
+ }
124
+ /**
125
+ * 补回域名 ⇒ 绝对地址(加载前的唯一落点)。
126
+ * - 已是绝对 / blob / data ⇒ **原样返回**(幂等⇒ 旧存档进 `importData` 不会被二次拼接)
127
+ * - `a/b.jpg` ⇒ `https://www.3dkeshihua.com/a/b.jpg`
128
+ * - `/a/b.jpg`(旧存档的根相对形态)⇒ 同样认,补成绝对(**旧存档不必重存也能用**)
129
+ * - `{项目名}/x_0.jpg`(19:53 旨落盘形态)⇒ 还原成真身
130
+ * `https://host/three-base_upload/panorama/x_0.jpg`
131
+ * ⚠️ 落盘时顶掉了上传目录前缀,这里必须还原,否则 json 里的地址永远指不回原图。
132
+ *
133
+ * ⛔ 认错的后果(所以判据要严):把「项目名段」误当成真目录,地址会指向**不存在的图**(图片裂开);
134
+ * 漏认则地址多一段目录名,同样指不到图。两种都只影响显示、不会崩,所以宁可漏认不可误认 ——
135
+ * 判据 =「首段命中已知站点目录 ⇒ 一律不还原」+「首段等于传入项目名 ⇒ 才还原」。
136
+ *
137
+ * @param projName 项目名。**传了就以它为准**(最可靠);没传则只认「首段是已知站点目录之外的名字」这一条。
138
+ */
139
+ export function absAssetUrl(url, projName) {
140
+ if (typeof url !== "string" || url === "")
141
+ return url;
142
+ if (isLocalUrl(url) || !isPathLike(url))
143
+ return url;
144
+ const path = url.replace(/^\/+/, "");
145
+ const segs = path.split("/");
146
+ // 只在「恰好两段」且「首段不是已知站点目录」时,才按「项目名/文件名」还原
147
+ if (segs.length === 2 && !KNOWN_TOP_DIRS.includes(segs[0])) {
148
+ const dir = safeSeg(projName ?? "");
149
+ // 传了项目名就严格比它(最可靠);没传才放宽到「非已知目录即可认」
150
+ if (!dir || segs[0] === dir)
151
+ return `${MKT_ASSET_HOST}/${UPLOAD_FULL_PREFIX}/${segs[1]}`;
152
+ }
153
+ return `${MKT_ASSET_HOST}/${path}`;
154
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * 全景图片地址的「相对 ⇄ 绝对」互转 —— **零 import 纯函数**(护栏可用 esbuild 直接转译真跑)。
3
+ *
4
+ * ⭐ 皇上旨 2026-10-08「json 里面的图片的地址,把域名去掉,只保留相对的根路径」;
5
+ * 同日 18:46 补充「图片路径要相对路径,**前面不要有 `/`**」⇒ 落盘形态最终定为
6
+ * **纯相对路径**(`three-base_upload/panorama/xxx_0.jpg`,⛔ 无前导 `/`),换来两件事:
7
+ * ① 换域名 / 换环境部署时,已有存档与下载的 zip 不用改一字就能用;
8
+ * ② 导出的 json 里不再写死机器名,人眼可读、可 diff。
9
+ * 为什么连前导 `/` 都不留:站点部署在子路径 `/three-base/` 下,相对路径随 `<base href>`
10
+ * 一起解析,换部署位置仍指到同一张图;根相对 `/xxx` 会被钉死在域名根,放到子目录就 404。
11
+ *
12
+ * ⛔ 代价必须在同一处偿还:落盘时剥了域名,**加载前就必须补回来**。
13
+ * 补的动作由 `MktMesh.importData()` 一处统一做(⛔ 不许在拼合器 / 预览 / UI 各补一份 ——
14
+ * 那就是老坑「旁路即失控」的重演)。同理,剥的动作只在 `MktMesh.exportData()` 一处。
15
+ *
16
+ * 🔒 两个函数必须满足两条硬性质(护栏 BR 组逐条真跑):
17
+ * - **幂等**:`strip(strip(x)) === strip(x)`、`abs(abs(x)) === abs(x)`
18
+ * ⇒ 旧存档本来存的绝对地址绝不会被反复剥/补成一串 `https://host/https://host/...`
19
+ * - **互逆**:`strip(abs(rel)) === rel`(相对 → 绝对 → 相对,回到原样)
20
+ * ⚠️ **向后兼容**:`absAssetUrl` 同时认「纯相对」与「旧存档的根相对」两种形态
21
+ * (`/a/b.jpg` 也补成绝对)⇒ 上午发布前存下的那些存档**不必重存**也能正常加载。
22
+ *
23
+ * ⚠️ 本模块刻意**不 import 任何东西**(连 three 都不):它是地址字符串规则,不是渲染代码,
24
+ * 保持零依赖才能被测试单独转译真跑。
25
+ */
26
+
27
+ /** 资源公网域名(⛔ 与 `panoramaUpload.OSS_PUBLIC_HOST` 必须同值,护栏 BR ② 钉着两边一致) */
28
+ export const MKT_ASSET_HOST = "https://www.3dkeshihua.com";
29
+
30
+ /** http 明文版 host:历史数据里可能存过这个形态,一并认,避免出现「剥不掉」的漏网地址 */
31
+ const MKT_ASSET_HOST_HTTP = "http://www.3dkeshihua.com";
32
+
33
+ /**
34
+ * 这些前缀的地址**不参与**剥/补 —— 它们压根不是远端资源:
35
+ * `blob:` 是本地内存里的 File/ObjectURL,`data:` 是内联字节,补域名只会把它们弄坏。
36
+ */
37
+ function isLocalUrl(url: string): boolean {
38
+ return url.startsWith("blob:") || url.startsWith("data:");
39
+ }
40
+
41
+ /** 是不是「路径式地址」(相对 `three-base_upload/xxx` 或根相对 `/three-base_upload/xxx`;`//host/x` 是协议相对 = 绝对,不算) */
42
+ function isPathLike(url: string): boolean {
43
+ return !url.startsWith("//") && (url.startsWith("/") || !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url));
44
+ }
45
+
46
+ /**
47
+ * OSS 上传目录前缀(`three-base_upload/panorama/…`)⇒ 落盘时整段丢掉,只留**文件名**。
48
+ * ⭐ 皇上旨10-08 19:53「导出时图片地址改成 `{项目名称}/xxx.jpg`」⇒ 这一段要被**项目名**顶替。
49
+ * 取不到项目名时(库内直接调 `exportData()` 而没传)**仍按老口径剥成纯相对**(不退回带`/` 的形态)。
50
+ */
51
+ /** OSS 上传目录(`three-base_upload/panorama/`)——⛔ **不带尾斜杠**(拼接时自己补 `/`,否则会拼出双斜杠) */
52
+ const UPLOAD_DIR_PREFIX = "three-base_upload";
53
+ /** 上传目录的**完整**两级路径(还原时用) */
54
+ const UPLOAD_FULL_PREFIX = `${UPLOAD_DIR_PREFIX}/panorama`;
55
+ /**
56
+ * 本桶上「不是资源目录」的一级目录名—— 命中它们就说明是**站点自身路径**,不是项目名。
57
+ *(`three-base/` = 站点根,`three-base_upload/` = 资源上传根;两者都是固定目录,与项目名无关。)
58
+ */
59
+ const KNOWN_TOP_DIRS = [UPLOAD_DIR_PREFIX, "three-base"];
60
+
61
+ /** 首段看起来像「项目名」(= 不是已知站点目录)—— `strip` 与 `abs` 两侧**必须用同一把尺**,否则闭环对不上 */
62
+ function segs0IsProjectDir(seg0: string): boolean {
63
+ return seg0.length > 0 && !KNOWN_TOP_DIRS.includes(seg0);
64
+ }
65
+
66
+ /**
67
+ * 文件名安全化(与 `projectZipLayout.safeFileSegment` **逐字同口径**,两处必须同步 ——
68
+ * 一个存 json 里的地址、一个存zip 里的目录名,规则不一致会让「按 json 里的地址去 zip 找图」扑空)。
69
+ * Windows 与URL 都待见的字符 → 下划线;空名回落。
70
+ */
71
+ function safeSeg(name: string): string {
72
+ const s = (name || "").replace(/[\\/:*?"<>|]+/g, "_").trim();
73
+ return s.length > 0 ? s : "";
74
+ }
75
+
76
+ /**
77
+ * 剥掉本站域名 ⇒ **纯相对路径**(⭐ 皇上旨10-08 18:46「图片路径要相对路径,前面不要有 "/"」)。
78
+ * @param projName 项目名。给了 ⇒ 落盘成 `{项目名}/文件名.jpg`(19:53 旨);没给 ⇒ 落成 `文件名.jpg`。
79
+ * - 已是相对 / 根相对 / blob / data / 别的域名 ⇒ **原样返回**(幂等,也保证旧存档安全)
80
+ * - `https://www.3dkeshihua.com/a/b.jpg` ⇒ `a/b.jpg`(⛔ 不带前导 `/`)
81
+ * - `https://www.3dkeshihua.com/three-base_upload/panorama/x_0.jpg` ⇒ `x_0.jpg` 或 `{proj}/x_0.jpg`
82
+ * - host 后面直接跟 `?` `#` 也认(OSS 地址可能带参),或直接到尾 ⇒ `""`(空串:无路径可留)
83
+ *
84
+ * ⚠️ 为何要「不带 `/`」:站点部署在子路径 `/three-base/` 下,相对路径能随
85
+ * `<base href>` 一起解析,换部署位置仍指到同一张图;而根相对 `/xxx` 会被钉死在域名根,
86
+ * 放到子目录就404。
87
+ * ⚠️ 为何要顶掉上传目录前缀:落盘地址是给「换环境部署 / 交给别人」用的,
88
+ * `three-base_upload/panorama` 是本项目的 OSS 内部目录,跟内容无关;顶成项目名后
89
+ * 「一个项目一套目录」的语义直接体现在地址里。
90
+ */
91
+ export function stripAssetHost(url: string, projName?: string): string {
92
+ if (typeof url !== "string" || url === "") return url;
93
+ if (isLocalUrl(url) || isPathLike(url)) return url;
94
+ for (const host of [MKT_ASSET_HOST, MKT_ASSET_HOST_HTTP]) {
95
+ if (!url.startsWith(host)) continue;
96
+ const rest = url.slice(host.length);
97
+ // host 是前缀但后面不是 path/query/fragment 的边界 ⇒ 不是本站资源(例如`...3dkeshihua.com.evil.com`)
98
+ if (rest !== "" && !/^[/?#]/.test(rest)) return url;
99
+ // 去掉前导 `/`:得到真正的相对路径(`/a/b.jpg` → `a/b.jpg`;`/` → `""`)
100
+ let path = rest.replace(/^\/+/, "");
101
+ // 上传目录前缀整段丢掉,只留文件名(query / fragment 原样跟在后面)
102
+ if (path === UPLOAD_DIR_PREFIX || path.startsWith(`${UPLOAD_DIR_PREFIX}/`)) {
103
+ const tail = path.slice(UPLOAD_DIR_PREFIX.length + 1); // 越过 `three-base_upload/` ⇒ `panorama/x_0.jpg`
104
+ const cut = tail.search(/[/?#]/); // 截到第一个分隔符(`/` `?` `#`)
105
+ // ⚠️ slice(cut) 会把那个分隔符一起带上(`panorama/x.jpg` → `/x.jpg`)⇒ 要 slice(cut+1)
106
+ const file = cut < 0 ? tail : tail.slice(cut + 1); // 文件名(含后面的 ?/#)
107
+ const dir = safeSeg(projName ?? "");
108
+ path = dir ? `${dir}/${file}` : file;
109
+ return path;
110
+ }
111
+ // ⭐ 幂等对称:`{项目名}/x.jpg` 这种**已顶替过**的形态(还原回绝对后再剥)要认出来,
112
+ // 否则项目名段会被原样留着、形态跟首轮落盘的不一致 ⇒ 导出→导入→导出闭环对不上。
113
+ // 判据 = 首段既不是已知站点目录、也不是上传目录(与 `absAssetUrl` 的还原判据镜像)。
114
+ const seg0 = path.split("/")[0];
115
+ if (segs0IsProjectDir(seg0)) {
116
+ const tail = path.slice(seg0.length + 1); // 越过 `{项目名}/`
117
+ const dir = safeSeg(projName ?? "");
118
+ // 传了项目名且首段与它一致 ⇒ 剥掉首段还原成裸文件名(无项目名时的落盘形态)
119
+ if (dir && seg0 === dir) return tail;
120
+ // 没传项目名 ⇒ 首段无从确认是不是项目名,⛔ 原样留着(宁可多一段也不猜错)
121
+ return path;
122
+ }
123
+ return path;
124
+ }
125
+ return url;
126
+ }
127
+
128
+ /**
129
+ * 补回域名 ⇒ 绝对地址(加载前的唯一落点)。
130
+ * - 已是绝对 / blob / data ⇒ **原样返回**(幂等⇒ 旧存档进 `importData` 不会被二次拼接)
131
+ * - `a/b.jpg` ⇒ `https://www.3dkeshihua.com/a/b.jpg`
132
+ * - `/a/b.jpg`(旧存档的根相对形态)⇒ 同样认,补成绝对(**旧存档不必重存也能用**)
133
+ * - `{项目名}/x_0.jpg`(19:53 旨落盘形态)⇒ 还原成真身
134
+ * `https://host/three-base_upload/panorama/x_0.jpg`
135
+ * ⚠️ 落盘时顶掉了上传目录前缀,这里必须还原,否则 json 里的地址永远指不回原图。
136
+ *
137
+ * ⛔ 认错的后果(所以判据要严):把「项目名段」误当成真目录,地址会指向**不存在的图**(图片裂开);
138
+ * 漏认则地址多一段目录名,同样指不到图。两种都只影响显示、不会崩,所以宁可漏认不可误认 ——
139
+ * 判据 =「首段命中已知站点目录 ⇒ 一律不还原」+「首段等于传入项目名 ⇒ 才还原」。
140
+ *
141
+ * @param projName 项目名。**传了就以它为准**(最可靠);没传则只认「首段是已知站点目录之外的名字」这一条。
142
+ */
143
+ export function absAssetUrl(url: string, projName?: string): string {
144
+ if (typeof url !== "string" || url === "") return url;
145
+ if (isLocalUrl(url) || !isPathLike(url)) return url;
146
+ const path = url.replace(/^\/+/, "");
147
+ const segs = path.split("/");
148
+ // 只在「恰好两段」且「首段不是已知站点目录」时,才按「项目名/文件名」还原
149
+ if (segs.length === 2 && !KNOWN_TOP_DIRS.includes(segs[0])) {
150
+ const dir = safeSeg(projName ?? "");
151
+ // 传了项目名就严格比它(最可靠);没传才放宽到「非已知目录即可认」
152
+ if (!dir || segs[0] === dir) return `${MKT_ASSET_HOST}/${UPLOAD_FULL_PREFIX}/${segs[1]}`;
153
+ }
154
+ return `${MKT_ASSET_HOST}/${path}`;
155
+ }
@@ -0,0 +1,12 @@
1
+ import { ShaderMaterial } from "three";
2
+ /**
3
+ * 创建「全景序号直取」着色器材质(皇上旨 2026-09-29「三模式三 shader」之二):
4
+ * 编辑器「映射设置模式」的墙材质 + **基类 MktMesh(发布/预览)的固定墙材质**
5
+ * (皇上旨:mktmesh 就只有按照 attribute 配置来显示全景这一种固定的 shader)。
6
+ * uniform 形状与 blend 工厂同档(panoramas[16]/textureMatrices[8]/weights[8]/uPanoTiled[8])⇒
7
+ * 喂料复用 feedBlendProjection 的槽位指派路(feedFaceMapping),零新喂料路。
8
+ * uFloorCoreRadius/uFloorFadeRange/uFloorOpacity 三根本 shader 不读,但喂料统一入口会写 ⇒ JS 侧留着(静默忽略)。
9
+ */
10
+ export declare function createMktFloorMappingShaderMaterial(options?: {
11
+ maxPano?: number;
12
+ }): ShaderMaterial;
@@ -0,0 +1,139 @@
1
+ /*
2
+ * 全景「序号直取」投射着色器(mapping shader):MktMesh 基类(发布/预览)、
3
+ * 编辑器 mapping 档墙体、家具立方体三处共用。
4
+ *
5
+ * 取图口径:按几何 attribute aPanoIndex 在 panoIndices 槽里对号取图,不看距离、不看夹角。
6
+ * 与 blend / selected 共用同一份 floorVertexShader 与同一套采样口径(改采样两处同改)。
7
+ */
8
+ import { BackSide, Matrix4, ShaderMaterial, Vector3 } from "three";
9
+ import { createPlaceholderTexture, floorVertexShader, MKT_PANO_MAX_SLOTS, RT_STITCH_SLOT_COUNT } from "./mktShader";
10
+ /**
11
+ * 序号直取片元(皇上旨 2026-09-29「三模式三 shader」之二):
12
+ * 按三角面 aPanoIndex 在 panoIndices 槽里找对应全景纹理 —— **不看距离、不看夹角**(blend 那套打分/归约整段没有)。
13
+ * 槽内采样段(等距柱状投射 + 左右 2 张拼合槽 + 采样即线性、不再手动 pow 解码)与 blend **逐字同口径**;改采样口径两处同改。
14
+ * 未设序号 / 序号不在槽里 / 该槽瓦片没绑好(weights≤0)→ uFaceMissingColor 深灰兜底
15
+ * (皇上旨 09-28「装不下就深灰」;weights 闸是 09-29 拆分时补的 —— 原运行时分支没这道闸,
16
+ * 槽没绑好但序号命中会采到 vec3(0) 纯黑,深灰才是钦定兜底色)。
17
+ */
18
+ function buildFloorMappingFragment(maxPano) {
19
+ // 每个全景点占 **2** 张拼合槽纹理(32 片每 16 片拼一张:左半幅 / 右半幅)⇒ 8 × 2 = 16 = sampler 硬顶
20
+ const tileCount = maxPano * RT_STITCH_SLOT_COUNT;
21
+ let slotCalc = ""; // 每槽各自采样出 linK(weights[k]>0 才采;没绑好保持 0,由 facePick 的 weights 闸兜成深灰)
22
+ let slotFacePick = ""; // 逐槽展开「panoIndices[k]==vPanoIndex 则取 lin{k}」(ANGLE 禁动态索引 ⇒ JS 常量展开)
23
+ for (let k = 0; k < maxPano; k++) {
24
+ const base = k * RT_STITCH_SLOT_COUNT; // 本点占 2 个连续纹理槽:base=k*2 .. base+1(顺序 [左半幅, 右半幅])
25
+ slotFacePick += `
26
+ if (panoIndices[${k}] >= 0.0 && weights[${k}] > 0.0 && abs(panoIndices[${k}] - vPanoIndex) < 0.5) faceCol = lin${k};`;
27
+ slotCalc += `
28
+ // ── 点槽 ${k}(纹理 ${base}..${base + 1})──
29
+ vec3 lin${k} = vec3(0.0);
30
+ if (weights[${k}] > 0.0) {
31
+ mat4 tm = textureMatrices[${k}];
32
+ vec3 texPos = tm[3].xyz; // 投影中心 = 纹理矩阵位置
33
+ mat3 texRot = mat3(tm); // 纹理朝向 = 旋转分量
34
+ vec3 dir = normalize(vProjPos - texPos);
35
+ dir = texRot * dir; // 转到纹理局部方向
36
+ float lon = atan(dir.z, dir.x);
37
+ float u = lon * 0.15915494309189535 + 0.5; // 经度 -> u
38
+ float lat = asin(clamp(dir.y, -1.0, 1.0));
39
+ // 等距柱状线性 v(= 全图 UV),与 blend / 单全景分支**逐字同口径**
40
+ float v = 0.5 + lat * 0.3183098861837907; // 1/PI
41
+ // 左右两半 -> 拼合槽下标 [左半幅=0, 右半幅=1](每槽 = 8×4 网格里 4×4=16 片拼成,见 RT_STITCH_SLOT_TILES)
42
+ // ⭐ 10-06 晚旨:竖着不再分块 ⇒ 槽只随 u 走(与 blend / 单全景分支**逐字同口径**)
43
+ int tileIdx = (u >= 0.5) ? 1 : 0;
44
+ float localU = (u < 0.5) ? (u * 2.0) : (u * 2.0 - 1.0);
45
+ // ⛔ 纵向不再减半(槽就是全高)⇒ localV 恒等于 v;写成 2v/2v-1 = 纵向分辨率白丢一半
46
+ float localV = v;
47
+ // 🧩 该点这 2 槽装的是整图(whole 形态 / 没切过片)⇒ 还原成全图 UV 再采样(与 blend 逐字同口径)
48
+ float panoU = (uPanoTiled[${k}] > 0.5) ? localU : u;
49
+ float panoV = (uPanoTiled[${k}] > 0.5) ? localV : v;
50
+ // ⚠️ ANGLE(D3D) 禁止动态索引 sampler 数组:以下 2 个采样下标均为「base + 字面量」常量表达式,编译安全
51
+ // ⭐ 采样即线性(与单全景 / blend / selected 三路同一条口径,10-07 旨「色彩空间统一使用 liner」):纹理都标了
52
+ // colorSpace = SRGBColorSpace ⇒ 硬件上传成 SRGB8_ALPHA8、texture2D 已解码一次;
53
+ // 这里**不再手动 pow 解码**(那遍是二次解码 = 整体压暗),末端只编码一次 ⇒ 与源图逐字节还原。
54
+ if (tileIdx == 0) lin${k} = texture2D(panoramas[${base}], vec2(panoU, panoV)).rgb;
55
+ else lin${k} = texture2D(panoramas[${base + 1}], vec2(panoU, panoV)).rgb;
56
+ }`;
57
+ }
58
+ return /* glsl */ `
59
+ #include <logdepthbuf_pars_fragment>
60
+ uniform sampler2D panoramas[${tileCount}];
61
+ uniform mat4 textureMatrices[${maxPano}];
62
+ uniform float weights[${maxPano}]; // 该槽瓦片绑好了没(writeSlot 按账本写)
63
+ uniform float uPanoTiled[${maxPano}];
64
+ // ── 全景序号映射(皇上旨 2026-09-28):本材质**只有这一种取图方式**(原 uFaceMappingMode 运行时分支已按
65
+ // 09-29「三模式三 shader」拆出成独立 shader)──
66
+ uniform float panoIndices[${maxPano}]; // 每个 slot 对应的「全景全局序号」(-1=空)
67
+ uniform vec3 uFaceMissingColor; // 未设序号 / 序号不在槽里 / 槽没绑好 时的深灰兜底色(线性空间)
68
+ // 色彩空间只有一条路(10-07 旨「统一使用 liner」):原 uniform float uColorSpace; + 那段 pow 分支已整条撤除(与 blend 同口径)
69
+ uniform float uPanoramaVisible;
70
+ varying vec3 vProjPos;
71
+ varying float vPanoIndex;
72
+ #ifdef USE_INSTANCING_COLOR
73
+ varying vec3 vInstanceColor;
74
+ #endif
75
+ void main() {
76
+ // ⭐ 先逐槽采样出 linK(weights[k]>0 才采;没绑好保持 vec3(0),由下面 facePick 的 weights 闸兜成深灰)。
77
+ // ⛔ 这一段必须先跑:下面 slotFacePick 直接引用 linK —— 漏插本段就是
78
+ // 「ERROR: 'lin0' : undeclared identifier」编译失败(2026-10-02 shader error 1282 的根因)。
79
+ ${slotCalc}
80
+ vec3 faceCol = uFaceMissingColor; // 默认深灰(未设序号 vPanoIndex<0 / 不在 panoIndices 里 / 槽没绑好)
81
+ ${slotFacePick}
82
+ vec3 col = faceCol;
83
+ // 全景投射显隐:关闭时回落中性素色(与 blend 一致)
84
+ if (uPanoramaVisible < 0.5) col = vec3(0.78, 0.78, 0.80);
85
+ #ifdef USE_INSTANCING_COLOR
86
+ col *= vInstanceColor;
87
+ #endif
88
+ gl_FragColor = vec4(col, 1.0);
89
+ // 输出颜色空间编码:渲染到线性 RT(主编辑器后处理链)时 passthrough,渲染到 sRGB 屏幕(预览弹窗)时自动编码 sRGB
90
+ #include <colorspace_fragment>
91
+ #include <logdepthbuf_fragment>
92
+ }
93
+ `;
94
+ }
95
+ /**
96
+ * 创建「全景序号直取」着色器材质(皇上旨 2026-09-29「三模式三 shader」之二):
97
+ * 编辑器「映射设置模式」的墙材质 + **基类 MktMesh(发布/预览)的固定墙材质**
98
+ * (皇上旨:mktmesh 就只有按照 attribute 配置来显示全景这一种固定的 shader)。
99
+ * uniform 形状与 blend 工厂同档(panoramas[16]/textureMatrices[8]/weights[8]/uPanoTiled[8])⇒
100
+ * 喂料复用 feedBlendProjection 的槽位指派路(feedFaceMapping),零新喂料路。
101
+ * uFloorCoreRadius/uFloorFadeRange/uFloorOpacity 三根本 shader 不读,但喂料统一入口会写 ⇒ JS 侧留着(静默忽略)。
102
+ */
103
+ export function createMktFloorMappingShaderMaterial(options = {}) {
104
+ const maxPano = Math.min(options.maxPano ?? MKT_PANO_MAX_SLOTS, MKT_PANO_MAX_SLOTS);
105
+ const tileCount = maxPano * RT_STITCH_SLOT_COUNT; // 8 点 × 左/右 2 槽 = 16 = sampler 硬顶
106
+ const panoramas = [];
107
+ const matrices = [];
108
+ for (let i = 0; i < tileCount; i++)
109
+ panoramas.push(createPlaceholderTexture());
110
+ for (let i = 0; i < maxPano; i++)
111
+ matrices.push(new Matrix4());
112
+ return new ShaderMaterial({
113
+ uniforms: {
114
+ panoramas: { value: panoramas },
115
+ textureMatrices: { value: matrices },
116
+ weights: { value: new Float32Array(maxPano) },
117
+ uPanoTiled: { value: new Float32Array(maxPano).fill(1) },
118
+ panoIndices: { value: new Float32Array(maxPano).fill(-1) },
119
+ uFaceMissingColor: { value: new Vector3(0.18, 0.18, 0.18) },
120
+ // 喂料统一入口会写(见上注释);本 shader 不读 ⇒ 声明在此只为喂料零分支
121
+ uFloorCoreRadius: { value: 4 },
122
+ uFloorFadeRange: { value: 4 },
123
+ uFloorOpacity: { value: 1 },
124
+ uPanoramaVisible: { value: 1 },
125
+ // 色彩空间只有一条路(10-07 旨「统一使用 liner」)⇒ 原 `uColorSpace: { value: 0 }` 已随那段 pow 分支整条撤除
126
+ // 投影空间逆矩阵:与 blend 同口径(顶点着色器共用 floorVertexShader)
127
+ uProjInvMatrix: { value: new Matrix4() },
128
+ },
129
+ vertexShader: floorVertexShader,
130
+ fragmentShader: buildFloorMappingFragment(maxPano),
131
+ // ⭐ 墙专用材质(基类 MktMesh = 预览/发布 + 编辑器 mapping 模式墙,**同一座工厂同一份参数**,
132
+ // 皇上旨 2026-10-07 明确以此为准):shader 默认**反面显示**(BackSide),零厚度幕墙只画朝房间内的那一面。
133
+ // 地板走「仅显示选中」工厂、维持 DoubleSide;立方体 mapping 档与墙共用这份实例 ⇒ 朝向跟着墙(10-02 旨),
134
+ // 另两档另建一份并显式拉回 DoubleSide(见渲染器 `_applyDisplayMode`)。
135
+ side: BackSide,
136
+ transparent: false,
137
+ toneMapped: false,
138
+ });
139
+ }