@hypersoniclabs/helix-cli 0.1.6 → 0.1.23

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 (1715) hide show
  1. package/README.md +627 -6
  2. package/assets/bridge/metahuman-eye-baked.png +0 -0
  3. package/assets/item-def-sounds/Common/A_DryShot.ogg +0 -0
  4. package/assets/item-def-sounds/Common/A_LMG_CloseCoverA.ogg +0 -0
  5. package/assets/item-def-sounds/Common/A_LMG_CloseCoverB.ogg +0 -0
  6. package/assets/item-def-sounds/Common/A_LMG_InsertMag.ogg +0 -0
  7. package/assets/item-def-sounds/Common/A_LMG_RemoveMag.ogg +0 -0
  8. package/assets/item-def-sounds/Common/A_Pistol_InsertMag_001.ogg +0 -0
  9. package/assets/item-def-sounds/Common/A_Pistol_InsertMag_002.ogg +0 -0
  10. package/assets/item-def-sounds/Common/A_Pistol_InsertMag_003.ogg +0 -0
  11. package/assets/item-def-sounds/Common/A_Pistol_RemoveMag_001.ogg +0 -0
  12. package/assets/item-def-sounds/Common/A_Pistol_RemoveMag_002.ogg +0 -0
  13. package/assets/item-def-sounds/Common/A_Pistol_RemoveMag_003.ogg +0 -0
  14. package/assets/item-def-sounds/Common/A_Rifle_ChargingHandle_001.ogg +0 -0
  15. package/assets/item-def-sounds/Common/A_Rifle_ChargingHandle_002.ogg +0 -0
  16. package/assets/item-def-sounds/Common/A_Rifle_ChargingHandle_003.ogg +0 -0
  17. package/assets/item-def-sounds/Common/A_Rifle_InsertMag_001.ogg +0 -0
  18. package/assets/item-def-sounds/Common/A_Rifle_InsertMag_002.ogg +0 -0
  19. package/assets/item-def-sounds/Common/A_Rifle_InsertMag_003.ogg +0 -0
  20. package/assets/item-def-sounds/Common/A_Rifle_RemoveMag_001.ogg +0 -0
  21. package/assets/item-def-sounds/Common/A_Rifle_RemoveMag_002.ogg +0 -0
  22. package/assets/item-def-sounds/Common/A_Rifle_RemoveMag_003.ogg +0 -0
  23. package/assets/item-def-sounds/Common/A_SMG_ChargingHandle_001.ogg +0 -0
  24. package/assets/item-def-sounds/Common/A_SMG_ChargingHandle_002.ogg +0 -0
  25. package/assets/item-def-sounds/Common/A_SMG_ChargingHandle_003.ogg +0 -0
  26. package/assets/item-def-sounds/Common/A_Sniper_ChargingHandle.ogg +0 -0
  27. package/assets/item-def-sounds/Common/A_Sniper_RemoveMag_001.ogg +0 -0
  28. package/assets/item-def-sounds/Common/A_Sniper_RemoveMag_002.ogg +0 -0
  29. package/assets/item-def-sounds/Common/A_Sniper_RemoveMag_003.ogg +0 -0
  30. package/assets/item-def-sounds/Explosions/Explosions_Grenade_Punch-Close_01.ogg +0 -0
  31. package/assets/item-def-sounds/Explosions/Explosions_Grenade_Punch-Close_02.ogg +0 -0
  32. package/assets/item-def-sounds/Explosions/Explosions_Grenade_Punch-Close_03.ogg +0 -0
  33. package/assets/item-def-sounds/LMG/A_LWS-32_Shot_001.ogg +0 -0
  34. package/assets/item-def-sounds/LMG/A_LWS-32_Shot_002.ogg +0 -0
  35. package/assets/item-def-sounds/LMG/A_LWS-32_Shot_003.ogg +0 -0
  36. package/assets/item-def-sounds/Pistol/A_Gaston_Shot_001.ogg +0 -0
  37. package/assets/item-def-sounds/Pistol/A_Gaston_Shot_002.ogg +0 -0
  38. package/assets/item-def-sounds/Pistol/A_Gaston_Shot_003.ogg +0 -0
  39. package/assets/item-def-sounds/Rifle/A_KAL_Shot_001.ogg +0 -0
  40. package/assets/item-def-sounds/Rifle/A_KAL_Shot_002.ogg +0 -0
  41. package/assets/item-def-sounds/Rifle/A_KAL_Shot_003.ogg +0 -0
  42. package/assets/item-def-sounds/SMG/A_Bison_InsertMag.ogg +0 -0
  43. package/assets/item-def-sounds/SMG/A_Bison_RemoveMag.ogg +0 -0
  44. package/assets/item-def-sounds/SMG/A_Bison_Shot_001.ogg +0 -0
  45. package/assets/item-def-sounds/SMG/A_Bison_Shot_002.ogg +0 -0
  46. package/assets/item-def-sounds/SMG/A_Bison_Shot_003.ogg +0 -0
  47. package/assets/item-def-sounds/Shotgun/A_DB-12_Shot_001.ogg +0 -0
  48. package/assets/item-def-sounds/Shotgun/A_DB-12_Shot_002.ogg +0 -0
  49. package/assets/item-def-sounds/Shotgun/A_DB-12_Shot_003.ogg +0 -0
  50. package/assets/item-def-sounds/Sniper/A_CS-446_Shot_001.ogg +0 -0
  51. package/assets/item-def-sounds/Sniper/A_CS-446_Shot_002.ogg +0 -0
  52. package/assets/item-def-sounds/Sniper/A_CS-446_Shot_003.ogg +0 -0
  53. package/bridge-plugins/helix.bridge.unreal/bin/unreal-skeletal-mesh-extract.js +812 -0
  54. package/bridge-plugins/helix.bridge.unreal/bridge-plugin.json +40 -0
  55. package/bridge-plugins/helix.bridge.unreal/test/exporter.test.js +418 -0
  56. package/bridge-plugins/helix.bridge.unreal/test/handler.test.js +417 -0
  57. package/bridge-plugins/helix.bridge.unreal/unreal/export_skeletal_mesh.py +384 -0
  58. package/bridge-recipes/cyberpunk/cyberpunk-female-bone-map.json +476 -0
  59. package/bridge-recipes/sims4/README.md +43 -0
  60. package/bridge-recipes/sims4/sims4-female-bone-map.json +65 -0
  61. package/bridge-recipes/unreal/character-customizer/README.md +58 -0
  62. package/bridge-recipes/unreal/character-customizer/all.json +13 -0
  63. package/bridge-recipes/unreal/character-customizer/base-male-body.json +6 -0
  64. package/bridge-recipes/unreal/character-customizer/base-male-head.json +6 -0
  65. package/bridge-recipes/unreal/character-customizer/beard.addon.json +1 -0
  66. package/bridge-recipes/unreal/character-customizer/beard.json +6 -0
  67. package/bridge-recipes/unreal/character-customizer/bottom.addon.json +1 -0
  68. package/bridge-recipes/unreal/character-customizer/bottom.json +6 -0
  69. package/bridge-recipes/unreal/character-customizer/eyewear.addon.json +1 -0
  70. package/bridge-recipes/unreal/character-customizer/eyewear.json +6 -0
  71. package/bridge-recipes/unreal/character-customizer/footwear.addon.json +1 -0
  72. package/bridge-recipes/unreal/character-customizer/footwear.json +6 -0
  73. package/bridge-recipes/unreal/character-customizer/hair.addon.json +1 -0
  74. package/bridge-recipes/unreal/character-customizer/hair.json +6 -0
  75. package/bridge-recipes/unreal/character-customizer/test/recipes.test.js +74 -0
  76. package/bridge-recipes/unreal/character-customizer/top.addon.json +1 -0
  77. package/bridge-recipes/unreal/character-customizer/top.json +6 -0
  78. package/bridge-recipes/unreal/helix-character-creator-female/README.md +57 -0
  79. package/bridge-recipes/unreal/helix-character-creator-female/all.json +18 -0
  80. package/bridge-recipes/unreal/helix-character-creator-female/back.addon.json +1 -0
  81. package/bridge-recipes/unreal/helix-character-creator-female/back.json +1 -0
  82. package/bridge-recipes/unreal/helix-character-creator-female/base-female-face.json +1 -0
  83. package/bridge-recipes/unreal/helix-character-creator-female/base-female.json +1 -0
  84. package/bridge-recipes/unreal/helix-character-creator-female/bottom.addon.json +1 -0
  85. package/bridge-recipes/unreal/helix-character-creator-female/bottom.json +1 -0
  86. package/bridge-recipes/unreal/helix-character-creator-female/eyewear.addon.json +1 -0
  87. package/bridge-recipes/unreal/helix-character-creator-female/eyewear.json +1 -0
  88. package/bridge-recipes/unreal/helix-character-creator-female/female-basic-t-shirt.addon.json +1 -0
  89. package/bridge-recipes/unreal/helix-character-creator-female/female-basic-t-shirt.json +1 -0
  90. package/bridge-recipes/unreal/helix-character-creator-female/female-batch-hair.json +23 -0
  91. package/bridge-recipes/unreal/helix-character-creator-female/female-batch-nebula-urbanghost.json +7 -0
  92. package/bridge-recipes/unreal/helix-character-creator-female/female-batch-singles.json +19 -0
  93. package/bridge-recipes/unreal/helix-character-creator-female/female-batch5.json +19 -0
  94. package/bridge-recipes/unreal/helix-character-creator-female/female-batch6.json +19 -0
  95. package/bridge-recipes/unreal/helix-character-creator-female/female-batch7.json +11 -0
  96. package/bridge-recipes/unreal/helix-character-creator-female/female-cap-2.addon.json +1 -0
  97. package/bridge-recipes/unreal/helix-character-creator-female/female-cap-2.json +1 -0
  98. package/bridge-recipes/unreal/helix-character-creator-female/female-cap.addon.json +1 -0
  99. package/bridge-recipes/unreal/helix-character-creator-female/female-cap.json +1 -0
  100. package/bridge-recipes/unreal/helix-character-creator-female/female-cargo-pants.addon.json +1 -0
  101. package/bridge-recipes/unreal/helix-character-creator-female/female-cargo-pants.json +1 -0
  102. package/bridge-recipes/unreal/helix-character-creator-female/female-carnival-mask-3.addon.json +1 -0
  103. package/bridge-recipes/unreal/helix-character-creator-female/female-carnival-mask-3.json +1 -0
  104. package/bridge-recipes/unreal/helix-character-creator-female/female-chain.addon.json +1 -0
  105. package/bridge-recipes/unreal/helix-character-creator-female/female-chain.json +1 -0
  106. package/bridge-recipes/unreal/helix-character-creator-female/female-clare-pants.addon.json +1 -0
  107. package/bridge-recipes/unreal/helix-character-creator-female/female-clare-pants.json +1 -0
  108. package/bridge-recipes/unreal/helix-character-creator-female/female-clare-shoes.addon.json +1 -0
  109. package/bridge-recipes/unreal/helix-character-creator-female/female-clare-shoes.json +1 -0
  110. package/bridge-recipes/unreal/helix-character-creator-female/female-clare-top.addon.json +1 -0
  111. package/bridge-recipes/unreal/helix-character-creator-female/female-clare-top.json +1 -0
  112. package/bridge-recipes/unreal/helix-character-creator-female/female-compression-bodysuit-2.addon.json +1 -0
  113. package/bridge-recipes/unreal/helix-character-creator-female/female-compression-bodysuit-2.json +1 -0
  114. package/bridge-recipes/unreal/helix-character-creator-female/female-compression-bodysuit.addon.json +1 -0
  115. package/bridge-recipes/unreal/helix-character-creator-female/female-compression-bodysuit.json +1 -0
  116. package/bridge-recipes/unreal/helix-character-creator-female/female-eye-hole-mask-2.addon.json +1 -0
  117. package/bridge-recipes/unreal/helix-character-creator-female/female-eye-hole-mask-2.json +1 -0
  118. package/bridge-recipes/unreal/helix-character-creator-female/female-eye-patch.addon.json +1 -0
  119. package/bridge-recipes/unreal/helix-character-creator-female/female-eye-patch.json +1 -0
  120. package/bridge-recipes/unreal/helix-character-creator-female/female-firefight-outfit.addon.json +1 -0
  121. package/bridge-recipes/unreal/helix-character-creator-female/female-firefight-outfit.json +1 -0
  122. package/bridge-recipes/unreal/helix-character-creator-female/female-fruity-pants.addon.json +1 -0
  123. package/bridge-recipes/unreal/helix-character-creator-female/female-fruity-pants.json +1 -0
  124. package/bridge-recipes/unreal/helix-character-creator-female/female-fruity-sneakers.addon.json +1 -0
  125. package/bridge-recipes/unreal/helix-character-creator-female/female-fruity-sneakers.json +1 -0
  126. package/bridge-recipes/unreal/helix-character-creator-female/female-fruity-top.addon.json +1 -0
  127. package/bridge-recipes/unreal/helix-character-creator-female/female-fruity-top.json +1 -0
  128. package/bridge-recipes/unreal/helix-character-creator-female/female-gamer-mask.addon.json +1 -0
  129. package/bridge-recipes/unreal/helix-character-creator-female/female-gamer-mask.json +1 -0
  130. package/bridge-recipes/unreal/helix-character-creator-female/female-guy-fawkes-mask.addon.json +1 -0
  131. package/bridge-recipes/unreal/helix-character-creator-female/female-guy-fawkes-mask.json +1 -0
  132. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a1.addon.json +1 -0
  133. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a1.json +1 -0
  134. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a10.addon.json +1 -0
  135. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a10.json +1 -0
  136. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a11.addon.json +1 -0
  137. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a11.json +1 -0
  138. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a12.addon.json +1 -0
  139. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a12.json +1 -0
  140. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a13.addon.json +1 -0
  141. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a13.json +1 -0
  142. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a14.addon.json +1 -0
  143. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a14.json +1 -0
  144. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a15.addon.json +1 -0
  145. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a15.json +1 -0
  146. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a16.addon.json +1 -0
  147. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a16.json +1 -0
  148. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a17.addon.json +1 -0
  149. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a17.json +1 -0
  150. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a18.addon.json +1 -0
  151. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a18.json +1 -0
  152. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a19.addon.json +1 -0
  153. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a19.json +1 -0
  154. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a2.addon.json +1 -0
  155. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a2.json +1 -0
  156. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a20.addon.json +1 -0
  157. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a20.json +1 -0
  158. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a21.addon.json +1 -0
  159. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a21.json +1 -0
  160. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a3.addon.json +1 -0
  161. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a3.json +1 -0
  162. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a4.addon.json +1 -0
  163. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a4.json +1 -0
  164. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a5.addon.json +1 -0
  165. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a5.json +1 -0
  166. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a6.addon.json +1 -0
  167. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a6.json +1 -0
  168. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a7.addon.json +1 -0
  169. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a7.json +1 -0
  170. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a8.addon.json +1 -0
  171. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a8.json +1 -0
  172. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a9.addon.json +1 -0
  173. package/bridge-recipes/unreal/helix-character-creator-female/female-hair-a9.json +1 -0
  174. package/bridge-recipes/unreal/helix-character-creator-female/female-high-waist-tailored-trousers.addon.json +1 -0
  175. package/bridge-recipes/unreal/helix-character-creator-female/female-high-waist-tailored-trousers.json +1 -0
  176. package/bridge-recipes/unreal/helix-character-creator-female/female-hogwarts-pants.addon.json +1 -0
  177. package/bridge-recipes/unreal/helix-character-creator-female/female-hogwarts-pants.json +1 -0
  178. package/bridge-recipes/unreal/helix-character-creator-female/female-hoody.addon.json +1 -0
  179. package/bridge-recipes/unreal/helix-character-creator-female/female-hoody.json +1 -0
  180. package/bridge-recipes/unreal/helix-character-creator-female/female-jacket-6.addon.json +1 -0
  181. package/bridge-recipes/unreal/helix-character-creator-female/female-jacket-6.json +1 -0
  182. package/bridge-recipes/unreal/helix-character-creator-female/female-joker-mask.addon.json +1 -0
  183. package/bridge-recipes/unreal/helix-character-creator-female/female-joker-mask.json +1 -0
  184. package/bridge-recipes/unreal/helix-character-creator-female/female-kombes-brown.addon.json +1 -0
  185. package/bridge-recipes/unreal/helix-character-creator-female/female-kombes-brown.json +1 -0
  186. package/bridge-recipes/unreal/helix-character-creator-female/female-kombes.addon.json +1 -0
  187. package/bridge-recipes/unreal/helix-character-creator-female/female-kombes.json +1 -0
  188. package/bridge-recipes/unreal/helix-character-creator-female/female-marshmallow-mask.addon.json +1 -0
  189. package/bridge-recipes/unreal/helix-character-creator-female/female-marshmallow-mask.json +1 -0
  190. package/bridge-recipes/unreal/helix-character-creator-female/female-maska.addon.json +1 -0
  191. package/bridge-recipes/unreal/helix-character-creator-female/female-maska.json +1 -0
  192. package/bridge-recipes/unreal/helix-character-creator-female/female-nebula-executive-pants.addon.json +1 -0
  193. package/bridge-recipes/unreal/helix-character-creator-female/female-nebula-executive-pants.json +1 -0
  194. package/bridge-recipes/unreal/helix-character-creator-female/female-nebula-executive-shoes.addon.json +1 -0
  195. package/bridge-recipes/unreal/helix-character-creator-female/female-nebula-executive-shoes.json +1 -0
  196. package/bridge-recipes/unreal/helix-character-creator-female/female-ninza-costume.addon.json +1 -0
  197. package/bridge-recipes/unreal/helix-character-creator-female/female-ninza-costume.json +1 -0
  198. package/bridge-recipes/unreal/helix-character-creator-female/female-orange-kombes.addon.json +1 -0
  199. package/bridge-recipes/unreal/helix-character-creator-female/female-orange-kombes.json +1 -0
  200. package/bridge-recipes/unreal/helix-character-creator-female/female-panama.addon.json +1 -0
  201. package/bridge-recipes/unreal/helix-character-creator-female/female-panama.json +1 -0
  202. package/bridge-recipes/unreal/helix-character-creator-female/female-panda-bag-2.addon.json +1 -0
  203. package/bridge-recipes/unreal/helix-character-creator-female/female-panda-bag-2.json +1 -0
  204. package/bridge-recipes/unreal/helix-character-creator-female/female-pantslow.addon.json +1 -0
  205. package/bridge-recipes/unreal/helix-character-creator-female/female-pantslow.json +1 -0
  206. package/bridge-recipes/unreal/helix-character-creator-female/female-paper-bag-mask.addon.json +1 -0
  207. package/bridge-recipes/unreal/helix-character-creator-female/female-paper-bag-mask.json +1 -0
  208. package/bridge-recipes/unreal/helix-character-creator-female/female-pig-mask.addon.json +1 -0
  209. package/bridge-recipes/unreal/helix-character-creator-female/female-pig-mask.json +1 -0
  210. package/bridge-recipes/unreal/helix-character-creator-female/female-police-uniform.addon.json +1 -0
  211. package/bridge-recipes/unreal/helix-character-creator-female/female-police-uniform.json +1 -0
  212. package/bridge-recipes/unreal/helix-character-creator-female/female-prison-outfit.addon.json +1 -0
  213. package/bridge-recipes/unreal/helix-character-creator-female/female-prison-outfit.json +1 -0
  214. package/bridge-recipes/unreal/helix-character-creator-female/female-short-b4low.addon.json +1 -0
  215. package/bridge-recipes/unreal/helix-character-creator-female/female-short-b4low.json +1 -0
  216. package/bridge-recipes/unreal/helix-character-creator-female/female-short-swimming.addon.json +1 -0
  217. package/bridge-recipes/unreal/helix-character-creator-female/female-short-swimming.json +1 -0
  218. package/bridge-recipes/unreal/helix-character-creator-female/female-shortlow.addon.json +1 -0
  219. package/bridge-recipes/unreal/helix-character-creator-female/female-shortlow.json +1 -0
  220. package/bridge-recipes/unreal/helix-character-creator-female/female-shorts-b3.addon.json +1 -0
  221. package/bridge-recipes/unreal/helix-character-creator-female/female-shorts-b3.json +1 -0
  222. package/bridge-recipes/unreal/helix-character-creator-female/female-sterile-gloves.addon.json +1 -0
  223. package/bridge-recipes/unreal/helix-character-creator-female/female-sterile-gloves.json +1 -0
  224. package/bridge-recipes/unreal/helix-character-creator-female/female-surgeon-mask.addon.json +1 -0
  225. package/bridge-recipes/unreal/helix-character-creator-female/female-surgeon-mask.json +1 -0
  226. package/bridge-recipes/unreal/helix-character-creator-female/female-swat.addon.json +1 -0
  227. package/bridge-recipes/unreal/helix-character-creator-female/female-swat.json +1 -0
  228. package/bridge-recipes/unreal/helix-character-creator-female/female-top.addon.json +1 -0
  229. package/bridge-recipes/unreal/helix-character-creator-female/female-top.json +1 -0
  230. package/bridge-recipes/unreal/helix-character-creator-female/female-topx1.addon.json +1 -0
  231. package/bridge-recipes/unreal/helix-character-creator-female/female-topx1.json +1 -0
  232. package/bridge-recipes/unreal/helix-character-creator-female/female-urban-ghost-hoodie.addon.json +1 -0
  233. package/bridge-recipes/unreal/helix-character-creator-female/female-urban-ghost-hoodie.json +1 -0
  234. package/bridge-recipes/unreal/helix-character-creator-female/female-urban-ghost-mask.addon.json +1 -0
  235. package/bridge-recipes/unreal/helix-character-creator-female/female-urban-ghost-mask.json +1 -0
  236. package/bridge-recipes/unreal/helix-character-creator-female/female-urban-ghost-pants.addon.json +1 -0
  237. package/bridge-recipes/unreal/helix-character-creator-female/female-urban-ghost-pants.json +1 -0
  238. package/bridge-recipes/unreal/helix-character-creator-female/female-warehouse-uniform.addon.json +1 -0
  239. package/bridge-recipes/unreal/helix-character-creator-female/female-warehouse-uniform.json +1 -0
  240. package/bridge-recipes/unreal/helix-character-creator-female/female-wide-top.addon.json +1 -0
  241. package/bridge-recipes/unreal/helix-character-creator-female/female-wide-top.json +1 -0
  242. package/bridge-recipes/unreal/helix-character-creator-female/female-wolf-set.addon.json +1 -0
  243. package/bridge-recipes/unreal/helix-character-creator-female/female-wolf-set.json +1 -0
  244. package/bridge-recipes/unreal/helix-character-creator-female/female-woman-mask-3.addon.json +1 -0
  245. package/bridge-recipes/unreal/helix-character-creator-female/female-woman-mask-3.json +1 -0
  246. package/bridge-recipes/unreal/helix-character-creator-female/female-women-out-2.addon.json +1 -0
  247. package/bridge-recipes/unreal/helix-character-creator-female/female-women-out-2.json +1 -0
  248. package/bridge-recipes/unreal/helix-character-creator-female/footwear.addon.json +1 -0
  249. package/bridge-recipes/unreal/helix-character-creator-female/footwear.json +1 -0
  250. package/bridge-recipes/unreal/helix-character-creator-female/gloves.addon.json +1 -0
  251. package/bridge-recipes/unreal/helix-character-creator-female/gloves.json +1 -0
  252. package/bridge-recipes/unreal/helix-character-creator-female/hair.addon.json +1 -0
  253. package/bridge-recipes/unreal/helix-character-creator-female/hair.json +1 -0
  254. package/bridge-recipes/unreal/helix-character-creator-female/headwear.addon.json +1 -0
  255. package/bridge-recipes/unreal/helix-character-creator-female/headwear.json +1 -0
  256. package/bridge-recipes/unreal/helix-character-creator-female/mask.addon.json +1 -0
  257. package/bridge-recipes/unreal/helix-character-creator-female/mask.json +1 -0
  258. package/bridge-recipes/unreal/helix-character-creator-female/neck.addon.json +1 -0
  259. package/bridge-recipes/unreal/helix-character-creator-female/neck.json +1 -0
  260. package/bridge-recipes/unreal/helix-character-creator-female/outfit.addon.json +1 -0
  261. package/bridge-recipes/unreal/helix-character-creator-female/outfit.json +1 -0
  262. package/bridge-recipes/unreal/helix-character-creator-female/top.addon.json +1 -0
  263. package/bridge-recipes/unreal/helix-character-creator-female/top.json +1 -0
  264. package/bridge-recipes/unreal/helix-character-creator-male/README.md +15 -0
  265. package/bridge-recipes/unreal/helix-character-creator-male/all.json +18 -0
  266. package/bridge-recipes/unreal/helix-character-creator-male/back.addon.json +1 -0
  267. package/bridge-recipes/unreal/helix-character-creator-male/back.json +1 -0
  268. package/bridge-recipes/unreal/helix-character-creator-male/base-male-face.json +6 -0
  269. package/bridge-recipes/unreal/helix-character-creator-male/base-male.json +6 -0
  270. package/bridge-recipes/unreal/helix-character-creator-male/bottom.addon.json +1 -0
  271. package/bridge-recipes/unreal/helix-character-creator-male/bottom.json +1 -0
  272. package/bridge-recipes/unreal/helix-character-creator-male/eyewear.addon.json +1 -0
  273. package/bridge-recipes/unreal/helix-character-creator-male/eyewear.json +1 -0
  274. package/bridge-recipes/unreal/helix-character-creator-male/footwear.addon.json +1 -0
  275. package/bridge-recipes/unreal/helix-character-creator-male/footwear.json +1 -0
  276. package/bridge-recipes/unreal/helix-character-creator-male/gloves.addon.json +1 -0
  277. package/bridge-recipes/unreal/helix-character-creator-male/gloves.json +1 -0
  278. package/bridge-recipes/unreal/helix-character-creator-male/hair.addon.json +1 -0
  279. package/bridge-recipes/unreal/helix-character-creator-male/hair.json +1 -0
  280. package/bridge-recipes/unreal/helix-character-creator-male/headwear.addon.json +1 -0
  281. package/bridge-recipes/unreal/helix-character-creator-male/headwear.json +1 -0
  282. package/bridge-recipes/unreal/helix-character-creator-male/male-batch-hair.json +23 -0
  283. package/bridge-recipes/unreal/helix-character-creator-male/male-batch-nebula-urbanghost.json +7 -0
  284. package/bridge-recipes/unreal/helix-character-creator-male/male-batch-uniforms.json +10 -0
  285. package/bridge-recipes/unreal/helix-character-creator-male/male-batch6-slot2.json +7 -0
  286. package/bridge-recipes/unreal/helix-character-creator-male/male-batch7-slot3.json +6 -0
  287. package/bridge-recipes/unreal/helix-character-creator-male/male-batch8-combined.json +11 -0
  288. package/bridge-recipes/unreal/helix-character-creator-male/male-boots-25.addon.json +1 -0
  289. package/bridge-recipes/unreal/helix-character-creator-male/male-cargo-pants.addon.json +1 -0
  290. package/bridge-recipes/unreal/helix-character-creator-male/male-cargo-pants.json +1 -0
  291. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-2.addon.json +1 -0
  292. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-2.json +1 -0
  293. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-3.addon.json +1 -0
  294. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-3.json +1 -0
  295. package/bridge-recipes/unreal/helix-character-creator-male/male-clare-pants.addon.json +1 -0
  296. package/bridge-recipes/unreal/helix-character-creator-male/male-clare-pants.json +1 -0
  297. package/bridge-recipes/unreal/helix-character-creator-male/male-clare-shoes.addon.json +1 -0
  298. package/bridge-recipes/unreal/helix-character-creator-male/male-clare-shoes.json +1 -0
  299. package/bridge-recipes/unreal/helix-character-creator-male/male-clare-top.addon.json +1 -0
  300. package/bridge-recipes/unreal/helix-character-creator-male/male-clare-top.json +1 -0
  301. package/bridge-recipes/unreal/helix-character-creator-male/male-diamond-hands.addon.json +1 -0
  302. package/bridge-recipes/unreal/helix-character-creator-male/male-diamond-hands.json +1 -0
  303. package/bridge-recipes/unreal/helix-character-creator-male/male-doctor-outfit.addon.json +1 -0
  304. package/bridge-recipes/unreal/helix-character-creator-male/male-doctor-outfit.json +1 -0
  305. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask-2.addon.json +1 -0
  306. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask-2.json +1 -0
  307. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask.addon.json +1 -0
  308. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask.json +1 -0
  309. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-patch.addon.json +1 -0
  310. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-patch.json +1 -0
  311. package/bridge-recipes/unreal/helix-character-creator-male/male-fb-cargo-pants-2.addon.json +1 -0
  312. package/bridge-recipes/unreal/helix-character-creator-male/male-fb-cargo-pants-2.json +1 -0
  313. package/bridge-recipes/unreal/helix-character-creator-male/male-fb-cargo-pants.addon.json +1 -0
  314. package/bridge-recipes/unreal/helix-character-creator-male/male-fb-puffy.addon.json +1 -0
  315. package/bridge-recipes/unreal/helix-character-creator-male/male-fb-puffy.json +1 -0
  316. package/bridge-recipes/unreal/helix-character-creator-male/male-firefight-outfit.addon.json +1 -0
  317. package/bridge-recipes/unreal/helix-character-creator-male/male-firefight-outfit.json +1 -0
  318. package/bridge-recipes/unreal/helix-character-creator-male/male-gamer-mask-2.addon.json +1 -0
  319. package/bridge-recipes/unreal/helix-character-creator-male/male-gamer-mask-2.json +1 -0
  320. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a1.addon.json +1 -0
  321. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a1.json +1 -0
  322. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a10.addon.json +1 -0
  323. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a10.json +1 -0
  324. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a11.addon.json +1 -0
  325. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a11.json +1 -0
  326. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a12.addon.json +1 -0
  327. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a12.json +1 -0
  328. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a13.addon.json +1 -0
  329. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a13.json +1 -0
  330. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a14.addon.json +1 -0
  331. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a14.json +1 -0
  332. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a15.addon.json +1 -0
  333. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a15.json +1 -0
  334. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a16.addon.json +1 -0
  335. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a16.json +1 -0
  336. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a17.addon.json +1 -0
  337. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a17.json +1 -0
  338. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a18.addon.json +1 -0
  339. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a18.json +1 -0
  340. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a19.addon.json +1 -0
  341. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a19.json +1 -0
  342. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a2.addon.json +1 -0
  343. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a2.json +1 -0
  344. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a20.addon.json +1 -0
  345. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a20.json +1 -0
  346. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a21.addon.json +1 -0
  347. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a21.json +1 -0
  348. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a3.addon.json +1 -0
  349. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a3.json +1 -0
  350. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a4.addon.json +1 -0
  351. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a4.json +1 -0
  352. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a5.addon.json +1 -0
  353. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a5.json +1 -0
  354. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a6.addon.json +1 -0
  355. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a6.json +1 -0
  356. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a7.addon.json +1 -0
  357. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a7.json +1 -0
  358. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a8.addon.json +1 -0
  359. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a8.json +1 -0
  360. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a9.addon.json +1 -0
  361. package/bridge-recipes/unreal/helix-character-creator-male/male-hair-a9.json +1 -0
  362. package/bridge-recipes/unreal/helix-character-creator-male/male-kombes-brown-v2.addon.json +1 -0
  363. package/bridge-recipes/unreal/helix-character-creator-male/male-kombes-brown-v2.json +1 -0
  364. package/bridge-recipes/unreal/helix-character-creator-male/male-kombes-brown.addon.json +1 -0
  365. package/bridge-recipes/unreal/helix-character-creator-male/male-man-mask-3.addon.json +1 -0
  366. package/bridge-recipes/unreal/helix-character-creator-male/male-marshmallow-mask.addon.json +1 -0
  367. package/bridge-recipes/unreal/helix-character-creator-male/male-marshmallow-mask.json +1 -0
  368. package/bridge-recipes/unreal/helix-character-creator-male/male-mikejack-jacket.addon.json +1 -0
  369. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-coat.addon.json +1 -0
  370. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-coat.json +1 -0
  371. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-pants.addon.json +1 -0
  372. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-pants.json +1 -0
  373. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-shoes.addon.json +1 -0
  374. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-shoes.json +1 -0
  375. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-gloves.addon.json +1 -0
  376. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-gloves.json +1 -0
  377. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-mantle.addon.json +1 -0
  378. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-mantle.json +1 -0
  379. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-tunic.addon.json +1 -0
  380. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-tunic.json +1 -0
  381. package/bridge-recipes/unreal/helix-character-creator-male/male-orangekombes.addon.json +1 -0
  382. package/bridge-recipes/unreal/helix-character-creator-male/male-orangekombes.json +1 -0
  383. package/bridge-recipes/unreal/helix-character-creator-male/male-out2-boots.addon.json +1 -0
  384. package/bridge-recipes/unreal/helix-character-creator-male/male-out2-boots.json +1 -0
  385. package/bridge-recipes/unreal/helix-character-creator-male/male-panda-bag-2.addon.json +1 -0
  386. package/bridge-recipes/unreal/helix-character-creator-male/male-panda-bag-2.json +1 -0
  387. package/bridge-recipes/unreal/helix-character-creator-male/male-pants-2.addon.json +1 -0
  388. package/bridge-recipes/unreal/helix-character-creator-male/male-pants-2.json +1 -0
  389. package/bridge-recipes/unreal/helix-character-creator-male/male-paper-bag-mask.addon.json +1 -0
  390. package/bridge-recipes/unreal/helix-character-creator-male/male-paper-bag-mask.json +1 -0
  391. package/bridge-recipes/unreal/helix-character-creator-male/male-pig-mask.addon.json +1 -0
  392. package/bridge-recipes/unreal/helix-character-creator-male/male-pig-mask.json +1 -0
  393. package/bridge-recipes/unreal/helix-character-creator-male/male-police-uniform.addon.json +1 -0
  394. package/bridge-recipes/unreal/helix-character-creator-male/male-police-uniform.json +1 -0
  395. package/bridge-recipes/unreal/helix-character-creator-male/male-prison-outfit.addon.json +1 -0
  396. package/bridge-recipes/unreal/helix-character-creator-male/male-prison-outfit.json +1 -0
  397. package/bridge-recipes/unreal/helix-character-creator-male/male-puffer-jacket.addon.json +1 -0
  398. package/bridge-recipes/unreal/helix-character-creator-male/male-puffer-jacket.json +1 -0
  399. package/bridge-recipes/unreal/helix-character-creator-male/male-pullover-hoodie.addon.json +1 -0
  400. package/bridge-recipes/unreal/helix-character-creator-male/male-pullover-hoodie.json +1 -0
  401. package/bridge-recipes/unreal/helix-character-creator-male/male-robber-mask.addon.json +1 -0
  402. package/bridge-recipes/unreal/helix-character-creator-male/male-robber-mask.json +1 -0
  403. package/bridge-recipes/unreal/helix-character-creator-male/male-short-swimming.addon.json +1 -0
  404. package/bridge-recipes/unreal/helix-character-creator-male/male-short-swimming.json +1 -0
  405. package/bridge-recipes/unreal/helix-character-creator-male/male-sinth-ascetic-jacket.addon.json +1 -0
  406. package/bridge-recipes/unreal/helix-character-creator-male/male-sinth-ascetic-jacket.json +1 -0
  407. package/bridge-recipes/unreal/helix-character-creator-male/male-sip-cap.addon.json +1 -0
  408. package/bridge-recipes/unreal/helix-character-creator-male/male-sip-cap.json +1 -0
  409. package/bridge-recipes/unreal/helix-character-creator-male/male-socks.addon.json +1 -0
  410. package/bridge-recipes/unreal/helix-character-creator-male/male-socks.json +1 -0
  411. package/bridge-recipes/unreal/helix-character-creator-male/male-surgeon-mask.addon.json +1 -0
  412. package/bridge-recipes/unreal/helix-character-creator-male/male-surgeon-mask.json +1 -0
  413. package/bridge-recipes/unreal/helix-character-creator-male/male-tech-hoodie.addon.json +1 -0
  414. package/bridge-recipes/unreal/helix-character-creator-male/male-tech-hoodie.json +1 -0
  415. package/bridge-recipes/unreal/helix-character-creator-male/male-urban-ghost-hoodie.addon.json +1 -0
  416. package/bridge-recipes/unreal/helix-character-creator-male/male-urban-ghost-hoodie.json +1 -0
  417. package/bridge-recipes/unreal/helix-character-creator-male/male-urban-ghost-mask.addon.json +1 -0
  418. package/bridge-recipes/unreal/helix-character-creator-male/male-urban-ghost-mask.json +1 -0
  419. package/bridge-recipes/unreal/helix-character-creator-male/male-urban-ghost-pants.addon.json +1 -0
  420. package/bridge-recipes/unreal/helix-character-creator-male/male-urban-ghost-pants.json +1 -0
  421. package/bridge-recipes/unreal/helix-character-creator-male/male-warehouse-uniform.addon.json +1 -0
  422. package/bridge-recipes/unreal/helix-character-creator-male/male-warehouse-uniform.json +1 -0
  423. package/bridge-recipes/unreal/helix-character-creator-male/mask.addon.json +1 -0
  424. package/bridge-recipes/unreal/helix-character-creator-male/mask.json +1 -0
  425. package/bridge-recipes/unreal/helix-character-creator-male/neck.addon.json +1 -0
  426. package/bridge-recipes/unreal/helix-character-creator-male/neck.json +1 -0
  427. package/bridge-recipes/unreal/helix-character-creator-male/outfit.addon.json +1 -0
  428. package/bridge-recipes/unreal/helix-character-creator-male/outfit.json +1 -0
  429. package/bridge-recipes/unreal/helix-character-creator-male/palette-pilot.json +1 -0
  430. package/bridge-recipes/unreal/helix-character-creator-male/test/recipes.test.js +122 -0
  431. package/bridge-recipes/unreal/helix-character-creator-male/top.addon.json +1 -0
  432. package/bridge-recipes/unreal/helix-character-creator-male/top.json +1 -0
  433. package/bridge-recipes/unreal/helix-character-creator-male/variants/bottom-nopalette.addon.json +1 -0
  434. package/bridge-recipes/unreal/helix-character-creator-male/variants/headwear-nopalette.addon.json +1 -0
  435. package/bridge-recipes/unreal/helix-character-creator-male/variants/neck-nopalette.addon.json +1 -0
  436. package/dist/ability.d.ts +2 -2
  437. package/dist/ability.js +6 -5
  438. package/dist/ability.js.map +1 -1
  439. package/dist/achievement.d.ts +123 -0
  440. package/dist/achievement.js +400 -0
  441. package/dist/achievement.js.map +1 -0
  442. package/dist/api.d.ts +19 -1
  443. package/dist/api.js +80 -6
  444. package/dist/api.js.map +1 -1
  445. package/dist/assets.d.ts +595 -0
  446. package/dist/assets.js +1121 -0
  447. package/dist/assets.js.map +1 -0
  448. package/dist/audioCommand.d.ts +65 -0
  449. package/dist/audioCommand.js +181 -0
  450. package/dist/audioCommand.js.map +1 -0
  451. package/dist/bakeBodyRegionsCli.d.ts +19 -0
  452. package/dist/bakeBodyRegionsCli.js +25 -0
  453. package/dist/bakeBodyRegionsCli.js.map +1 -0
  454. package/dist/bakeLighting.d.ts +173 -0
  455. package/dist/bakeLighting.js +452 -0
  456. package/dist/bakeLighting.js.map +1 -0
  457. package/dist/bridge/bridge.testutil.d.ts +105 -0
  458. package/dist/bridge/bridge.testutil.js +259 -0
  459. package/dist/bridge/bridge.testutil.js.map +1 -0
  460. package/dist/bridge/characterAddon.d.ts +47 -0
  461. package/dist/bridge/characterAddon.js +146 -0
  462. package/dist/bridge/characterAddon.js.map +1 -0
  463. package/dist/bridge/detect.d.ts +4 -0
  464. package/dist/bridge/detect.js +154 -0
  465. package/dist/bridge/detect.js.map +1 -0
  466. package/dist/bridge/execute.d.ts +3 -0
  467. package/dist/bridge/execute.js +206 -0
  468. package/dist/bridge/execute.js.map +1 -0
  469. package/dist/bridge/glb.d.ts +60 -0
  470. package/dist/bridge/glb.js +81 -0
  471. package/dist/bridge/glb.js.map +1 -0
  472. package/dist/bridge/hash.d.ts +6 -0
  473. package/dist/bridge/hash.js +40 -0
  474. package/dist/bridge/hash.js.map +1 -0
  475. package/dist/bridge/index.d.ts +11 -0
  476. package/dist/bridge/index.js +28 -0
  477. package/dist/bridge/index.js.map +1 -0
  478. package/dist/bridge/internal/artifacts.d.ts +5 -0
  479. package/dist/bridge/internal/artifacts.js +31 -0
  480. package/dist/bridge/internal/artifacts.js.map +1 -0
  481. package/dist/bridge/internal/builtInExecution.d.ts +4 -0
  482. package/dist/bridge/internal/builtInExecution.js +514 -0
  483. package/dist/bridge/internal/builtInExecution.js.map +1 -0
  484. package/dist/bridge/internal/externalExecution.d.ts +12 -0
  485. package/dist/bridge/internal/externalExecution.js +261 -0
  486. package/dist/bridge/internal/externalExecution.js.map +1 -0
  487. package/dist/bridge/internal/outputReservation.d.ts +13 -0
  488. package/dist/bridge/internal/outputReservation.js +151 -0
  489. package/dist/bridge/internal/outputReservation.js.map +1 -0
  490. package/dist/bridge/internal/snapshots.d.ts +32 -0
  491. package/dist/bridge/internal/snapshots.js +562 -0
  492. package/dist/bridge/internal/snapshots.js.map +1 -0
  493. package/dist/bridge/management.d.ts +40 -0
  494. package/dist/bridge/management.js +333 -0
  495. package/dist/bridge/management.js.map +1 -0
  496. package/dist/bridge/manifest.d.ts +8 -0
  497. package/dist/bridge/manifest.js +192 -0
  498. package/dist/bridge/manifest.js.map +1 -0
  499. package/dist/bridge/metahuman.d.ts +26 -0
  500. package/dist/bridge/metahuman.js +332 -0
  501. package/dist/bridge/metahuman.js.map +1 -0
  502. package/dist/bridge/planner.d.ts +9 -0
  503. package/dist/bridge/planner.js +246 -0
  504. package/dist/bridge/planner.js.map +1 -0
  505. package/dist/bridge/plugins.d.ts +17 -0
  506. package/dist/bridge/plugins.js +307 -0
  507. package/dist/bridge/plugins.js.map +1 -0
  508. package/dist/bridge/resolution.d.ts +8 -0
  509. package/dist/bridge/resolution.js +55 -0
  510. package/dist/bridge/resolution.js.map +1 -0
  511. package/dist/bridge/types.d.ts +247 -0
  512. package/dist/bridge/types.js +60 -0
  513. package/dist/bridge/types.js.map +1 -0
  514. package/dist/bridge/validate.d.ts +12 -0
  515. package/dist/bridge/validate.js +370 -0
  516. package/dist/bridge/validate.js.map +1 -0
  517. package/dist/bridge/zipSource.d.ts +27 -0
  518. package/dist/bridge/zipSource.js +90 -0
  519. package/dist/bridge/zipSource.js.map +1 -0
  520. package/dist/browser/hostSlots.d.ts +57 -0
  521. package/dist/browser/hostSlots.js +371 -0
  522. package/dist/browser/hostSlots.js.map +1 -0
  523. package/dist/browser/launch.d.ts +25 -0
  524. package/dist/browser/launch.js +74 -0
  525. package/dist/browser/launch.js.map +1 -0
  526. package/dist/browser/nonSystemChrome.d.ts +15 -0
  527. package/dist/browser/nonSystemChrome.js +91 -0
  528. package/dist/browser/nonSystemChrome.js.map +1 -0
  529. package/dist/builds/index.d.ts +54 -0
  530. package/dist/builds/index.js +215 -0
  531. package/dist/builds/index.js.map +1 -0
  532. package/dist/bundle.d.ts +35 -2
  533. package/dist/bundle.js +437 -20
  534. package/dist/bundle.js.map +1 -1
  535. package/dist/canary/cli.d.ts +17 -0
  536. package/dist/canary/cli.js +95 -0
  537. package/dist/canary/cli.js.map +1 -0
  538. package/dist/canary/index.d.ts +4 -0
  539. package/dist/canary/index.js +21 -0
  540. package/dist/canary/index.js.map +1 -0
  541. package/dist/canary/inventory.d.ts +46 -0
  542. package/dist/canary/inventory.js +499 -0
  543. package/dist/canary/inventory.js.map +1 -0
  544. package/dist/canary/pacifica.d.ts +51 -0
  545. package/dist/canary/pacifica.js +247 -0
  546. package/dist/canary/pacifica.js.map +1 -0
  547. package/dist/canary/plan.d.ts +115 -0
  548. package/dist/canary/plan.js +479 -0
  549. package/dist/canary/plan.js.map +1 -0
  550. package/dist/character/addonPushOut.d.ts +66 -0
  551. package/dist/character/addonPushOut.js +211 -0
  552. package/dist/character/addonPushOut.js.map +1 -0
  553. package/dist/character/avatarPreflight.d.ts +92 -0
  554. package/dist/character/avatarPreflight.js +261 -0
  555. package/dist/character/avatarPreflight.js.map +1 -0
  556. package/dist/character/bakeBodyRegions.d.ts +80 -0
  557. package/dist/character/bakeBodyRegions.js +190 -0
  558. package/dist/character/bakeBodyRegions.js.map +1 -0
  559. package/dist/character/baseColorGuard.d.ts +62 -0
  560. package/dist/character/baseColorGuard.js +141 -0
  561. package/dist/character/baseColorGuard.js.map +1 -0
  562. package/dist/character/bodyGeometryGuard.d.ts +243 -0
  563. package/dist/character/bodyGeometryGuard.js +417 -0
  564. package/dist/character/bodyGeometryGuard.js.map +1 -0
  565. package/dist/character/bodyRegionHide.d.ts +57 -0
  566. package/dist/character/bodyRegionHide.js +97 -0
  567. package/dist/character/bodyRegionHide.js.map +1 -0
  568. package/dist/character/composeBaseMale.d.ts +25 -0
  569. package/dist/character/composeBaseMale.js +712 -0
  570. package/dist/character/composeBaseMale.js.map +1 -0
  571. package/dist/character/esm.js +3 -1
  572. package/dist/character/esm.js.map +1 -1
  573. package/dist/character/faceManifest.d.ts +53 -0
  574. package/dist/character/faceManifest.js +256 -0
  575. package/dist/character/faceManifest.js.map +1 -0
  576. package/dist/character/fbx.d.ts +61 -0
  577. package/dist/character/fbx.js +360 -0
  578. package/dist/character/fbx.js.map +1 -0
  579. package/dist/character/fitGarmentFile.d.ts +39 -0
  580. package/dist/character/fitGarmentFile.js +53 -0
  581. package/dist/character/fitGarmentFile.js.map +1 -0
  582. package/dist/character/helixPalette.d.ts +68 -0
  583. package/dist/character/helixPalette.js +183 -0
  584. package/dist/character/helixPalette.js.map +1 -0
  585. package/dist/character/hideCoverage.d.ts +135 -0
  586. package/dist/character/hideCoverage.js +450 -0
  587. package/dist/character/hideCoverage.js.map +1 -0
  588. package/dist/character/imageLimits.d.ts +3 -0
  589. package/dist/character/imageLimits.js +7 -0
  590. package/dist/character/imageLimits.js.map +1 -0
  591. package/dist/character/importCharacter.d.ts +188 -10
  592. package/dist/character/importCharacter.js +853 -76
  593. package/dist/character/importCharacter.js.map +1 -1
  594. package/dist/character/importTrust.d.ts +11 -0
  595. package/dist/character/importTrust.js +56 -0
  596. package/dist/character/importTrust.js.map +1 -0
  597. package/dist/character/ktx2Decode.d.ts +11 -0
  598. package/dist/character/ktx2Decode.js +87 -0
  599. package/dist/character/ktx2Decode.js.map +1 -0
  600. package/dist/character/mat4.d.ts +7 -0
  601. package/dist/character/mat4.js +25 -2
  602. package/dist/character/mat4.js.map +1 -1
  603. package/dist/character/meshyToHelixMap.d.ts +10 -0
  604. package/dist/character/meshyToHelixMap.js +92 -3
  605. package/dist/character/meshyToHelixMap.js.map +1 -1
  606. package/dist/character/metahumanToHelixMap.d.ts +2 -0
  607. package/dist/character/metahumanToHelixMap.js +34 -0
  608. package/dist/character/metahumanToHelixMap.js.map +1 -0
  609. package/dist/character/normalMapGuard.d.ts +59 -0
  610. package/dist/character/normalMapGuard.js +139 -0
  611. package/dist/character/normalMapGuard.js.map +1 -0
  612. package/dist/character/quat.js +2 -2
  613. package/dist/character/quat.js.map +1 -1
  614. package/dist/character/reframeSkeletonFile.d.ts +19 -0
  615. package/dist/character/reframeSkeletonFile.js +45 -0
  616. package/dist/character/reframeSkeletonFile.js.map +1 -0
  617. package/dist/character/repairLodSkeletonsFile.d.ts +25 -0
  618. package/dist/character/repairLodSkeletonsFile.js +49 -0
  619. package/dist/character/repairLodSkeletonsFile.js.map +1 -0
  620. package/dist/character/restFrameGuard.d.ts +52 -0
  621. package/dist/character/restFrameGuard.js +90 -0
  622. package/dist/character/restFrameGuard.js.map +1 -0
  623. package/dist/character/slotBudgets.d.ts +37 -0
  624. package/dist/character/slotBudgets.generated.d.ts +15 -0
  625. package/dist/character/slotBudgets.generated.js +110 -0
  626. package/dist/character/slotBudgets.generated.js.map +1 -0
  627. package/dist/character/slotBudgets.js +45 -0
  628. package/dist/character/slotBudgets.js.map +1 -0
  629. package/dist/character/stages/atlasMaterials.d.ts +53 -0
  630. package/dist/character/stages/atlasMaterials.js +246 -0
  631. package/dist/character/stages/atlasMaterials.js.map +1 -0
  632. package/dist/character/stages/bakeTexture.d.ts +28 -7
  633. package/dist/character/stages/bakeTexture.js +586 -108
  634. package/dist/character/stages/bakeTexture.js.map +1 -1
  635. package/dist/character/stages/conform.d.ts +54 -9
  636. package/dist/character/stages/conform.js +686 -113
  637. package/dist/character/stages/conform.js.map +1 -1
  638. package/dist/character/stages/earClearance.d.ts +105 -0
  639. package/dist/character/stages/earClearance.js +480 -0
  640. package/dist/character/stages/earClearance.js.map +1 -0
  641. package/dist/character/stages/faceRegion.d.ts +55 -0
  642. package/dist/character/stages/faceRegion.js +183 -0
  643. package/dist/character/stages/faceRegion.js.map +1 -0
  644. package/dist/character/stages/firstPersonHide.d.ts +42 -0
  645. package/dist/character/stages/firstPersonHide.js +140 -0
  646. package/dist/character/stages/firstPersonHide.js.map +1 -0
  647. package/dist/character/stages/joinPerMesh.d.ts +18 -0
  648. package/dist/character/stages/joinPerMesh.js +46 -0
  649. package/dist/character/stages/joinPerMesh.js.map +1 -0
  650. package/dist/character/stages/ktx2.d.ts +6 -4
  651. package/dist/character/stages/ktx2.js +81 -29
  652. package/dist/character/stages/ktx2.js.map +1 -1
  653. package/dist/character/stages/limbBridge.d.ts +114 -0
  654. package/dist/character/stages/limbBridge.js +1286 -0
  655. package/dist/character/stages/limbBridge.js.map +1 -0
  656. package/dist/character/stages/mergeLods.d.ts +19 -0
  657. package/dist/character/stages/mergeLods.js +58 -2
  658. package/dist/character/stages/mergeLods.js.map +1 -1
  659. package/dist/character/stages/poseAwareFit.d.ts +192 -0
  660. package/dist/character/stages/poseAwareFit.js +417 -0
  661. package/dist/character/stages/poseAwareFit.js.map +1 -0
  662. package/dist/character/stages/poseFit/math.d.ts +141 -0
  663. package/dist/character/stages/poseFit/math.js +514 -0
  664. package/dist/character/stages/poseFit/math.js.map +1 -0
  665. package/dist/character/stages/poseFit/measure.d.ts +88 -0
  666. package/dist/character/stages/poseFit/measure.js +148 -0
  667. package/dist/character/stages/poseFit/measure.js.map +1 -0
  668. package/dist/character/stages/poseFit/model.d.ts +46 -0
  669. package/dist/character/stages/poseFit/model.js +147 -0
  670. package/dist/character/stages/poseFit/model.js.map +1 -0
  671. package/dist/character/stages/poseFit/offsetField.d.ts +76 -0
  672. package/dist/character/stages/poseFit/offsetField.js +249 -0
  673. package/dist/character/stages/poseFit/offsetField.js.map +1 -0
  674. package/dist/character/stages/poseFit/posing.d.ts +52 -0
  675. package/dist/character/stages/poseFit/posing.js +330 -0
  676. package/dist/character/stages/poseFit/posing.js.map +1 -0
  677. package/dist/character/stages/poseFit/testRig.d.ts +31 -0
  678. package/dist/character/stages/poseFit/testRig.js +85 -0
  679. package/dist/character/stages/poseFit/testRig.js.map +1 -0
  680. package/dist/character/stages/reframeSkeletonToHost.d.ts +35 -0
  681. package/dist/character/stages/reframeSkeletonToHost.js +111 -0
  682. package/dist/character/stages/reframeSkeletonToHost.js.map +1 -0
  683. package/dist/character/stages/report.js +0 -2
  684. package/dist/character/stages/report.js.map +1 -1
  685. package/dist/character/stages/resizeTextures.js +4 -4
  686. package/dist/character/stages/resizeTextures.js.map +1 -1
  687. package/dist/character/stages/retarget.d.ts +8 -2
  688. package/dist/character/stages/retarget.js +52 -4
  689. package/dist/character/stages/retarget.js.map +1 -1
  690. package/dist/character/stages/retargetClip.d.ts +6 -2
  691. package/dist/character/stages/retargetClip.js +53 -16
  692. package/dist/character/stages/retargetClip.js.map +1 -1
  693. package/dist/character/stages/sanitizeMaterials.d.ts +39 -0
  694. package/dist/character/stages/sanitizeMaterials.js +136 -0
  695. package/dist/character/stages/sanitizeMaterials.js.map +1 -0
  696. package/dist/character/stages/simplifyMesh.js +6 -3
  697. package/dist/character/stages/simplifyMesh.js.map +1 -1
  698. package/dist/character/stages/splitFacePrimitive.js +0 -2
  699. package/dist/character/stages/splitFacePrimitive.js.map +1 -1
  700. package/dist/character/stages/splitSharedLodSkeletons.d.ts +31 -0
  701. package/dist/character/stages/splitSharedLodSkeletons.js +104 -0
  702. package/dist/character/stages/splitSharedLodSkeletons.js.map +1 -0
  703. package/dist/character/stages/unifyMaterial.d.ts +66 -0
  704. package/dist/character/stages/unifyMaterial.js +136 -0
  705. package/dist/character/stages/unifyMaterial.js.map +1 -0
  706. package/dist/character/stages/vrmDynamics.d.ts +144 -0
  707. package/dist/character/stages/vrmDynamics.js +421 -0
  708. package/dist/character/stages/vrmDynamics.js.map +1 -0
  709. package/dist/character/stages/vrmFace.d.ts +82 -0
  710. package/dist/character/stages/vrmFace.js +280 -0
  711. package/dist/character/stages/vrmFace.js.map +1 -0
  712. package/dist/character/stages/weldedSimplify.d.ts +15 -1
  713. package/dist/character/stages/weldedSimplify.js +90 -9
  714. package/dist/character/stages/weldedSimplify.js.map +1 -1
  715. package/dist/character/testutil/meshyBridgeFixture.d.ts +33 -0
  716. package/dist/character/testutil/meshyBridgeFixture.js +219 -0
  717. package/dist/character/testutil/meshyBridgeFixture.js.map +1 -0
  718. package/dist/character/testutil/meshyHead.d.ts +19 -0
  719. package/dist/character/testutil/meshyHead.js +99 -0
  720. package/dist/character/testutil/meshyHead.js.map +1 -0
  721. package/dist/character/testutil/meshyRig.d.ts +17 -0
  722. package/dist/character/testutil/meshyRig.js +117 -0
  723. package/dist/character/testutil/meshyRig.js.map +1 -0
  724. package/dist/character/testutil/slotBudgetExpectations.d.ts +11 -0
  725. package/dist/character/testutil/slotBudgetExpectations.js +21 -0
  726. package/dist/character/testutil/slotBudgetExpectations.js.map +1 -0
  727. package/dist/character/testutil/syntheticRig.d.ts +12 -1
  728. package/dist/character/testutil/syntheticRig.js +88 -3
  729. package/dist/character/testutil/syntheticRig.js.map +1 -1
  730. package/dist/character/unrealMaterialSidecar.d.ts +94 -0
  731. package/dist/character/unrealMaterialSidecar.js +879 -0
  732. package/dist/character/unrealMaterialSidecar.js.map +1 -0
  733. package/dist/character/untrustedSource.d.ts +54 -0
  734. package/dist/character/untrustedSource.js +181 -0
  735. package/dist/character/untrustedSource.js.map +1 -0
  736. package/dist/character/vrmCharacter.d.ts +156 -0
  737. package/dist/character/vrmCharacter.js +678 -0
  738. package/dist/character/vrmCharacter.js.map +1 -0
  739. package/dist/config.d.ts +5 -0
  740. package/dist/config.js +72 -16
  741. package/dist/config.js.map +1 -1
  742. package/dist/continuum/adminPackagePinCommand.d.ts +14 -0
  743. package/dist/continuum/adminPackagePinCommand.js +46 -0
  744. package/dist/continuum/adminPackagePinCommand.js.map +1 -0
  745. package/dist/continuum/adminPackagePublishCommand.d.ts +13 -0
  746. package/dist/continuum/adminPackagePublishCommand.js +22 -0
  747. package/dist/continuum/adminPackagePublishCommand.js.map +1 -0
  748. package/dist/continuum/authoring.d.ts +52 -0
  749. package/dist/continuum/authoring.js +143 -0
  750. package/dist/continuum/authoring.js.map +1 -0
  751. package/dist/continuum/characterMigrationGenerator.d.ts +17 -0
  752. package/dist/continuum/characterMigrationGenerator.js +242 -0
  753. package/dist/continuum/characterMigrationGenerator.js.map +1 -0
  754. package/dist/continuum/cli.d.ts +69 -0
  755. package/dist/continuum/cli.js +145 -0
  756. package/dist/continuum/cli.js.map +1 -0
  757. package/dist/continuum/cliAuthority.d.ts +17 -0
  758. package/dist/continuum/cliAuthority.js +28 -0
  759. package/dist/continuum/cliAuthority.js.map +1 -0
  760. package/dist/continuum/client.d.ts +103 -0
  761. package/dist/continuum/client.js +190 -0
  762. package/dist/continuum/client.js.map +1 -0
  763. package/dist/continuum/homeScenePackage.d.ts +159 -0
  764. package/dist/continuum/homeScenePackage.js +267 -0
  765. package/dist/continuum/homeScenePackage.js.map +1 -0
  766. package/dist/continuum/homeScenePackageCommand.d.ts +44 -0
  767. package/dist/continuum/homeScenePackageCommand.js +107 -0
  768. package/dist/continuum/homeScenePackageCommand.js.map +1 -0
  769. package/dist/continuum/identity.d.ts +4 -0
  770. package/dist/continuum/identity.js +20 -0
  771. package/dist/continuum/identity.js.map +1 -0
  772. package/dist/continuum/index.d.ts +11 -0
  773. package/dist/continuum/index.js +28 -0
  774. package/dist/continuum/index.js.map +1 -0
  775. package/dist/continuum/missingNormals.d.ts +48 -0
  776. package/dist/continuum/missingNormals.js +99 -0
  777. package/dist/continuum/missingNormals.js.map +1 -0
  778. package/dist/continuum/objectUpload.d.ts +64 -0
  779. package/dist/continuum/objectUpload.js +375 -0
  780. package/dist/continuum/objectUpload.js.map +1 -0
  781. package/dist/continuum/pull.d.ts +22 -0
  782. package/dist/continuum/pull.js +47 -0
  783. package/dist/continuum/pull.js.map +1 -0
  784. package/dist/continuum/sourceBundle.d.ts +18 -0
  785. package/dist/continuum/sourceBundle.js +160 -0
  786. package/dist/continuum/sourceBundle.js.map +1 -0
  787. package/dist/continuum/state.d.ts +41 -0
  788. package/dist/continuum/state.js +198 -0
  789. package/dist/continuum/state.js.map +1 -0
  790. package/dist/continuum/types.d.ts +156 -0
  791. package/dist/continuum/types.js +6 -0
  792. package/dist/continuum/types.js.map +1 -0
  793. package/dist/continuum/workflow.d.ts +62 -0
  794. package/dist/continuum/workflow.js +592 -0
  795. package/dist/continuum/workflow.js.map +1 -0
  796. package/dist/cs2/areas.d.ts +41 -0
  797. package/dist/cs2/areas.js +82 -0
  798. package/dist/cs2/areas.js.map +1 -0
  799. package/dist/cs2/binary.d.ts +10 -0
  800. package/dist/cs2/binary.js +59 -0
  801. package/dist/cs2/binary.js.map +1 -0
  802. package/dist/cs2/cityLegacy.d.ts +29 -0
  803. package/dist/cs2/cityLegacy.js +48 -0
  804. package/dist/cs2/cityLegacy.js.map +1 -0
  805. package/dist/cs2/convert/areas.d.ts +32 -0
  806. package/dist/cs2/convert/areas.js +96 -0
  807. package/dist/cs2/convert/areas.js.map +1 -0
  808. package/dist/cs2/convert/glb.d.ts +48 -0
  809. package/dist/cs2/convert/glb.js +178 -0
  810. package/dist/cs2/convert/glb.js.map +1 -0
  811. package/dist/cs2/convert/markings.d.ts +96 -0
  812. package/dist/cs2/convert/markings.js +329 -0
  813. package/dist/cs2/convert/markings.js.map +1 -0
  814. package/dist/cs2/convert/nodeSeams.d.ts +20 -0
  815. package/dist/cs2/convert/nodeSeams.js +164 -0
  816. package/dist/cs2/convert/nodeSeams.js.map +1 -0
  817. package/dist/cs2/convert/roadBake.d.ts +112 -0
  818. package/dist/cs2/convert/roadBake.js +290 -0
  819. package/dist/cs2/convert/roadBake.js.map +1 -0
  820. package/dist/cs2/convert/roads.d.ts +110 -0
  821. package/dist/cs2/convert/roads.js +419 -0
  822. package/dist/cs2/convert/roads.js.map +1 -0
  823. package/dist/cs2/convert/terrain.d.ts +67 -0
  824. package/dist/cs2/convert/terrain.js +141 -0
  825. package/dist/cs2/convert/terrain.js.map +1 -0
  826. package/dist/cs2/convert/terrainConform.d.ts +58 -0
  827. package/dist/cs2/convert/terrainConform.js +284 -0
  828. package/dist/cs2/convert/terrainConform.js.map +1 -0
  829. package/dist/cs2/convert/treeRoadClearance.d.ts +17 -0
  830. package/dist/cs2/convert/treeRoadClearance.js +78 -0
  831. package/dist/cs2/convert/treeRoadClearance.js.map +1 -0
  832. package/dist/cs2/convert/trees.d.ts +33 -0
  833. package/dist/cs2/convert/trees.js +68 -0
  834. package/dist/cs2/convert/trees.js.map +1 -0
  835. package/dist/cs2/csm2.d.ts +58 -0
  836. package/dist/cs2/csm2.js +188 -0
  837. package/dist/cs2/csm2.js.map +1 -0
  838. package/dist/cs2/fixtures.d.ts +5 -0
  839. package/dist/cs2/fixtures.js +15 -0
  840. package/dist/cs2/fixtures.js.map +1 -0
  841. package/dist/cs2/impostors.d.ts +31 -0
  842. package/dist/cs2/impostors.js +42 -0
  843. package/dist/cs2/impostors.js.map +1 -0
  844. package/dist/cs2/index.d.ts +22 -0
  845. package/dist/cs2/index.js +191 -0
  846. package/dist/cs2/index.js.map +1 -0
  847. package/dist/cs2/inspect.d.ts +81 -0
  848. package/dist/cs2/inspect.js +181 -0
  849. package/dist/cs2/inspect.js.map +1 -0
  850. package/dist/cs2/lods.d.ts +22 -0
  851. package/dist/cs2/lods.js +136 -0
  852. package/dist/cs2/lods.js.map +1 -0
  853. package/dist/cs2/materials.d.ts +43 -0
  854. package/dist/cs2/materials.js +137 -0
  855. package/dist/cs2/materials.js.map +1 -0
  856. package/dist/cs2/placements.d.ts +57 -0
  857. package/dist/cs2/placements.js +116 -0
  858. package/dist/cs2/placements.js.map +1 -0
  859. package/dist/cs2/roadPieces.d.ts +79 -0
  860. package/dist/cs2/roadPieces.js +263 -0
  861. package/dist/cs2/roadPieces.js.map +1 -0
  862. package/dist/cs2/roads.d.ts +92 -0
  863. package/dist/cs2/roads.js +329 -0
  864. package/dist/cs2/roads.js.map +1 -0
  865. package/dist/cs2/terrain.d.ts +37 -0
  866. package/dist/cs2/terrain.js +91 -0
  867. package/dist/cs2/terrain.js.map +1 -0
  868. package/dist/cs2/types.d.ts +444 -0
  869. package/dist/cs2/types.js +22 -0
  870. package/dist/cs2/types.js.map +1 -0
  871. package/dist/cs2/water.d.ts +26 -0
  872. package/dist/cs2/water.js +58 -0
  873. package/dist/cs2/water.js.map +1 -0
  874. package/dist/currency.d.ts +94 -0
  875. package/dist/currency.js +425 -0
  876. package/dist/currency.js.map +1 -0
  877. package/dist/dev/devServer.d.ts +53 -0
  878. package/dist/dev/devServer.js +228 -0
  879. package/dist/dev/devServer.js.map +1 -0
  880. package/dist/dev/shellPage.d.ts +3 -0
  881. package/dist/dev/shellPage.js +297 -0
  882. package/dist/dev/shellPage.js.map +1 -0
  883. package/dist/dev/state.d.ts +84 -0
  884. package/dist/dev/state.js +265 -0
  885. package/dist/dev/state.js.map +1 -0
  886. package/dist/device.d.ts +104 -0
  887. package/dist/device.js +2744 -0
  888. package/dist/device.js.map +1 -0
  889. package/dist/deviceActionValidation.d.ts +21 -0
  890. package/dist/deviceActionValidation.js +460 -0
  891. package/dist/deviceActionValidation.js.map +1 -0
  892. package/dist/distribution.d.ts +69 -0
  893. package/dist/distribution.js +138 -0
  894. package/dist/distribution.js.map +1 -0
  895. package/dist/enginePins.d.ts +45 -0
  896. package/dist/enginePins.js +134 -0
  897. package/dist/enginePins.js.map +1 -0
  898. package/dist/gesture/axisReference.d.ts +22 -0
  899. package/dist/gesture/axisReference.js +32 -0
  900. package/dist/gesture/axisReference.js.map +1 -0
  901. package/dist/gesture/capture.d.ts +84 -0
  902. package/dist/gesture/capture.js +303 -0
  903. package/dist/gesture/capture.js.map +1 -0
  904. package/dist/gesture/harnessPage.d.ts +23 -0
  905. package/dist/gesture/harnessPage.js +153 -0
  906. package/dist/gesture/harnessPage.js.map +1 -0
  907. package/dist/gesture/inspect.d.ts +189 -0
  908. package/dist/gesture/inspect.js +678 -0
  909. package/dist/gesture/inspect.js.map +1 -0
  910. package/dist/gesture/rig.d.ts +12 -0
  911. package/dist/gesture/rig.js +111 -0
  912. package/dist/gesture/rig.js.map +1 -0
  913. package/dist/gesture/worldRuntime.d.ts +65 -0
  914. package/dist/gesture/worldRuntime.js +110 -0
  915. package/dist/gesture/worldRuntime.js.map +1 -0
  916. package/dist/glb/draw-calls.d.ts +60 -0
  917. package/dist/glb/draw-calls.js +160 -0
  918. package/dist/glb/draw-calls.js.map +1 -0
  919. package/dist/glb/glb-summary.d.ts +68 -0
  920. package/dist/glb/glb-summary.js +450 -0
  921. package/dist/glb/glb-summary.js.map +1 -0
  922. package/dist/glb/index.d.ts +12 -0
  923. package/dist/glb/index.js +24 -0
  924. package/dist/glb/index.js.map +1 -0
  925. package/dist/glb/smooth-normals.d.ts +98 -0
  926. package/dist/glb/smooth-normals.js +395 -0
  927. package/dist/glb/smooth-normals.js.map +1 -0
  928. package/dist/index.d.ts +1 -1
  929. package/dist/index.js +6786 -128
  930. package/dist/index.js.map +1 -1
  931. package/dist/init.d.ts +35 -1
  932. package/dist/init.js +1115 -47
  933. package/dist/init.js.map +1 -1
  934. package/dist/install.d.ts +16 -5
  935. package/dist/install.js +123 -23
  936. package/dist/install.js.map +1 -1
  937. package/dist/interactable.d.ts +154 -0
  938. package/dist/interactable.js +1219 -0
  939. package/dist/interactable.js.map +1 -0
  940. package/dist/item.d.ts +217 -0
  941. package/dist/item.js +892 -0
  942. package/dist/item.js.map +1 -0
  943. package/dist/itemDef/distribute.d.ts +17 -0
  944. package/dist/itemDef/distribute.js +29 -0
  945. package/dist/itemDef/distribute.js.map +1 -0
  946. package/dist/itemDef/emoteRules.d.ts +85 -0
  947. package/dist/itemDef/emoteRules.js +368 -0
  948. package/dist/itemDef/emoteRules.js.map +1 -0
  949. package/dist/itemDef/gunConfigRules.d.ts +24 -0
  950. package/dist/itemDef/gunConfigRules.js +300 -0
  951. package/dist/itemDef/gunConfigRules.js.map +1 -0
  952. package/dist/itemDef/inspect.d.ts +19 -0
  953. package/dist/itemDef/inspect.js +144 -0
  954. package/dist/itemDef/inspect.js.map +1 -0
  955. package/dist/itemDef/scaffold.d.ts +41 -0
  956. package/dist/itemDef/scaffold.js +303 -0
  957. package/dist/itemDef/scaffold.js.map +1 -0
  958. package/dist/itemDef/tables.d.ts +81 -0
  959. package/dist/itemDef/tables.js +226 -0
  960. package/dist/itemDef/tables.js.map +1 -0
  961. package/dist/itemDef/validate.d.ts +30 -0
  962. package/dist/itemDef/validate.js +175 -0
  963. package/dist/itemDef/validate.js.map +1 -0
  964. package/dist/itemPublishVersion.d.ts +46 -0
  965. package/dist/itemPublishVersion.js +242 -0
  966. package/dist/itemPublishVersion.js.map +1 -0
  967. package/dist/itemSearch.d.ts +128 -0
  968. package/dist/itemSearch.js +140 -0
  969. package/dist/itemSearch.js.map +1 -0
  970. package/dist/itemSetVersion.d.ts +42 -0
  971. package/dist/itemSetVersion.js +94 -0
  972. package/dist/itemSetVersion.js.map +1 -0
  973. package/dist/itemThumbnail.d.ts +40 -0
  974. package/dist/itemThumbnail.js +116 -0
  975. package/dist/itemThumbnail.js.map +1 -0
  976. package/dist/lib.d.ts +571 -7
  977. package/dist/lib.js +1178 -15
  978. package/dist/lib.js.map +1 -1
  979. package/dist/looks.d.ts +103 -0
  980. package/dist/looks.js +102 -0
  981. package/dist/looks.js.map +1 -0
  982. package/dist/materials/availability.d.ts +8 -0
  983. package/dist/materials/availability.js +22 -0
  984. package/dist/materials/availability.js.map +1 -0
  985. package/dist/materials/identity.d.ts +30 -0
  986. package/dist/materials/identity.js +68 -0
  987. package/dist/materials/identity.js.map +1 -0
  988. package/dist/materials/pins.d.ts +23 -0
  989. package/dist/materials/pins.js +93 -0
  990. package/dist/materials/pins.js.map +1 -0
  991. package/dist/mesh/surfaceBvh.d.ts +13 -0
  992. package/dist/mesh/surfaceBvh.js +197 -0
  993. package/dist/mesh/surfaceBvh.js.map +1 -0
  994. package/dist/module-validation.d.ts +2 -0
  995. package/dist/module-validation.js +24 -0
  996. package/dist/module-validation.js.map +1 -0
  997. package/dist/money.d.ts +183 -0
  998. package/dist/money.js +299 -0
  999. package/dist/money.js.map +1 -0
  1000. package/dist/npc/capture.d.ts +41 -0
  1001. package/dist/npc/capture.js +99 -0
  1002. package/dist/npc/capture.js.map +1 -0
  1003. package/dist/npc/format.d.ts +2 -0
  1004. package/dist/npc/format.js +102 -0
  1005. package/dist/npc/format.js.map +1 -0
  1006. package/dist/npc/inspect.d.ts +23 -0
  1007. package/dist/npc/inspect.js +54 -0
  1008. package/dist/npc/inspect.js.map +1 -0
  1009. package/dist/npc/metrics.d.ts +132 -0
  1010. package/dist/npc/metrics.js +214 -0
  1011. package/dist/npc/metrics.js.map +1 -0
  1012. package/dist/npc/session.d.ts +35 -0
  1013. package/dist/npc/session.js +172 -0
  1014. package/dist/npc/session.js.map +1 -0
  1015. package/dist/pack/locomotionReference.d.ts +40 -0
  1016. package/dist/pack/locomotionReference.js +132 -0
  1017. package/dist/pack/locomotionReference.js.map +1 -0
  1018. package/dist/pack/scaffold.d.ts +15 -0
  1019. package/dist/pack/scaffold.js +144 -0
  1020. package/dist/pack/scaffold.js.map +1 -0
  1021. package/dist/pack/validate.d.ts +67 -0
  1022. package/dist/pack/validate.js +317 -0
  1023. package/dist/pack/validate.js.map +1 -0
  1024. package/dist/package.d.ts +14 -1
  1025. package/dist/package.js +106 -7
  1026. package/dist/package.js.map +1 -1
  1027. package/dist/paletteShowcase.d.ts +183 -0
  1028. package/dist/paletteShowcase.js +454 -0
  1029. package/dist/paletteShowcase.js.map +1 -0
  1030. package/dist/paletteShowcaseProfiles.d.ts +112 -0
  1031. package/dist/paletteShowcaseProfiles.js +121 -0
  1032. package/dist/paletteShowcaseProfiles.js.map +1 -0
  1033. package/dist/paths.d.ts +4 -0
  1034. package/dist/paths.js +11 -0
  1035. package/dist/paths.js.map +1 -0
  1036. package/dist/placementRequest.d.ts +37 -0
  1037. package/dist/placementRequest.js +220 -0
  1038. package/dist/placementRequest.js.map +1 -0
  1039. package/dist/placements/assets.d.ts +7 -0
  1040. package/dist/placements/assets.js +114 -0
  1041. package/dist/placements/assets.js.map +1 -0
  1042. package/dist/placements/attachFoliageLods.d.ts +4 -0
  1043. package/dist/placements/attachFoliageLods.js +70 -0
  1044. package/dist/placements/attachFoliageLods.js.map +1 -0
  1045. package/dist/placements/attachImpostors.d.ts +3 -0
  1046. package/dist/placements/attachImpostors.js +51 -0
  1047. package/dist/placements/attachImpostors.js.map +1 -0
  1048. package/dist/placements/authored.d.ts +11 -0
  1049. package/dist/placements/authored.js +65 -0
  1050. package/dist/placements/authored.js.map +1 -0
  1051. package/dist/placements/bake.d.ts +2 -0
  1052. package/dist/placements/bake.js +235 -0
  1053. package/dist/placements/bake.js.map +1 -0
  1054. package/dist/placements/captureHost.d.ts +4 -0
  1055. package/dist/placements/captureHost.js +125 -0
  1056. package/dist/placements/captureHost.js.map +1 -0
  1057. package/dist/placements/foliageCapture.d.ts +2 -0
  1058. package/dist/placements/foliageCapture.js +98 -0
  1059. package/dist/placements/foliageCapture.js.map +1 -0
  1060. package/dist/placements/foliageCards.d.ts +17 -0
  1061. package/dist/placements/foliageCards.js +120 -0
  1062. package/dist/placements/foliageCards.js.map +1 -0
  1063. package/dist/placements/foliageLods.d.ts +8 -0
  1064. package/dist/placements/foliageLods.js +240 -0
  1065. package/dist/placements/foliageLods.js.map +1 -0
  1066. package/dist/placements/foliageTexture.d.ts +4 -0
  1067. package/dist/placements/foliageTexture.js +91 -0
  1068. package/dist/placements/foliageTexture.js.map +1 -0
  1069. package/dist/placements/impostorCapture.d.ts +2 -0
  1070. package/dist/placements/impostorCapture.js +111 -0
  1071. package/dist/placements/impostorCapture.js.map +1 -0
  1072. package/dist/placements/impostors.d.ts +18 -0
  1073. package/dist/placements/impostors.js +279 -0
  1074. package/dist/placements/impostors.js.map +1 -0
  1075. package/dist/placements/source.d.ts +53 -0
  1076. package/dist/placements/source.js +111 -0
  1077. package/dist/placements/source.js.map +1 -0
  1078. package/dist/product.d.ts +84 -0
  1079. package/dist/product.js +285 -0
  1080. package/dist/product.js.map +1 -0
  1081. package/dist/publications.d.ts +116 -0
  1082. package/dist/publications.js +180 -0
  1083. package/dist/publications.js.map +1 -0
  1084. package/dist/publishDuplicateGuard.d.ts +92 -0
  1085. package/dist/publishDuplicateGuard.js +128 -0
  1086. package/dist/publishDuplicateGuard.js.map +1 -0
  1087. package/dist/realizability.d.ts +36 -0
  1088. package/dist/realizability.js +269 -0
  1089. package/dist/realizability.js.map +1 -0
  1090. package/dist/resolve.d.ts +7 -1
  1091. package/dist/resolve.js +77 -11
  1092. package/dist/resolve.js.map +1 -1
  1093. package/dist/sceneElements/compiler.d.ts +21 -0
  1094. package/dist/sceneElements/compiler.js +976 -0
  1095. package/dist/sceneElements/compiler.js.map +1 -0
  1096. package/dist/sceneElements/continuum.d.ts +54 -0
  1097. package/dist/sceneElements/continuum.js +451 -0
  1098. package/dist/sceneElements/continuum.js.map +1 -0
  1099. package/dist/sceneElements/devices.d.ts +12 -0
  1100. package/dist/sceneElements/devices.js +212 -0
  1101. package/dist/sceneElements/devices.js.map +1 -0
  1102. package/dist/sceneElements/meshCells.d.ts +49 -0
  1103. package/dist/sceneElements/meshCells.js +174 -0
  1104. package/dist/sceneElements/meshCells.js.map +1 -0
  1105. package/dist/sceneElements/types.d.ts +261 -0
  1106. package/dist/sceneElements/types.js +3 -0
  1107. package/dist/sceneElements/types.js.map +1 -0
  1108. package/dist/sceneHlod/approximate.d.ts +23 -0
  1109. package/dist/sceneHlod/approximate.js +171 -0
  1110. package/dist/sceneHlod/approximate.js.map +1 -0
  1111. package/dist/sceneHlod/artifact.d.ts +3 -0
  1112. package/dist/sceneHlod/artifact.js +29 -0
  1113. package/dist/sceneHlod/artifact.js.map +1 -0
  1114. package/dist/sceneHlod/bakeSurface.d.ts +2 -0
  1115. package/dist/sceneHlod/bakeSurface.js +59 -0
  1116. package/dist/sceneHlod/bakeSurface.js.map +1 -0
  1117. package/dist/sceneHlod/basisEncoder.d.ts +3 -0
  1118. package/dist/sceneHlod/basisEncoder.js +68 -0
  1119. package/dist/sceneHlod/basisEncoder.js.map +1 -0
  1120. package/dist/sceneHlod/cellArtifact.d.ts +17 -0
  1121. package/dist/sceneHlod/cellArtifact.js +78 -0
  1122. package/dist/sceneHlod/cellArtifact.js.map +1 -0
  1123. package/dist/sceneHlod/commands.d.ts +2 -0
  1124. package/dist/sceneHlod/commands.js +114 -0
  1125. package/dist/sceneHlod/commands.js.map +1 -0
  1126. package/dist/sceneHlod/complete.d.ts +78 -0
  1127. package/dist/sceneHlod/complete.js +261 -0
  1128. package/dist/sceneHlod/complete.js.map +1 -0
  1129. package/dist/sceneHlod/completePrototype.d.ts +39 -0
  1130. package/dist/sceneHlod/completePrototype.js +121 -0
  1131. package/dist/sceneHlod/completePrototype.js.map +1 -0
  1132. package/dist/sceneHlod/geometry.d.ts +6 -0
  1133. package/dist/sceneHlod/geometry.js +101 -0
  1134. package/dist/sceneHlod/geometry.js.map +1 -0
  1135. package/dist/sceneHlod/imageSampling.d.ts +11 -0
  1136. package/dist/sceneHlod/imageSampling.js +100 -0
  1137. package/dist/sceneHlod/imageSampling.js.map +1 -0
  1138. package/dist/sceneHlod/refinement.d.ts +46 -0
  1139. package/dist/sceneHlod/refinement.js +204 -0
  1140. package/dist/sceneHlod/refinement.js.map +1 -0
  1141. package/dist/sceneHlod/scene.d.ts +80 -0
  1142. package/dist/sceneHlod/scene.js +264 -0
  1143. package/dist/sceneHlod/scene.js.map +1 -0
  1144. package/dist/sceneHlod/sharedAtlas.d.ts +37 -0
  1145. package/dist/sceneHlod/sharedAtlas.js +84 -0
  1146. package/dist/sceneHlod/sharedAtlas.js.map +1 -0
  1147. package/dist/sceneHlod/source.d.ts +17 -0
  1148. package/dist/sceneHlod/source.js +241 -0
  1149. package/dist/sceneHlod/source.js.map +1 -0
  1150. package/dist/sceneHlod/textureCodec.d.ts +4 -0
  1151. package/dist/sceneHlod/textureCodec.js +66 -0
  1152. package/dist/sceneHlod/textureCodec.js.map +1 -0
  1153. package/dist/sceneHlod/transfer.d.ts +8 -0
  1154. package/dist/sceneHlod/transfer.js +147 -0
  1155. package/dist/sceneHlod/transfer.js.map +1 -0
  1156. package/dist/sceneHlod/types.d.ts +69 -0
  1157. package/dist/sceneHlod/types.js +3 -0
  1158. package/dist/sceneHlod/types.js.map +1 -0
  1159. package/dist/sceneImport/archive.d.ts +30 -0
  1160. package/dist/sceneImport/archive.js +336 -0
  1161. package/dist/sceneImport/archive.js.map +1 -0
  1162. package/dist/sceneImport/bistroMaterialBake.d.ts +20 -0
  1163. package/dist/sceneImport/bistroMaterialBake.js +169 -0
  1164. package/dist/sceneImport/bistroMaterialBake.js.map +1 -0
  1165. package/dist/sceneImport/bistroTexturePlan.d.ts +6 -0
  1166. package/dist/sceneImport/bistroTexturePlan.js +23 -0
  1167. package/dist/sceneImport/bistroTexturePlan.js.map +1 -0
  1168. package/dist/sceneImport/compiler.d.ts +36 -0
  1169. package/dist/sceneImport/compiler.js +251 -0
  1170. package/dist/sceneImport/compiler.js.map +1 -0
  1171. package/dist/sceneImport/composeMounts.d.ts +159 -0
  1172. package/dist/sceneImport/composeMounts.js +687 -0
  1173. package/dist/sceneImport/composeMounts.js.map +1 -0
  1174. package/dist/sceneImport/defaultMaterials.d.ts +45 -0
  1175. package/dist/sceneImport/defaultMaterials.js +121 -0
  1176. package/dist/sceneImport/defaultMaterials.js.map +1 -0
  1177. package/dist/sceneImport/externalTextures.d.ts +44 -0
  1178. package/dist/sceneImport/externalTextures.js +146 -0
  1179. package/dist/sceneImport/externalTextures.js.map +1 -0
  1180. package/dist/sceneImport/fbx.d.ts +101 -0
  1181. package/dist/sceneImport/fbx.js +307 -0
  1182. package/dist/sceneImport/fbx.js.map +1 -0
  1183. package/dist/sceneImport/glbSplit.d.ts +24 -0
  1184. package/dist/sceneImport/glbSplit.js +95 -0
  1185. package/dist/sceneImport/glbSplit.js.map +1 -0
  1186. package/dist/sceneImport/gltfIo.d.ts +32 -0
  1187. package/dist/sceneImport/gltfIo.js +39 -0
  1188. package/dist/sceneImport/gltfIo.js.map +1 -0
  1189. package/dist/sceneImport/helsinkiTiles.d.ts +31 -0
  1190. package/dist/sceneImport/helsinkiTiles.js +52 -0
  1191. package/dist/sceneImport/helsinkiTiles.js.map +1 -0
  1192. package/dist/sceneImport/materials/binding.d.ts +16 -0
  1193. package/dist/sceneImport/materials/binding.js +42 -0
  1194. package/dist/sceneImport/materials/binding.js.map +1 -0
  1195. package/dist/sceneImport/materials/catalog.d.ts +36 -0
  1196. package/dist/sceneImport/materials/catalog.js +233 -0
  1197. package/dist/sceneImport/materials/catalog.js.map +1 -0
  1198. package/dist/sceneImport/materials/index.d.ts +4 -0
  1199. package/dist/sceneImport/materials/index.js +21 -0
  1200. package/dist/sceneImport/materials/index.js.map +1 -0
  1201. package/dist/sceneImport/materials/matcher.d.ts +33 -0
  1202. package/dist/sceneImport/materials/matcher.js +127 -0
  1203. package/dist/sceneImport/materials/matcher.js.map +1 -0
  1204. package/dist/sceneImport/materials/recipe.d.ts +30 -0
  1205. package/dist/sceneImport/materials/recipe.js +134 -0
  1206. package/dist/sceneImport/materials/recipe.js.map +1 -0
  1207. package/dist/sceneImport/multiAsset.d.ts +79 -0
  1208. package/dist/sceneImport/multiAsset.js +535 -0
  1209. package/dist/sceneImport/multiAsset.js.map +1 -0
  1210. package/dist/sceneImport/multiAssetSpawn.d.ts +40 -0
  1211. package/dist/sceneImport/multiAssetSpawn.js +98 -0
  1212. package/dist/sceneImport/multiAssetSpawn.js.map +1 -0
  1213. package/dist/sceneImport/obj.d.ts +41 -0
  1214. package/dist/sceneImport/obj.js +276 -0
  1215. package/dist/sceneImport/obj.js.map +1 -0
  1216. package/dist/sceneImport/package.d.ts +104 -0
  1217. package/dist/sceneImport/package.js +1004 -0
  1218. package/dist/sceneImport/package.js.map +1 -0
  1219. package/dist/sceneImport/packageReader.d.ts +10 -0
  1220. package/dist/sceneImport/packageReader.js +250 -0
  1221. package/dist/sceneImport/packageReader.js.map +1 -0
  1222. package/dist/sceneImport/proxy.d.ts +100 -0
  1223. package/dist/sceneImport/proxy.js +334 -0
  1224. package/dist/sceneImport/proxy.js.map +1 -0
  1225. package/dist/sceneImport/publish.d.ts +47 -0
  1226. package/dist/sceneImport/publish.js +98 -0
  1227. package/dist/sceneImport/publish.js.map +1 -0
  1228. package/dist/sceneImport/semantic.d.ts +75 -0
  1229. package/dist/sceneImport/semantic.js +296 -0
  1230. package/dist/sceneImport/semantic.js.map +1 -0
  1231. package/dist/sceneImport/spawnProbe.d.ts +55 -0
  1232. package/dist/sceneImport/spawnProbe.js +159 -0
  1233. package/dist/sceneImport/spawnProbe.js.map +1 -0
  1234. package/dist/sceneImport/streamLayout.d.ts +96 -0
  1235. package/dist/sceneImport/streamLayout.js +203 -0
  1236. package/dist/sceneImport/streamLayout.js.map +1 -0
  1237. package/dist/sceneImport/textureDecode.d.ts +21 -0
  1238. package/dist/sceneImport/textureDecode.js +292 -0
  1239. package/dist/sceneImport/textureDecode.js.map +1 -0
  1240. package/dist/sceneImport/types.d.ts +84 -0
  1241. package/dist/sceneImport/types.js +58 -0
  1242. package/dist/sceneImport/types.js.map +1 -0
  1243. package/dist/scenePerformance.d.ts +162 -0
  1244. package/dist/scenePerformance.js +905 -0
  1245. package/dist/scenePerformance.js.map +1 -0
  1246. package/dist/screenshot/captureScreenshots.d.ts +36 -0
  1247. package/dist/screenshot/captureScreenshots.js +229 -0
  1248. package/dist/screenshot/captureScreenshots.js.map +1 -0
  1249. package/dist/screenshot/encodeImagePreview.d.ts +5 -0
  1250. package/dist/screenshot/encodeImagePreview.js +42 -0
  1251. package/dist/screenshot/encodeImagePreview.js.map +1 -0
  1252. package/dist/screenshot/publishThumbnail.d.ts +4 -0
  1253. package/dist/screenshot/publishThumbnail.js +56 -0
  1254. package/dist/screenshot/publishThumbnail.js.map +1 -0
  1255. package/dist/screenshot/staticServe.d.ts +14 -0
  1256. package/dist/screenshot/staticServe.js +120 -0
  1257. package/dist/screenshot/staticServe.js.map +1 -0
  1258. package/dist/source-archive.d.ts +101 -0
  1259. package/dist/source-archive.js +309 -0
  1260. package/dist/source-archive.js.map +1 -0
  1261. package/dist/stdioBlocking.d.ts +1 -0
  1262. package/dist/stdioBlocking.js +9 -0
  1263. package/dist/stdioBlocking.js.map +1 -0
  1264. package/dist/terrain/bake.d.ts +40 -0
  1265. package/dist/terrain/bake.js +318 -0
  1266. package/dist/terrain/bake.js.map +1 -0
  1267. package/dist/terrain/generate.d.ts +21 -0
  1268. package/dist/terrain/generate.js +239 -0
  1269. package/dist/terrain/generate.js.map +1 -0
  1270. package/dist/terrain/grid.d.ts +48 -0
  1271. package/dist/terrain/grid.js +133 -0
  1272. package/dist/terrain/grid.js.map +1 -0
  1273. package/dist/terrain/import.d.ts +40 -0
  1274. package/dist/terrain/import.js +252 -0
  1275. package/dist/terrain/import.js.map +1 -0
  1276. package/dist/terrain/index.d.ts +11 -0
  1277. package/dist/terrain/index.js +88 -0
  1278. package/dist/terrain/index.js.map +1 -0
  1279. package/dist/terrain/inspect.d.ts +35 -0
  1280. package/dist/terrain/inspect.js +115 -0
  1281. package/dist/terrain/inspect.js.map +1 -0
  1282. package/dist/terrain/island.d.ts +31 -0
  1283. package/dist/terrain/island.js +128 -0
  1284. package/dist/terrain/island.js.map +1 -0
  1285. package/dist/terrain/noise.d.ts +79 -0
  1286. package/dist/terrain/noise.js +162 -0
  1287. package/dist/terrain/noise.js.map +1 -0
  1288. package/dist/terrain/normals.d.ts +21 -0
  1289. package/dist/terrain/normals.js +55 -0
  1290. package/dist/terrain/normals.js.map +1 -0
  1291. package/dist/terrain/presets/alpine.json +20 -0
  1292. package/dist/terrain/presets/coast.json +20 -0
  1293. package/dist/terrain/presets/desert.json +22 -0
  1294. package/dist/terrain/presets/forest.json +20 -0
  1295. package/dist/terrain/presets/meadow.json +20 -0
  1296. package/dist/terrain/presets/volcanic.json +22 -0
  1297. package/dist/terrain/presets.d.ts +29 -0
  1298. package/dist/terrain/presets.js +128 -0
  1299. package/dist/terrain/presets.js.map +1 -0
  1300. package/dist/terrain/source.d.ts +42 -0
  1301. package/dist/terrain/source.js +241 -0
  1302. package/dist/terrain/source.js.map +1 -0
  1303. package/dist/terrain/tile.d.ts +92 -0
  1304. package/dist/terrain/tile.js +268 -0
  1305. package/dist/terrain/tile.js.map +1 -0
  1306. package/dist/textureAudit.d.ts +23 -0
  1307. package/dist/textureAudit.js +192 -0
  1308. package/dist/textureAudit.js.map +1 -0
  1309. package/dist/tsconfig.build.tsbuildinfo +1 -1
  1310. package/dist/unreal/avatarMode.d.ts +21 -0
  1311. package/dist/unreal/avatarMode.js +33 -0
  1312. package/dist/unreal/avatarMode.js.map +1 -0
  1313. package/dist/unreal/delivery.d.ts +46 -0
  1314. package/dist/unreal/delivery.js +89 -0
  1315. package/dist/unreal/delivery.js.map +1 -0
  1316. package/dist/unreal/doctor.d.ts +50 -0
  1317. package/dist/unreal/doctor.js +102 -0
  1318. package/dist/unreal/doctor.js.map +1 -0
  1319. package/dist/unreal/package.d.ts +80 -0
  1320. package/dist/unreal/package.js +273 -0
  1321. package/dist/unreal/package.js.map +1 -0
  1322. package/dist/unreal/release.d.ts +41 -0
  1323. package/dist/unreal/release.js +122 -0
  1324. package/dist/unreal/release.js.map +1 -0
  1325. package/dist/unreal.d.ts +30 -0
  1326. package/dist/unreal.js +362 -0
  1327. package/dist/unreal.js.map +1 -0
  1328. package/dist/unrealCandidate.d.ts +213 -0
  1329. package/dist/unrealCandidate.js +765 -0
  1330. package/dist/unrealCandidate.js.map +1 -0
  1331. package/dist/unrealManifest.d.ts +74 -0
  1332. package/dist/unrealManifest.js +354 -0
  1333. package/dist/unrealManifest.js.map +1 -0
  1334. package/dist/unrealPublish.d.ts +111 -0
  1335. package/dist/unrealPublish.js +331 -0
  1336. package/dist/unrealPublish.js.map +1 -0
  1337. package/dist/vault.d.ts +121 -0
  1338. package/dist/vault.js +136 -0
  1339. package/dist/vault.js.map +1 -0
  1340. package/dist/vehicle/addOnDefinition.d.ts +19 -0
  1341. package/dist/vehicle/addOnDefinition.js +21 -0
  1342. package/dist/vehicle/addOnDefinition.js.map +1 -0
  1343. package/dist/vehicle/addon.d.ts +158 -0
  1344. package/dist/vehicle/addon.js +441 -0
  1345. package/dist/vehicle/addon.js.map +1 -0
  1346. package/dist/vehicle/addonDefinitionGuard.d.ts +53 -0
  1347. package/dist/vehicle/addonDefinitionGuard.js +206 -0
  1348. package/dist/vehicle/addonDefinitionGuard.js.map +1 -0
  1349. package/dist/vehicle/addonPublished.d.ts +80 -0
  1350. package/dist/vehicle/addonPublished.js +221 -0
  1351. package/dist/vehicle/addonPublished.js.map +1 -0
  1352. package/dist/vehicle/audio.d.ts +259 -0
  1353. package/dist/vehicle/audio.js +1069 -0
  1354. package/dist/vehicle/audio.js.map +1 -0
  1355. package/dist/vehicle/audioDefects.d.ts +125 -0
  1356. package/dist/vehicle/audioDefects.js +591 -0
  1357. package/dist/vehicle/audioDefects.js.map +1 -0
  1358. package/dist/vehicle/audioDistinct.d.ts +62 -0
  1359. package/dist/vehicle/audioDistinct.js +479 -0
  1360. package/dist/vehicle/audioDistinct.js.map +1 -0
  1361. package/dist/vehicle/audioImport.d.ts +217 -0
  1362. package/dist/vehicle/audioImport.js +610 -0
  1363. package/dist/vehicle/audioImport.js.map +1 -0
  1364. package/dist/vehicle/audioProvenance.d.ts +47 -0
  1365. package/dist/vehicle/audioProvenance.js +364 -0
  1366. package/dist/vehicle/audioProvenance.js.map +1 -0
  1367. package/dist/vehicle/audioReference.d.ts +36 -0
  1368. package/dist/vehicle/audioReference.js +176 -0
  1369. package/dist/vehicle/audioReference.js.map +1 -0
  1370. package/dist/vehicle/audioSlots.d.ts +27 -0
  1371. package/dist/vehicle/audioSlots.js +43 -0
  1372. package/dist/vehicle/audioSlots.js.map +1 -0
  1373. package/dist/vehicle/audioSpectral.d.ts +71 -0
  1374. package/dist/vehicle/audioSpectral.js +534 -0
  1375. package/dist/vehicle/audioSpectral.js.map +1 -0
  1376. package/dist/vehicle/cabinCheck.d.ts +209 -0
  1377. package/dist/vehicle/cabinCheck.js +799 -0
  1378. package/dist/vehicle/cabinCheck.js.map +1 -0
  1379. package/dist/vehicle/drive.d.ts +66 -0
  1380. package/dist/vehicle/drive.js +228 -0
  1381. package/dist/vehicle/drive.js.map +1 -0
  1382. package/dist/vehicle/enginePolicy.d.ts +68 -0
  1383. package/dist/vehicle/enginePolicy.js +209 -0
  1384. package/dist/vehicle/enginePolicy.js.map +1 -0
  1385. package/dist/vehicle/hostFrame.d.ts +78 -0
  1386. package/dist/vehicle/hostFrame.js +136 -0
  1387. package/dist/vehicle/hostFrame.js.map +1 -0
  1388. package/dist/vehicle/inspectionSheets.d.ts +42 -0
  1389. package/dist/vehicle/inspectionSheets.js +173 -0
  1390. package/dist/vehicle/inspectionSheets.js.map +1 -0
  1391. package/dist/vehicle/keyboardLaunch.d.ts +189 -0
  1392. package/dist/vehicle/keyboardLaunch.js +340 -0
  1393. package/dist/vehicle/keyboardLaunch.js.map +1 -0
  1394. package/dist/vehicle/lampCoverage.d.ts +49 -0
  1395. package/dist/vehicle/lampCoverage.js +252 -0
  1396. package/dist/vehicle/lampCoverage.js.map +1 -0
  1397. package/dist/vehicle/mintAddOn.d.ts +110 -0
  1398. package/dist/vehicle/mintAddOn.js +321 -0
  1399. package/dist/vehicle/mintAddOn.js.map +1 -0
  1400. package/dist/vehicle/mintVehicle.d.ts +189 -0
  1401. package/dist/vehicle/mintVehicle.js +417 -0
  1402. package/dist/vehicle/mintVehicle.js.map +1 -0
  1403. package/dist/vehicle/paintHue.d.ts +89 -0
  1404. package/dist/vehicle/paintHue.js +248 -0
  1405. package/dist/vehicle/paintHue.js.map +1 -0
  1406. package/dist/vehicle/panelCoverage.d.ts +37 -0
  1407. package/dist/vehicle/panelCoverage.js +93 -0
  1408. package/dist/vehicle/panelCoverage.js.map +1 -0
  1409. package/dist/vehicle/platform.d.ts +71 -0
  1410. package/dist/vehicle/platform.js +144 -0
  1411. package/dist/vehicle/platform.js.map +1 -0
  1412. package/dist/vehicle/preview.d.ts +339 -0
  1413. package/dist/vehicle/preview.js +854 -0
  1414. package/dist/vehicle/preview.js.map +1 -0
  1415. package/dist/vehicle/previewAddon.d.ts +197 -0
  1416. package/dist/vehicle/previewAddon.js +520 -0
  1417. package/dist/vehicle/previewAddon.js.map +1 -0
  1418. package/dist/vehicle/previewSeats.d.ts +102 -0
  1419. package/dist/vehicle/previewSeats.js +190 -0
  1420. package/dist/vehicle/previewSeats.js.map +1 -0
  1421. package/dist/vehicle/publish.d.ts +78 -0
  1422. package/dist/vehicle/publish.js +111 -0
  1423. package/dist/vehicle/publish.js.map +1 -0
  1424. package/dist/vehicle/recheckListed.d.ts +87 -0
  1425. package/dist/vehicle/recheckListed.js +200 -0
  1426. package/dist/vehicle/recheckListed.js.map +1 -0
  1427. package/dist/vehicle/renditionManifest.d.ts +10 -0
  1428. package/dist/vehicle/renditionManifest.js +56 -0
  1429. package/dist/vehicle/renditionManifest.js.map +1 -0
  1430. package/dist/vehicle/sealCheck.d.ts +30 -0
  1431. package/dist/vehicle/sealCheck.js +59 -0
  1432. package/dist/vehicle/sealCheck.js.map +1 -0
  1433. package/dist/vehicle/simulate.d.ts +219 -0
  1434. package/dist/vehicle/simulate.js +536 -0
  1435. package/dist/vehicle/simulate.js.map +1 -0
  1436. package/dist/vehicle/simulateRuntime.d.ts +76 -0
  1437. package/dist/vehicle/simulateRuntime.js +252 -0
  1438. package/dist/vehicle/simulateRuntime.js.map +1 -0
  1439. package/dist/vehicle/testutil/sourcedAudio.d.ts +13 -0
  1440. package/dist/vehicle/testutil/sourcedAudio.js +96 -0
  1441. package/dist/vehicle/testutil/sourcedAudio.js.map +1 -0
  1442. package/dist/vehicle/validate.d.ts +225 -0
  1443. package/dist/vehicle/validate.js +956 -0
  1444. package/dist/vehicle/validate.js.map +1 -0
  1445. package/dist/vehicle/wheelDesign.d.ts +98 -0
  1446. package/dist/vehicle/wheelDesign.js +276 -0
  1447. package/dist/vehicle/wheelDesign.js.map +1 -0
  1448. package/dist/verifySubPath.d.ts +69 -0
  1449. package/dist/verifySubPath.js +458 -0
  1450. package/dist/verifySubPath.js.map +1 -0
  1451. package/dist/visualQa/animation.d.ts +57 -0
  1452. package/dist/visualQa/animation.js +503 -0
  1453. package/dist/visualQa/animation.js.map +1 -0
  1454. package/dist/visualQa/avatar.d.ts +3 -0
  1455. package/dist/visualQa/avatar.js +443 -0
  1456. package/dist/visualQa/avatar.js.map +1 -0
  1457. package/dist/visualQa/avatarMath.d.ts +180 -0
  1458. package/dist/visualQa/avatarMath.js +318 -0
  1459. package/dist/visualQa/avatarMath.js.map +1 -0
  1460. package/dist/visualQa/avatarPreview.d.ts +26 -0
  1461. package/dist/visualQa/avatarPreview.js +167 -0
  1462. package/dist/visualQa/avatarPreview.js.map +1 -0
  1463. package/dist/visualQa/avatarPreviewMath.d.ts +189 -0
  1464. package/dist/visualQa/avatarPreviewMath.js +696 -0
  1465. package/dist/visualQa/avatarPreviewMath.js.map +1 -0
  1466. package/dist/visualQa/avatarPreviewProbe.d.ts +7 -0
  1467. package/dist/visualQa/avatarPreviewProbe.js +181 -0
  1468. package/dist/visualQa/avatarPreviewProbe.js.map +1 -0
  1469. package/dist/visualQa/avatarProbe.d.ts +1 -0
  1470. package/dist/visualQa/avatarProbe.js +264 -0
  1471. package/dist/visualQa/avatarProbe.js.map +1 -0
  1472. package/dist/visualQa/browser.d.ts +116 -0
  1473. package/dist/visualQa/browser.js +817 -0
  1474. package/dist/visualQa/browser.js.map +1 -0
  1475. package/dist/visualQa/candidate.d.ts +116 -0
  1476. package/dist/visualQa/candidate.js +265 -0
  1477. package/dist/visualQa/candidate.js.map +1 -0
  1478. package/dist/visualQa/checklists.d.ts +31 -0
  1479. package/dist/visualQa/checklists.js +160 -0
  1480. package/dist/visualQa/checklists.js.map +1 -0
  1481. package/dist/visualQa/command.d.ts +110 -0
  1482. package/dist/visualQa/command.js +521 -0
  1483. package/dist/visualQa/command.js.map +1 -0
  1484. package/dist/visualQa/commonAsserts.d.ts +68 -0
  1485. package/dist/visualQa/commonAsserts.js +239 -0
  1486. package/dist/visualQa/commonAsserts.js.map +1 -0
  1487. package/dist/visualQa/contactSheet.d.ts +32 -0
  1488. package/dist/visualQa/contactSheet.js +153 -0
  1489. package/dist/visualQa/contactSheet.js.map +1 -0
  1490. package/dist/visualQa/critic.d.ts +18 -0
  1491. package/dist/visualQa/critic.js +179 -0
  1492. package/dist/visualQa/critic.js.map +1 -0
  1493. package/dist/visualQa/emotePage.d.ts +10 -0
  1494. package/dist/visualQa/emotePage.js +249 -0
  1495. package/dist/visualQa/emotePage.js.map +1 -0
  1496. package/dist/visualQa/evidenceOverlay.d.ts +74 -0
  1497. package/dist/visualQa/evidenceOverlay.js +268 -0
  1498. package/dist/visualQa/evidenceOverlay.js.map +1 -0
  1499. package/dist/visualQa/harness.d.ts +57 -0
  1500. package/dist/visualQa/harness.js +3 -0
  1501. package/dist/visualQa/harness.js.map +1 -0
  1502. package/dist/visualQa/hosted.d.ts +152 -0
  1503. package/dist/visualQa/hosted.js +367 -0
  1504. package/dist/visualQa/hosted.js.map +1 -0
  1505. package/dist/visualQa/labelLimit.d.ts +12 -0
  1506. package/dist/visualQa/labelLimit.js +31 -0
  1507. package/dist/visualQa/labelLimit.js.map +1 -0
  1508. package/dist/visualQa/mirror.d.ts +36 -0
  1509. package/dist/visualQa/mirror.js +98 -0
  1510. package/dist/visualQa/mirror.js.map +1 -0
  1511. package/dist/visualQa/moveUx.d.ts +44 -0
  1512. package/dist/visualQa/moveUx.js +103 -0
  1513. package/dist/visualQa/moveUx.js.map +1 -0
  1514. package/dist/visualQa/pinnedChrome.d.ts +63 -0
  1515. package/dist/visualQa/pinnedChrome.js +214 -0
  1516. package/dist/visualQa/pinnedChrome.js.map +1 -0
  1517. package/dist/visualQa/resolve.d.ts +88 -0
  1518. package/dist/visualQa/resolve.js +248 -0
  1519. package/dist/visualQa/resolve.js.map +1 -0
  1520. package/dist/visualQa/scene.d.ts +14 -0
  1521. package/dist/visualQa/scene.js +688 -0
  1522. package/dist/visualQa/scene.js.map +1 -0
  1523. package/dist/visualQa/shot.d.ts +2 -0
  1524. package/dist/visualQa/shot.js +27 -0
  1525. package/dist/visualQa/shot.js.map +1 -0
  1526. package/dist/visualQa/signIn.d.ts +35 -0
  1527. package/dist/visualQa/signIn.js +144 -0
  1528. package/dist/visualQa/signIn.js.map +1 -0
  1529. package/dist/visualQa/submitBudget.d.ts +25 -0
  1530. package/dist/visualQa/submitBudget.js +82 -0
  1531. package/dist/visualQa/submitBudget.js.map +1 -0
  1532. package/dist/visualQa/types.d.ts +195 -0
  1533. package/dist/visualQa/types.js +12 -0
  1534. package/dist/visualQa/types.js.map +1 -0
  1535. package/dist/visualQa/vehicle.d.ts +130 -0
  1536. package/dist/visualQa/vehicle.js +2001 -0
  1537. package/dist/visualQa/vehicle.js.map +1 -0
  1538. package/dist/visualQa/vehicleAddonProbe.d.ts +1 -0
  1539. package/dist/visualQa/vehicleAddonProbe.js +115 -0
  1540. package/dist/visualQa/vehicleAddonProbe.js.map +1 -0
  1541. package/dist/visualQa/vehicleCabinFit.d.ts +78 -0
  1542. package/dist/visualQa/vehicleCabinFit.js +55 -0
  1543. package/dist/visualQa/vehicleCabinFit.js.map +1 -0
  1544. package/dist/visualQa/vehicleClearance.d.ts +135 -0
  1545. package/dist/visualQa/vehicleClearance.js +299 -0
  1546. package/dist/visualQa/vehicleClearance.js.map +1 -0
  1547. package/dist/visualQa/vehicleDriveAlive.d.ts +55 -0
  1548. package/dist/visualQa/vehicleDriveAlive.js +103 -0
  1549. package/dist/visualQa/vehicleDriveAlive.js.map +1 -0
  1550. package/dist/visualQa/vehicleGrip.d.ts +156 -0
  1551. package/dist/visualQa/vehicleGrip.js +439 -0
  1552. package/dist/visualQa/vehicleGrip.js.map +1 -0
  1553. package/dist/visualQa/vehicleInspection.d.ts +30 -0
  1554. package/dist/visualQa/vehicleInspection.js +189 -0
  1555. package/dist/visualQa/vehicleInspection.js.map +1 -0
  1556. package/dist/visualQa/vehicleLamps.d.ts +118 -0
  1557. package/dist/visualQa/vehicleLamps.js +374 -0
  1558. package/dist/visualQa/vehicleLamps.js.map +1 -0
  1559. package/dist/visualQa/vehicleMath.d.ts +440 -0
  1560. package/dist/visualQa/vehicleMath.js +967 -0
  1561. package/dist/visualQa/vehicleMath.js.map +1 -0
  1562. package/dist/visualQa/vehicleOccupants.d.ts +66 -0
  1563. package/dist/visualQa/vehicleOccupants.js +448 -0
  1564. package/dist/visualQa/vehicleOccupants.js.map +1 -0
  1565. package/dist/visualQa/vehiclePenetration.d.ts +172 -0
  1566. package/dist/visualQa/vehiclePenetration.js +1157 -0
  1567. package/dist/visualQa/vehiclePenetration.js.map +1 -0
  1568. package/dist/visualQa/vehiclePoses.d.ts +25 -0
  1569. package/dist/visualQa/vehiclePoses.js +56 -0
  1570. package/dist/visualQa/vehiclePoses.js.map +1 -0
  1571. package/dist/visualQa/vehiclePosture.d.ts +78 -0
  1572. package/dist/visualQa/vehiclePosture.js +263 -0
  1573. package/dist/visualQa/vehiclePosture.js.map +1 -0
  1574. package/dist/visualQa/vehiclePreview.d.ts +103 -0
  1575. package/dist/visualQa/vehiclePreview.js +312 -0
  1576. package/dist/visualQa/vehiclePreview.js.map +1 -0
  1577. package/dist/visualQa/vehicleProbe.d.ts +3 -0
  1578. package/dist/visualQa/vehicleProbe.js +670 -0
  1579. package/dist/visualQa/vehicleProbe.js.map +1 -0
  1580. package/dist/visualQa/vehicleStance.d.ts +108 -0
  1581. package/dist/visualQa/vehicleStance.js +200 -0
  1582. package/dist/visualQa/vehicleStance.js.map +1 -0
  1583. package/dist/visualQa/viewer.d.ts +6 -0
  1584. package/dist/visualQa/viewer.js +750 -0
  1585. package/dist/visualQa/viewer.js.map +1 -0
  1586. package/dist/visualQa/viewerGeometry.d.ts +116 -0
  1587. package/dist/visualQa/viewerGeometry.js +476 -0
  1588. package/dist/visualQa/viewerGeometry.js.map +1 -0
  1589. package/dist/visualQa/viewerPage.d.ts +11 -0
  1590. package/dist/visualQa/viewerPage.js +414 -0
  1591. package/dist/visualQa/viewerPage.js.map +1 -0
  1592. package/dist/visualQa/webgpuSelfTest.d.ts +52 -0
  1593. package/dist/visualQa/webgpuSelfTest.js +113 -0
  1594. package/dist/visualQa/webgpuSelfTest.js.map +1 -0
  1595. package/dist/visualQa/winChrome.d.ts +92 -0
  1596. package/dist/visualQa/winChrome.js +445 -0
  1597. package/dist/visualQa/winChrome.js.map +1 -0
  1598. package/dist/visualQa/worldWalk.d.ts +59 -0
  1599. package/dist/visualQa/worldWalk.js +523 -0
  1600. package/dist/visualQa/worldWalk.js.map +1 -0
  1601. package/dist/visualQa/worldWalkMath.d.ts +263 -0
  1602. package/dist/visualQa/worldWalkMath.js +873 -0
  1603. package/dist/visualQa/worldWalkMath.js.map +1 -0
  1604. package/dist/visualQa/worldWalkProbe.d.ts +1 -0
  1605. package/dist/visualQa/worldWalkProbe.js +206 -0
  1606. package/dist/visualQa/worldWalkProbe.js.map +1 -0
  1607. package/dist/visualsConfig.d.ts +65 -0
  1608. package/dist/visualsConfig.js +1333 -0
  1609. package/dist/visualsConfig.js.map +1 -0
  1610. package/dist/voiceCommand.d.ts +11 -0
  1611. package/dist/voiceCommand.js +101 -0
  1612. package/dist/voiceCommand.js.map +1 -0
  1613. package/dist/world/analyze.d.ts +51 -0
  1614. package/dist/world/analyze.js +767 -0
  1615. package/dist/world/analyze.js.map +1 -0
  1616. package/dist/world/baseline.d.ts +33 -0
  1617. package/dist/world/baseline.js +111 -0
  1618. package/dist/world/baseline.js.map +1 -0
  1619. package/dist/world/canonicalize.d.ts +7 -0
  1620. package/dist/world/canonicalize.js +110 -0
  1621. package/dist/world/canonicalize.js.map +1 -0
  1622. package/dist/world/diff.d.ts +18 -0
  1623. package/dist/world/diff.js +58 -0
  1624. package/dist/world/diff.js.map +1 -0
  1625. package/dist/world/focus.d.ts +11 -0
  1626. package/dist/world/focus.js +161 -0
  1627. package/dist/world/focus.js.map +1 -0
  1628. package/dist/world/format.d.ts +6 -0
  1629. package/dist/world/format.js +125 -0
  1630. package/dist/world/format.js.map +1 -0
  1631. package/dist/world/readWorldMetrics.d.ts +10 -0
  1632. package/dist/world/readWorldMetrics.js +31 -0
  1633. package/dist/world/readWorldMetrics.js.map +1 -0
  1634. package/dist/world/snapshotTypes.d.ts +59 -0
  1635. package/dist/world/snapshotTypes.js +8 -0
  1636. package/dist/world/snapshotTypes.js.map +1 -0
  1637. package/dist/world/snapshotWorld.d.ts +16 -0
  1638. package/dist/world/snapshotWorld.js +142 -0
  1639. package/dist/world/snapshotWorld.js.map +1 -0
  1640. package/dist/world/wiring.d.ts +21 -0
  1641. package/dist/world/wiring.js +73 -0
  1642. package/dist/world/wiring.js.map +1 -0
  1643. package/dist/world/zones.d.ts +23 -0
  1644. package/dist/world/zones.js +134 -0
  1645. package/dist/world/zones.js.map +1 -0
  1646. package/docs/bridge.md +304 -0
  1647. package/docs/device-hand-reach.md +44 -0
  1648. package/docs/scene-hlod.md +139 -0
  1649. package/docs/vehicle-audio-import.md +299 -0
  1650. package/docs/vehicle-simulate.md +165 -0
  1651. package/docs/visual-qa-candidate-versions.md +71 -0
  1652. package/docs/visual-qa.md +462 -0
  1653. package/package.json +69 -4
  1654. package/plugins/beamng-vehicle/README.md +129 -0
  1655. package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
  1656. package/plugins/beamng-vehicle/bin/__pycache__/beamng_audio.cpython-312.pyc +0 -0
  1657. package/plugins/beamng-vehicle/bin/__pycache__/beamng_engine.cpython-312.pyc +0 -0
  1658. package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
  1659. package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
  1660. package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
  1661. package/plugins/beamng-vehicle/bin/__pycache__/beamng_stock_events.cpython-312.pyc +0 -0
  1662. package/plugins/beamng-vehicle/bin/__pycache__/fmod_studio.cpython-312.pyc +0 -0
  1663. package/plugins/beamng-vehicle/bin/__pycache__/fsb5.cpython-312.pyc +0 -0
  1664. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio.cpython-312.pyc +0 -0
  1665. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_ac.cpython-312.pyc +0 -0
  1666. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_beamng.cpython-312.pyc +0 -0
  1667. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_fixtures.cpython-312.pyc +0 -0
  1668. package/plugins/beamng-vehicle/bin/__pycache__/test_beamng_engine.cpython-312.pyc +0 -0
  1669. package/plugins/beamng-vehicle/bin/__pycache__/test_sim_audio.cpython-312.pyc +0 -0
  1670. package/plugins/beamng-vehicle/bin/beamng-vehicle.mjs +1295 -0
  1671. package/plugins/beamng-vehicle/bin/beamng_abs.py +408 -0
  1672. package/plugins/beamng-vehicle/bin/beamng_audio.py +394 -0
  1673. package/plugins/beamng-vehicle/bin/beamng_engine.py +184 -0
  1674. package/plugins/beamng-vehicle/bin/beamng_expression.py +373 -0
  1675. package/plugins/beamng-vehicle/bin/beamng_jbeam.py +858 -0
  1676. package/plugins/beamng-vehicle/bin/beamng_materials.py +261 -0
  1677. package/plugins/beamng-vehicle/bin/beamng_stock_events.py +171 -0
  1678. package/plugins/beamng-vehicle/bin/convert-beamng.py +1563 -0
  1679. package/plugins/beamng-vehicle/bin/fmod_studio.py +524 -0
  1680. package/plugins/beamng-vehicle/bin/fsb5.py +304 -0
  1681. package/plugins/beamng-vehicle/bin/index-beamng-common.mjs +16 -0
  1682. package/plugins/beamng-vehicle/bin/index-beamng.py +69 -0
  1683. package/plugins/beamng-vehicle/bin/lower-beamng.py +2399 -0
  1684. package/plugins/beamng-vehicle/bin/sim_audio.py +250 -0
  1685. package/plugins/beamng-vehicle/bin/sim_audio_ac.py +378 -0
  1686. package/plugins/beamng-vehicle/bin/sim_audio_beamng.py +878 -0
  1687. package/plugins/beamng-vehicle/bin/sim_audio_fixtures.py +287 -0
  1688. package/plugins/beamng-vehicle/bin/sim_audio_import.py +135 -0
  1689. package/plugins/beamng-vehicle/bin/test_beamng_engine.py +124 -0
  1690. package/plugins/beamng-vehicle/bin/test_beamng_jbeam.py +383 -0
  1691. package/plugins/beamng-vehicle/bin/test_beamng_precedence.py +134 -0
  1692. package/plugins/beamng-vehicle/bin/test_sim_audio.py +249 -0
  1693. package/plugins/beamng-vehicle/bridge-plugin.json +55 -0
  1694. package/plugins/beamng-vehicle/controller-adapters/beamng-0.39.4-abs.json +52 -0
  1695. package/plugins/beamng-vehicle/lib/common-dependency.mjs +357 -0
  1696. package/plugins/beamng-vehicle/lib/dae-scan.mjs +40 -0
  1697. package/plugins/beamng-vehicle/lib/file-identity.mjs +46 -0
  1698. package/plugins/beamng-vehicle/lib/material-semantics.mjs +312 -0
  1699. package/plugins/beamng-vehicle/lib/palette-contract.mjs +6 -0
  1700. package/plugins/beamng-vehicle/lib/vehicle-qa.mjs +567 -0
  1701. package/plugins/beamng-vehicle/lib/vehicle-source.mjs +247 -0
  1702. package/plugins/beamng-vehicle/profiles/source-compatibility.json +14 -0
  1703. package/plugins/beamng-vehicle/vendor/vehicle-package-v2.mjs +6451 -0
  1704. package/vendor/README.md +63 -0
  1705. package/vendor/ability-manifest.mjs +527 -6
  1706. package/vendor/audits/asset-credits.mjs +96 -0
  1707. package/vendor/audits/check-loaders.mjs +80 -0
  1708. package/vendor/audits/perf-gate.mjs +759 -0
  1709. package/vendor/audits/prove-live.mjs +307 -0
  1710. package/vendor/audits/source-audit.mjs +530 -0
  1711. package/vendor/audits/world-audit.mjs +439 -0
  1712. package/vendor/capture-body.glb +0 -0
  1713. package/vendor/capture-face.glb +0 -0
  1714. package/vendor/installed-index.mjs +1 -1
  1715. package/vendor/vehicle-package-v2.mjs +6451 -0
@@ -0,0 +1,1563 @@
1
+ #!/usr/bin/env python3
2
+ """Blender-side BeamNG DAE to canonical HELIX GLB conversion.
3
+
4
+ The converter never fabricates cabin or wheel meshes. It measures authored source
5
+ geometry, restores source material maps, emits canonical sockets, and leaves final
6
+ acceptance to vehicle-qa.mjs.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import math
13
+ import os
14
+ import re
15
+ import sys
16
+ import xml.etree.ElementTree as ET
17
+ from pathlib import Path
18
+
19
+ # Blender runs `--python <script>` without the script's directory on sys.path (4.0 and 4.5 on
20
+ # Linux), so sibling modules must be made importable explicitly before they are imported.
21
+ _SCRIPT_DIR = str(Path(__file__).resolve().parent)
22
+ if _SCRIPT_DIR not in sys.path:
23
+ sys.path.insert(0, _SCRIPT_DIR)
24
+
25
+ import bpy
26
+ import numpy as np
27
+ from mathutils import Matrix, Vector
28
+ from beamng_materials import TextureChain, parse_material_tree
29
+ from beamng_jbeam import (
30
+ JBeamDocumentCache,
31
+ MAX_PC_FILE_BYTES,
32
+ MAX_PC_SOURCES,
33
+ MAX_PC_TOTAL_BYTES,
34
+ bounded_asset_paths,
35
+ build_part_catalog,
36
+ build_selected_part_index,
37
+ preselect_configuration,
38
+ resolve_part_closure,
39
+ selected_part_summary,
40
+ write_bound_selected_part_index,
41
+ )
42
+
43
+
44
+ INTERIOR = re.compile(r"seat|dash|interior|cabin|console|steer|gauge|door.?(?:card|panel)|pedal", re.I)
45
+ # `trim`, `wheelwell`, and `steeringwheel` are common vehicle mesh names but are
46
+ # not road-wheel custody. Keep this predicate exact because it controls both
47
+ # configuration retention and destructive wheel-source normalization.
48
+ WHEEL = re.compile(r"tyre|tire|(?<!steering)(?<!steering_)(?<!steer_)wheel(?!well)|(?<!t)rim", re.I)
49
+ TYRE = re.compile(r"tyre|tire", re.I)
50
+ BRAKE_HARDWARE = re.compile(r"brake.?disc|brake.?rotor|brake.?drum|(?:^|_)drum(?:_|$)|"
51
+ r"wheel.?hub|(?:^|_)hub(?:_|$)", re.I)
52
+ REAR = re.compile(r"exhaust|muffler|tail.?pipe|taillight|tailgate|trunk|boot|bumper.?r", re.I)
53
+ COLLADA_NS = {"c": "http://www.collada.org/2005/11/COLLADASchema"}
54
+ MATERIAL_SCAN_LIMIT = (4_096, 256 * 1024 * 1024, 32 * 1024 * 1024)
55
+ TEXTURE_SCAN_LIMIT = (200_000, 128 * 1024 * 1024 * 1024, 2 * 1024 * 1024 * 1024)
56
+ AUDIO_SCAN_LIMIT = (20_000, 16 * 1024 * 1024 * 1024, 1024 * 1024 * 1024)
57
+
58
+
59
+ def local_tag(element) -> str:
60
+ return element.tag.split("}")[-1]
61
+
62
+
63
+ def primitive_inputs(element) -> list[dict]:
64
+ return [{"semantic": row.get("semantic"), "source": row.get("source", "").lstrip("#"),
65
+ "offset": int(row.get("offset", "0")), "set": int(row.get("set") or 0)}
66
+ for row in element.findall("c:input", COLLADA_NS) if row.get("source")]
67
+
68
+
69
+ def fan_triangulate(rows: np.ndarray, counts: np.ndarray) -> np.ndarray:
70
+ output = []
71
+ cursor = 0
72
+ for count in counts:
73
+ face = rows[cursor:cursor + int(count)]
74
+ cursor += int(count)
75
+ for index in range(1, len(face) - 1):
76
+ output.extend((face[0], face[index], face[index + 1]))
77
+ return np.asarray(output, dtype=np.int64)
78
+
79
+
80
+ def read_geometry(element) -> tuple[str, dict] | None:
81
+ mesh = element.find("c:mesh", COLLADA_NS)
82
+ if mesh is None:
83
+ return None
84
+ sources = {}
85
+ for source in mesh.findall("c:source", COLLADA_NS):
86
+ values = source.find("c:float_array", COLLADA_NS)
87
+ accessor = source.find("c:technique_common/c:accessor", COLLADA_NS)
88
+ if values is None:
89
+ continue
90
+ stride = int(accessor.get("stride", "1")) if accessor is not None else 1
91
+ numbers = np.fromstring(values.text or "", dtype=np.float64, sep=" ")
92
+ numbers = numbers[:numbers.size - numbers.size % stride] if stride else numbers
93
+ sources[source.get("id")] = numbers.reshape(-1, stride)
94
+ vertices = {}
95
+ for vertex in mesh.findall("c:vertices", COLLADA_NS):
96
+ position = vertex.find('c:input[@semantic="POSITION"]', COLLADA_NS)
97
+ if position is not None:
98
+ vertices[vertex.get("id")] = position.get("source", "").lstrip("#")
99
+ primitives = []
100
+ for primitive in list(mesh.findall("c:triangles", COLLADA_NS)) + list(mesh.findall("c:polylist", COLLADA_NS)):
101
+ inputs = primitive_inputs(primitive)
102
+ payload = primitive.find("c:p", COLLADA_NS)
103
+ if not inputs or payload is None:
104
+ continue
105
+ stride = max(row["offset"] for row in inputs) + 1
106
+ values = np.fromstring(payload.text or "", dtype=np.int64, sep=" ")
107
+ values = values[:values.size - values.size % stride].reshape(-1, stride)
108
+ if local_tag(primitive) == "polylist":
109
+ counts_element = primitive.find("c:vcount", COLLADA_NS)
110
+ counts = np.fromstring(counts_element.text or "", dtype=np.int64, sep=" ") if counts_element is not None else np.zeros(0)
111
+ values = fan_triangulate(values, counts)
112
+ else:
113
+ values = values[:len(values) - len(values) % 3]
114
+ if len(values):
115
+ primitives.append({"material": primitive.get("material") or "default", "inputs": inputs, "indices": values})
116
+ identifier = element.get("id")
117
+ return (identifier, {"name": element.get("name") or identifier, "sources": sources,
118
+ "vertices": vertices, "primitives": primitives}) if identifier else None
119
+
120
+
121
+ def read_scene(element) -> list[dict]:
122
+ nodes = []
123
+
124
+ def visit(parent, parent_matrix):
125
+ for node in parent.findall("c:node", COLLADA_NS):
126
+ local = np.eye(4)
127
+ for child in node:
128
+ if local_tag(child) == "matrix" and (child.text or "").strip():
129
+ local = local @ np.fromstring(child.text, dtype=np.float64, sep=" ").reshape(4, 4)
130
+ world = parent_matrix @ local
131
+ instance = node.find("c:instance_geometry", COLLADA_NS)
132
+ bindings = {}
133
+ if instance is not None:
134
+ for binding in instance.findall(".//c:instance_material", COLLADA_NS):
135
+ bindings[binding.get("symbol")] = binding.get("target", "").lstrip("#")
136
+ nodes.append({"name": node.get("name") or node.get("id") or "mesh",
137
+ "geometry": instance.get("url", "").lstrip("#") if instance is not None else None,
138
+ "matrix": world, "materials": bindings})
139
+ visit(node, world)
140
+ for scene in element.findall("c:visual_scene", COLLADA_NS):
141
+ visit(scene, np.eye(4))
142
+ return nodes
143
+
144
+
145
+ def parse_collada(path: Path) -> dict:
146
+ geometries, materials, nodes = {}, {}, []
147
+ up_axis, metre = "Z_UP", 1.0
148
+ for _, element in ET.iterparse(path, events=("end",)):
149
+ tag = local_tag(element)
150
+ if tag == "asset":
151
+ up = element.find("c:up_axis", COLLADA_NS)
152
+ unit = element.find("c:unit", COLLADA_NS)
153
+ up_axis = (up.text or "Z_UP").strip() if up is not None else "Z_UP"
154
+ metre = float(unit.get("meter", "1")) if unit is not None else 1.0
155
+ element.clear()
156
+ elif tag == "library_materials":
157
+ for material in element.findall("c:material", COLLADA_NS):
158
+ materials[material.get("id")] = material.get("name") or material.get("id") or "default"
159
+ element.clear()
160
+ elif tag == "geometry":
161
+ result = read_geometry(element)
162
+ if result:
163
+ geometries[result[0]] = result[1]
164
+ element.clear()
165
+ elif tag == "library_visual_scenes":
166
+ nodes.extend(read_scene(element))
167
+ element.clear()
168
+ return {"geometries": geometries, "materials": materials, "nodes": nodes,
169
+ "upAxis": up_axis, "metre": metre}
170
+
171
+
172
+ def build_collada(path: Path) -> list[bpy.types.Object]:
173
+ document = parse_collada(path)
174
+ built = []
175
+ path_parts = [part.lower() for part in path.parts]
176
+ wheel_axle = "front" if "wheels" in path_parts and "f" in path_parts else \
177
+ "rear" if "wheels" in path_parts and "r" in path_parts else None
178
+ for node in document["nodes"]:
179
+ geometry = document["geometries"].get(node["geometry"])
180
+ if not geometry or not geometry["primitives"]:
181
+ continue
182
+ position_source = None
183
+ for primitive in geometry["primitives"]:
184
+ vertex = next((row for row in primitive["inputs"] if row["semantic"] == "VERTEX"), None)
185
+ if vertex:
186
+ position_source = geometry["vertices"].get(vertex["source"], vertex["source"])
187
+ break
188
+ positions = geometry["sources"].get(position_source)
189
+ if positions is None or len(positions) == 0:
190
+ continue
191
+ faces, face_materials, loop_uvs, slots = [], [], [], []
192
+ for primitive in geometry["primitives"]:
193
+ input_by_semantic = {}
194
+ for row in primitive["inputs"]:
195
+ if row["semantic"] != "TEXCOORD" or row["semantic"] not in input_by_semantic or row["set"] == 0:
196
+ input_by_semantic[row["semantic"]] = row
197
+ vertex = input_by_semantic.get("VERTEX")
198
+ if not vertex:
199
+ continue
200
+ material_id = node["materials"].get(primitive["material"], primitive["material"])
201
+ material_name = document["materials"].get(material_id, material_id or "default")
202
+ if material_name not in slots:
203
+ slots.append(material_name)
204
+ material_index = slots.index(material_name)
205
+ texcoord = input_by_semantic.get("TEXCOORD")
206
+ tex_values = geometry["sources"].get(texcoord["source"]) if texcoord else None
207
+ rows = primitive["indices"]
208
+ for start in range(0, len(rows), 3):
209
+ triangle = rows[start:start + 3]
210
+ if len(triangle) < 3:
211
+ continue
212
+ faces.append(tuple(int(row[vertex["offset"]]) for row in triangle))
213
+ face_materials.append(material_index)
214
+ loop_uvs.extend(tuple(tex_values[int(row[texcoord["offset"]])][:2]) if tex_values is not None else (0.0, 0.0)
215
+ for row in triangle)
216
+ if not faces:
217
+ continue
218
+ object_name = f"{node['name']}_{wheel_axle}" if wheel_axle and WHEEL.search(node["name"]) else node["name"]
219
+ mesh = bpy.data.meshes.new(object_name)
220
+ mesh.from_pydata([tuple(row[:3] * document["metre"]) for row in positions], [], faces)
221
+ mesh.update()
222
+ obj = bpy.data.objects.new(object_name, mesh)
223
+ bpy.context.scene.collection.objects.link(obj)
224
+ obj["helix_source_dae"] = str(path)
225
+ obj["helix_dedicated_wheel"] = bool(wheel_axle)
226
+ for name in slots:
227
+ material = bpy.data.materials.get(name) or bpy.data.materials.new(name)
228
+ mesh.materials.append(material)
229
+ for polygon, index in zip(mesh.polygons, face_materials):
230
+ polygon.material_index = index
231
+ uv_layer = mesh.uv_layers.new(name="UVMap")
232
+ for index, value in enumerate(loop_uvs[:len(uv_layer.data)]):
233
+ uv_layer.data[index].uv = value
234
+ obj.matrix_world = Matrix(node["matrix"].tolist())
235
+ if document["upAxis"] == "Y_UP":
236
+ obj.matrix_world = Matrix.Rotation(math.radians(90), 4, "X") @ obj.matrix_world
237
+ built.append(obj)
238
+ return built
239
+
240
+
241
+ def config_overrides(path: Path, cache: JBeamDocumentCache) -> dict:
242
+ try:
243
+ document = cache.load(path)
244
+ return document.get("parts", {}) if isinstance(document, dict) and isinstance(document.get("parts"), dict) else {}
245
+ except Exception:
246
+ return {}
247
+
248
+
249
+ def choose_config(root: Path, daes: list[str], parts: dict[str, dict],
250
+ cache: JBeamDocumentCache,
251
+ configuration_hint: str | None = None,
252
+ present_meshes: set[str] | None = None) -> Path | None:
253
+ variants = [root.name.lower(), *(Path(value).parent.name.lower() for value in daes)]
254
+ configs = bounded_asset_paths(
255
+ [root], {".pc": (MAX_PC_SOURCES, MAX_PC_TOTAL_BYTES, MAX_PC_FILE_BYTES)},
256
+ recursive=False)
257
+ preselected = preselect_configuration(root, variants, configuration_hint)
258
+ if preselected is not None:
259
+ return preselected
260
+ if not configs:
261
+ return None
262
+
263
+ def score(path: Path) -> tuple:
264
+ overrides = config_overrides(path, cache)
265
+ chosen = resolve_part_closure(parts, overrides)["parts"]
266
+ meshes = {mesh for part in chosen for mesh in part["meshes"] if isinstance(mesh, str)}
267
+ semantic = " ".join([path.stem, *overrides.keys(), *map(str, overrides.values()),
268
+ *(part["name"] for part in chosen), *meshes]).lower()
269
+ present = meshes & present_meshes if present_meshes is not None else meshes
270
+ present_semantic = " ".join(present).lower()
271
+ present_coverage = sum(bool(re.search(pattern, present_semantic)) for pattern in (
272
+ r"body|shell|frame|chassis", r"seat|interior|cabin", r"steer", r"wheel|rim|tire|tyre",
273
+ r"engine|motor", r"suspension|hub", r"door", r"glass|windshield|windscreen"))
274
+ coverage = sum(bool(re.search(pattern, semantic)) for pattern in (
275
+ r"body|shell|frame|chassis", r"seat|interior|cabin", r"steer", r"wheel|rim|tire|tyre",
276
+ r"engine|motor", r"suspension|hub", r"door", r"glass|windshield|windscreen"))
277
+ stem = re.sub(r"[^a-z0-9]", "", path.stem.lower())
278
+ candidate = re.sub(r"[^a-z0-9]", "", root.name.lower())
279
+ canonical = int(stem == candidate)
280
+ configured = " ".join(str(value) for value in overrides.values() if isinstance(value, str)).lower()
281
+ active_seats = {value for value in overrides.values()
282
+ if isinstance(value, str) and value != "none" and re.search(r"(?:^|_)seat(?:s)?(?:_|$)", value, re.I)}
283
+ front_sides = sum(bool(re.search(pattern, configured)) for pattern in (
284
+ r"seat[^ ]*(?:_fl|_l(?:_|$)|driver)", r"seat[^ ]*(?:_fr|_r(?:_|$)|passenger)"))
285
+ rear_seats = int(bool(re.search(r"seat(?:s)?[^ ]*(?:_r(?:_|$)|rear)", configured)))
286
+ visual_steering = int(any(isinstance(value, str) and value != "none"
287
+ and re.search(r"(?:^|_)steer(?:_|$)", value, re.I)
288
+ for value in overrides.values()))
289
+ # A complete road cabin outranks optional mesh volume. This rejects a
290
+ # one-seat hillclimb shell in favour of the authored two/four-seat trim
291
+ # while leaving requested compatibility-profile configurations intact.
292
+ cabin = min(len(active_seats), 4) + front_sides + rear_seats + visual_steering * 2
293
+ stock = int(bool(re.search(r"(?:^|_)(?:base|stock|factory|default|vanilla)(?:_|$)", path.stem, re.I)))
294
+ info_path = root / f"info_{path.stem}.json"
295
+ if info_path.is_file():
296
+ try:
297
+ info = cache.load(info_path)
298
+ description = f"{info.get('Configuration', '')} {info.get('Description', '')}".lower()
299
+ stock += int(bool(re.search(r"base model|stock|factory|standard model|entry.?level", description))) * 2
300
+ except Exception:
301
+ pass
302
+ stock += int(bool(re.fullmatch(r"sa_1000", path.stem, re.I))) * 2
303
+ modified = int(bool(re.search(r"race|drift|drag|hillclimb|maxspeed|stanced|stripped|offroad|jato|godmode|"
304
+ r"derby|custom|tuned|owner|experience|camo|colour|special|rhd|police|pursuit",
305
+ path.stem, re.I)))
306
+ if modified:
307
+ stock = 0
308
+ # Completeness comes first. Among complete configurations, canonical or
309
+ # explicitly stock/base spawns win before raw optional-part volume.
310
+ return (present_coverage, coverage, canonical, cabin, stock, -modified,
311
+ len(present), len(meshes), len(chosen), len(overrides), path.name)
312
+
313
+ scored = [(score(path), path) for path in configs]
314
+ selected = max(scored, key=lambda row: row[0])[1]
315
+ top = sorted(scored, key=lambda row: row[0], reverse=True)[:5]
316
+ print(f"[beamng] configuration candidates top={[(path.name, values[:-1]) for values, path in top]}")
317
+ return selected
318
+
319
+
320
+ def configured_meshes(root: Path, source_root: Path, daes: list[str],
321
+ configuration_hint: str | None = None,
322
+ present_meshes: set[str] | None = None,
323
+ dependency_roots: list[Path] | None = None,
324
+ part_index_path: Path | None = None,
325
+ bridge_plan_key: str | None = None,
326
+ source_sha256: str | None = None,
327
+ common_closure_sha256: str | None = None) -> tuple[set[str], dict]:
328
+ dependency_roots = dependency_roots or []
329
+ cache = JBeamDocumentCache(source_root, dependency_roots)
330
+ common = source_root / "vehicles" / "common"
331
+ dependency_common = [path / "vehicles" / "common" for path in dependency_roots]
332
+ ranked_roots = [(root, 0), (common, 1), *((source, 2) for source in dependency_common)]
333
+ admitted = [(source, rank) for source, rank in ranked_roots if source.is_dir()]
334
+ parts, errors = build_part_catalog(
335
+ [source for source, _ in admitted], cache, [rank for _, rank in admitted])
336
+ config = choose_config(root, daes, parts, cache, configuration_hint, present_meshes)
337
+ overrides = {}
338
+ config_document = {}
339
+ if config:
340
+ try:
341
+ config_document = cache.load(config)
342
+ overrides = config_overrides(config, cache)
343
+ except Exception as error:
344
+ errors.append(f"{config.name}: {error}")
345
+ requested_main = config_document.get("mainPartName") if isinstance(config_document, dict) else None
346
+ graph = resolve_part_closure(parts, overrides, requested_main)
347
+ chosen = graph["parts"]
348
+ wanted = {mesh for part in chosen for mesh in part["meshes"] if isinstance(mesh, str)}
349
+ slot_closure = graph["slotClosure"]
350
+ semantic = " ".join([*(part["name"] for part in chosen), *wanted]).lower()
351
+ if len(wanted) < 4 or not re.search(r"body|shell|frame|chassis", semantic):
352
+ raise RuntimeError(f"configuration {config.name if config else '<default>'} has no complete vehicle shell "
353
+ f"({len(wanted)} render meshes; JBeam parse failures={len(errors)})")
354
+ paint_base = None
355
+ if config_document:
356
+ try:
357
+ paints = config_document.get("paints", [])
358
+ colors = config_document.get("colors", [])
359
+ raw_color = paints[0].get("baseColor") if (
360
+ isinstance(paints, list) and paints and isinstance(paints[0], dict)
361
+ ) else colors[0] if (
362
+ isinstance(colors, list) and colors and isinstance(colors[0], list)
363
+ ) else None
364
+ if isinstance(raw_color, list) and len(raw_color) >= 3:
365
+ paint_base = [round(max(0.0, min(1.0, float(value))), 4) for value in raw_color[:3]] + [1.0]
366
+ except Exception:
367
+ pass
368
+ selected_part_index = build_selected_part_index(root.name, config, graph, cache, parts) if config else None
369
+ selected_summary = None
370
+ if selected_part_index is not None:
371
+ if part_index_path is None or bridge_plan_key is None or source_sha256 is None:
372
+ raise RuntimeError("selected-part index output and Bridge identity are required")
373
+ selected_part_index["parseFailures"] = errors[:20]
374
+ selected_part_index["parseCounts"] = {
375
+ f"{cache.receipt(path)['root']}:{cache.receipt(path)['path']}": count
376
+ for path, count in sorted(cache.parse_counts.items(), key=lambda row: str(row[0]))
377
+ }
378
+ index_receipt = write_bound_selected_part_index(selected_part_index, part_index_path,
379
+ bridge_plan_key, source_sha256,
380
+ common_closure_sha256)
381
+ selected_summary = selected_part_summary(selected_part_index, index_receipt)
382
+ configuration_receipt = cache.receipt(config) if config else None
383
+ return wanted, {"file": config.name if config else None,
384
+ "path": configuration_receipt["path"] if configuration_receipt else None,
385
+ "sha256": configuration_receipt["sha256"] if configuration_receipt else None,
386
+ "sizeBytes": configuration_receipt["sizeBytes"] if configuration_receipt else None,
387
+ "parts": [part["name"] for part in chosen],
388
+ "slotClosure": slot_closure, "selectedMeshes": sorted(wanted),
389
+ "meshes": len(wanted), "explicitOverrides": len(overrides),
390
+ "selectedPartSummary": selected_summary,
391
+ "paintBaseColor": paint_base,
392
+ "semanticCoverage": {key: bool(re.search(pattern, semantic)) for key, pattern in {
393
+ "body": r"body|shell|frame|chassis", "cabin": r"seat|interior|cabin",
394
+ "steering": r"steer", "wheels": r"wheel|rim|tire|tyre", "engine": r"engine|motor"}.items()},
395
+ "jbeamParseFailures": errors[:20]}
396
+
397
+
398
+ def filter_configuration(objects: list[bpy.types.Object], wanted: set[str],
399
+ has_dedicated_wheels: bool,
400
+ configuration_hint: str | None = None) -> tuple[list[bpy.types.Object], list[str]]:
401
+ present = {obj.name for obj in objects}
402
+ # Wheel geometry is often authored directly in the main DAE and never
403
+ # repeated as a JBeam flexbody, so an exact config-only filter can delete
404
+ # every wheel (RX-7). When the selected trim supplies dedicated wheel DAEs
405
+ # (992 GT3 RS), those are authoritative and the unrelated base wheels must
406
+ # be excluded. Otherwise retain the main DAE's authored wheel geometry.
407
+ def configured(obj: bpy.types.Object) -> bool:
408
+ # Blender may disambiguate equal source names with a numeric suffix.
409
+ # That must not disconnect a JBeam-selected flexbody from its authored
410
+ # DAE mesh.
411
+ return obj.name in wanted or re.sub(r"\.\d+$", "", obj.name) in wanted
412
+
413
+ for obj in objects:
414
+ obj["helix_configured_mesh"] = configured(obj)
415
+
416
+ normalized_hint = re.sub(r"[^a-z0-9]", "", (configuration_hint or "").lower())
417
+
418
+ def cross_variant_source(obj: bpy.types.Object) -> bool:
419
+ # Some trims inherit their seats/interior from a sibling body-kit DAE.
420
+ # Importing that whole sibling stacks two exterior shells, while
421
+ # dropping it removes the authored H-points. Retain only semantic cabin
422
+ # meshes when an explicitly requested trim consumes a different
423
+ # body-kit variant.
424
+ dae = Path(str(obj.get("helix_source_dae", "")))
425
+ lowered_parts = [part.lower() for part in dae.parts]
426
+ if "body kits" not in lowered_parts:
427
+ return False
428
+ variant_tail = dae.parts[lowered_parts.index("body kits") + 1:]
429
+ normalized_variant = re.sub(r"[^a-z0-9]", "", "/".join(variant_tail).lower())
430
+ return bool(normalized_hint and normalized_hint not in normalized_variant)
431
+
432
+ def cross_variant_interior(obj: bpy.types.Object) -> bool:
433
+ return cross_variant_source(obj) and bool(INTERIOR.search(obj.name))
434
+
435
+ def inactive_damage_variant(obj: bpy.types.Object) -> bool:
436
+ # JBeam flexbody lists can include the replacement mesh used only after
437
+ # breakage. Exporting it alongside the intact part produces cracked
438
+ # glass and doubled/deformed panels on a freshly imported vehicle.
439
+ return bool(re.search(r"(?:^|[_-])(?:dmg|damage|broken|deformed)(?:[_-]|$)", obj.name, re.I))
440
+
441
+ configured_wheels = {obj.name for obj in objects if road_wheel_signal(obj) and (
442
+ not has_dedicated_wheels
443
+ or TYRE.search(obj.name)
444
+ # A dedicated axle DAE is an authored source library, not a placed road
445
+ # wheel. Retain its candidates until axle expansion can choose the
446
+ # configured trim, instantiate four corners, and prune the rest.
447
+ or "wheels" in [part.lower() for part in Path(str(obj.get("helix_source_dae", ""))).parts]
448
+ )}
449
+ keep = [obj for obj in objects if not inactive_damage_variant(obj) and (cross_variant_interior(obj) or (
450
+ not cross_variant_source(obj) and (
451
+ configured(obj) or obj.name in configured_wheels or BRAKE_HARDWARE.search(obj.name)
452
+ )
453
+ ))]
454
+ missing = sorted(wanted - present)
455
+ for obj in list(objects):
456
+ if obj not in keep:
457
+ bpy.data.objects.remove(obj, do_unlink=True)
458
+ if len(keep) < 5:
459
+ raise RuntimeError(f"default configuration retained only {len(keep)} authored mesh nodes")
460
+ return keep, missing
461
+
462
+
463
+ def arguments() -> argparse.Namespace:
464
+ argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
465
+ parser = argparse.ArgumentParser()
466
+ parser.add_argument("--source-root", required=True)
467
+ parser.add_argument("--vehicle-root", required=True)
468
+ parser.add_argument("--dae", action="append", required=True)
469
+ parser.add_argument("--output", required=True)
470
+ parser.add_argument("--receipt", required=True)
471
+ parser.add_argument("--palette", required=True)
472
+ parser.add_argument("--material-root", action="append", default=[])
473
+ parser.add_argument("--configuration-hint")
474
+ parser.add_argument("--part-index", required=True)
475
+ parser.add_argument("--bridge-plan-key", required=True)
476
+ parser.add_argument("--source-sha", required=True)
477
+ parser.add_argument("--common-closure")
478
+ parser.add_argument("--mirror-z", action="store_true")
479
+ return parser.parse_args(argv)
480
+
481
+
482
+ def parse_materials(root: Path, default_tier: str, vehicle_root: Path | None = None) -> dict:
483
+ parsed = parse_material_tree(root, default_tier, vehicle_root)
484
+ rows = parsed["materials"]
485
+ aliases = parsed["aliases"]
486
+ block_re = re.compile(r"(?:singleton\s+)?Material\s*\([^)]*\)\s*\{(.*?)\};", re.I | re.S)
487
+ for path in bounded_asset_paths(
488
+ [root], {"materials.cs": MATERIAL_SCAN_LIMIT},
489
+ exact_names={"materials.cs": "materials.cs"}):
490
+ text = path.read_text(errors="ignore")
491
+ for block in block_re.findall(text):
492
+ mapped = re.search(r'mapTo\s*=\s*"([^"]+)"', block, re.I)
493
+ if not mapped:
494
+ continue
495
+
496
+ def indexed(field: str) -> str | None:
497
+ values = [(int(index), value) for index, value in re.findall(
498
+ rf'{field}\s*\[(\d+)\]\s*=\s*"([^"]+)"', block, re.I)]
499
+ return max(values, default=(0, None))[1]
500
+
501
+ color_match = re.search(r'diffuseColor\s*\[\d+\]\s*=\s*"([\d.\s-]+)"', block, re.I)
502
+ power_match = re.search(r'specularPower\s*\[\d+\]\s*=\s*"?([\d.]+)', block, re.I)
503
+ power = float(power_match.group(1)) if power_match else 32.0
504
+ identity = mapped.group(1).lower()
505
+ base = indexed("diffuseMap")
506
+ normal = indexed("normalMap")
507
+ roughness_map = indexed("roughnessMap")
508
+ metallic_map = indexed("metallicMap")
509
+ rows[identity] = {
510
+ "key": mapped.group(1), "mapTo": mapped.group(1), "stageCount": 0, "stages": [],
511
+ "maps": {key: value for key, value in {"baseColor": base, "normal": normal,
512
+ "roughness": roughness_map, "metallic": metallic_map}.items() if value},
513
+ "orderedMaps": [], "factors": {},
514
+ "base": base,
515
+ "paletteMask": None,
516
+ "normal": normal,
517
+ "roughnessMap": roughness_map,
518
+ "metallicMap": metallic_map,
519
+ "color": [float(value) for value in color_match.group(1).split()] if color_match else None,
520
+ "metallic": 0.75 if re.search(r'dynamicCubemap\s*=\s*(?:true|"1")', block, re.I) else 0.0,
521
+ "roughness": max(0.08, min(0.9, 1.0 - power / 160.0)),
522
+ "alpha": {"mode": "BLEND" if re.search(r'translucent\s*=\s*(?:true|"1")', block, re.I) else "OPAQUE",
523
+ "source": "materials.cs"},
524
+ "provenance": {"declarationTier": default_tier,
525
+ "declarationFile": str(path.relative_to(root)).replace(os.sep, "/"),
526
+ "declarationKey": mapped.group(1), "mapTo": mapped.group(1)},
527
+ }
528
+ aliases[identity] = identity
529
+ return {"materials": rows, "aliases": aliases, "diagnostics": parsed["diagnostics"]}
530
+
531
+
532
+ def texture_index(root: Path) -> dict[str, list[Path]]:
533
+ result: dict[str, list[Path]] = {}
534
+ extensions = {extension: TEXTURE_SCAN_LIMIT for extension in
535
+ (".dds", ".png", ".jpg", ".jpeg", ".tga", ".bmp")}
536
+ for path in bounded_asset_paths([root], extensions):
537
+ result.setdefault(path.name.lower(), []).append(path)
538
+ return result
539
+
540
+
541
+ def material_row(definitions: dict[str, tuple[dict, Path]], name: str) -> tuple[dict, Path] | None:
542
+ key = name.lower()
543
+ candidates = [key, key.removeprefix("m_"), re.sub(r"\.\d{3}$", "", key)]
544
+ return next((definitions[value] for value in candidates if value in definitions), None)
545
+
546
+
547
+ def connect_image(nodes, links, shader, socket: str, path: Path, non_color: bool = False,
548
+ link_alpha: bool = False) -> bool:
549
+ try:
550
+ image = bpy.data.images.load(str(path), check_existing=True)
551
+ except Exception as error:
552
+ print(f"[beamng] texture refused {path}: {error}")
553
+ return False
554
+ if non_color:
555
+ image.colorspace_settings.name = "Non-Color"
556
+ texture = nodes.new("ShaderNodeTexImage")
557
+ texture.image = image
558
+ if socket == "Normal":
559
+ normal = nodes.new("ShaderNodeNormalMap")
560
+ links.new(texture.outputs["Color"], normal.inputs["Color"])
561
+ links.new(normal.outputs["Normal"], shader.inputs["Normal"])
562
+ else:
563
+ links.new(texture.outputs["Color"], shader.inputs[socket])
564
+ # BeamNG commonly stores masks/palette data in the alpha channel of an
565
+ # otherwise opaque surface. Treating every image alpha as transparency
566
+ # made paint, dashboards and upholstery ghost through the car in HELIX.
567
+ # Only real glazing is allowed to drive the Principled alpha socket.
568
+ if link_alpha and "Alpha" in texture.outputs and "Alpha" in shader.inputs:
569
+ links.new(texture.outputs["Alpha"], shader.inputs["Alpha"])
570
+ return True
571
+
572
+
573
+ VEHICLE_MATERIAL_ROLES = (
574
+ ("tyre", re.compile(r"tyre|tire|tread", re.I), "tire-rubber", (0.012, 0.012, 0.014, 1.0)),
575
+ ("glass", re.compile(r"glass|window|windscreen|windshield", re.I), "vehicle-glass", (0.025, 0.035, 0.045, 1.0)),
576
+ ("brake-caliper", re.compile(r"caliper", re.I), "brake-caliper-painted", (0.55, 0.025, 0.015, 1.0)),
577
+ ("brake-rotor", re.compile(r"brake|rotor|disc", re.I), "brake-rotor", (0.18, 0.19, 0.20, 1.0)),
578
+ ("headlamp", re.compile(r"head.?l(?:amp|ight)|light[_-]?f", re.I), "headlamp-lens", (0.78, 0.86, 1.0, 1.0)),
579
+ ("taillamp", re.compile(r"tail.?l(?:amp|ight)|brake.?light|light[_-]?r", re.I), "taillamp-lens", (0.52, 0.01, 0.008, 1.0)),
580
+ ("carbon", re.compile(r"carbon|cfiber|(?:^|[_-])cf(?:[_-]|$)", re.I), "carbon-fiber", (0.012, 0.014, 0.016, 1.0)),
581
+ ("carpet", re.compile(r"carpet|floor.?mat", re.I), "vehicle-carpet", (0.012, 0.013, 0.015, 1.0)),
582
+ ("leather", re.compile(r"leather|upholstery|seat", re.I), "vehicle-leather", (0.024, 0.027, 0.032, 1.0)),
583
+ ("dashboard", re.compile(r"dashboard|dash|gauges?|instrument|cluster|soft.?touch", re.I),
584
+ "dashboard-soft-touch", (0.018, 0.020, 0.024, 1.0)),
585
+ ("interior", re.compile(r"interior|plastic|console|door.?card|trim|fabric|speaker|symbol", re.I),
586
+ "vehicle-interior-plastic", (0.018, 0.020, 0.024, 1.0)),
587
+ ("underbody", re.compile(r"underbody|under.?tray|chassis|subframe|suspension", re.I),
588
+ "underbody-coating", (0.014, 0.015, 0.017, 1.0)),
589
+ ("exhaust", re.compile(r"exhaust|muffler|tail.?pipe", re.I), "stainless-steel", (0.24, 0.25, 0.27, 1.0)),
590
+ ("chrome", re.compile(r"chrome", re.I), "chrome", (0.62, 0.65, 0.68, 1.0)),
591
+ ("wheel", re.compile(r"wheel|rim|alloy|aluminium|aluminum", re.I), "aluminum-brushed", (0.28, 0.30, 0.32, 1.0)),
592
+ ("paint", re.compile(r"paint|body|bodyshell|main|exterior", re.I), "vehicle-paint-metallic", (0.42, 0.025, 0.035, 1.0)),
593
+ ("metal", re.compile(r"metal|steel|iron", re.I), "stainless-steel", (0.22, 0.24, 0.26, 1.0)),
594
+ )
595
+
596
+
597
+ # Contracts whose `families[]` carry the fields this converter reads (id, definitionVersion,
598
+ # physical.sizeMeters, shader.defaults). /4 is the current asset pack; /3 stays accepted.
599
+ SUPPORTED_PALETTE_CONTRACTS = ("helix-palette/4", "helix-palette/3")
600
+
601
+
602
+ def load_vehicle_palette(path: Path) -> tuple[str, dict[str, dict]]:
603
+ try:
604
+ document = json.loads(path.read_text())
605
+ except Exception as error:
606
+ raise RuntimeError(f"Palette input is not valid JSON: {error}")
607
+ contract = document.get("contract")
608
+ if contract not in SUPPORTED_PALETTE_CONTRACTS or not isinstance(document.get("families"), list):
609
+ raise RuntimeError(f"Palette input must use contract {' or '.join(SUPPORTED_PALETTE_CONTRACTS)} "
610
+ "with a families array")
611
+ families = {row.get("id"): row for row in document["families"] if isinstance(row, dict)}
612
+ required = {row[2] for row in VEHICLE_MATERIAL_ROLES} | {"vehicle-interior-plastic"}
613
+ for family_id in required:
614
+ family = families.get(family_id)
615
+ defaults = family.get("shader", {}).get("defaults", {}) if family else {}
616
+ if not family or not isinstance(family.get("definitionVersion"), str):
617
+ raise RuntimeError(f"Palette has no versioned reusable family {family_id}")
618
+ for field in ("roughness", "metallic"):
619
+ value = defaults.get(field)
620
+ if not isinstance(value, (int, float)) or not 0 <= value <= 1:
621
+ raise RuntimeError(f"Palette family {family_id} has invalid {field}")
622
+ return contract, families
623
+
624
+
625
+ def vehicle_material_role(name: str) -> tuple[str, str, tuple]:
626
+ return next(((role, family, tint) for role, pattern, family, tint in VEHICLE_MATERIAL_ROLES
627
+ if pattern.search(name)),
628
+ ("generic", "vehicle-interior-plastic", (0.12, 0.13, 0.15, 1.0)))
629
+
630
+
631
+ def helix_material_binding(family: dict, source_id: str, tint: tuple | None,
632
+ authored_maps: bool, alpha_mode: str) -> dict:
633
+ physical = family.get("physical", {})
634
+ size = physical.get("sizeMeters")
635
+ physical_size = size if isinstance(size, list) and len(size) == 2 else None
636
+ overrides = {}
637
+ if tint is not None:
638
+ overrides["tint"] = "#" + "".join(f"{max(0, min(255, round(channel*255))):02X}" for channel in tint[:3])
639
+ return {
640
+ "contract": "helix-material-binding/1",
641
+ "familyId": family["id"],
642
+ "familyVersion": family["definitionVersion"],
643
+ "presetId": None,
644
+ "overrides": overrides,
645
+ "uv": {"uvSet": 0, "offset": [0.0, 0.0], "scale": [1.0, 1.0], "rotationRadians": 0.0,
646
+ "physicalSizeMeters": physical_size},
647
+ "preserved": {"identityBearingDetails": authored_maps, "sourceMaterialId": source_id,
648
+ "maskBindings": [], "decals": []},
649
+ "renderState": {"alphaMode": alpha_mode, "alphaCutoff": 0.5, "doubleSided": False,
650
+ "depthWrite": True, "depthTest": True, "blendMode": "normal"},
651
+ }
652
+
653
+
654
+ def restore_materials(roots: list[Path], palette_path: Path, vehicle_root: Path,
655
+ configured_paint: list[float] | None = None) -> dict:
656
+ palette_contract, palette = load_vehicle_palette(palette_path)
657
+ definitions: dict[str, tuple[dict, Path]] = {}
658
+ source_diagnostics = {key: [] for key in ("parseFailures", "shadowedDeclarations", "stageConflicts",
659
+ "dynamicTextures", "sentinels", "unsupportedMaps")}
660
+ # Shared/base-game dependencies first; the mod's own definitions win when
661
+ # both bind the same mapTo name.
662
+ for index in reversed(range(len(roots))):
663
+ root = roots[index]
664
+ tier = "mod" if index == 0 else "basegame"
665
+ parsed = parse_materials(root, tier, vehicle_root if index == 0 else None)
666
+ for alias, identity in parsed["aliases"].items():
667
+ row = parsed["materials"].get(identity)
668
+ if row:
669
+ definitions[alias] = (row, root)
670
+ for key in source_diagnostics:
671
+ source_diagnostics[key].extend(parsed["diagnostics"].get(key, []))
672
+ texture_chain = TextureChain([("mod", roots[0]), *[("basegame", root) for root in roots[1:]]])
673
+ resolved = mapped = factor_defaults = 0
674
+ role_counts: dict[str, int] = {}
675
+ bindings = []
676
+ texture_resolutions, unresolved_textures, ambiguous_textures = [], [], []
677
+
678
+ def material_texture(row: dict, role: str, material_name: str) -> Path | None:
679
+ raw = row.get("maps", {}).get(role)
680
+ resolution = texture_chain.resolve(raw)
681
+ if raw is None:
682
+ return None
683
+ portable = {key: value for key, value in resolution.items() if key != "path"}
684
+ portable.update({"material": material_name, "role": role})
685
+ if resolution.get("resolved"):
686
+ hit = Path(resolution["path"])
687
+ root = next((candidate for _tier, candidate in texture_chain.roots
688
+ if candidate == hit or candidate in hit.parents), roots[0])
689
+ portable["path"] = str(hit.relative_to(root)).replace(os.sep, "/")
690
+ texture_resolutions.append(portable)
691
+ return hit
692
+ if resolution.get("reason") == "ambiguous":
693
+ portable["candidates"] = [Path(value).name for value in resolution.get("candidates", [])]
694
+ ambiguous_textures.append(portable)
695
+ elif not resolution.get("dynamic") and not resolution.get("sentinel"):
696
+ unresolved_textures.append(portable)
697
+ return None
698
+
699
+ for material in bpy.data.materials:
700
+ material.use_nodes = True
701
+ shader = next((node for node in material.node_tree.nodes if node.type == "BSDF_PRINCIPLED"), None)
702
+ if not shader:
703
+ continue
704
+ material_binding = material_row(definitions, material.name)
705
+ row, owner_root = material_binding if material_binding is not None else (None, roots[0])
706
+ semantic = material.name.lower()
707
+ is_glazing = bool(re.search(r"glass|window|windscreen|windshield", semantic))
708
+ role, family_id, tint = vehicle_material_role(semantic)
709
+ configured_paint_active = role == "paint" and bool(configured_paint and len(configured_paint) >= 3)
710
+ if configured_paint_active:
711
+ tint = tuple(configured_paint[:3]) + (1.0,)
712
+ is_transmissive = is_glazing or role in {"headlamp", "taillamp"}
713
+ family = palette[family_id]
714
+ source_color = False
715
+ incomplete_paint = False
716
+ if row:
717
+ resolved += 1
718
+ color = row.get("color")
719
+ if isinstance(color, list) and len(color) >= 3 and not configured_paint_active:
720
+ rgba = tuple(float(value) for value in color[:4])
721
+ if len(rgba) == 3:
722
+ rgba += (1.0,)
723
+ shader.inputs["Base Color"].default_value = rgba
724
+ source_color = True
725
+ if isinstance(row.get("metallic"), (int, float)):
726
+ shader.inputs["Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic"].default_value = float(row["metallic"])
727
+ if isinstance(row.get("roughness"), (int, float)):
728
+ shader.inputs["Roughness"].default_value = float(row["roughness"])
729
+ base = material_texture(row, "baseColor", material.name)
730
+ palette_mask = material_texture(row, "colorPalette", material.name)
731
+ normal = material_texture(row, "normal", material.name)
732
+ roughness_map = material_texture(row, "roughness", material.name)
733
+ metallic_map = material_texture(row, "metallic", material.name)
734
+ attached = False
735
+ # A configured BeamNG paint is assembled from multi-channel palette
736
+ # masks in-engine. A portable glTF cannot reproduce that shader by
737
+ # wiring the mask itself as RGB; doing so yielded disconnected
738
+ # white/red panels. Keep normal/roughness detail, but use the
739
+ # selected paint as the portable base-color factor.
740
+ if base and not configured_paint_active:
741
+ attached |= connect_image(material.node_tree.nodes, material.node_tree.links, shader, "Base Color", base,
742
+ link_alpha=is_transmissive)
743
+ elif palette_mask and role == "paint" and not configured_paint_active:
744
+ # glTF multiplies baseColorTexture by baseColorFactor, so a
745
+ # BeamNG palette mask can stay authored while the Palette tint
746
+ # supplies the missing in-engine color factor.
747
+ attached |= connect_image(material.node_tree.nodes, material.node_tree.links, shader, "Base Color", palette_mask)
748
+ incomplete_paint = True
749
+ if normal:
750
+ attached |= connect_image(material.node_tree.nodes, material.node_tree.links, shader, "Normal", normal, True)
751
+ if roughness_map:
752
+ attached |= connect_image(material.node_tree.nodes, material.node_tree.links, shader, "Roughness", roughness_map, True)
753
+ if metallic_map:
754
+ attached |= connect_image(material.node_tree.nodes, material.node_tree.links, shader,
755
+ "Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic",
756
+ metallic_map, True)
757
+ mapped += int(attached)
758
+ # `translucent` in BeamNG is also used for palette masks, decals,
759
+ # illuminated gauges and chrome. Those surfaces are not transparent
760
+ # in the final vehicle. Restrict blend materials to actual glazing.
761
+ if is_transmissive:
762
+ shader.inputs["Alpha"].default_value = min(shader.inputs["Alpha"].default_value, 0.42)
763
+ if hasattr(material, "surface_render_method"):
764
+ material.surface_render_method = "DITHERED"
765
+ else:
766
+ shader.inputs["Alpha"].default_value = 1.0
767
+ elif re.search(r"glass|window|windscreen|windshield", semantic):
768
+ shader.inputs["Base Color"].default_value = (0.025, 0.035, 0.045, 1.0)
769
+ shader.inputs["Roughness"].default_value = 0.08
770
+ shader.inputs["Alpha"].default_value = 0.26
771
+ if hasattr(material, "surface_render_method"):
772
+ material.surface_render_method = "DITHERED"
773
+ elif re.search(r"tyre|tire|rubber", semantic):
774
+ shader.inputs["Base Color"].default_value = (0.012, 0.012, 0.014, 1.0)
775
+ shader.inputs["Roughness"].default_value = 0.82
776
+ elif re.search(r"chrome|wheel|rim|metal|exhaust", semantic):
777
+ shader.inputs["Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic"].default_value = 0.82
778
+ shader.inputs["Roughness"].default_value = 0.25
779
+ elif re.search(r"paint|body|main", semantic):
780
+ shader.inputs["Base Color"].default_value = (0.42, 0.025, 0.018, 1.0)
781
+ shader.inputs["Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic"].default_value = 0.18
782
+ shader.inputs["Roughness"].default_value = 0.24
783
+ if "Coat Weight" in shader.inputs:
784
+ shader.inputs["Coat Weight"].default_value = 1.0
785
+ shader.inputs["Coat Roughness"].default_value = 0.06
786
+ else:
787
+ shader.inputs["Roughness"].default_value = 0.55
788
+ # A large class of older BeamNG interiors declares neutral white
789
+ # factors and relies on an in-engine skin tint. HELIX has no such tint
790
+ # target, so preserve embedded maps but close the untextured factors to
791
+ # the selected black cabin instead of rendering white plastic/leather.
792
+ if not shader.inputs["Base Color"].is_linked:
793
+ if re.search(r"leather|upper_leather|interior_d", semantic):
794
+ shader.inputs["Base Color"].default_value = (0.024, 0.027, 0.032, 1.0)
795
+ shader.inputs["Roughness"].default_value = 0.42
796
+ elif re.search(r"plastic|rubber|carpet|fabric|speakers|symbols", semantic):
797
+ shader.inputs["Base Color"].default_value = (0.018, 0.020, 0.024, 1.0)
798
+ shader.inputs["Roughness"].default_value = 0.58
799
+ if re.search(r"(?:int|gauges?)_chrome", semantic):
800
+ shader.inputs["Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic"].default_value = 0.88
801
+ shader.inputs["Roughness"].default_value = 0.2
802
+ authored_maps = any(shader.inputs[name].is_linked for name in (
803
+ "Base Color", "Normal", "Roughness",
804
+ "Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic"))
805
+ applied_tint = None
806
+ # BeamNG paint textures are frequently grayscale/palette masks whose
807
+ # actual color comes from the selected `.pc` configuration. Preserve
808
+ # the authored image/UV and supply that missing multiplicative factor.
809
+ palette_authoritative = role in {"tyre", "glass", "brake-rotor", "carbon", "carpet",
810
+ "dashboard", "underbody", "exhaust", "wheel", "interior", "leather"}
811
+ if role != "generic" and (configured_paint_active or not source_color or palette_authoritative):
812
+ shader.inputs["Base Color"].default_value = tint
813
+ applied_tint = tint
814
+ if not authored_maps or incomplete_paint or configured_paint_active:
815
+ defaults = family["shader"]["defaults"]
816
+ if configured_paint_active or not source_color or incomplete_paint:
817
+ shader.inputs["Base Color"].default_value = tint
818
+ applied_tint = tint
819
+ shader.inputs["Metallic IOR Level" if "Metallic IOR Level" in shader.inputs else "Metallic"].default_value = float(defaults["metallic"])
820
+ shader.inputs["Roughness"].default_value = float(defaults["roughness"])
821
+ factor_defaults += 1
822
+ if role == "paint" and "Coat Weight" in shader.inputs:
823
+ shader.inputs["Coat Weight"].default_value = 1.0
824
+ shader.inputs["Coat Roughness"].default_value = 0.06
825
+ if role == "glass":
826
+ shader.inputs["Base Color"].default_value = (0.025, 0.04, 0.055, 1.0)
827
+ shader.inputs["Alpha"].default_value = 0.28
828
+ shader.inputs["Roughness"].default_value = min(0.12, shader.inputs["Roughness"].default_value)
829
+ transmission = shader.inputs.get("Transmission Weight") or shader.inputs.get("Transmission")
830
+ if transmission is not None:
831
+ transmission.default_value = 0.72
832
+ ior = shader.inputs.get("IOR")
833
+ if ior is not None:
834
+ ior.default_value = 1.45
835
+ if hasattr(material, "surface_render_method"):
836
+ material.surface_render_method = "DITHERED"
837
+ declared_alpha = row.get("alpha", {}) if row else {}
838
+ alpha_mode = str(declared_alpha.get("mode", "blend" if is_transmissive else "opaque")).lower()
839
+ if alpha_mode == "blend" and not is_transmissive:
840
+ # Preserve the declaration in provenance but retain the existing
841
+ # portable opaque fallback for palette/decal materials until the
842
+ # runtime has the matching staged compositor.
843
+ alpha_mode = "opaque"
844
+ bindings.append({"name": material.name, "role": role, "binding": helix_material_binding(
845
+ family, material.name, applied_tint, authored_maps, alpha_mode),
846
+ "source": {"provenance": row.get("provenance") if row else None,
847
+ "stages": row.get("stages", []) if row else [],
848
+ "factors": row.get("factors", {}) if row else {},
849
+ "alpha": declared_alpha}})
850
+ material["helix_vehicle_role"] = role
851
+ role_counts[role] = role_counts.get(role, 0) + 1
852
+ total = len(bpy.data.materials)
853
+ declaration_provenance = sorted({json.dumps(row[0].get("provenance"), sort_keys=True)
854
+ for row in definitions.values() if row[0].get("provenance")})
855
+ return {"definitions": len({id(row[0]) for row in definitions.values()}), "resolved": resolved, "mapped": mapped, "materials": total,
856
+ "paletteContract": palette_contract, "bound": total, "factorDefaults": factor_defaults,
857
+ "familyVersions": {family_id: family["definitionVersion"] for family_id, family in palette.items()},
858
+ "renderReady": total, "renderReadyCoverage": 1.0 if total else 0.0,
859
+ "roles": role_counts, "bindings": bindings,
860
+ "declarationProvenance": [json.loads(value) for value in declaration_provenance],
861
+ **source_diagnostics, "textureResolutions": texture_resolutions,
862
+ "unresolvedTextures": unresolved_textures, "ambiguousTextures": ambiguous_textures}
863
+
864
+
865
+ def meshes() -> list[bpy.types.Object]:
866
+ return [obj for obj in bpy.context.scene.objects if obj.type == "MESH" and len(obj.data.polygons) > 0]
867
+
868
+
869
+ def world_bounds(obj: bpy.types.Object) -> tuple[Vector, Vector]:
870
+ points = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
871
+ return Vector((min(point.x for point in points), min(point.y for point in points), min(point.z for point in points))), \
872
+ Vector((max(point.x for point in points), max(point.y for point in points), max(point.z for point in points)))
873
+
874
+
875
+ def bounds_of(objects: list[bpy.types.Object]) -> tuple[Vector, Vector]:
876
+ values = [world_bounds(obj) for obj in objects]
877
+ return Vector(tuple(min(row[0][axis] for row in values) for axis in range(3))), \
878
+ Vector(tuple(max(row[1][axis] for row in values) for axis in range(3)))
879
+
880
+
881
+ def centre(obj: bpy.types.Object) -> Vector:
882
+ lo, hi = world_bounds(obj)
883
+ return (lo + hi) * 0.5
884
+
885
+
886
+ def dimensions(obj: bpy.types.Object) -> Vector:
887
+ lo, hi = world_bounds(obj)
888
+ return hi - lo
889
+
890
+
891
+ def road_wheel_signal(obj: bpy.types.Object) -> bool:
892
+ """Recognize road-wheel meshes by semantic name or authored source library.
893
+
894
+ BeamNG flexbodies often call a rim by a style name (for example
895
+ `gavril_VSpoke_17x7_5H_F`) that contains neither `wheel` nor `rim`. The DAE
896
+ path remains explicit. Steering-wheel libraries are deliberately excluded.
897
+ """
898
+ if WHEEL.search(obj.name):
899
+ return True
900
+ source = str(obj.get("helix_source_dae", "")).lower()
901
+ return bool(re.search(r"wheel|rim", source) and not re.search(r"steer|wheelwell", source))
902
+
903
+
904
+ def canonical(point: Vector) -> list[float]:
905
+ # Blender/BeamNG +X-left, +Z-up, -Y-forward -> glTF/HELIX +X-left, +Y-up, +Z-forward.
906
+ return [round(float(point.x), 4), round(float(point.z), 4), round(float(-point.y), 4)]
907
+
908
+
909
+ def tag_for(point: Vector, split_y: float) -> str:
910
+ return ("l" if point.x >= 0 else "r") + ("f" if point.y <= split_y else "r")
911
+
912
+
913
+ def select_wheels(objects: list[bpy.types.Object]) -> tuple[dict[str, bpy.types.Object], dict[str, bpy.types.Object]]:
914
+ candidates = []
915
+ for obj in objects:
916
+ if not road_wheel_signal(obj):
917
+ continue
918
+ size = dimensions(obj)
919
+ c = centre(obj)
920
+ if max(size) < 0.12 or max(size) > 1.5 or abs(c.x) < 0.25:
921
+ continue
922
+ candidates.append(obj)
923
+ if not candidates:
924
+ return {}, {}
925
+ ys = sorted(centre(obj).y for obj in candidates)
926
+ split_y = (ys[0] + ys[-1]) / 2
927
+ rims: dict[str, bpy.types.Object] = {}
928
+ tyres: dict[str, bpy.types.Object] = {}
929
+ for obj in candidates:
930
+ tag = tag_for(centre(obj), split_y)
931
+ target = tyres if TYRE.search(obj.name) else rims
932
+ if tag not in target or len(obj.data.polygons) > len(target[tag].data.polygons):
933
+ target[tag] = obj
934
+ # Some sources call the complete wheel a tyre; allow that authored mesh to be the rim visual too.
935
+ for tag in ("lf", "rf", "lr", "rr"):
936
+ if tag not in rims and tag in tyres:
937
+ rims[tag] = tyres[tag]
938
+ return rims, tyres
939
+
940
+
941
+ def normalize_authored_tyres(objects: list[bpy.types.Object]) -> tuple[list[str], dict, dict]:
942
+ """Export exactly four instances of one rim-compatible authored tyre source."""
943
+ rims, _ = select_wheels(objects)
944
+ actual_rims = {tag: obj for tag, obj in rims.items() if not TYRE.search(obj.name)}
945
+ if len(actual_rims) != 4:
946
+ raise RuntimeError("cannot normalize authored tyres without four measured authored rim corners")
947
+ candidates = [obj for obj in objects if TYRE.search(obj.name)]
948
+ if not candidates:
949
+ raise RuntimeError("default configuration contains no authored tyre mesh")
950
+ rim_radii = {tag: max(dimensions(obj).y, dimensions(obj).z) * 0.5 for tag, obj in actual_rims.items()}
951
+ reference_radius = sum(rim_radii.values()) / len(rim_radii)
952
+
953
+ def compatibility(obj: bpy.types.Object) -> tuple[float, int, str] | None:
954
+ size = dimensions(obj)
955
+ radius = max(size.y, size.z) * 0.5
956
+ radial_roundness = abs(size.y-size.z) / max(size.y, size.z, 0.001)
957
+ ratio = radius / max(reference_radius, 0.001)
958
+ if not (0.04 <= size.x <= 0.65 and 0.18 <= radius <= 0.70
959
+ and radial_roundness <= 0.25 and 0.90 <= ratio <= 1.65):
960
+ return None
961
+ return abs(ratio-1.18) + radial_roundness * 0.5, -len(obj.data.polygons), obj.name
962
+
963
+ compatible = [(score, obj) for obj in candidates if (score := compatibility(obj)) is not None]
964
+ if not compatible:
965
+ measured = [(obj.name, tuple(round(value, 3) for value in dimensions(obj))) for obj in candidates]
966
+ raise RuntimeError(f"no authored tyre variant is compatible with the selected rims: {measured}")
967
+ _, source = min(compatible, key=lambda row: row[0])
968
+ source_identity = f"{Path(str(source.get('helix_source_dae', 'unknown'))).name}:{source.name}"
969
+ source_centre = centre(source)
970
+ source_radius = max(dimensions(source).y, dimensions(source).z) * 0.5
971
+ instances = []
972
+ for tag in ("lf", "rf", "lr", "rr"):
973
+ target = centre(actual_rims[tag])
974
+ desired_radius = max(0.22, min(0.55, rim_radii[tag] * 1.18))
975
+ copy = source.copy()
976
+ copy.data = source.data
977
+ copy.name = f"tyre_{tag}"
978
+ copy["helix_tyre_source"] = source_identity
979
+ bpy.context.scene.collection.objects.link(copy)
980
+ copy.matrix_world = (Matrix.Translation(target) @ Matrix.Scale(desired_radius / source_radius, 4)
981
+ @ Matrix.Translation(-source_centre)) @ copy.matrix_world
982
+ source_side = "l" if source_centre.x >= 0.25 else "r" if source_centre.x <= -0.25 else None
983
+ if ((source_side is not None and source_side != tag[0])
984
+ or (source_side is None and tag[0] == "r")):
985
+ copy.matrix_world = (Matrix.Translation(target) @ Matrix.Rotation(math.pi, 4, "Z")
986
+ @ Matrix.Translation(-target)) @ copy.matrix_world
987
+ instances.append(copy)
988
+ chosen_rims = set(actual_rims.values())
989
+ rim_sources = []
990
+ for tag, rim in actual_rims.items():
991
+ identity = f"{Path(str(rim.get('helix_source_dae', 'unknown'))).name}:{rim.name}"
992
+ rim["helix_rim_source"] = identity
993
+ rim_sources.append(identity)
994
+ # Configuration libraries contain centered templates and color variants.
995
+ # Once four measured rims have won custody, every other true road-wheel
996
+ # object must be removed so it cannot render beneath the vehicle.
997
+ for candidate in list(objects):
998
+ if candidate not in chosen_rims and road_wheel_signal(candidate):
999
+ bpy.data.objects.remove(candidate, do_unlink=True)
1000
+ objects[:] = meshes()
1001
+ receipt = {"source": source_identity, "instances": [obj.name for obj in instances]}
1002
+ rim_receipt = {"sources": sorted(set(rim_sources)),
1003
+ "instances": [f"rim_{tag}" for tag in ("lf", "rf", "lr", "rr")]}
1004
+ print(f"[beamng] normalized tyre custody={receipt}")
1005
+ print(f"[beamng] normalized rim custody={rim_receipt}")
1006
+ return [f"instanced one compatible authored tyre source {source_identity} to four measured rim centres",
1007
+ "pruned every unselected authored road-wheel template after four-corner rim custody"], receipt, rim_receipt
1008
+
1009
+
1010
+ def expand_axle_wheels(objects: list[bpy.types.Object]) -> list[str]:
1011
+ rims, tyres = select_wheels(objects)
1012
+ # `select_wheels` intentionally lets a complete tyre stand in for a rim
1013
+ # when a source only authors complete wheels. Do not let that fallback hide
1014
+ # centred trim-specific rim geometry: the 992 then rendered four bare rims
1015
+ # and silently discarded its four correctly placed base tyres.
1016
+ actual_rims = {tag: obj for tag, obj in rims.items() if not TYRE.search(obj.name)}
1017
+ if len(actual_rims) == 4 and len(tyres) == 4:
1018
+ return []
1019
+ sources = [obj for obj in objects if road_wheel_signal(obj)]
1020
+ body = vehicle_shell(objects)
1021
+ print(f"[beamng] axle wheel expansion sources={[obj.name for obj in sources]} body={body.name if body else None}")
1022
+ if not sources or not body:
1023
+ return []
1024
+ lo, hi = world_bounds(body)
1025
+ width, length = hi.x-lo.x, hi.y-lo.y
1026
+ # Measured road-car wheel centres sit at roughly 41.5% of body width. The
1027
+ # older 38.5% envelope rule put dependency wheels too far inboard; in the
1028
+ # 992 the driver's line of sight went over the inner fender and straight
1029
+ # onto the left-front tyre.
1030
+ half_track = width * 0.415
1031
+ # Axle-pair legacy assets carry no per-corner transforms. A measured 64%
1032
+ # shell-length wheelbase is conservative across compact and sports-car
1033
+ # bodies; the older 55% rule collapsed short legacy cars below a plausible
1034
+ # road-car wheelbase even though their authored body proportions were sound.
1035
+ front_y, rear_y = lo.y + length * 0.18, hi.y - length * 0.18
1036
+ target_radius = max(0.28, min(0.40, length * 0.075))
1037
+ hub_z = lo.z + target_radius
1038
+ expanded, messages = [], []
1039
+ for tyre_role, pattern in ((False, re.compile(r"(?<!steering)(?<!steering_)(?<!steer_)wheel(?!well)|(?<!t)rim", re.I)),
1040
+ (True, TYRE)):
1041
+ matching = [obj for obj in sources if pattern.search(obj.name)]
1042
+ def source_axle(obj: bpy.types.Object) -> str | None:
1043
+ dae = Path(str(obj.get("helix_source_dae", "")))
1044
+ parent = dae.parent.name.lower()
1045
+ stem = dae.stem.lower()
1046
+ if parent in {"f", "front"} or re.search(r"(?:^|_)f$|front", stem):
1047
+ return "f"
1048
+ if parent in {"r", "rear"} or re.search(r"(?:^|_)r$|rear", stem):
1049
+ return "r"
1050
+ return None
1051
+
1052
+ def source_rank(obj: bpy.types.Object) -> tuple[int, int, str]:
1053
+ return (int(bool(obj.get("helix_configured_mesh"))), len(obj.data.polygons), obj.name)
1054
+
1055
+ shared_tyre_source = max(matching, key=source_rank, default=None) if tyre_role else None
1056
+ axle_named = [obj for obj in matching if source_axle(obj) is not None
1057
+ or re.search(r"(?:^|_)[fr](?:\d|_|$)|front|rear", obj.name, re.I)]
1058
+ shared_rim_source = (max(matching, key=source_rank, default=None)
1059
+ if not tyre_role and len({source_axle(obj) for obj in axle_named if source_axle(obj)}) < 2
1060
+ else None)
1061
+ for axle, axle_pattern, target_y in (("f", re.compile(r"(?:^|_)f(?:\d|_|$)|front", re.I), front_y),
1062
+ ("r", re.compile(r"(?:^|_)r(?:\d|_|$)|rear", re.I), rear_y)):
1063
+ source = shared_tyre_source if tyre_role else shared_rim_source or max(
1064
+ (obj for obj in matching if source_axle(obj) == axle or axle_pattern.search(obj.name)),
1065
+ key=source_rank, default=None)
1066
+ if source is None:
1067
+ continue
1068
+ for side, x in (("l", half_track), ("r", -half_track)):
1069
+ copy = source.copy()
1070
+ copy.data = source.data.copy()
1071
+ copy.name = f"{'tyre' if tyre_role else 'wheel'}_source_{side}{axle}"
1072
+ copy["helix_expanded_axle"] = True
1073
+ bpy.context.scene.collection.objects.link(copy)
1074
+ target = Vector((x, target_y, hub_z))
1075
+ copy.matrix_world = Matrix.Translation(target - centre(copy)) @ copy.matrix_world
1076
+ # Legacy common libraries often store spare/axle-pair wheels
1077
+ # flat on Z. Canonical road wheels rotate around local X, so
1078
+ # orient the measured thinnest axis before any corner/radius
1079
+ # checks rather than rejecting an otherwise authored tyre.
1080
+ source_size = dimensions(copy)
1081
+ thin_axis = min(range(3), key=lambda index: source_size[index])
1082
+ if thin_axis == 2:
1083
+ copy.matrix_world = (Matrix.Translation(target) @ Matrix.Rotation(math.pi / 2, 4, "Y")
1084
+ @ Matrix.Translation(-target)) @ copy.matrix_world
1085
+ elif thin_axis == 1:
1086
+ copy.matrix_world = (Matrix.Translation(target) @ Matrix.Rotation(math.pi / 2, 4, "Z")
1087
+ @ Matrix.Translation(-target)) @ copy.matrix_world
1088
+ if side == "r":
1089
+ copy.matrix_world = (Matrix.Translation(target) @ Matrix.Rotation(math.pi, 4, "Z")
1090
+ @ Matrix.Translation(-target)) @ copy.matrix_world
1091
+ size = dimensions(copy)
1092
+ radius = max(size.y, size.z) * 0.5
1093
+ if radius > 0 and not 0.22 <= radius <= 0.55:
1094
+ current = centre(copy)
1095
+ copy.matrix_world = (Matrix.Translation(current) @ Matrix.Scale(target_radius / radius, 4)
1096
+ @ Matrix.Translation(-current)) @ copy.matrix_world
1097
+ expanded.append(copy)
1098
+ if expanded:
1099
+ for source in sources:
1100
+ if source.name in bpy.data.objects:
1101
+ bpy.data.objects.remove(source, do_unlink=True)
1102
+ objects[:] = meshes()
1103
+ print(f"[beamng] expanded wheel nodes={[(obj.name, tuple(round(v, 3) for v in centre(obj)), tuple(round(v, 3) for v in dimensions(obj))) for obj in expanded]}")
1104
+ messages.append(f"expanded authored axle wheel meshes to four measured body-envelope hub placements")
1105
+ return messages
1106
+
1107
+
1108
+ def fit_authored_wheels(objects: list[bpy.types.Object], rims: dict[str, bpy.types.Object],
1109
+ tyres: dict[str, bpy.types.Object], specified_tyre_radius: float | None = None) -> list[str]:
1110
+ candidates = [obj for obj in objects if BRAKE_HARDWARE.search(obj.name)
1111
+ and not road_wheel_signal(obj) and abs(centre(obj).x) > 0.3 and max(dimensions(obj)) < 1.0]
1112
+ repairs = []
1113
+ if candidates:
1114
+ ys = sorted(centre(obj).y for obj in candidates)
1115
+ split = (ys[0] + ys[-1]) / 2
1116
+ hubs = {}
1117
+ for obj in candidates:
1118
+ tag = tag_for(centre(obj), split)
1119
+ if tag not in hubs or len(obj.data.polygons) > len(hubs[tag].data.polygons):
1120
+ hubs[tag] = obj
1121
+ else:
1122
+ hubs = {}
1123
+ body = semantic_object(objects, re.compile(r"(?:^|_)(?:body|bodyshell|shell|chassis)(?:$|_)", re.I))
1124
+ target_radius = max(0.24, min(0.46, dimensions(body).y * 0.075)) if body else 0.32
1125
+ for tag in ("lf", "rf", "lr", "rr"):
1126
+ authored = {obj for obj in (rims.get(tag), tyres.get(tag)) if obj is not None}
1127
+ measured_tyre = tyres.get(tag)
1128
+ measured_tyre_radius = (max(dimensions(measured_tyre).y, dimensions(measured_tyre).z) * 0.5
1129
+ if measured_tyre is not None else None)
1130
+ configured_scale = (specified_tyre_radius / measured_tyre_radius
1131
+ if specified_tyre_radius and measured_tyre_radius else 1.0)
1132
+ # Four authored per-corner discs/drums/hubs are stronger evidence than
1133
+ # a body-envelope estimate. A partial/ambiguous legacy set is not.
1134
+ expanded_corner = rims.get(tag) if rims.get(tag) is not None and rims[tag].get("helix_expanded_axle") else None
1135
+ measured_hub = hubs.get(tag) if len(hubs) == 4 else None
1136
+ hub = centre(measured_hub) if measured_hub else (centre(expanded_corner) if expanded_corner else (
1137
+ centre(rims[tag]) if tag in rims else None))
1138
+ for obj in authored:
1139
+ lo, hi = world_bounds(obj)
1140
+ current_centre = (lo + hi) * 0.5
1141
+ radius = max(hi.y-lo.y, hi.z-lo.z) * 0.5
1142
+ fitted_radius = target_radius
1143
+ needs_radius_fit = not 0.22 <= radius <= 0.55 or abs(configured_scale - 1.0) > 0.04
1144
+ if abs(configured_scale - 1.0) > 0.04:
1145
+ fitted_radius = radius * configured_scale
1146
+ rim = rims.get(tag)
1147
+ if TYRE.search(obj.name) and rim is not None and rim is not obj:
1148
+ rim_size = dimensions(rim)
1149
+ rim_radius = max(rim_size.y, rim_size.z) * 0.5
1150
+ ratio = radius / max(0.001, rim_radius)
1151
+ # A shared dependency tyre must wrap the selected trim rim,
1152
+ # not merely fall inside a broad road-wheel range. The first
1153
+ # common-asset Porsche pass produced a 0.362 m tyre around a
1154
+ # 0.274 m rim and visibly pushed through the GT3 RS arch.
1155
+ if ratio < 1.08 or ratio > 1.28:
1156
+ fitted_radius = rim_radius * 1.22
1157
+ needs_radius_fit = True
1158
+ scale = fitted_radius / radius if radius > 0 and needs_radius_fit else 1.0
1159
+ target = hub or current_centre
1160
+ obj.matrix_world = (Matrix.Translation(target) @ Matrix.Scale(scale, 4)
1161
+ @ Matrix.Translation(-current_centre)) @ obj.matrix_world
1162
+ if abs(scale - 1.0) > 0.01 or (target-current_centre).length > 0.02:
1163
+ repairs.append(f"fitted authored {obj.name} to measured {tag} hub at {fitted_radius:.3f} m radius")
1164
+ return repairs
1165
+
1166
+
1167
+ def configured_tyre_radius(parts: list[str]) -> float | None:
1168
+ """Return the physical outside radius encoded by common BeamNG tyre part IDs."""
1169
+ sizes = []
1170
+ for part in parts:
1171
+ match = re.search(r"(?:tire|tyre)[^\d]*(\d{3})_(\d{2})_(\d{2})(?:_|$)", part, re.I)
1172
+ if not match:
1173
+ continue
1174
+ width_mm, aspect, rim_inches = map(float, match.groups())
1175
+ radius = rim_inches * 0.0127 + width_mm * (aspect / 100.0) / 1000.0
1176
+ if 0.22 <= radius <= 0.55:
1177
+ sizes.append(radius)
1178
+ return sum(sizes) / len(sizes) if sizes else None
1179
+
1180
+
1181
+ def normalize_wheel_hardware(objects: list[bpy.types.Object],
1182
+ rims: dict[str, bpy.types.Object]) -> tuple[list[str], dict | None]:
1183
+ """Keep or instance selected authored brake/hub hardware at exactly four wheel corners."""
1184
+ candidates = [obj for obj in objects if BRAKE_HARDWARE.search(obj.name) and not road_wheel_signal(obj)]
1185
+ if not candidates or len(rims) != 4:
1186
+ return [], None
1187
+ def rank(obj: bpy.types.Object) -> tuple:
1188
+ strong_brake = int(bool(re.search(r"disc|rotor|drum", obj.name, re.I)))
1189
+ size = max(dimensions(obj))
1190
+ return strong_brake, -abs(size-0.38), len(obj.data.polygons)
1191
+ measured = [obj for obj in candidates if abs(centre(obj).x) > 0.3]
1192
+ selected: dict[str, bpy.types.Object] = {}
1193
+ if measured:
1194
+ ys = sorted(centre(obj).y for obj in measured)
1195
+ split = (ys[0] + ys[-1]) / 2
1196
+ for obj in measured:
1197
+ tag = tag_for(centre(obj), split)
1198
+ if tag not in selected or rank(obj) > rank(selected[tag]):
1199
+ selected[tag] = obj
1200
+ instances: dict[str, bpy.types.Object] = {}
1201
+ if len(selected) == 4:
1202
+ instances = selected
1203
+ else:
1204
+ front_pattern = re.compile(r"(?:^|_)f(?:_|$)|front", re.I)
1205
+ rear_pattern = re.compile(r"(?:^|_)r(?:_|$)|rear", re.I)
1206
+ axle_sources = {
1207
+ "f": max((obj for obj in candidates if front_pattern.search(obj.name)), key=rank, default=None),
1208
+ "r": max((obj for obj in candidates if rear_pattern.search(obj.name)), key=rank, default=None),
1209
+ }
1210
+ shared = max(candidates, key=rank)
1211
+ for tag in ("lf", "rf", "lr", "rr"):
1212
+ source = axle_sources[tag[1]] or shared
1213
+ target = centre(rims[tag])
1214
+ copy = source.copy()
1215
+ copy.data = source.data.copy()
1216
+ copy["helix_brake_source_identity"] = f"{Path(str(source.get('helix_source_dae', 'unknown'))).name}:{source.name}"
1217
+ bpy.context.scene.collection.objects.link(copy)
1218
+ copy.matrix_world = Matrix.Translation(target-centre(copy)) @ copy.matrix_world
1219
+ if tag[0] == "r":
1220
+ copy.matrix_world = (Matrix.Translation(target) @ Matrix.Rotation(math.pi, 4, "Z")
1221
+ @ Matrix.Translation(-target)) @ copy.matrix_world
1222
+ instances[tag] = copy
1223
+ chosen = set(instances.values())
1224
+ sources = []
1225
+ for tag, obj in instances.items():
1226
+ identity = str(obj.get("helix_brake_source_identity",
1227
+ f"{Path(str(obj.get('helix_source_dae', 'unknown'))).name}:{obj.name}"))
1228
+ obj["helix_brake_source"] = identity
1229
+ obj.name = f"brake_{tag}"
1230
+ sources.append(identity)
1231
+ for candidate in candidates:
1232
+ if candidate not in chosen and candidate.name in bpy.data.objects:
1233
+ bpy.data.objects.remove(candidate, do_unlink=True)
1234
+ objects[:] = meshes()
1235
+ receipt = {"sources": sorted(set(sources)), "instances": [f"brake_{tag}" for tag in ("lf", "rf", "lr", "rr")]}
1236
+ print(f"[beamng] normalized wheel hardware custody={receipt}")
1237
+ action = "retained four authored per-corner brake/hub meshes" if len(selected) == 4 else \
1238
+ "instanced selected authored axle brake/hub templates to four measured wheel centres"
1239
+ return [action], receipt
1240
+
1241
+
1242
+ def rename_wheels(rims: dict[str, bpy.types.Object], tyres: dict[str, bpy.types.Object]) -> None:
1243
+ chosen = set(rims.values()) | set(tyres.values())
1244
+ for obj in chosen:
1245
+ obj.name = f"source_{obj.name}"
1246
+ for tag, obj in rims.items():
1247
+ obj.name = f"rim_{tag}"
1248
+ for tag, obj in tyres.items():
1249
+ if obj not in rims.values():
1250
+ obj.name = f"tyre_{tag}"
1251
+
1252
+
1253
+ def seat_points(objects: list[bpy.types.Object]) -> tuple[dict[str, Vector], list[str]]:
1254
+ candidates = [obj for obj in objects if re.search(r"seat", obj.name, re.I)
1255
+ and not re.search(r"seat.?belt|belt.*seat|seat.?rail|seat.?bracket|seat.?mount", obj.name, re.I)
1256
+ and max(dimensions(obj)) >= 0.25]
1257
+ if not candidates:
1258
+ return {}, []
1259
+ # Collapse multi-piece seats by side/row, keeping the most substantial authored mesh.
1260
+ ys = sorted(centre(obj).y for obj in candidates)
1261
+ split = (ys[0] + ys[-1]) / 2 if len(ys) > 2 and ys[-1] - ys[0] > 0.45 else math.inf
1262
+ selected: dict[str, bpy.types.Object] = {}
1263
+ for obj in candidates:
1264
+ c = centre(obj)
1265
+ # A singular `seat_R` is normally the right-front seat (CCF); plural
1266
+ # `seats_R` and `bench_R` are the common rear-bench conventions.
1267
+ explicitly_rear = bool(re.search(r"(?:seats|bench)_R(?:$|_)|rear", obj.name, re.I))
1268
+ front = not explicitly_rear and c.y <= split
1269
+ if abs(c.x) < 0.08 and dimensions(obj).x >= 0.5:
1270
+ # One authored rear bench represents two real seating positions.
1271
+ selected["dr" if not front else "df"] = obj
1272
+ selected["pr" if not front else "pf"] = obj
1273
+ continue
1274
+ key = ("d" if c.x >= 0 else "p") + ("f" if front else "r")
1275
+ if key not in selected or len(obj.data.polygons) > len(selected[key].data.polygons):
1276
+ selected[key] = obj
1277
+ sockets: dict[str, Vector] = {}
1278
+ names = {"df": "seat_dside_f", "pf": "seat_pside_f", "dr": "seat_dside_r1", "pr": "seat_pside_r1"}
1279
+ for key, obj in selected.items():
1280
+ lo, hi = world_bounds(obj)
1281
+ lateral = centre(obj).x
1282
+ if abs(lateral) < 0.08 and hi.x-lo.x >= 0.5:
1283
+ lateral = (hi.x-lo.x) * (0.25 if key.startswith("d") else -0.25)
1284
+ # A source seat mesh centre is usually the backrest; the H-point is over the cushion.
1285
+ sockets[names[key]] = Vector((lateral, lo.y * 0.35 + hi.y * 0.65, lo.z + min(0.42, (hi.z-lo.z) * 0.38)))
1286
+ return sockets, [obj.name for obj in selected.values()]
1287
+
1288
+
1289
+ def semantic_object(objects: list[bpy.types.Object], pattern: re.Pattern) -> bpy.types.Object | None:
1290
+ candidates = [obj for obj in objects if pattern.search(obj.name)]
1291
+ return max(candidates, key=lambda obj: len(obj.data.polygons), default=None)
1292
+
1293
+
1294
+ def vehicle_shell(objects: list[bpy.types.Object]) -> bpy.types.Object | None:
1295
+ named = semantic_object(objects, re.compile(r"(?:^|_)(?:body|bodyshell|shell|chassis)(?:$|_)", re.I))
1296
+ if named:
1297
+ return named
1298
+ candidates = [obj for obj in objects if dimensions(obj).x >= 1.2 and dimensions(obj).y >= 3.0
1299
+ and dimensions(obj).z >= 0.55 and not road_wheel_signal(obj)]
1300
+ return max(candidates, key=lambda obj: len(obj.data.polygons), default=None)
1301
+
1302
+
1303
+ def decimate(objects: list[bpy.types.Object], limit: int = 285_000) -> tuple[int, int]:
1304
+ before = sum(len(obj.data.polygons) for obj in objects)
1305
+ if before <= limit:
1306
+ return before, before
1307
+ # Preserve first-person cabin controls and wheels. Exterior panels may receive a shallow
1308
+ # source-mesh decimation so a high-detail body cannot make the whole vehicle miss budget.
1309
+ protected = [obj for obj in objects if INTERIOR.search(obj.name) or road_wheel_signal(obj)]
1310
+ protected_count = sum(len(obj.data.polygons) for obj in protected)
1311
+ reducible = [obj for obj in objects if obj not in protected and len(obj.data.polygons) > 200]
1312
+ reducible_count = max(1, before - protected_count)
1313
+ target_reducible = max(5_000, limit - protected_count)
1314
+ ratio = min(1.0, target_reducible / reducible_count)
1315
+ for obj in reducible:
1316
+ bpy.context.view_layer.objects.active = obj
1317
+ obj.select_set(True)
1318
+ modifier = obj.modifiers.new("HELIX_web_budget", "DECIMATE")
1319
+ modifier.ratio = max(0.04, ratio)
1320
+ try:
1321
+ bpy.ops.object.modifier_apply(modifier=modifier.name)
1322
+ except Exception as error:
1323
+ print(f"[beamng] decimation skipped for {obj.name}: {error}")
1324
+ obj.select_set(False)
1325
+ return before, sum(len(obj.data.polygons) for obj in objects)
1326
+
1327
+
1328
+ def main() -> None:
1329
+ args = arguments()
1330
+ source_root = Path(args.source_root).resolve()
1331
+ vehicle_root = Path(args.vehicle_root).resolve()
1332
+ bpy.ops.wm.read_factory_settings(use_empty=True)
1333
+ for dae in args.dae:
1334
+ print(f"[beamng] importing {dae}")
1335
+ build_collada(Path(dae).resolve())
1336
+ objects = meshes()
1337
+ if not objects:
1338
+ raise RuntimeError("selected BeamNG DAE contains no renderable meshes")
1339
+ wanted, config = configured_meshes(vehicle_root, source_root, args.dae,
1340
+ args.configuration_hint, {obj.name for obj in objects},
1341
+ [Path(value).resolve() for value in args.material_root],
1342
+ Path(args.part_index).resolve(), args.bridge_plan_key,
1343
+ args.source_sha, args.common_closure)
1344
+ has_dedicated_wheels = any("wheels" in [part.lower() for part in Path(dae).parts] for dae in args.dae)
1345
+ objects, missing_meshes = filter_configuration(objects, wanted, has_dedicated_wheels,
1346
+ args.configuration_hint)
1347
+ config["missingMeshes"] = missing_meshes
1348
+ print(f"[beamng] default config {config['file']}: {len(config['parts'])} parts, "
1349
+ f"{len(objects)} present meshes, {len(missing_meshes)} missing")
1350
+
1351
+ if args.mirror_z:
1352
+ reflection = Matrix.Scale(-1.0, 4, Vector((0.0, 1.0, 0.0)))
1353
+ for obj in objects:
1354
+ obj.matrix_world = reflection @ obj.matrix_world
1355
+
1356
+ # Put the measured tyre contact patch at zero. Underbody/optional meshes can extend below
1357
+ # the tyres and must not lift the whole cabin (the original RX-7 roof-seat defect).
1358
+ axle_repairs = expand_axle_wheels(objects)
1359
+ initial_rims, initial_tyres = select_wheels(objects)
1360
+ wheel_repairs = fit_authored_wheels(objects, initial_rims, initial_tyres,
1361
+ configured_tyre_radius(config.get("parts", [])))
1362
+ brake_repairs, brake_custody = normalize_wheel_hardware(objects, initial_rims)
1363
+ grounding = list(initial_tyres.values()) or list(initial_rims.values())
1364
+ if len(initial_rims) != 4:
1365
+ raise RuntimeError(f"default configuration exposes {len(initial_rims)}/4 measurable wheel corners")
1366
+ ground = min(world_bounds(obj)[0].z for obj in grounding)
1367
+ lowest = min(objects, key=lambda obj: world_bounds(obj)[0].z)
1368
+ highest = max(objects, key=lambda obj: world_bounds(obj)[1].z)
1369
+ print(f"[beamng] ground={ground:.4f} from tyres; vertical extremes "
1370
+ f"{lowest.name}={world_bounds(lowest)[0].z:.4f}, {highest.name}={world_bounds(highest)[1].z:.4f}")
1371
+ shift = Matrix.Translation(Vector((0.0, 0.0, -ground)))
1372
+ for obj in objects:
1373
+ obj.matrix_world = shift @ obj.matrix_world
1374
+ obj.select_set(True)
1375
+ bpy.context.view_layer.objects.active = objects[0]
1376
+ bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
1377
+
1378
+ tyre_repairs, tyre_custody, rim_custody = normalize_authored_tyres(objects)
1379
+
1380
+ material_roots = [source_root, *(Path(value).resolve() for value in args.material_root)]
1381
+ materials = restore_materials(material_roots, Path(args.palette).resolve(), vehicle_root,
1382
+ config.get("paintBaseColor"))
1383
+ rims, tyres = select_wheels(objects)
1384
+ rename_wheels(rims, tyres)
1385
+ before, after = decimate(objects)
1386
+ lo, hi = bounds_of(objects)
1387
+ sockets_raw, seat_sources = seat_points(objects)
1388
+ steering_obj = semantic_object(objects, re.compile(r"steer(?:ing)?(?:wheel|rim)?", re.I))
1389
+ engine_obj = semantic_object(objects, re.compile(r"(?:^|[_-])engine(?:[_-]|\d|$)", re.I))
1390
+ exhaust_obj = semantic_object(objects, re.compile(r"exhaust|muffler|tail.?pipe", re.I))
1391
+ steering = centre(steering_obj) if steering_obj else None
1392
+ seat_side_repaired = False
1393
+ if steering is not None and "seat_dside_f" in sockets_raw and "seat_pside_f" in sockets_raw:
1394
+ driver_distance = abs(sockets_raw["seat_dside_f"].x-steering.x)
1395
+ passenger_distance = abs(sockets_raw["seat_pside_f"].x-steering.x)
1396
+ if passenger_distance + 0.05 < driver_distance:
1397
+ sockets_raw["seat_dside_f"], sockets_raw["seat_pside_f"] = (
1398
+ sockets_raw["seat_pside_f"], sockets_raw["seat_dside_f"])
1399
+ if "seat_dside_r1" in sockets_raw and "seat_pside_r1" in sockets_raw:
1400
+ sockets_raw["seat_dside_r1"], sockets_raw["seat_pside_r1"] = (
1401
+ sockets_raw["seat_pside_r1"], sockets_raw["seat_dside_r1"])
1402
+ seat_side_repaired = True
1403
+
1404
+ # Preserve what was genuinely authored before any ergonomic repair. Runtime
1405
+ # sockets may be repaired, but those repairs must never make an incomplete
1406
+ # source look eligible to the Bridge acceptance gate.
1407
+ authored_seat_count = len(sockets_raw)
1408
+ authored_steering_mesh = steering_obj is not None
1409
+
1410
+ repairs: list[str] = list(axle_repairs) + list(wheel_repairs) + list(brake_repairs) + list(tyre_repairs)
1411
+ if seat_side_repaired:
1412
+ repairs.append("assigned driver/passenger sockets from the authored steering-wheel side")
1413
+ driver = sockets_raw.get("seat_dside_f")
1414
+ cabin_shell = vehicle_shell(objects)
1415
+ roof = world_bounds(cabin_shell)[1].z if cabin_shell else hi.z
1416
+ interior_objects = [obj for obj in objects if INTERIOR.search(obj.name)]
1417
+ if driver is None and len(interior_objects) >= 8 and cabin_shell and len(rims) == 4:
1418
+ shell_lo, shell_hi = world_bounds(cabin_shell)
1419
+ front = (canonical(centre(rims["lf"]))[2] + canonical(centre(rims["rf"]))[2]) / 2
1420
+ rear = (canonical(centre(rims["lr"]))[2] + canonical(centre(rims["rr"]))[2]) / 2
1421
+ if steering is not None:
1422
+ # Combined legacy cabins may not expose separate seat nodes. Anchor
1423
+ # their H-point behind the actual authored steering rim instead of
1424
+ # guessing from the body midpoint; this supports both LHD and RHD
1425
+ # layouts and keeps the cockpit lens behind the controls.
1426
+ seat_z = canonical(steering)[2] - 0.45
1427
+ seat_x = steering.x
1428
+ else:
1429
+ seat_z = (front + rear) / 2 + 0.16
1430
+ seat_x = (shell_hi.x-shell_lo.x) * 0.178
1431
+ seat_height = roof - 0.86
1432
+ sockets_raw = {"seat_dside_f": Vector((seat_x, -seat_z, seat_height)),
1433
+ "seat_pside_f": Vector((-seat_x, -seat_z, seat_height))}
1434
+ driver = sockets_raw["seat_dside_f"]
1435
+ if steering is None:
1436
+ steering = Vector((driver.x, driver.y - 0.45, driver.z + 0.30))
1437
+ seat_sources = [obj.name for obj in interior_objects]
1438
+ repairs.append("inferred two H-points and control anchor inside source-authored combined cabin geometry")
1439
+ if driver is not None and roof - driver.z < 0.86:
1440
+ delta = 0.86 - (roof - driver.z)
1441
+ for key in list(sockets_raw):
1442
+ sockets_raw[key].z -= delta
1443
+ driver = sockets_raw["seat_dside_f"]
1444
+ repairs.append(f"lowered measured H-points {delta:.3f} m for occupant headroom")
1445
+ if driver is not None and steering is not None:
1446
+ driver_gltf, steering_gltf = canonical(driver), canonical(steering)
1447
+ reach = math.dist(driver_gltf, steering_gltf)
1448
+ if not (0.30 <= reach <= 0.80 and steering.z-driver.z >= 0.14 and -steering.y-(-driver.y) >= 0.20):
1449
+ # Keep source lateral placement; refit only the interaction point from the measured H-point.
1450
+ steering = Vector((steering.x if abs(steering.x-driver.x) <= 0.18 else driver.x,
1451
+ driver.y - 0.45, driver.z + 0.30))
1452
+ repairs.append("re-fitted steering interaction point from measured driver H-point")
1453
+
1454
+ wheel_sockets = {f"wheel_{tag}": canonical(centre(obj)) for tag, obj in rims.items()}
1455
+ sockets = {**wheel_sockets, **{key: canonical(value) for key, value in sockets_raw.items()}}
1456
+ if steering is not None:
1457
+ sockets["steering"] = canonical(steering)
1458
+ if engine_obj is not None:
1459
+ sockets["engine"] = canonical(centre(engine_obj))
1460
+ rear_axle = min(sockets["wheel_lr"][2], sockets["wheel_rr"][2])
1461
+ body_length = dimensions(cabin_shell).y if cabin_shell else hi.y-lo.y
1462
+ if exhaust_obj:
1463
+ exhaust_lo, exhaust_hi = world_bounds(exhaust_obj)
1464
+ authored_exhaust = [round(float(centre(exhaust_obj).x), 4),
1465
+ round(float(exhaust_lo.z + min(0.12, (exhaust_hi.z-exhaust_lo.z) * 0.25)), 4),
1466
+ round(float(-exhaust_hi.y), 4)]
1467
+ if authored_exhaust[2] < rear_axle - 0.3:
1468
+ sockets["exhaust"] = authored_exhaust
1469
+ else:
1470
+ sockets["exhaust"] = [authored_exhaust[0], authored_exhaust[1],
1471
+ round(float(rear_axle - max(0.45, body_length * 0.20)), 4)]
1472
+ repairs.append("extended exhaust interaction point from source assembly to measured rear overhang")
1473
+ else:
1474
+ sockets["exhaust"] = [0.0, round(float(lo.z + 0.35), 4),
1475
+ round(float(rear_axle - max(0.45, body_length * 0.20)), 4)]
1476
+ repairs.append("derived exhaust interaction point from measured rear axle and source body length")
1477
+ if driver is not None:
1478
+ driver_canonical = canonical(driver)
1479
+ pedal_y = max(float(lo.z + 0.12), driver_canonical[1] - 0.40)
1480
+ pedal_z = min(float(-lo.y - 0.18), driver_canonical[2] + 0.72)
1481
+ sockets["pedal_throttle"] = [round(driver_canonical[0] - 0.07, 4), round(pedal_y, 4), round(pedal_z, 4)]
1482
+ sockets["pedal_brake"] = [round(driver_canonical[0] + 0.02, 4), round(pedal_y, 4), round(pedal_z, 4)]
1483
+ sockets["pedal_clutch"] = [round(driver_canonical[0] + 0.12, 4), round(pedal_y, 4), round(pedal_z, 4)]
1484
+ # Put the lens at the eye plane and FORWARD of the head centre. The old +2 cm anchor remained inside
1485
+ # the avatar's skull/upper torso on low sports seats, producing a first-person frame filled by skin.
1486
+ cockpit_forward = driver_canonical[2] + 0.04
1487
+ if steering_obj is not None:
1488
+ # The lens must remain behind the actual authored rim, including
1489
+ # legacy cabins whose cushion-derived H-point sits unusually close
1490
+ # to it. Otherwise the QA camera can land beyond the wheel and show
1491
+ # only the hood despite a valid interior.
1492
+ cockpit_forward = min(cockpit_forward, canonical(centre(steering_obj))[2] - 0.32)
1493
+ sockets["cam_cockpit"] = [driver_canonical[0], round(min(float(hi.z - 0.14), driver.z + 0.65), 4),
1494
+ round(cockpit_forward, 4)]
1495
+ sockets["cam_chase"] = [0.0, round(float(lo.z + 1.25), 4), round(float(-hi.y - 4.8), 4)]
1496
+ sockets["cam_hood"] = [0.0, round(float(lo.z + (hi.z-lo.z)*0.72), 4), round(float(-lo.y - 0.25), 4)]
1497
+
1498
+ wheel_radii = []
1499
+ for obj in rims.values():
1500
+ size = dimensions(obj)
1501
+ wheel_radii.append(max(size.y, size.z) * 0.5)
1502
+ audio_limits = {extension: AUDIO_SCAN_LIMIT for extension in (".wav", ".ogg", ".flac", ".mp3")}
1503
+ audio_files = len(bounded_asset_paths([vehicle_root], audio_limits))
1504
+ audio_refs = int(bool(config.get("selectedPartSummary", {}).get("semanticEvidence", {}).get("audio")))
1505
+ def geometry_receipt(value: str) -> str:
1506
+ path = Path(value).resolve()
1507
+ try:
1508
+ return str(path.relative_to(source_root))
1509
+ except ValueError:
1510
+ return f"common-dependency:{path.name}"
1511
+
1512
+ receipt = {
1513
+ "HELIX_frame": "+X left, +Y up, +Z forward; metres" + (" (source rear-at-+Z mirrored during import)" if args.mirror_z else ""),
1514
+ "HELIX_bounds": {"min": canonical(Vector((lo.x, hi.y, lo.z))), "max": canonical(Vector((hi.x, lo.y, hi.z)))},
1515
+ "HELIX_vehicle": {
1516
+ "version": 1,
1517
+ "class": "sports-car" if len(sockets_raw) <= 2 else "car",
1518
+ "wheelRadius": round(float(sum(wheel_radii) / len(wheel_radii)), 4) if wheel_radii else None,
1519
+ "sockets": sockets,
1520
+ },
1521
+ "HELIX_import_quality": {
1522
+ "geometry": [geometry_receipt(value) for value in args.dae],
1523
+ "configuration": config,
1524
+ "socketSources": {"wheels": [obj.name for obj in rims.values()], "seats": seat_sources,
1525
+ "steering": steering_obj.name if steering_obj else None,
1526
+ "engine": engine_obj.name if engine_obj else None,
1527
+ "exhaust": exhaust_obj.name if exhaust_obj else "rear-bound fallback"},
1528
+ "tyreCustody": tyre_custody,
1529
+ "rimCustody": rim_custody,
1530
+ "brakeCustody": brake_custody,
1531
+ "materials": materials,
1532
+ "triangles": {"source": before, "export": after},
1533
+ "audio": {"recordedFiles": audio_files, "jbeamReferences": audio_refs},
1534
+ "sourceMeasurements": {
1535
+ "seatHPoints": authored_seat_count,
1536
+ "steeringMesh": authored_steering_mesh,
1537
+ "interiorMeshes": len(interior_objects),
1538
+ "engineMesh": engine_obj is not None,
1539
+ "exhaustMesh": exhaust_obj is not None,
1540
+ "massKg": None,
1541
+ },
1542
+ "repairs": repairs,
1543
+ },
1544
+ }
1545
+
1546
+ for obj in objects:
1547
+ bpy.context.view_layer.objects.active = obj
1548
+ try:
1549
+ bpy.ops.object.shade_auto_smooth(angle=math.radians(35))
1550
+ except Exception:
1551
+ pass
1552
+ obj.select_set(True)
1553
+ bpy.context.view_layer.objects.active = objects[0]
1554
+ bpy.ops.export_scene.gltf(filepath=str(Path(args.output).resolve()), export_format="GLB", use_selection=True,
1555
+ export_yup=True, export_apply=True, export_image_format="AUTO",
1556
+ export_materials="EXPORT", export_texcoords=True, export_normals=True,
1557
+ export_cameras=False, export_lights=False, export_extras=True)
1558
+ Path(args.receipt).write_text(json.dumps(receipt, indent=2) + "\n")
1559
+ print(f"[beamng] exported {after} triangles, {materials['mapped']}/{materials['materials']} mapped materials")
1560
+
1561
+
1562
+ if __name__ == "__main__":
1563
+ main()