@cavegiant/cave-world-r3f 1.0.0

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 (339) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +189 -0
  4. package/dist/core/connector-label/readiness.d.ts +11 -0
  5. package/dist/core/connector-label/readiness.d.ts.map +1 -0
  6. package/dist/core/connector-label/readiness.js +31 -0
  7. package/dist/core/connector-label/readiness.js.map +1 -0
  8. package/dist/core/connector-label/types.d.ts +64 -0
  9. package/dist/core/connector-label/types.d.ts.map +1 -0
  10. package/dist/core/connector-label/types.js +47 -0
  11. package/dist/core/connector-label/types.js.map +1 -0
  12. package/dist/core/context/cave-world-context.d.ts +96 -0
  13. package/dist/core/context/cave-world-context.d.ts.map +1 -0
  14. package/dist/core/context/cave-world-context.js +100 -0
  15. package/dist/core/context/cave-world-context.js.map +1 -0
  16. package/dist/core/errors.d.ts +34 -0
  17. package/dist/core/errors.d.ts.map +1 -0
  18. package/dist/core/errors.js +17 -0
  19. package/dist/core/errors.js.map +1 -0
  20. package/dist/core/events/event-bus.d.ts +122 -0
  21. package/dist/core/events/event-bus.d.ts.map +1 -0
  22. package/dist/core/events/event-bus.js +62 -0
  23. package/dist/core/events/event-bus.js.map +1 -0
  24. package/dist/core/hooks/use-cave-event.d.ts +13 -0
  25. package/dist/core/hooks/use-cave-event.d.ts.map +1 -0
  26. package/dist/core/hooks/use-cave-event.js +12 -0
  27. package/dist/core/hooks/use-cave-event.js.map +1 -0
  28. package/dist/core/i18n/messages.d.ts +82 -0
  29. package/dist/core/i18n/messages.d.ts.map +1 -0
  30. package/dist/core/i18n/messages.js +131 -0
  31. package/dist/core/i18n/messages.js.map +1 -0
  32. package/dist/core/index.d.ts +11 -0
  33. package/dist/core/index.d.ts.map +1 -0
  34. package/dist/core/index.js +47 -0
  35. package/dist/core/index.js.map +1 -0
  36. package/dist/core/logging/logger.d.ts +29 -0
  37. package/dist/core/logging/logger.d.ts.map +1 -0
  38. package/dist/core/logging/logger.js +47 -0
  39. package/dist/core/logging/logger.js.map +1 -0
  40. package/dist/core/registry/component-registry.d.ts +41 -0
  41. package/dist/core/registry/component-registry.d.ts.map +1 -0
  42. package/dist/core/registry/component-registry.js +34 -0
  43. package/dist/core/registry/component-registry.js.map +1 -0
  44. package/dist/core/registry/object-registry.d.ts +38 -0
  45. package/dist/core/registry/object-registry.d.ts.map +1 -0
  46. package/dist/core/registry/object-registry.js +32 -0
  47. package/dist/core/registry/object-registry.js.map +1 -0
  48. package/dist/explore/cave-explore.d.ts +85 -0
  49. package/dist/explore/cave-explore.d.ts.map +1 -0
  50. package/dist/explore/cave-explore.js +204 -0
  51. package/dist/explore/cave-explore.js.map +1 -0
  52. package/dist/explore/cave-orbit-viewer.d.ts +76 -0
  53. package/dist/explore/cave-orbit-viewer.d.ts.map +1 -0
  54. package/dist/explore/cave-orbit-viewer.js +123 -0
  55. package/dist/explore/cave-orbit-viewer.js.map +1 -0
  56. package/dist/explore/characters/character-configs.d.ts +38 -0
  57. package/dist/explore/characters/character-configs.d.ts.map +1 -0
  58. package/dist/explore/characters/character-configs.js +45 -0
  59. package/dist/explore/characters/character-configs.js.map +1 -0
  60. package/dist/explore/characters/character-controls.d.ts +44 -0
  61. package/dist/explore/characters/character-controls.d.ts.map +1 -0
  62. package/dist/explore/characters/character-pose.d.ts +19 -0
  63. package/dist/explore/characters/character-pose.d.ts.map +1 -0
  64. package/dist/explore/characters/character-pose.js +13 -0
  65. package/dist/explore/characters/character-pose.js.map +1 -0
  66. package/dist/explore/characters/local-character-model.d.ts +53 -0
  67. package/dist/explore/characters/local-character-model.d.ts.map +1 -0
  68. package/dist/explore/characters/local-character-model.js +36 -0
  69. package/dist/explore/characters/local-character-model.js.map +1 -0
  70. package/dist/explore/characters/mixamo-style-bone-map.d.ts +9 -0
  71. package/dist/explore/characters/mixamo-style-bone-map.d.ts.map +1 -0
  72. package/dist/explore/characters/mixamo-style-bone-map.js +28 -0
  73. package/dist/explore/characters/mixamo-style-bone-map.js.map +1 -0
  74. package/dist/explore/characters/remote-character-controller.d.ts +38 -0
  75. package/dist/explore/characters/remote-character-controller.d.ts.map +1 -0
  76. package/dist/explore/characters/remote-character-controller.js +135 -0
  77. package/dist/explore/characters/remote-character-controller.js.map +1 -0
  78. package/dist/explore/characters/remote-character.d.ts +19 -0
  79. package/dist/explore/characters/remote-character.d.ts.map +1 -0
  80. package/dist/explore/characters/remote-character.js +63 -0
  81. package/dist/explore/characters/remote-character.js.map +1 -0
  82. package/dist/explore/connected-spaces-renderer.d.ts +16 -0
  83. package/dist/explore/connected-spaces-renderer.d.ts.map +1 -0
  84. package/dist/explore/connected-spaces-renderer.js +103 -0
  85. package/dist/explore/connected-spaces-renderer.js.map +1 -0
  86. package/dist/explore/hooks/use-action-shortcuts.d.ts +16 -0
  87. package/dist/explore/hooks/use-action-shortcuts.d.ts.map +1 -0
  88. package/dist/explore/hooks/use-action-shortcuts.js +23 -0
  89. package/dist/explore/hooks/use-action-shortcuts.js.map +1 -0
  90. package/dist/explore/hooks/use-active-space-notify.d.ts +11 -0
  91. package/dist/explore/hooks/use-active-space-notify.d.ts.map +1 -0
  92. package/dist/explore/hooks/use-active-space-notify.js +17 -0
  93. package/dist/explore/hooks/use-active-space-notify.js.map +1 -0
  94. package/dist/explore/hooks/use-character-sync.d.ts +46 -0
  95. package/dist/explore/hooks/use-character-sync.d.ts.map +1 -0
  96. package/dist/explore/hooks/use-character-sync.js +72 -0
  97. package/dist/explore/hooks/use-character-sync.js.map +1 -0
  98. package/dist/explore/hooks/use-connector-preloader.d.ts +20 -0
  99. package/dist/explore/hooks/use-connector-preloader.d.ts.map +1 -0
  100. package/dist/explore/hooks/use-connector-preloader.js +78 -0
  101. package/dist/explore/hooks/use-connector-preloader.js.map +1 -0
  102. package/dist/explore/hooks/use-connector-transitions.d.ts +31 -0
  103. package/dist/explore/hooks/use-connector-transitions.d.ts.map +1 -0
  104. package/dist/explore/hooks/use-connector-transitions.js +63 -0
  105. package/dist/explore/hooks/use-connector-transitions.js.map +1 -0
  106. package/dist/explore/hooks/use-explore-actions.d.ts +17 -0
  107. package/dist/explore/hooks/use-explore-actions.d.ts.map +1 -0
  108. package/dist/explore/hooks/use-explore-actions.js +134 -0
  109. package/dist/explore/hooks/use-explore-actions.js.map +1 -0
  110. package/dist/explore/hooks/use-load-error-notify.d.ts +11 -0
  111. package/dist/explore/hooks/use-load-error-notify.d.ts.map +1 -0
  112. package/dist/explore/hooks/use-load-error-notify.js +18 -0
  113. package/dist/explore/hooks/use-load-error-notify.js.map +1 -0
  114. package/dist/explore/hooks/use-primary-space-loader.d.ts +24 -0
  115. package/dist/explore/hooks/use-primary-space-loader.d.ts.map +1 -0
  116. package/dist/explore/hooks/use-primary-space-loader.js +29 -0
  117. package/dist/explore/hooks/use-primary-space-loader.js.map +1 -0
  118. package/dist/explore/index.d.ts +37 -0
  119. package/dist/explore/index.d.ts.map +1 -0
  120. package/dist/explore/index.js +107 -0
  121. package/dist/explore/index.js.map +1 -0
  122. package/dist/explore/loaders/cave-loader.d.ts +58 -0
  123. package/dist/explore/loaders/cave-loader.d.ts.map +1 -0
  124. package/dist/explore/loaders/cave-loader.js +79 -0
  125. package/dist/explore/loaders/cave-loader.js.map +1 -0
  126. package/dist/explore/math/space-transforms.d.ts +49 -0
  127. package/dist/explore/math/space-transforms.d.ts.map +1 -0
  128. package/dist/explore/math/space-transforms.js +54 -0
  129. package/dist/explore/math/space-transforms.js.map +1 -0
  130. package/dist/explore/overlays/action-bubble.d.ts +8 -0
  131. package/dist/explore/overlays/action-bubble.d.ts.map +1 -0
  132. package/dist/explore/overlays/action-bubble.js +18 -0
  133. package/dist/explore/overlays/action-bubble.js.map +1 -0
  134. package/dist/explore/overlays/action-panel.d.ts +12 -0
  135. package/dist/explore/overlays/action-panel.d.ts.map +1 -0
  136. package/dist/explore/overlays/action-panel.js +69 -0
  137. package/dist/explore/overlays/action-panel.js.map +1 -0
  138. package/dist/explore/overlays/connector-label-beacon.d.ts +11 -0
  139. package/dist/explore/overlays/connector-label-beacon.d.ts.map +1 -0
  140. package/dist/explore/overlays/connector-label-beacon.js +38 -0
  141. package/dist/explore/overlays/connector-label-beacon.js.map +1 -0
  142. package/dist/explore/overlays/connector-label-tag-shell.d.ts +16 -0
  143. package/dist/explore/overlays/connector-label-tag-shell.d.ts.map +1 -0
  144. package/dist/explore/overlays/connector-label-tag-shell.js +18 -0
  145. package/dist/explore/overlays/connector-label-tag-shell.js.map +1 -0
  146. package/dist/explore/overlays/detail-window.d.ts +9 -0
  147. package/dist/explore/overlays/detail-window.d.ts.map +1 -0
  148. package/dist/explore/overlays/detail-window.js +92 -0
  149. package/dist/explore/overlays/detail-window.js.map +1 -0
  150. package/dist/explore/overlays/explore-helper.d.ts +44 -0
  151. package/dist/explore/overlays/explore-helper.d.ts.map +1 -0
  152. package/dist/explore/overlays/explore-helper.js +57 -0
  153. package/dist/explore/overlays/explore-helper.js.map +1 -0
  154. package/dist/explore/overlays/icons.d.ts +16 -0
  155. package/dist/explore/overlays/icons.d.ts.map +1 -0
  156. package/dist/explore/overlays/icons.js +34 -0
  157. package/dist/explore/overlays/icons.js.map +1 -0
  158. package/dist/explore/overlays/space-load-error-overlay.d.ts +27 -0
  159. package/dist/explore/overlays/space-load-error-overlay.d.ts.map +1 -0
  160. package/dist/explore/overlays/space-load-error-overlay.js +24 -0
  161. package/dist/explore/overlays/space-load-error-overlay.js.map +1 -0
  162. package/dist/explore/services/websocket.service.d.ts +180 -0
  163. package/dist/explore/services/websocket.service.d.ts.map +1 -0
  164. package/dist/explore/services/websocket.service.js +187 -0
  165. package/dist/explore/services/websocket.service.js.map +1 -0
  166. package/dist/explore/simple-character.d.ts +46 -0
  167. package/dist/explore/simple-character.d.ts.map +1 -0
  168. package/dist/explore/simple-character.js +119 -0
  169. package/dist/explore/simple-character.js.map +1 -0
  170. package/dist/explore/stores/action-prompt.store.d.ts +27 -0
  171. package/dist/explore/stores/action-prompt.store.d.ts.map +1 -0
  172. package/dist/explore/stores/action-prompt.store.js +14 -0
  173. package/dist/explore/stores/action-prompt.store.js.map +1 -0
  174. package/dist/explore/stores/cave-explore.store.d.ts +77 -0
  175. package/dist/explore/stores/cave-explore.store.d.ts.map +1 -0
  176. package/dist/explore/stores/cave-explore.store.js +66 -0
  177. package/dist/explore/stores/cave-explore.store.js.map +1 -0
  178. package/dist/explore/stores/explore-context.d.ts +35 -0
  179. package/dist/explore/stores/explore-context.d.ts.map +1 -0
  180. package/dist/explore/stores/explore-context.js +43 -0
  181. package/dist/explore/stores/explore-context.js.map +1 -0
  182. package/dist/explore/styles/cave-world-css.d.ts +10 -0
  183. package/dist/explore/styles/cave-world-css.d.ts.map +1 -0
  184. package/dist/explore/styles/cave-world-css.js +935 -0
  185. package/dist/explore/styles/cave-world-css.js.map +1 -0
  186. package/dist/explore/styles/inject.d.ts +32 -0
  187. package/dist/explore/styles/inject.d.ts.map +1 -0
  188. package/dist/explore/styles/inject.js +40 -0
  189. package/dist/explore/styles/inject.js.map +1 -0
  190. package/dist/explore/utils/schedule-idle.d.ts +12 -0
  191. package/dist/explore/utils/schedule-idle.d.ts.map +1 -0
  192. package/dist/explore/utils/schedule-idle.js +16 -0
  193. package/dist/explore/utils/schedule-idle.js.map +1 -0
  194. package/dist/explore/viewer-shared.d.ts +56 -0
  195. package/dist/explore/viewer-shared.d.ts.map +1 -0
  196. package/dist/index.d.ts +16 -0
  197. package/dist/index.d.ts.map +1 -0
  198. package/dist/index.js +78 -0
  199. package/dist/index.js.map +1 -0
  200. package/dist/space/cave-base-layer.d.ts +42 -0
  201. package/dist/space/cave-base-layer.d.ts.map +1 -0
  202. package/dist/space/cave-base-layer.js +221 -0
  203. package/dist/space/cave-base-layer.js.map +1 -0
  204. package/dist/space/cave-custom-layer.d.ts +12 -0
  205. package/dist/space/cave-custom-layer.d.ts.map +1 -0
  206. package/dist/space/cave-custom-layer.js +17 -0
  207. package/dist/space/cave-custom-layer.js.map +1 -0
  208. package/dist/space/cave-env.d.ts +6 -0
  209. package/dist/space/cave-env.d.ts.map +1 -0
  210. package/dist/space/cave-env.js +23 -0
  211. package/dist/space/cave-env.js.map +1 -0
  212. package/dist/space/cave-space.d.ts +46 -0
  213. package/dist/space/cave-space.d.ts.map +1 -0
  214. package/dist/space/cave-space.js +37 -0
  215. package/dist/space/cave-space.js.map +1 -0
  216. package/dist/space/connector-label-host.d.ts +21 -0
  217. package/dist/space/connector-label-host.d.ts.map +1 -0
  218. package/dist/space/connector-label-host.js +65 -0
  219. package/dist/space/connector-label-host.js.map +1 -0
  220. package/dist/space/index.d.ts +18 -0
  221. package/dist/space/index.d.ts.map +1 -0
  222. package/dist/space/index.js +47 -0
  223. package/dist/space/index.js.map +1 -0
  224. package/dist/space/objects/builtin-registrar.d.ts +17 -0
  225. package/dist/space/objects/builtin-registrar.d.ts.map +1 -0
  226. package/dist/space/objects/builtin-registrar.js +19 -0
  227. package/dist/space/objects/builtin-registrar.js.map +1 -0
  228. package/dist/space/objects/custom-object.d.ts +9 -0
  229. package/dist/space/objects/custom-object.d.ts.map +1 -0
  230. package/dist/space/objects/custom-object.js +56 -0
  231. package/dist/space/objects/custom-object.js.map +1 -0
  232. package/dist/space/objects/image-object.d.ts +7 -0
  233. package/dist/space/objects/image-object.d.ts.map +1 -0
  234. package/dist/space/objects/image-object.js +14 -0
  235. package/dist/space/objects/image-object.js.map +1 -0
  236. package/dist/space/objects/model-object/fbx-object.d.ts +8 -0
  237. package/dist/space/objects/model-object/fbx-object.d.ts.map +1 -0
  238. package/dist/space/objects/model-object/fbx-object.js +26 -0
  239. package/dist/space/objects/model-object/fbx-object.js.map +1 -0
  240. package/dist/space/objects/model-object/gltf-object.d.ts +9 -0
  241. package/dist/space/objects/model-object/gltf-object.d.ts.map +1 -0
  242. package/dist/space/objects/model-object/gltf-object.js +66 -0
  243. package/dist/space/objects/model-object/gltf-object.js.map +1 -0
  244. package/dist/space/objects/model-object/index.d.ts +9 -0
  245. package/dist/space/objects/model-object/index.d.ts.map +1 -0
  246. package/dist/space/objects/model-object/index.js +12 -0
  247. package/dist/space/objects/model-object/index.js.map +1 -0
  248. package/dist/space/objects/model-object/splat-object.d.ts +9 -0
  249. package/dist/space/objects/model-object/splat-object.d.ts.map +1 -0
  250. package/dist/space/objects/model-object/splat-object.js +19 -0
  251. package/dist/space/objects/model-object/splat-object.js.map +1 -0
  252. package/dist/space/objects/object-components/windows/index.d.ts +5 -0
  253. package/dist/space/objects/object-components/windows/index.d.ts.map +1 -0
  254. package/dist/space/objects/object-components/windows/index.js +20 -0
  255. package/dist/space/objects/object-components/windows/index.js.map +1 -0
  256. package/dist/space/objects/placeholder-object.d.ts +7 -0
  257. package/dist/space/objects/placeholder-object.d.ts.map +1 -0
  258. package/dist/space/objects/placeholder-object.js +12 -0
  259. package/dist/space/objects/placeholder-object.js.map +1 -0
  260. package/dist/space/objects/poi-object.d.ts +7 -0
  261. package/dist/space/objects/poi-object.d.ts.map +1 -0
  262. package/dist/space/objects/poi-object.js +38 -0
  263. package/dist/space/objects/poi-object.js.map +1 -0
  264. package/dist/space/objects/portal-object.d.ts +7 -0
  265. package/dist/space/objects/portal-object.d.ts.map +1 -0
  266. package/dist/space/objects/portal-object.js +84 -0
  267. package/dist/space/objects/portal-object.js.map +1 -0
  268. package/dist/space/objects/video-object.d.ts +7 -0
  269. package/dist/space/objects/video-object.d.ts.map +1 -0
  270. package/dist/space/objects/video-object.js +32 -0
  271. package/dist/space/objects/video-object.js.map +1 -0
  272. package/dist/space/polygon-boundary.d.ts +10 -0
  273. package/dist/space/polygon-boundary.d.ts.map +1 -0
  274. package/dist/space/polygon-boundary.js +205 -0
  275. package/dist/space/polygon-boundary.js.map +1 -0
  276. package/dist/space/splats/spark/robust-splat-mesh.d.ts +59 -0
  277. package/dist/space/splats/spark/robust-splat-mesh.d.ts.map +1 -0
  278. package/dist/space/splats/spark/robust-splat-mesh.js +105 -0
  279. package/dist/space/splats/spark/robust-splat-mesh.js.map +1 -0
  280. package/dist/space/splats/spark/splat-mesh.d.ts +5 -0
  281. package/dist/space/splats/spark/splat-mesh.d.ts.map +1 -0
  282. package/dist/space/splats/spark/splat-mesh.js +7 -0
  283. package/dist/space/splats/spark/splat-mesh.js.map +1 -0
  284. package/dist/space/splats/spark/splat-spread-reveal.d.ts +14 -0
  285. package/dist/space/splats/spark/splat-spread-reveal.d.ts.map +1 -0
  286. package/dist/space/splats/spark/splat-spread-reveal.js +45 -0
  287. package/dist/space/splats/spark/splat-spread-reveal.js.map +1 -0
  288. package/dist/space/utils/cave-world-player.d.ts +3 -0
  289. package/dist/space/utils/cave-world-player.d.ts.map +1 -0
  290. package/dist/space/utils/cave-world-player.js +5 -0
  291. package/dist/space/utils/cave-world-player.js.map +1 -0
  292. package/dist/space/utils/configure-canvas.d.ts +20 -0
  293. package/dist/space/utils/configure-canvas.d.ts.map +1 -0
  294. package/dist/space/utils/configure-canvas.js +12 -0
  295. package/dist/space/utils/configure-canvas.js.map +1 -0
  296. package/dist/space/utils/polygon-boundary-geometry.d.ts +8 -0
  297. package/dist/space/utils/polygon-boundary-geometry.d.ts.map +1 -0
  298. package/dist/space/utils/polygon-boundary-geometry.js +20 -0
  299. package/dist/space/utils/polygon-boundary-geometry.js.map +1 -0
  300. package/dist/space/utils/polygon-boundary-segments.d.ts +50 -0
  301. package/dist/space/utils/polygon-boundary-segments.d.ts.map +1 -0
  302. package/dist/space/utils/polygon-boundary-segments.js +64 -0
  303. package/dist/space/utils/polygon-boundary-segments.js.map +1 -0
  304. package/dist/space/utils/rotation-compat.d.ts +4 -0
  305. package/dist/space/utils/rotation-compat.d.ts.map +1 -0
  306. package/dist/space/utils/rotation-compat.js +9 -0
  307. package/dist/space/utils/rotation-compat.js.map +1 -0
  308. package/dist/space/utils/select-splat-level.d.ts +52 -0
  309. package/dist/space/utils/select-splat-level.d.ts.map +1 -0
  310. package/dist/space/utils/select-splat-level.js +43 -0
  311. package/dist/space/utils/select-splat-level.js.map +1 -0
  312. package/dist/space/utils/spark-performance.d.ts +10 -0
  313. package/dist/space/utils/spark-performance.d.ts.map +1 -0
  314. package/dist/space/utils/spark-performance.js +10 -0
  315. package/dist/space/utils/spark-performance.js.map +1 -0
  316. package/dist/space/utils/splat-collider-helper.d.ts +5 -0
  317. package/dist/space/utils/splat-collider-helper.d.ts.map +1 -0
  318. package/dist/space/utils/splat-collider-helper.js +162 -0
  319. package/dist/space/utils/splat-collider-helper.js.map +1 -0
  320. package/dist/space/utils/touch-primary-device.d.ts +16 -0
  321. package/dist/space/utils/touch-primary-device.d.ts.map +1 -0
  322. package/dist/space/utils/touch-primary-device.js +52 -0
  323. package/dist/space/utils/touch-primary-device.js.map +1 -0
  324. package/dist/space/utils/use-visibility-frameloop.d.ts +20 -0
  325. package/dist/space/utils/use-visibility-frameloop.d.ts.map +1 -0
  326. package/dist/space/utils/use-visibility-frameloop.js +18 -0
  327. package/dist/space/utils/use-visibility-frameloop.js.map +1 -0
  328. package/dist/styles.css +930 -0
  329. package/dist/types/index.d.ts +2 -0
  330. package/dist/types/index.d.ts.map +1 -0
  331. package/dist/types/index.js +2 -0
  332. package/dist/types/index.js.map +1 -0
  333. package/dist/types/space-types.d.ts +404 -0
  334. package/dist/types/space-types.d.ts.map +1 -0
  335. package/doc/API.md +617 -0
  336. package/doc/ARCHITECTURE.md +264 -0
  337. package/doc/multiplayer-protocol.md +757 -0
  338. package/doc/scene-json-structure.md +567 -0
  339. package/package.json +131 -0
@@ -0,0 +1,20 @@
1
+ import { BufferGeometry as s, BoxGeometry as f, Quaternion as u, Vector3 as e, Matrix4 as w } from "three";
2
+ import { mergeGeometries as x } from "three/examples/jsm/utils/BufferGeometryUtils.js";
3
+ const c = /* @__PURE__ */ new w(), a = /* @__PURE__ */ new e(), l = /* @__PURE__ */ new u(), g = /* @__PURE__ */ new e(), d = /* @__PURE__ */ new e(0, 1, 0);
4
+ function A(t, p, n = {}) {
5
+ if (t.length === 0) return new s();
6
+ const h = n.length ?? 0, m = n.height ?? 0, r = t.map((o) => {
7
+ const i = new f(
8
+ o.length + h * 2,
9
+ o.height + m * 2,
10
+ p
11
+ );
12
+ return l.setFromAxisAngle(d, o.yaw), a.set(o.position[0], o.position[1], o.position[2]), c.compose(a, l, g.set(1, 1, 1)), i.applyMatrix4(c), i;
13
+ }), y = x(r, !1);
14
+ for (const o of r) o.dispose();
15
+ return y ?? new s();
16
+ }
17
+ export {
18
+ A as buildPolygonBoundaryColliderGeometry
19
+ };
20
+ //# sourceMappingURL=polygon-boundary-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polygon-boundary-geometry.js","sources":["../../../space/utils/polygon-boundary-geometry.ts"],"sourcesContent":["import { BoxGeometry, BufferGeometry, Matrix4, Quaternion, Vector3 } from 'three'\nimport { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport type { PolygonBoundarySegment } from './polygon-boundary-segments'\n\nconst _matrix = /* @__PURE__ */ new Matrix4()\nconst _position = /* @__PURE__ */ new Vector3()\nconst _quaternion = /* @__PURE__ */ new Quaternion()\nconst _scale = /* @__PURE__ */ new Vector3()\nconst _yAxis = /* @__PURE__ */ new Vector3(0, 1, 0)\n\n/** Merge segment wall boxes into one static BufferGeometry for a single BVH body. */\nexport function buildPolygonBoundaryColliderGeometry(\n segments: PolygonBoundarySegment[],\n depth: number,\n padding: { length?: number; height?: number } = {}\n): BufferGeometry {\n if (segments.length === 0) return new BufferGeometry()\n\n const lengthPad = padding.length ?? 0\n const heightPad = padding.height ?? 0\n\n const boxes = segments.map((segment) => {\n const geometry = new BoxGeometry(\n segment.length + lengthPad * 2,\n segment.height + heightPad * 2,\n depth\n )\n _quaternion.setFromAxisAngle(_yAxis, segment.yaw)\n _position.set(segment.position[0], segment.position[1], segment.position[2])\n _matrix.compose(_position, _quaternion, _scale.set(1, 1, 1))\n geometry.applyMatrix4(_matrix)\n return geometry\n })\n\n const merged = mergeGeometries(boxes, false)\n for (const geometry of boxes) geometry.dispose()\n return merged ?? new BufferGeometry()\n}\n"],"names":["_matrix","Matrix4","_position","Vector3","_quaternion","Quaternion","_scale","_yAxis","buildPolygonBoundaryColliderGeometry","segments","depth","padding","BufferGeometry","lengthPad","heightPad","boxes","segment","geometry","BoxGeometry","merged","mergeGeometries"],"mappings":";;AAIA,MAAMA,wBAA8BC,EAAA,GAC9BC,wBAAgCC,EAAA,GAChCC,wBAAkCC,EAAA,GAClCC,wBAA6BH,EAAA,GAC7BI,IAAyB,oBAAIJ,EAAQ,GAAG,GAAG,CAAC;AAG3C,SAASK,EACdC,GACAC,GACAC,IAAgD,CAAA,GAChC;AAChB,MAAIF,EAAS,WAAW,EAAG,QAAO,IAAIG,EAAA;AAEtC,QAAMC,IAAYF,EAAQ,UAAU,GAC9BG,IAAYH,EAAQ,UAAU,GAE9BI,IAAQN,EAAS,IAAI,CAACO,MAAY;AACtC,UAAMC,IAAW,IAAIC;AAAA,MACnBF,EAAQ,SAASH,IAAY;AAAA,MAC7BG,EAAQ,SAASF,IAAY;AAAA,MAC7BJ;AAAA,IAAA;AAEF,WAAAN,EAAY,iBAAiBG,GAAQS,EAAQ,GAAG,GAChDd,EAAU,IAAIc,EAAQ,SAAS,CAAC,GAAGA,EAAQ,SAAS,CAAC,GAAGA,EAAQ,SAAS,CAAC,CAAC,GAC3EhB,EAAQ,QAAQE,GAAWE,GAAaE,EAAO,IAAI,GAAG,GAAG,CAAC,CAAC,GAC3DW,EAAS,aAAajB,CAAO,GACtBiB;AAAA,EACT,CAAC,GAEKE,IAASC,EAAgBL,GAAO,EAAK;AAC3C,aAAWE,KAAYF,EAAO,CAAAE,EAAS,QAAA;AACvC,SAAOE,KAAU,IAAIP,EAAA;AACvB;"}
@@ -0,0 +1,50 @@
1
+ import type { Vector3 } from '../../types/index.js';
2
+ export interface PolygonBoundarySegment {
3
+ index: number;
4
+ /** Segment midpoint in baseLayer-local space */
5
+ position: Vector3;
6
+ /** Horizontal length along the ground plane (m) */
7
+ length: number;
8
+ /** Wall height (m) */
9
+ height: number;
10
+ /** Y-axis rotation in radians — aligns wall width with the segment direction */
11
+ yaw: number;
12
+ /** Ground elevation at segment midpoint (m) */
13
+ baseY: number;
14
+ }
15
+ /** Max horizontal distance (m) from player to a segment line to show the warning blob. */
16
+ export declare const BOUNDARY_WARNING_PICK_RADIUS = 1.35;
17
+ /**
18
+ * Build collision/visual segments from an open polyline (`points` are NOT auto-closed).
19
+ */
20
+ export declare function buildPolygonBoundarySegments(points: Vector3[], height: number): PolygonBoundarySegment[];
21
+ export interface SegmentWallProjection {
22
+ /** Contact anchor in segment-local plane space (meters, centered origin). */
23
+ localX: number;
24
+ localY: number;
25
+ }
26
+ export interface BoundaryContactAnchor extends SegmentWallProjection {
27
+ segmentIndex: number;
28
+ /** Horizontal distance (m) from player to the segment line — used for picking. */
29
+ lineDistance: number;
30
+ }
31
+ /**
32
+ * Project the player onto a vertical fence segment plane (baseLayer-local coords).
33
+ * Segment-local +X runs along the wall width, +Y is up.
34
+ */
35
+ export declare function projectPlayerOntoSegmentWall(segment: PolygonBoundarySegment, player: {
36
+ x: number;
37
+ y: number;
38
+ z: number;
39
+ }): SegmentWallProjection;
40
+ /** Pick the nearest fence segment and wall-local anchor for the warning blob. */
41
+ export declare function findBoundaryContactAnchor(points: Vector3[], height: number, player: {
42
+ x: number;
43
+ y: number;
44
+ z: number;
45
+ }, segments?: PolygonBoundarySegment[], pickRadius?: number): BoundaryContactAnchor | null;
46
+ /** Map a wall-local contact anchor to baseLayer-local world position. */
47
+ export declare function boundaryContactAnchorToPosition(segment: PolygonBoundarySegment, anchor: SegmentWallProjection, target: {
48
+ set: (x: number, y: number, z: number) => void;
49
+ }): void;
50
+ //# sourceMappingURL=polygon-boundary-segments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polygon-boundary-segments.d.ts","sourceRoot":"","sources":["../../../space/utils/polygon-boundary-segments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAA;IACjB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAA;IACd,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAA;CACd;AAGD,0FAA0F;AAC1F,eAAO,MAAM,4BAA4B,OAAO,CAAA;AAEhD;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,OAAO,EAAE,EACjB,MAAM,EAAE,MAAM,GACb,sBAAsB,EAAE,CAyB1B;AAED,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,YAAY,EAAE,MAAM,CAAA;IACpB,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAA;CACrB;AAwBD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1C,qBAAqB,CAiBvB;AAED,iFAAiF;AACjF,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3C,QAAQ,GAAE,sBAAsB,EAAiD,EACjF,UAAU,SAA+B,GACxC,qBAAqB,GAAG,IAAI,CAsB9B;AAED,yEAAyE;AACzE,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE;IAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACzD,IAAI,CAQN"}
@@ -0,0 +1,64 @@
1
+ const x = 1.35;
2
+ function r(n, t) {
3
+ if (n.length < 2 || t <= 0) return [];
4
+ const o = [];
5
+ for (let c = 0; c < n.length - 1; c += 1) {
6
+ const l = n[c], i = n[c + 1], s = i[0] - l[0], a = i[2] - l[2], h = Math.hypot(s, a);
7
+ if (h < 0.01) continue;
8
+ const e = (l[1] + i[1]) / 2;
9
+ o.push({
10
+ index: c,
11
+ position: [(l[0] + i[0]) / 2, e + t / 2, (l[2] + i[2]) / 2],
12
+ length: h,
13
+ height: t,
14
+ yaw: Math.atan2(-a, s),
15
+ baseY: e
16
+ });
17
+ }
18
+ return o;
19
+ }
20
+ function y(n, t, o, c, l, i) {
21
+ const s = l - o, a = i - c, h = s * s + a * a;
22
+ if (h < 1e-8)
23
+ return Math.hypot(n - o, t - c);
24
+ let e = ((n - o) * s + (t - c) * a) / h;
25
+ e = Math.max(0, Math.min(1, e));
26
+ const u = o + e * s, M = c + e * a;
27
+ return Math.hypot(n - u, t - M);
28
+ }
29
+ function N(n, t) {
30
+ const o = t.x - n.position[0], c = t.y - n.position[1], l = t.z - n.position[2], i = Math.cos(n.yaw), s = Math.sin(n.yaw), a = o * i - l * s, h = c, e = n.length / 2, u = n.height / 2;
31
+ return {
32
+ localX: Math.max(-e, Math.min(e, a)),
33
+ localY: Math.max(-u, Math.min(u, h))
34
+ };
35
+ }
36
+ function _(n, t, o, c = r(n, t), l = 1.35) {
37
+ if (c.length === 0) return null;
38
+ let i = null;
39
+ for (const s of c) {
40
+ const a = n[s.index], h = n[s.index + 1], e = Math.min(a[1], h[1]) - 0.5, u = Math.max(a[1], h[1]) + t + 0.5;
41
+ if (o.y < e || o.y > u) continue;
42
+ const M = y(o.x, o.z, a[0], a[2], h[0], h[2]);
43
+ if (M > l) continue;
44
+ const { localX: d, localY: f } = N(s, o);
45
+ (!i || M < i.lineDistance) && (i = { segmentIndex: s.index, localX: d, localY: f, lineDistance: M });
46
+ }
47
+ return i;
48
+ }
49
+ function I(n, t, o) {
50
+ const c = Math.cos(n.yaw), l = Math.sin(n.yaw);
51
+ o.set(
52
+ n.position[0] + t.localX * c,
53
+ n.position[1] + t.localY,
54
+ n.position[2] - t.localX * l
55
+ );
56
+ }
57
+ export {
58
+ x as BOUNDARY_WARNING_PICK_RADIUS,
59
+ I as boundaryContactAnchorToPosition,
60
+ r as buildPolygonBoundarySegments,
61
+ _ as findBoundaryContactAnchor,
62
+ N as projectPlayerOntoSegmentWall
63
+ };
64
+ //# sourceMappingURL=polygon-boundary-segments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polygon-boundary-segments.js","sources":["../../../space/utils/polygon-boundary-segments.ts"],"sourcesContent":["import type { Vector3 } from '../../types'\n\nexport interface PolygonBoundarySegment {\n index: number\n /** Segment midpoint in baseLayer-local space */\n position: Vector3\n /** Horizontal length along the ground plane (m) */\n length: number\n /** Wall height (m) */\n height: number\n /** Y-axis rotation in radians — aligns wall width with the segment direction */\n yaw: number\n /** Ground elevation at segment midpoint (m) */\n baseY: number\n}\n\nconst MIN_SEGMENT_LENGTH = 0.01\n/** Max horizontal distance (m) from player to a segment line to show the warning blob. */\nexport const BOUNDARY_WARNING_PICK_RADIUS = 1.35\n\n/**\n * Build collision/visual segments from an open polyline (`points` are NOT auto-closed).\n */\nexport function buildPolygonBoundarySegments(\n points: Vector3[],\n height: number\n): PolygonBoundarySegment[] {\n if (points.length < 2 || height <= 0) return []\n\n const segments: PolygonBoundarySegment[] = []\n\n for (let i = 0; i < points.length - 1; i += 1) {\n const p0 = points[i]\n const p1 = points[i + 1]\n const dx = p1[0] - p0[0]\n const dz = p1[2] - p0[2]\n const length = Math.hypot(dx, dz)\n if (length < MIN_SEGMENT_LENGTH) continue\n\n const baseY = (p0[1] + p1[1]) / 2\n segments.push({\n index: i,\n position: [(p0[0] + p1[0]) / 2, baseY + height / 2, (p0[2] + p1[2]) / 2],\n length,\n height,\n yaw: Math.atan2(-dz, dx),\n baseY\n })\n }\n\n return segments\n}\n\nexport interface SegmentWallProjection {\n /** Contact anchor in segment-local plane space (meters, centered origin). */\n localX: number\n localY: number\n}\n\nexport interface BoundaryContactAnchor extends SegmentWallProjection {\n segmentIndex: number\n /** Horizontal distance (m) from player to the segment line — used for picking. */\n lineDistance: number\n}\n\nfunction distancePointToSegmentXZ(\n px: number,\n pz: number,\n x0: number,\n z0: number,\n x1: number,\n z1: number\n): number {\n const dx = x1 - x0\n const dz = z1 - z0\n const lengthSq = dx * dx + dz * dz\n if (lengthSq < 1e-8) {\n return Math.hypot(px - x0, pz - z0)\n }\n\n let t = ((px - x0) * dx + (pz - z0) * dz) / lengthSq\n t = Math.max(0, Math.min(1, t))\n const closestX = x0 + t * dx\n const closestZ = z0 + t * dz\n return Math.hypot(px - closestX, pz - closestZ)\n}\n\n/**\n * Project the player onto a vertical fence segment plane (baseLayer-local coords).\n * Segment-local +X runs along the wall width, +Y is up.\n */\nexport function projectPlayerOntoSegmentWall(\n segment: PolygonBoundarySegment,\n player: { x: number; y: number; z: number }\n): SegmentWallProjection {\n const dx = player.x - segment.position[0]\n const dy = player.y - segment.position[1]\n const dz = player.z - segment.position[2]\n const cos = Math.cos(segment.yaw)\n const sin = Math.sin(segment.yaw)\n\n const localX = dx * cos - dz * sin\n const localY = dy\n\n const halfLength = segment.length / 2\n const halfHeight = segment.height / 2\n\n return {\n localX: Math.max(-halfLength, Math.min(halfLength, localX)),\n localY: Math.max(-halfHeight, Math.min(halfHeight, localY))\n }\n}\n\n/** Pick the nearest fence segment and wall-local anchor for the warning blob. */\nexport function findBoundaryContactAnchor(\n points: Vector3[],\n height: number,\n player: { x: number; y: number; z: number },\n segments: PolygonBoundarySegment[] = buildPolygonBoundarySegments(points, height),\n pickRadius = BOUNDARY_WARNING_PICK_RADIUS\n): BoundaryContactAnchor | null {\n if (segments.length === 0) return null\n\n let best: BoundaryContactAnchor | null = null\n\n for (const segment of segments) {\n const p0 = points[segment.index]\n const p1 = points[segment.index + 1]\n const yMin = Math.min(p0[1], p1[1]) - 0.5\n const yMax = Math.max(p0[1], p1[1]) + height + 0.5\n if (player.y < yMin || player.y > yMax) continue\n\n const lineDistance = distancePointToSegmentXZ(player.x, player.z, p0[0], p0[2], p1[0], p1[2])\n if (lineDistance > pickRadius) continue\n\n const { localX, localY } = projectPlayerOntoSegmentWall(segment, player)\n if (!best || lineDistance < best.lineDistance) {\n best = { segmentIndex: segment.index, localX, localY, lineDistance }\n }\n }\n\n return best\n}\n\n/** Map a wall-local contact anchor to baseLayer-local world position. */\nexport function boundaryContactAnchorToPosition(\n segment: PolygonBoundarySegment,\n anchor: SegmentWallProjection,\n target: { set: (x: number, y: number, z: number) => void }\n): void {\n const cos = Math.cos(segment.yaw)\n const sin = Math.sin(segment.yaw)\n target.set(\n segment.position[0] + anchor.localX * cos,\n segment.position[1] + anchor.localY,\n segment.position[2] - anchor.localX * sin\n )\n}\n"],"names":["BOUNDARY_WARNING_PICK_RADIUS","buildPolygonBoundarySegments","points","height","segments","i","p0","p1","dx","dz","length","baseY","distancePointToSegmentXZ","px","pz","x0","z0","x1","z1","lengthSq","t","closestX","closestZ","projectPlayerOntoSegmentWall","segment","player","dy","cos","sin","localX","localY","halfLength","halfHeight","findBoundaryContactAnchor","pickRadius","best","yMin","yMax","lineDistance","boundaryContactAnchorToPosition","anchor","target"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Quaternion as QuatTuple, Vector3 } from '../../types/index.js';
2
+ /** Legacy scene JSON used Euler `[x, y, z]`; current data uses quaternion `[x, y, z, w]`. */
3
+ export declare function toQuaternionRotation(rotation: QuatTuple | Vector3 | readonly number[] | undefined): QuatTuple;
4
+ //# sourceMappingURL=rotation-compat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation-compat.d.ts","sourceRoot":"","sources":["../../../space/utils/rotation-compat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,IAAI,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAKnE,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,GAC5D,SAAS,CAcX"}
@@ -0,0 +1,9 @@
1
+ import { Euler as r, Quaternion as l } from "three";
2
+ const n = /* @__PURE__ */ new r(), u = /* @__PURE__ */ new l();
3
+ function s(e) {
4
+ return e?.length ? e.length === 4 ? [e[0], e[1], e[2], e[3]] : e.length === 3 ? (n.set(e[0], e[1], e[2]), u.setFromEuler(n), [u.x, u.y, u.z, u.w]) : [0, 0, 0, 1] : [0, 0, 0, 1];
5
+ }
6
+ export {
7
+ s as toQuaternionRotation
8
+ };
9
+ //# sourceMappingURL=rotation-compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation-compat.js","sources":["../../../space/utils/rotation-compat.ts"],"sourcesContent":["import { Euler, Quaternion } from 'three'\nimport type { Quaternion as QuatTuple, Vector3 } from '../../types'\n\nconst _euler = /* @__PURE__ */ new Euler()\nconst _quat = /* @__PURE__ */ new Quaternion()\n\n/** Legacy scene JSON used Euler `[x, y, z]`; current data uses quaternion `[x, y, z, w]`. */\nexport function toQuaternionRotation(\n rotation: QuatTuple | Vector3 | readonly number[] | undefined\n): QuatTuple {\n if (!rotation?.length) return [0, 0, 0, 1]\n\n if (rotation.length === 4) {\n return [rotation[0], rotation[1], rotation[2], rotation[3]]\n }\n\n if (rotation.length === 3) {\n _euler.set(rotation[0], rotation[1], rotation[2])\n _quat.setFromEuler(_euler)\n return [_quat.x, _quat.y, _quat.z, _quat.w]\n }\n\n return [0, 0, 0, 1]\n}\n"],"names":["_euler","Euler","_quat","Quaternion","toQuaternionRotation","rotation"],"mappings":";AAGA,MAAMA,wBAA6BC,EAAA,GAC7BC,wBAA4BC,EAAA;AAG3B,SAASC,EACdC,GACW;AACX,SAAKA,GAAU,SAEXA,EAAS,WAAW,IACf,CAACA,EAAS,CAAC,GAAGA,EAAS,CAAC,GAAGA,EAAS,CAAC,GAAGA,EAAS,CAAC,CAAC,IAGxDA,EAAS,WAAW,KACtBL,EAAO,IAAIK,EAAS,CAAC,GAAGA,EAAS,CAAC,GAAGA,EAAS,CAAC,CAAC,GAChDH,EAAM,aAAaF,CAAM,GAClB,CAACE,EAAM,GAAGA,EAAM,GAAGA,EAAM,GAAGA,EAAM,CAAC,KAGrC,CAAC,GAAG,GAAG,GAAG,CAAC,IAZY,CAAC,GAAG,GAAG,GAAG,CAAC;AAa3C;"}
@@ -0,0 +1,52 @@
1
+ import type { BaseSceneRef } from '../../types/index.js';
2
+ /**
3
+ * Soft upper bound on 3DGS point count that a typical desktop GPU can stream
4
+ * smoothly at 60fps. Used by {@link selectSplatLevel} to pick a LOD.
5
+ */
6
+ export declare const DESKTOP_SPLAT_COUNT_CAP = 2500000;
7
+ /**
8
+ * Soft upper bound on 3DGS point count for mobile / coarse-pointer devices.
9
+ */
10
+ export declare const MOBILE_SPLAT_COUNT_CAP = 1200000;
11
+ /**
12
+ * Detect whether the current device should be treated as mobile for LOD
13
+ * selection. Uses {@link isTouchPrimaryDevice} — same logic as explore touch
14
+ * controls, including WebViews that misreport `(pointer: coarse)`.
15
+ *
16
+ * The check is evaluated once per call (no React subscription) because the
17
+ * splat asset is loaded at most once per scene-mount and we don't want a
18
+ * resize / device-orientation change to trigger a re-download.
19
+ */
20
+ export declare function isMobileDevice(): boolean;
21
+ /**
22
+ * The point-count cap appropriate for the current device.
23
+ */
24
+ export declare function getDeviceSplatCountCap(): number;
25
+ /**
26
+ * Result of LOD selection: the URL that should actually be loaded, the chosen
27
+ * level's metadata (if it came from `splatLevels`), and the device cap that
28
+ * informed the decision. Returns `undefined` when the scene has no usable URL.
29
+ */
30
+ export interface SelectedSplatLevel {
31
+ url: string;
32
+ splatCount?: number;
33
+ selectedFromLevels: boolean;
34
+ deviceCap: number;
35
+ }
36
+ /**
37
+ * Pick the splat asset URL to load for the current device.
38
+ *
39
+ * Rules (in order):
40
+ *
41
+ * 1. If `splatLevels` is present and non-empty:
42
+ * a. Among entries with `splatCount <= deviceCap`, pick the one with the
43
+ * LARGEST `splatCount` (the best quality the device can handle).
44
+ * b. If none fit, pick the entry with the SMALLEST `splatCount` (the least
45
+ * heavy variant — degraded quality but at least playable).
46
+ * 2. Otherwise fall back to `scene.url`. If `scene.splatCount` is also given
47
+ * and exceeds the cap, log a warning so authors notice.
48
+ *
49
+ * Returns `undefined` when no URL is available.
50
+ */
51
+ export declare function selectSplatLevel(scene: BaseSceneRef | undefined): SelectedSplatLevel | undefined;
52
+ //# sourceMappingURL=select-splat-level.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-splat-level.d.ts","sourceRoot":"","sources":["../../../space/utils/select-splat-level.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,aAAa,CAAA;AAK3D;;;GAGG;AACH,eAAO,MAAM,uBAAuB,UAAY,CAAA;AAEhD;;GAEG;AACH,eAAO,MAAM,sBAAsB,UAAY,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,kBAAkB,GAAG,SAAS,CAwChG"}
@@ -0,0 +1,43 @@
1
+ import { createLogger as i } from "../../core/logging/logger.js";
2
+ import { isTouchPrimaryDevice as u } from "./touch-primary-device.js";
3
+ const a = i("space:splat-lod"), p = 25e5, c = 12e5;
4
+ function C() {
5
+ return u();
6
+ }
7
+ function f() {
8
+ return C() ? c : p;
9
+ }
10
+ function g(t) {
11
+ if (!t) return;
12
+ const l = f(), s = t.splatLevels;
13
+ if (s && s.length > 0) {
14
+ let o, r;
15
+ for (const e of s)
16
+ e.url && (e.splatCount <= l && (!o || e.splatCount > o.splatCount) && (o = e), (!r || e.splatCount < r.splatCount) && (r = e));
17
+ const n = o ?? r;
18
+ if (n)
19
+ return {
20
+ url: n.url,
21
+ splatCount: n.splatCount,
22
+ selectedFromLevels: !0,
23
+ deviceCap: l
24
+ };
25
+ }
26
+ if (t.url)
27
+ return t.splatCount != null && t.splatCount > l && a.warn(
28
+ `Scene declares ${t.splatCount.toLocaleString()} splats but this device's cap is ${l.toLocaleString()}. Provide \`splatLevels\` for smoother playback.`
29
+ ), {
30
+ url: t.url,
31
+ splatCount: t.splatCount,
32
+ selectedFromLevels: !1,
33
+ deviceCap: l
34
+ };
35
+ }
36
+ export {
37
+ p as DESKTOP_SPLAT_COUNT_CAP,
38
+ c as MOBILE_SPLAT_COUNT_CAP,
39
+ f as getDeviceSplatCountCap,
40
+ C as isMobileDevice,
41
+ g as selectSplatLevel
42
+ };
43
+ //# sourceMappingURL=select-splat-level.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-splat-level.js","sources":["../../../space/utils/select-splat-level.ts"],"sourcesContent":["import { createLogger } from '../../core'\nimport type { BaseSceneRef, SplatLevel } from '../../types'\nimport { isTouchPrimaryDevice } from './touch-primary-device'\n\nconst logger = createLogger('space:splat-lod')\n\n/**\n * Soft upper bound on 3DGS point count that a typical desktop GPU can stream\n * smoothly at 60fps. Used by {@link selectSplatLevel} to pick a LOD.\n */\nexport const DESKTOP_SPLAT_COUNT_CAP = 2_500_000\n\n/**\n * Soft upper bound on 3DGS point count for mobile / coarse-pointer devices.\n */\nexport const MOBILE_SPLAT_COUNT_CAP = 1_200_000\n\n/**\n * Detect whether the current device should be treated as mobile for LOD\n * selection. Uses {@link isTouchPrimaryDevice} — same logic as explore touch\n * controls, including WebViews that misreport `(pointer: coarse)`.\n *\n * The check is evaluated once per call (no React subscription) because the\n * splat asset is loaded at most once per scene-mount and we don't want a\n * resize / device-orientation change to trigger a re-download.\n */\nexport function isMobileDevice(): boolean {\n return isTouchPrimaryDevice()\n}\n\n/**\n * The point-count cap appropriate for the current device.\n */\nexport function getDeviceSplatCountCap(): number {\n return isMobileDevice() ? MOBILE_SPLAT_COUNT_CAP : DESKTOP_SPLAT_COUNT_CAP\n}\n\n/**\n * Result of LOD selection: the URL that should actually be loaded, the chosen\n * level's metadata (if it came from `splatLevels`), and the device cap that\n * informed the decision. Returns `undefined` when the scene has no usable URL.\n */\nexport interface SelectedSplatLevel {\n url: string\n splatCount?: number\n selectedFromLevels: boolean\n deviceCap: number\n}\n\n/**\n * Pick the splat asset URL to load for the current device.\n *\n * Rules (in order):\n *\n * 1. If `splatLevels` is present and non-empty:\n * a. Among entries with `splatCount <= deviceCap`, pick the one with the\n * LARGEST `splatCount` (the best quality the device can handle).\n * b. If none fit, pick the entry with the SMALLEST `splatCount` (the least\n * heavy variant — degraded quality but at least playable).\n * 2. Otherwise fall back to `scene.url`. If `scene.splatCount` is also given\n * and exceeds the cap, log a warning so authors notice.\n *\n * Returns `undefined` when no URL is available.\n */\nexport function selectSplatLevel(scene: BaseSceneRef | undefined): SelectedSplatLevel | undefined {\n if (!scene) return undefined\n const deviceCap = getDeviceSplatCountCap()\n\n const levels = scene.splatLevels\n if (levels && levels.length > 0) {\n let bestFitting: SplatLevel | undefined\n let lightest: SplatLevel | undefined\n for (const level of levels) {\n if (!level.url) continue\n if (level.splatCount <= deviceCap) {\n if (!bestFitting || level.splatCount > bestFitting.splatCount) bestFitting = level\n }\n if (!lightest || level.splatCount < lightest.splatCount) lightest = level\n }\n const chosen = bestFitting ?? lightest\n if (chosen) {\n return {\n url: chosen.url,\n splatCount: chosen.splatCount,\n selectedFromLevels: true,\n deviceCap\n }\n }\n // No valid (non-empty url) entries — fall through to scene.url\n }\n\n if (!scene.url) return undefined\n if (scene.splatCount != null && scene.splatCount > deviceCap) {\n logger.warn(\n `Scene declares ${scene.splatCount.toLocaleString()} splats but this device's cap is ` +\n `${deviceCap.toLocaleString()}. Provide \\`splatLevels\\` for smoother playback.`\n )\n }\n return {\n url: scene.url,\n splatCount: scene.splatCount,\n selectedFromLevels: false,\n deviceCap\n }\n}\n"],"names":["logger","createLogger","DESKTOP_SPLAT_COUNT_CAP","MOBILE_SPLAT_COUNT_CAP","isMobileDevice","isTouchPrimaryDevice","getDeviceSplatCountCap","selectSplatLevel","scene","deviceCap","levels","bestFitting","lightest","level","chosen"],"mappings":";;AAIA,MAAMA,IAASC,EAAa,iBAAiB,GAMhCC,IAA0B,MAK1BC,IAAyB;AAW/B,SAASC,IAA0B;AACxC,SAAOC,EAAA;AACT;AAKO,SAASC,IAAiC;AAC/C,SAAOF,EAAA,IAAmBD,IAAyBD;AACrD;AA6BO,SAASK,EAAiBC,GAAiE;AAChG,MAAI,CAACA,EAAO;AACZ,QAAMC,IAAYH,EAAA,GAEZI,IAASF,EAAM;AACrB,MAAIE,KAAUA,EAAO,SAAS,GAAG;AAC/B,QAAIC,GACAC;AACJ,eAAWC,KAASH;AAClB,MAAKG,EAAM,QACPA,EAAM,cAAcJ,MAClB,CAACE,KAAeE,EAAM,aAAaF,EAAY,gBAAYA,IAAcE,KAE3E,CAACD,KAAYC,EAAM,aAAaD,EAAS,gBAAYA,IAAWC;AAEtE,UAAMC,IAASH,KAAeC;AAC9B,QAAIE;AACF,aAAO;AAAA,QACL,KAAKA,EAAO;AAAA,QACZ,YAAYA,EAAO;AAAA,QACnB,oBAAoB;AAAA,QACpB,WAAAL;AAAA,MAAA;AAAA,EAIN;AAEA,MAAKD,EAAM;AACX,WAAIA,EAAM,cAAc,QAAQA,EAAM,aAAaC,KACjDT,EAAO;AAAA,MACL,kBAAkBQ,EAAM,WAAW,eAAA,CAAgB,oCAC9CC,EAAU,gBAAgB;AAAA,IAAA,GAG5B;AAAA,MACL,KAAKD,EAAM;AAAA,MACX,YAAYA,EAAM;AAAA,MAClB,oBAAoB;AAAA,MACpB,WAAAC;AAAA,IAAA;AAEJ;"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Cap backing-store resolution for splat-heavy canvases.
3
+ *
4
+ * Splat blending cost scales with pixel count, so we clamp `devicePixelRatio`
5
+ * to keep GPU load (and heat) in check. This is renderer-version agnostic — it
6
+ * only feeds the R3F `<Canvas dpr>` / `gl.setPixelRatio()`.
7
+ * @see https://sparkjs.dev/docs/performance/
8
+ */
9
+ export declare function getSparkCanvasDpr(): number;
10
+ //# sourceMappingURL=spark-performance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-performance.d.ts","sourceRoot":"","sources":["../../../space/utils/spark-performance.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C"}
@@ -0,0 +1,10 @@
1
+ import { isMobileDevice as i } from "./select-splat-level.js";
2
+ function o() {
3
+ if (typeof window > "u") return 1;
4
+ const e = (i(), 2);
5
+ return Math.min(window.devicePixelRatio || 1, e);
6
+ }
7
+ export {
8
+ o as getSparkCanvasDpr
9
+ };
10
+ //# sourceMappingURL=spark-performance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-performance.js","sources":["../../../space/utils/spark-performance.ts"],"sourcesContent":["import { isMobileDevice } from './select-splat-level'\n\n/**\n * Cap backing-store resolution for splat-heavy canvases.\n *\n * Splat blending cost scales with pixel count, so we clamp `devicePixelRatio`\n * to keep GPU load (and heat) in check. This is renderer-version agnostic — it\n * only feeds the R3F `<Canvas dpr>` / `gl.setPixelRatio()`.\n * @see https://sparkjs.dev/docs/performance/\n */\nexport function getSparkCanvasDpr(): number {\n if (typeof window === 'undefined') return 1\n const cap = isMobileDevice() ? 2 : 2\n return Math.min(window.devicePixelRatio || 1, cap)\n}\n"],"names":["getSparkCanvasDpr","cap","isMobileDevice"],"mappings":";AAUO,SAASA,IAA4B;AAC1C,MAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,QAAMC,KAAMC,KAAmB;AAC/B,SAAO,KAAK,IAAI,OAAO,oBAAoB,GAAGD,CAAG;AACnD;"}
@@ -0,0 +1,5 @@
1
+ import type { SparkSplatMesh } from '../splats/spark/splat-mesh.js';
2
+ import * as THREE from 'three';
3
+ export declare function createSplatCollider(splatMesh: SparkSplatMesh, maxPoints: number): THREE.Mesh;
4
+ export declare function createSplatColliderByVoxel(splatMesh: SparkSplatMesh, voxelSize: number): THREE.Mesh;
5
+ //# sourceMappingURL=splat-collider-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splat-collider-helper.d.ts","sourceRoot":"","sources":["../../../space/utils/splat-collider-helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,IAAI,CA4N5F;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC,IAAI,CAwQZ"}
@@ -0,0 +1,162 @@
1
+ import * as s from "three";
2
+ function P(H, O) {
3
+ const x = [];
4
+ H.forEachSplat((e, t, c, E) => {
5
+ x.push({
6
+ center: t.clone(),
7
+ scales: c.clone(),
8
+ quaternion: E.clone()
9
+ });
10
+ });
11
+ const m = x.length;
12
+ if (m === 0)
13
+ return new s.Mesh(new s.BufferGeometry());
14
+ const d = [];
15
+ if (m <= O)
16
+ d.push(...x);
17
+ else {
18
+ const e = Array.from({ length: m }, (t, c) => c);
19
+ for (let t = 0; t < O; t++) {
20
+ const c = t + Math.floor(Math.random() * (m - t));
21
+ [e[t], e[c]] = [e[c], e[t]], d.push(x[e[t]]);
22
+ }
23
+ }
24
+ const b = new Float32Array([
25
+ -0.5,
26
+ -0.5,
27
+ 0.5,
28
+ // 0: front-bottom-left
29
+ 0.5,
30
+ -0.5,
31
+ 0.5,
32
+ // 1: front-bottom-right
33
+ 0.5,
34
+ 0.5,
35
+ 0.5,
36
+ // 2: front-top-right
37
+ -0.5,
38
+ 0.5,
39
+ 0.5,
40
+ // 3: front-top-left
41
+ -0.5,
42
+ -0.5,
43
+ -0.5,
44
+ // 4: back-bottom-left
45
+ -0.5,
46
+ 0.5,
47
+ -0.5,
48
+ // 5: back-top-left
49
+ 0.5,
50
+ 0.5,
51
+ -0.5,
52
+ // 6: back-top-right
53
+ 0.5,
54
+ -0.5,
55
+ -0.5
56
+ // 7: back-bottom-right
57
+ ]), L = new Uint16Array([
58
+ // Front face (z = 0.5)
59
+ 0,
60
+ 1,
61
+ 2,
62
+ 0,
63
+ 2,
64
+ 3,
65
+ // Back face (z = -0.5)
66
+ 4,
67
+ 5,
68
+ 6,
69
+ 4,
70
+ 6,
71
+ 7,
72
+ // Top face (y = 0.5)
73
+ 3,
74
+ 2,
75
+ 6,
76
+ 3,
77
+ 6,
78
+ 5,
79
+ // Bottom face (y = -0.5)
80
+ 0,
81
+ 7,
82
+ 1,
83
+ 0,
84
+ 4,
85
+ 7,
86
+ // Right face (x = 0.5)
87
+ 1,
88
+ 7,
89
+ 6,
90
+ 1,
91
+ 6,
92
+ 2,
93
+ // Left face (x = -0.5)
94
+ 0,
95
+ 3,
96
+ 5,
97
+ 0,
98
+ 5,
99
+ 4
100
+ ]), v = new Float32Array([
101
+ // Vertex normals (averaged from adjacent faces)
102
+ -0.577,
103
+ -0.577,
104
+ 0.577,
105
+ // 0: left-bottom-front
106
+ 0.577,
107
+ -0.577,
108
+ 0.577,
109
+ // 1: right-bottom-front
110
+ 0.577,
111
+ 0.577,
112
+ 0.577,
113
+ // 2: right-top-front
114
+ -0.577,
115
+ 0.577,
116
+ 0.577,
117
+ // 3: left-top-front
118
+ -0.577,
119
+ -0.577,
120
+ -0.577,
121
+ // 4: left-bottom-back
122
+ -0.577,
123
+ 0.577,
124
+ -0.577,
125
+ // 5: left-top-back
126
+ 0.577,
127
+ 0.577,
128
+ -0.577,
129
+ // 6: right-top-back
130
+ 0.577,
131
+ -0.577,
132
+ -0.577
133
+ // 7: right-bottom-back
134
+ ]), p = d.length, u = 8, M = 36, w = new Float32Array(p * u * 3), i = new Float32Array(p * u * 3), U = new Uint32Array(p * M), g = new s.Matrix4(), _ = new s.Vector3(), j = new s.Matrix3(), n = g.elements;
135
+ for (let e = 0; e < p; e++) {
136
+ const t = d[e], c = e * u * 3, E = e * M;
137
+ _.set(t.scales.x * 2, t.scales.y * 2, t.scales.z * 2), g.compose(t.center, t.quaternion, _), j.setFromMatrix4(g);
138
+ for (let a = 0; a < u; a++) {
139
+ const l = a * 3, o = c + l, I = b[l], S = b[l + 1], F = b[l + 2];
140
+ w[o] = n[0] * I + n[4] * S + n[8] * F + n[12], w[o + 1] = n[1] * I + n[5] * S + n[9] * F + n[13], w[o + 2] = n[2] * I + n[6] * S + n[10] * F + n[14];
141
+ const q = v[l], z = v[l + 1], G = v[l + 2], r = j.elements, h = r[0] * q + r[3] * z + r[6] * G, A = r[1] * q + r[4] * z + r[7] * G, B = r[2] * q + r[5] * z + r[8] * G, C = Math.sqrt(h * h + A * A + B * B);
142
+ if (C > 0) {
143
+ const V = 1 / C;
144
+ i[o] = h * V, i[o + 1] = A * V, i[o + 2] = B * V;
145
+ } else
146
+ i[o] = h, i[o + 1] = A, i[o + 2] = B;
147
+ }
148
+ const N = e * u;
149
+ for (let a = 0; a < M; a++)
150
+ U[E + a] = L[a] + N;
151
+ }
152
+ const f = new s.BufferGeometry();
153
+ f.setAttribute("position", new s.BufferAttribute(w, 3)), f.setAttribute("normal", new s.BufferAttribute(i, 3)), f.setIndex(new s.BufferAttribute(U, 1)), f.computeBoundingBox(), f.computeBoundingSphere();
154
+ const y = new s.Mesh(f);
155
+ return Array.isArray(y.material) ? y.material.forEach((e) => {
156
+ e.visible = !1;
157
+ }) : y.material.visible = !1, y;
158
+ }
159
+ export {
160
+ P as createSplatCollider
161
+ };
162
+ //# sourceMappingURL=splat-collider-helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splat-collider-helper.js","sources":["../../../space/utils/splat-collider-helper.ts"],"sourcesContent":["import type { SparkSplatMesh } from '../splats/spark/splat-mesh'\nimport * as THREE from 'three'\n\ninterface SplatData {\n center: THREE.Vector3\n scales: THREE.Vector3\n quaternion: THREE.Quaternion\n}\n\nexport function createSplatCollider(splatMesh: SparkSplatMesh, maxPoints: number): THREE.Mesh {\n // Step 1: Collect all splat data\n const allSplats: SplatData[] = []\n splatMesh.forEachSplat((_index, center, scales, quaternion) => {\n allSplats.push({\n center: center.clone(),\n scales: scales.clone(),\n quaternion: quaternion.clone()\n })\n })\n\n // Step 2: Random sampling to downsample\n const totalSplats = allSplats.length\n if (totalSplats === 0) {\n // Return empty mesh if no splats\n return new THREE.Mesh(new THREE.BufferGeometry())\n }\n\n const sampledSplats: SplatData[] = []\n if (totalSplats <= maxPoints) {\n // If we have fewer splats than maxPoints, use all\n sampledSplats.push(...allSplats)\n } else {\n // Optimized random sampling: Fisher-Yates shuffle approach\n // Create array of indices and shuffle first maxPoints elements\n const indices = Array.from({ length: totalSplats }, (_, i) => i)\n for (let i = 0; i < maxPoints; i++) {\n const j = i + Math.floor(Math.random() * (totalSplats - i))\n ;[indices[i], indices[j]] = [indices[j], indices[i]]\n sampledSplats.push(allSplats[indices[i]])\n }\n }\n\n // Step 3: Directly build BufferGeometry by manipulating vertex data\n // Define unit box vertices (8 vertices, centered at origin)\n // Box from -0.5 to 0.5 in each axis\n // Vertex order: front-bottom-left, front-bottom-right, front-top-right, front-top-left,\n // back-bottom-left, back-top-left, back-top-right, back-bottom-right\n const unitBoxVertices = new Float32Array([\n -0.5,\n -0.5,\n 0.5, // 0: front-bottom-left\n 0.5,\n -0.5,\n 0.5, // 1: front-bottom-right\n 0.5,\n 0.5,\n 0.5, // 2: front-top-right\n -0.5,\n 0.5,\n 0.5, // 3: front-top-left\n -0.5,\n -0.5,\n -0.5, // 4: back-bottom-left\n -0.5,\n 0.5,\n -0.5, // 5: back-top-left\n 0.5,\n 0.5,\n -0.5, // 6: back-top-right\n 0.5,\n -0.5,\n -0.5 // 7: back-bottom-right\n ])\n\n // Define box indices (12 triangles = 6 faces * 2 triangles per face)\n const unitBoxIndices = new Uint16Array([\n // Front face (z = 0.5)\n 0, 1, 2, 0, 2, 3,\n // Back face (z = -0.5)\n 4, 5, 6, 4, 6, 7,\n // Top face (y = 0.5)\n 3, 2, 6, 3, 6, 5,\n // Bottom face (y = -0.5)\n 0, 7, 1, 0, 4, 7,\n // Right face (x = 0.5)\n 1, 7, 6, 1, 6, 2,\n // Left face (x = -0.5)\n 0, 3, 5, 0, 5, 4\n ])\n\n // Define box normals per vertex (each vertex belongs to 3 faces, use average or face-specific)\n // For simplicity, we'll use face-specific normals and duplicate vertices per face\n // But for performance, we'll use per-vertex normals (average of adjacent faces)\n const unitBoxNormals = new Float32Array([\n // Vertex normals (averaged from adjacent faces)\n -0.577,\n -0.577,\n 0.577, // 0: left-bottom-front\n 0.577,\n -0.577,\n 0.577, // 1: right-bottom-front\n 0.577,\n 0.577,\n 0.577, // 2: right-top-front\n -0.577,\n 0.577,\n 0.577, // 3: left-top-front\n -0.577,\n -0.577,\n -0.577, // 4: left-bottom-back\n -0.577,\n 0.577,\n -0.577, // 5: left-top-back\n 0.577,\n 0.577,\n -0.577, // 6: right-top-back\n 0.577,\n -0.577,\n -0.577 // 7: right-bottom-back\n ])\n\n const numBoxes = sampledSplats.length\n const verticesPerBox = 8\n const indicesPerBox = 36 // 12 triangles * 3 indices\n\n // Pre-allocate arrays for final geometry\n const positions = new Float32Array(numBoxes * verticesPerBox * 3)\n const normals = new Float32Array(numBoxes * verticesPerBox * 3)\n const indices = new Uint32Array(numBoxes * indicesPerBox)\n\n // Reusable objects for transformations\n const matrix = new THREE.Matrix4()\n const scale = new THREE.Vector3()\n const quaternionMatrix = new THREE.Matrix3()\n const matrixElements = matrix.elements\n\n // Process each splat\n for (let boxIndex = 0; boxIndex < numBoxes; boxIndex++) {\n const splat = sampledSplats[boxIndex]\n const vertexOffset = boxIndex * verticesPerBox * 3\n const indexOffset = boxIndex * indicesPerBox\n\n // Set scale\n scale.set(splat.scales.x * 2, splat.scales.y * 2, splat.scales.z * 2)\n\n // Build transformation matrix\n matrix.compose(splat.center, splat.quaternion, scale)\n\n // Extract rotation matrix for normals (normals should not be translated)\n quaternionMatrix.setFromMatrix4(matrix)\n\n // Transform vertices and normals\n for (let v = 0; v < verticesPerBox; v++) {\n const srcIdx = v * 3\n const dstIdx = vertexOffset + srcIdx\n\n // Transform vertex directly using matrix multiplication\n const vx = unitBoxVertices[srcIdx]\n const vy = unitBoxVertices[srcIdx + 1]\n const vz = unitBoxVertices[srcIdx + 2]\n\n // Apply matrix transformation: v' = M * v\n positions[dstIdx] =\n matrixElements[0] * vx +\n matrixElements[4] * vy +\n matrixElements[8] * vz +\n matrixElements[12]\n positions[dstIdx + 1] =\n matrixElements[1] * vx +\n matrixElements[5] * vy +\n matrixElements[9] * vz +\n matrixElements[13]\n positions[dstIdx + 2] =\n matrixElements[2] * vx +\n matrixElements[6] * vy +\n matrixElements[10] * vz +\n matrixElements[14]\n\n // Transform normal (only rotation, no translation)\n const nx = unitBoxNormals[srcIdx]\n const ny = unitBoxNormals[srcIdx + 1]\n const nz = unitBoxNormals[srcIdx + 2]\n\n // Apply rotation matrix and normalize\n const rotElements = quaternionMatrix.elements\n const ntx = rotElements[0] * nx + rotElements[3] * ny + rotElements[6] * nz\n const nty = rotElements[1] * nx + rotElements[4] * ny + rotElements[7] * nz\n const ntz = rotElements[2] * nx + rotElements[5] * ny + rotElements[8] * nz\n\n // Normalize\n const len = Math.sqrt(ntx * ntx + nty * nty + ntz * ntz)\n if (len > 0) {\n const invLen = 1 / len\n normals[dstIdx] = ntx * invLen\n normals[dstIdx + 1] = nty * invLen\n normals[dstIdx + 2] = ntz * invLen\n } else {\n normals[dstIdx] = ntx\n normals[dstIdx + 1] = nty\n normals[dstIdx + 2] = ntz\n }\n }\n\n // Copy indices with offset\n const baseVertexIndex = boxIndex * verticesPerBox\n for (let i = 0; i < indicesPerBox; i++) {\n indices[indexOffset + i] = unitBoxIndices[i] + baseVertexIndex\n }\n }\n\n // Create final BufferGeometry\n const mergedGeometry = new THREE.BufferGeometry()\n mergedGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))\n mergedGeometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3))\n mergedGeometry.setIndex(new THREE.BufferAttribute(indices, 1))\n mergedGeometry.computeBoundingBox()\n mergedGeometry.computeBoundingSphere()\n\n // Step 4: Create and return mesh\n const mesh = new THREE.Mesh(mergedGeometry)\n if (Array.isArray(mesh.material)) {\n mesh.material.forEach((material) => {\n material.visible = false\n })\n } else {\n mesh.material.visible = false\n }\n\n return mesh\n}\n\nexport function createSplatColliderByVoxel(\n splatMesh: SparkSplatMesh,\n voxelSize: number\n): THREE.Mesh {\n // Step 1: Collect all splat centers and compute bounding box\n const splatCenters: THREE.Vector3[] = []\n const bbox = new THREE.Box3()\n const tempCenter = new THREE.Vector3()\n\n splatMesh.forEachSplat((_index, center) => {\n tempCenter.copy(center)\n splatCenters.push(tempCenter.clone())\n bbox.expandByPoint(tempCenter)\n })\n\n if (splatCenters.length === 0 || bbox.isEmpty()) {\n return new THREE.Mesh(new THREE.BufferGeometry())\n }\n\n const bboxSize = bbox.getSize(new THREE.Vector3())\n const bboxMin = bbox.min.clone()\n\n // Step 2: Calculate voxel grid dimensions\n // Use the provided voxelSize directly\n // Calculate grid dimensions based on bounding box size and voxel size\n const gridX = Math.max(1, Math.ceil(bboxSize.x / voxelSize))\n const gridY = Math.max(1, Math.ceil(bboxSize.y / voxelSize))\n const gridZ = Math.max(1, Math.ceil(bboxSize.z / voxelSize))\n\n // Use actual voxel sizes to ensure complete coverage\n const actualVoxelSizeX = bboxSize.x / gridX\n const actualVoxelSizeY = bboxSize.y / gridY\n const actualVoxelSizeZ = bboxSize.z / gridZ\n\n // Step 3: Assign splats to voxels\n // Use Map with string key for voxel coordinates\n const voxelMap = new Map<string, boolean>()\n\n for (const center of splatCenters) {\n // Calculate voxel grid coordinates\n const voxelX = Math.floor((center.x - bboxMin.x) / actualVoxelSizeX)\n const voxelY = Math.floor((center.y - bboxMin.y) / actualVoxelSizeY)\n const voxelZ = Math.floor((center.z - bboxMin.z) / actualVoxelSizeZ)\n\n // Clamp to valid range\n const clampedX = Math.max(0, Math.min(gridX - 1, voxelX))\n const clampedY = Math.max(0, Math.min(gridY - 1, voxelY))\n const clampedZ = Math.max(0, Math.min(gridZ - 1, voxelZ))\n\n // Mark voxel as occupied\n const voxelKey = `${clampedX},${clampedY},${clampedZ}`\n voxelMap.set(voxelKey, true)\n }\n\n const occupiedVoxels = Array.from(voxelMap.keys())\n\n // Step 4: Generate box geometry for each occupied voxel\n // Reuse unit box data from createSplatCollider\n const unitBoxVertices = new Float32Array([\n -0.5,\n -0.5,\n 0.5, // 0: front-bottom-left\n 0.5,\n -0.5,\n 0.5, // 1: front-bottom-right\n 0.5,\n 0.5,\n 0.5, // 2: front-top-right\n -0.5,\n 0.5,\n 0.5, // 3: front-top-left\n -0.5,\n -0.5,\n -0.5, // 4: back-bottom-left\n -0.5,\n 0.5,\n -0.5, // 5: back-top-left\n 0.5,\n 0.5,\n -0.5, // 6: back-top-right\n 0.5,\n -0.5,\n -0.5 // 7: back-bottom-right\n ])\n\n const unitBoxIndices = new Uint16Array([\n 0,\n 1,\n 2,\n 0,\n 2,\n 3, // Front face\n 4,\n 5,\n 6,\n 4,\n 6,\n 7, // Back face\n 3,\n 2,\n 6,\n 3,\n 6,\n 5, // Top face\n 0,\n 7,\n 1,\n 0,\n 4,\n 7, // Bottom face\n 1,\n 7,\n 6,\n 1,\n 6,\n 2, // Right face\n 0,\n 3,\n 5,\n 0,\n 5,\n 4 // Left face\n ])\n\n const unitBoxNormals = new Float32Array([\n -0.577,\n -0.577,\n 0.577, // 0\n 0.577,\n -0.577,\n 0.577, // 1\n 0.577,\n 0.577,\n 0.577, // 2\n -0.577,\n 0.577,\n 0.577, // 3\n -0.577,\n -0.577,\n -0.577, // 4\n -0.577,\n 0.577,\n -0.577, // 5\n 0.577,\n 0.577,\n -0.577, // 6\n 0.577,\n -0.577,\n -0.577 // 7\n ])\n\n const numVoxels = occupiedVoxels.length\n const verticesPerBox = 8\n const indicesPerBox = 36\n\n // Pre-allocate arrays\n const positions = new Float32Array(numVoxels * verticesPerBox * 3)\n const normals = new Float32Array(numVoxels * verticesPerBox * 3)\n const indices = new Uint32Array(numVoxels * indicesPerBox)\n\n // Reusable objects\n const matrix = new THREE.Matrix4()\n const scale = new THREE.Vector3()\n const position = new THREE.Vector3()\n const quaternionMatrix = new THREE.Matrix3()\n const matrixElements = matrix.elements\n\n // Process each occupied voxel\n for (let voxelIndex = 0; voxelIndex < numVoxels; voxelIndex++) {\n const voxelKey = occupiedVoxels[voxelIndex]\n const [vx, vy, vz] = voxelKey.split(',').map(Number)\n\n // Calculate voxel center position\n position.set(\n bboxMin.x + (vx + 0.5) * actualVoxelSizeX,\n // bboxMin.y + (vy + 0.5) * actualVoxelSizeY,\n bboxMin.y + (vy + 1) * actualVoxelSizeY, // 避免地板和脚底间隙,采用上边沿而非中心点对齐\n bboxMin.z + (vz + 0.5) * actualVoxelSizeZ\n )\n\n // Voxel box size\n scale.set(actualVoxelSizeX, actualVoxelSizeY, actualVoxelSizeZ)\n\n // Build transformation matrix (no rotation, just translation and scale)\n matrix.compose(position, new THREE.Quaternion(), scale)\n\n // Extract rotation matrix for normals (identity for axis-aligned boxes)\n quaternionMatrix.setFromMatrix4(matrix)\n\n const vertexOffset = voxelIndex * verticesPerBox * 3\n const indexOffset = voxelIndex * indicesPerBox\n\n // Transform vertices and normals\n for (let v = 0; v < verticesPerBox; v++) {\n const srcIdx = v * 3\n const dstIdx = vertexOffset + srcIdx\n\n // Transform vertex\n const vtx = unitBoxVertices[srcIdx]\n const vty = unitBoxVertices[srcIdx + 1]\n const vtz = unitBoxVertices[srcIdx + 2]\n\n positions[dstIdx] =\n matrixElements[0] * vtx +\n matrixElements[4] * vty +\n matrixElements[8] * vtz +\n matrixElements[12]\n positions[dstIdx + 1] =\n matrixElements[1] * vtx +\n matrixElements[5] * vty +\n matrixElements[9] * vtz +\n matrixElements[13]\n positions[dstIdx + 2] =\n matrixElements[2] * vtx +\n matrixElements[6] * vty +\n matrixElements[10] * vtz +\n matrixElements[14]\n\n // Transform normal (for axis-aligned boxes, normals don't change)\n const nx = unitBoxNormals[srcIdx]\n const ny = unitBoxNormals[srcIdx + 1]\n const nz = unitBoxNormals[srcIdx + 2]\n\n const rotElements = quaternionMatrix.elements\n const ntx = rotElements[0] * nx + rotElements[3] * ny + rotElements[6] * nz\n const nty = rotElements[1] * nx + rotElements[4] * ny + rotElements[7] * nz\n const ntz = rotElements[2] * nx + rotElements[5] * ny + rotElements[8] * nz\n\n const len = Math.sqrt(ntx * ntx + nty * nty + ntz * ntz)\n if (len > 0) {\n const invLen = 1 / len\n normals[dstIdx] = ntx * invLen\n normals[dstIdx + 1] = nty * invLen\n normals[dstIdx + 2] = ntz * invLen\n } else {\n normals[dstIdx] = ntx\n normals[dstIdx + 1] = nty\n normals[dstIdx + 2] = ntz\n }\n }\n\n // Copy indices with offset\n const baseVertexIndex = voxelIndex * verticesPerBox\n for (let i = 0; i < indicesPerBox; i++) {\n indices[indexOffset + i] = unitBoxIndices[i] + baseVertexIndex\n }\n }\n\n // Create final BufferGeometry\n const mergedGeometry = new THREE.BufferGeometry()\n mergedGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))\n mergedGeometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3))\n mergedGeometry.setIndex(new THREE.BufferAttribute(indices, 1))\n mergedGeometry.computeBoundingBox()\n mergedGeometry.computeBoundingSphere()\n\n // Step 5: Create and return mesh\n const mesh = new THREE.Mesh(mergedGeometry)\n if (Array.isArray(mesh.material)) {\n mesh.material.forEach((material) => {\n material.visible = false\n })\n } else {\n mesh.material.visible = false\n }\n\n return mesh\n}\n"],"names":["createSplatCollider","splatMesh","maxPoints","allSplats","_index","center","scales","quaternion","totalSplats","THREE","sampledSplats","indices","_","i","j","unitBoxVertices","unitBoxIndices","unitBoxNormals","numBoxes","verticesPerBox","indicesPerBox","positions","normals","matrix","scale","quaternionMatrix","matrixElements","boxIndex","splat","vertexOffset","indexOffset","v","srcIdx","dstIdx","vx","vy","vz","nx","ny","nz","rotElements","ntx","nty","ntz","len","invLen","baseVertexIndex","mergedGeometry","mesh","material"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Same media query as `@react-three/viverse` `useIsMobile`. */
2
+ export declare const VIVERSE_MOBILE_MEDIA_QUERY = "(hover: none) and (pointer: coarse)";
3
+ /**
4
+ * Whether the device should use on-screen joystick + touch look controls.
5
+ *
6
+ * viverse's `useIsMobile()` only matches `(hover: none) and (pointer: coarse)`.
7
+ * Some phone browsers / in-app WebViews (WeChat, etc.) report `pointer: fine`, so
8
+ * they get desktop pointer-lock bindings and `.mobile-only` UI stays hidden.
9
+ */
10
+ export declare function isTouchPrimaryDevice(): boolean;
11
+ /**
12
+ * Drop-in replacement for viverse `useIsMobile()` wherever touch locomotion UI
13
+ * is chosen (joystick, jump button, pointer-capture look).
14
+ */
15
+ export declare function useTouchPrimaryDevice(): boolean;
16
+ //# sourceMappingURL=touch-primary-device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"touch-primary-device.d.ts","sourceRoot":"","sources":["../../../space/utils/touch-primary-device.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,wCAAwC,CAAA;AAa/E;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAkC9C;AA4CD;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAY/C"}
@@ -0,0 +1,52 @@
1
+ import { useSyncExternalStore as c, useEffect as a } from "react";
2
+ const i = "(hover: none) and (pointer: coarse)", u = [
3
+ i,
4
+ "(hover: none)",
5
+ "(hover: hover)",
6
+ "(pointer: fine)",
7
+ "(any-pointer: coarse)",
8
+ "(max-width: 1024px)"
9
+ ], r = "cave-world-touch-controls-style";
10
+ function s() {
11
+ return typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia(i).matches ? !0 : !((navigator.maxTouchPoints ?? 0) > 0 || "ontouchstart" in window) || window.matchMedia("(pointer: fine)").matches && window.matchMedia("(hover: hover)").matches ? !1 : !!(window.matchMedia("(max-width: 1024px)").matches || window.matchMedia("(hover: none)").matches || window.matchMedia("(any-pointer: coarse)").matches);
12
+ }
13
+ function d(e) {
14
+ if (typeof window > "u" || typeof window.matchMedia != "function")
15
+ return () => {
16
+ };
17
+ const n = u.map((t) => window.matchMedia(t)), o = () => e();
18
+ for (const t of n)
19
+ t.addEventListener("change", o);
20
+ return () => {
21
+ for (const t of n)
22
+ t.removeEventListener("change", o);
23
+ };
24
+ }
25
+ function h() {
26
+ return s();
27
+ }
28
+ function m() {
29
+ if (typeof document > "u" || document.getElementById(r))
30
+ return;
31
+ const e = document.createElement("style");
32
+ e.id = r, e.textContent = "html.cave-world-touch-controls .mobile-only{display:unset!important;}", document.head.appendChild(e);
33
+ }
34
+ function f(e) {
35
+ typeof document > "u" || (m(), document.documentElement.classList.toggle("cave-world-touch-controls", e));
36
+ }
37
+ function w() {
38
+ const e = c(
39
+ d,
40
+ h,
41
+ () => !1
42
+ );
43
+ return a(() => {
44
+ f(e);
45
+ }, [e]), e;
46
+ }
47
+ export {
48
+ i as VIVERSE_MOBILE_MEDIA_QUERY,
49
+ s as isTouchPrimaryDevice,
50
+ w as useTouchPrimaryDevice
51
+ };
52
+ //# sourceMappingURL=touch-primary-device.js.map