evomeme 0.1.9 → 0.1.10-beta.13

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 (477) hide show
  1. package/README.md +2 -0
  2. package/dist/canvas-public/app.js +509 -5999
  3. package/dist/canvas-public/assets/presets/catalog.json +47 -0
  4. package/dist/canvas-public/assets/presets/tiles/A_01_CaoDi.png +0 -0
  5. package/dist/canvas-public/assets/presets/tiles/A_02_ShaMo.png +0 -0
  6. package/dist/canvas-public/assets/presets/tiles/A_03_TianKong.png +0 -0
  7. package/dist/canvas-public/assets/presets/tiles/A_04_ShuiBo.png +0 -0
  8. package/dist/canvas-public/assets/presets/tiles/A_05_HaiMian.png +0 -0
  9. package/dist/canvas-public/assets/presets/tiles/A_06_MuDiBan.png +0 -0
  10. package/dist/canvas-public/assets/presets/tiles/A_07_ShiZhuan.png +0 -0
  11. package/dist/canvas-public/assets/presets/tiles/A_08_AnRongYan.png +0 -0
  12. package/dist/canvas-public/assets/presets/tiles/A_09_QingTaiShi.png +0 -0
  13. package/dist/canvas-public/assets/presets/tiles/A_10_ShenHai.png +0 -0
  14. package/dist/canvas-public/assets/presets/tiles/A_11_HuiYanShi.png +0 -0
  15. package/dist/canvas-public/assets/presets/tiles/A_12_HongTu.png +0 -0
  16. package/dist/canvas-public/assets/presets/tiles/A_13_GengDi.png +0 -0
  17. package/dist/canvas-public/assets/presets/tiles/A_14_HuoShanYan.png +0 -0
  18. package/dist/canvas-public/assets/presets/tiles/A_15_NiTu.png +0 -0
  19. package/dist/canvas-public/assets/presets/tiles/A_16_HuangSha.png +0 -0
  20. package/dist/canvas-public/assets/presets/tiles/A_17_CongLin.png +0 -0
  21. package/dist/canvas-public/assets/presets/tiles/A_18_ZiYanShi.png +0 -0
  22. package/dist/canvas-public/assets/presets/tiles/A_19_MoGuTu.png +0 -0
  23. package/dist/canvas-public/assets/presets/tiles/A_20_HuaDian.png +0 -0
  24. package/dist/canvas-public/assets/presets/tiles/A_21_HaiLang.png +0 -0
  25. package/dist/canvas-public/assets/presets/tiles/A_22_GanTu.png +0 -0
  26. package/dist/canvas-public/assets/presets/tiles/A_23_QingTaiShiBan.png +0 -0
  27. package/dist/canvas-public/assets/presets/tiles/A_24_XingKong.png +0 -0
  28. package/dist/canvas-public/assets/presets/tiles/A_25_MoFaFuWen.png +0 -0
  29. package/dist/canvas-public/assets/presets/tiles/A_26_ShiTuYeDi.png +0 -0
  30. package/dist/canvas-public/assets/presets/tiles/A_27_LanZhuan.png +0 -0
  31. package/dist/canvas-public/assets/presets/tiles/A_28_HongZhuan.png +0 -0
  32. package/dist/canvas-public/assets/presets/tiles/A_29_YueQiu.png +0 -0
  33. package/dist/canvas-public/assets/presets/tiles/A_30_JinShuGe.png +0 -0
  34. package/dist/canvas-public/assets/presets/tiles/A_31_JinShuJingGai.png +0 -0
  35. package/dist/canvas-public/assets/presets/tiles/A_32_ShuZiChuang.png +0 -0
  36. package/dist/canvas-public/canvas/actions.js +144 -0
  37. package/dist/canvas-public/canvas/camera.js +28 -0
  38. package/dist/canvas-public/canvas/chrome.js +14 -0
  39. package/dist/canvas-public/canvas/data.js +123 -0
  40. package/dist/canvas-public/canvas/dependencies.js +19 -0
  41. package/dist/canvas-public/canvas/interactions.js +544 -0
  42. package/dist/canvas-public/canvas/panel-resize.js +5 -0
  43. package/dist/canvas-public/canvas/renderer.js +297 -0
  44. package/dist/canvas-public/canvas/selection.js +62 -0
  45. package/dist/canvas-public/canvas/serialization.js +118 -0
  46. package/dist/canvas-public/canvas/sync.js +202 -0
  47. package/dist/canvas-public/core/api.js +32 -0
  48. package/dist/canvas-public/core/feature-registry.js +29 -0
  49. package/dist/canvas-public/core/feature-state.js +228 -0
  50. package/dist/canvas-public/core/layout.js +133 -0
  51. package/dist/canvas-public/core/operations.js +38 -0
  52. package/dist/canvas-public/core/project.js +203 -0
  53. package/dist/canvas-public/core/state.js +27 -0
  54. package/dist/canvas-public/dialogs/context-menu.js +118 -0
  55. package/dist/canvas-public/elements/base.js +100 -0
  56. package/dist/canvas-public/elements/connections.js +105 -0
  57. package/dist/canvas-public/elements/drag.js +53 -0
  58. package/dist/canvas-public/elements/image.js +259 -0
  59. package/dist/canvas-public/elements/loading-recovery.js +5 -0
  60. package/dist/canvas-public/elements/loading.js +21 -0
  61. package/dist/canvas-public/elements/map.js +64 -0
  62. package/dist/canvas-public/elements/registry.js +58 -0
  63. package/dist/canvas-public/elements/replacement.js +78 -0
  64. package/dist/canvas-public/elements/resize.js +210 -0
  65. package/dist/canvas-public/elements/task-pipeline.js +235 -0
  66. package/dist/canvas-public/elements/types.js +21 -0
  67. package/dist/canvas-public/elements/ui.js +14 -0
  68. package/dist/canvas-public/elements/video.js +64 -0
  69. package/dist/canvas-public/features/assets/local-assets.js +72 -0
  70. package/dist/canvas-public/features/assets/official-assets.js +539 -0
  71. package/dist/canvas-public/features/editing/canvas-size.js +532 -0
  72. package/dist/canvas-public/features/editing/color-key.js +471 -0
  73. package/dist/canvas-public/features/editing/cutout.js +20 -0
  74. package/dist/canvas-public/features/editing/interactive-image-editing.js +982 -0
  75. package/dist/canvas-public/features/editing/layer-decomposition.js +45 -0
  76. package/dist/canvas-public/features/editing/manual-split.js +115 -0
  77. package/dist/canvas-public/features/editing/perfect-pixel.js +299 -0
  78. package/dist/canvas-public/features/editing/scroll-preview.js +130 -0
  79. package/dist/canvas-public/features/editing/scroll-seam-elimination.js +71 -0
  80. package/dist/canvas-public/features/editing/seam-elimination.js +46 -0
  81. package/dist/canvas-public/features/editing/smart-split.js +49 -0
  82. package/dist/canvas-public/features/editing/source-preview.js +43 -0
  83. package/dist/canvas-public/features/editing/video-cycle.js +49 -0
  84. package/dist/canvas-public/features/editing/video-frames.js +35 -0
  85. package/dist/canvas-public/features/frame-editor/frame-editing.js +755 -0
  86. package/dist/canvas-public/features/frame-editor/frame-model.js +63 -0
  87. package/dist/canvas-public/features/frame-editor/playback.js +83 -0
  88. package/dist/canvas-public/features/frame-editor/preview.js +151 -0
  89. package/dist/canvas-public/features/generation/animation.js +272 -0
  90. package/dist/canvas-public/features/generation/asset-batch.js +102 -0
  91. package/dist/canvas-public/features/generation/direction.js +163 -0
  92. package/dist/canvas-public/features/generation/fields.js +73 -0
  93. package/dist/canvas-public/features/generation/game-ui.js +64 -0
  94. package/dist/canvas-public/features/generation/image.js +96 -0
  95. package/dist/canvas-public/features/generation/scroll-image.js +157 -0
  96. package/dist/canvas-public/features/generation/seamless-tile.js +52 -0
  97. package/dist/canvas-public/features/generation/tile-transition-atlas.js +79 -0
  98. package/dist/canvas-public/features/generation/video.js +105 -0
  99. package/dist/canvas-public/features/map/creation.js +73 -0
  100. package/dist/canvas-public/features/map/tile-map-editor.js +2520 -0
  101. package/dist/canvas-public/features/registry.js +59 -0
  102. package/dist/canvas-public/index.html +84 -259
  103. package/dist/canvas-public/inspector/controls.js +49 -0
  104. package/dist/canvas-public/inspector/frame-preview.js +11 -0
  105. package/dist/canvas-public/inspector/inspector.js +190 -0
  106. package/dist/canvas-public/inspector/media-inspector.js +517 -0
  107. package/dist/canvas-public/inspector/media-preview.js +56 -0
  108. package/dist/canvas-public/inspector/number-stepper.js +96 -0
  109. package/dist/canvas-public/inspector/resize.js +88 -0
  110. package/dist/canvas-public/inspector/scroll-preview.js +11 -0
  111. package/dist/canvas-public/inspector/ui-inspector.js +14 -0
  112. package/dist/canvas-public/language/en-US.js +1059 -0
  113. package/dist/canvas-public/language/index.js +91 -0
  114. package/dist/canvas-public/language/zh-CN.js +1059 -0
  115. package/dist/canvas-public/map/export.js +10 -0
  116. package/dist/canvas-public/map/inspector.js +8 -0
  117. package/dist/canvas-public/map/model.js +409 -0
  118. package/dist/canvas-public/map/tile-map-editor.js +2 -0
  119. package/dist/canvas-public/media/metadata.js +183 -0
  120. package/dist/canvas-public/media/support.js +183 -0
  121. package/dist/canvas-public/styles/base.css +49 -0
  122. package/dist/canvas-public/styles/canvas.css +127 -0
  123. package/dist/canvas-public/styles/controls.css +203 -0
  124. package/dist/canvas-public/styles/dialogs.css +521 -0
  125. package/dist/canvas-public/styles/feature-form.css +409 -0
  126. package/dist/canvas-public/styles/inspector.css +441 -0
  127. package/dist/canvas-public/styles/layout.css +344 -0
  128. package/dist/canvas-public/styles/map.css +11 -0
  129. package/dist/canvas-public/styles/nodes.css +636 -0
  130. package/dist/canvas-public/styles.css +9 -2247
  131. package/dist/canvas-public/ui/controls.js +222 -0
  132. package/dist/canvas-public/ui/elements.js +9 -0
  133. package/dist/canvas-public/ui/feature-controller.js +296 -0
  134. package/dist/canvas-public/ui/feature-form.js +402 -0
  135. package/dist/canvas-public/ui/settings.js +84 -0
  136. package/dist/canvas-public/ui/toolbar.js +55 -0
  137. package/dist/canvas-public/vendor/pixi-tilemap.js +126 -0
  138. package/dist/canvas-public/vendor/pixi.min.js +2340 -0
  139. package/dist/canvas-public/workers/color-key-worker.js +289 -0
  140. package/dist/image-workers/perfect-pixel/perfect-pixel-core.js +441 -0
  141. package/dist/image-workers/perfect-pixel/perfect-pixel-worker.cjs +10 -0
  142. package/dist/index.js +46710 -12400
  143. package/dist/skills/en-US/evomeme-canvas/SKILL.md +52 -0
  144. package/dist/skills/en-US/evomeme-canvas/agents/openai.yaml +7 -0
  145. package/dist/skills/en-US/evomeme-cloud-fs/SKILL.md +203 -0
  146. package/dist/skills/en-US/evomeme-cloud-fs/agents/openai.yaml +7 -0
  147. package/dist/skills/en-US/evomeme-cover/SKILL.md +131 -0
  148. package/dist/skills/en-US/evomeme-cover/agents/openai.yaml +7 -0
  149. package/dist/skills/en-US/evomeme-game-maker/SKILL.md +288 -0
  150. package/dist/skills/en-US/evomeme-game-maker/agents/openai.yaml +4 -0
  151. package/dist/skills/en-US/evomeme-login/SKILL.md +97 -0
  152. package/dist/skills/en-US/evomeme-login/agents/openai.yaml +7 -0
  153. package/dist/skills/en-US/evomeme-mcp/SKILL.md +71 -0
  154. package/dist/skills/en-US/evomeme-mcp/agents/openai.yaml +7 -0
  155. package/dist/skills/en-US/evomeme-mcp/references/troubleshooting.md +55 -0
  156. package/dist/skills/en-US/evomeme-native-bridge-sdk/SKILL.md +286 -0
  157. package/dist/skills/en-US/evomeme-native-bridge-sdk/agents/openai.yaml +8 -0
  158. package/dist/skills/en-US/evomeme-tos-upload/SKILL.md +47 -0
  159. package/dist/skills/en-US/evomeme-tos-upload/agents/openai.yaml +5 -0
  160. package/dist/skills/en-US/evomeme-ui/SKILL.md +123 -0
  161. package/dist/skills/en-US/evomeme-ui/agents/openai.yaml +4 -0
  162. package/dist/skills/en-US/evomeme-ui/references/host-layout.md +69 -0
  163. package/dist/skills/en-US/game-art/SKILL.md +121 -0
  164. package/dist/skills/en-US/game-art/agents/openai.yaml +8 -0
  165. package/dist/skills/en-US/game-asset-align/SKILL.md +47 -0
  166. package/dist/skills/en-US/game-asset-align/agents/openai.yaml +8 -0
  167. package/dist/skills/en-US/game-asset-align/references/alignment-contract.md +61 -0
  168. package/dist/skills/en-US/game-asset-align/references/blender-scene-pipeline.md +35 -0
  169. package/dist/skills/en-US/game-asset-align/references/canonical-package.md +26 -0
  170. package/dist/skills/en-US/game-asset-align/references/engine-capabilities.md +14 -0
  171. package/dist/skills/en-US/game-asset-align/references/lightweight-review.md +34 -0
  172. package/dist/skills/en-US/game-asset-align/references/repair-policy.md +30 -0
  173. package/dist/skills/en-US/game-asset-align/references/tool-contract.md +69 -0
  174. package/dist/skills/en-US/game-asset-align/references/visual-review.md +26 -0
  175. package/dist/skills/en-US/game-asset-batch-generator/SKILL.md +117 -0
  176. package/dist/skills/en-US/game-asset-batch-generator/agents/openai.yaml +7 -0
  177. package/dist/skills/en-US/game-asset-image-generator/SKILL.md +86 -0
  178. package/dist/skills/en-US/game-asset-image-generator/agents/openai.yaml +7 -0
  179. package/dist/skills/en-US/game-asset-image-generator/references/background.md +34 -0
  180. package/dist/skills/en-US/game-asset-image-generator/references/static-game-assets.md +38 -0
  181. package/dist/skills/en-US/game-asset-mesh-generator/SKILL.md +35 -0
  182. package/dist/skills/en-US/game-asset-mesh-generator/agents/openai.yaml +7 -0
  183. package/dist/skills/en-US/game-asset-mesh-generator/references/animation-presets.md +313 -0
  184. package/dist/skills/en-US/game-asset-mesh-generator/references/generation-contracts.md +131 -0
  185. package/dist/skills/en-US/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
  186. package/dist/skills/en-US/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
  187. package/dist/skills/en-US/game-asset-mesh-generator/references/threejs-integration.md +113 -0
  188. package/dist/skills/en-US/game-asset-mesh-generator/references/tool-reference.md +67 -0
  189. package/dist/skills/en-US/game-audio/SKILL.md +121 -0
  190. package/dist/skills/en-US/game-audio/agents/openai.yaml +4 -0
  191. package/{skills → dist/skills/en-US}/game-audio/references/bgm-cdn.md +7 -11
  192. package/{skills → dist/skills/en-US}/game-audio/references/sfx-cdn.md +6 -4
  193. package/dist/skills/en-US/game-character-view-generator/SKILL.md +110 -0
  194. package/dist/skills/en-US/game-character-view-generator/agents/openai.yaml +7 -0
  195. package/dist/skills/en-US/game-demo/SKILL.md +103 -0
  196. package/dist/skills/en-US/game-demo/agents/openai.yaml +7 -0
  197. package/dist/skills/en-US/game-image-layer-decomposer/SKILL.md +99 -0
  198. package/dist/skills/en-US/game-image-layer-decomposer/agents/openai.yaml +7 -0
  199. package/dist/skills/en-US/game-image-splitter/SKILL.md +114 -0
  200. package/dist/skills/en-US/game-image-splitter/agents/openai.yaml +7 -0
  201. package/dist/skills/en-US/game-maker/SKILL.md +111 -0
  202. package/dist/skills/en-US/game-maker/agents/openai.yaml +4 -0
  203. package/dist/skills/en-US/game-perfect-pixel/SKILL.md +123 -0
  204. package/dist/skills/en-US/game-perfect-pixel/agents/openai.yaml +7 -0
  205. package/dist/skills/en-US/game-scroll-generator/SKILL.md +110 -0
  206. package/dist/skills/en-US/game-scroll-generator/agents/openai.yaml +7 -0
  207. package/dist/skills/en-US/game-test/SKILL.md +58 -0
  208. package/dist/skills/en-US/game-test/agents/openai.yaml +7 -0
  209. package/dist/skills/en-US/game-tile-generator/SKILL.md +152 -0
  210. package/dist/skills/en-US/game-tile-generator/agents/openai.yaml +7 -0
  211. package/dist/skills/en-US/game-ui/SKILL.md +177 -0
  212. package/dist/skills/en-US/game-ui/agents/openai.yaml +4 -0
  213. package/dist/skills/en-US/game-ui-generator/SKILL.md +82 -0
  214. package/dist/skills/en-US/game-ui-generator/agents/openai.yaml +7 -0
  215. package/dist/skills/en-US/game-ui-generator/references/component-patterns.md +65 -0
  216. package/dist/skills/en-US/game-ui-generator/references/runtime-integration.md +45 -0
  217. package/dist/skills/en-US/game-video-cycle/SKILL.md +98 -0
  218. package/dist/skills/en-US/game-video-cycle/agents/openai.yaml +7 -0
  219. package/dist/skills/en-US/game-video-generator/SKILL.md +78 -0
  220. package/dist/skills/en-US/game-video-generator/agents/openai.yaml +7 -0
  221. package/dist/skills/en-US/godot-3d-game/SKILL.md +41 -0
  222. package/dist/skills/en-US/godot-3d-game/agents/openai.yaml +8 -0
  223. package/dist/skills/en-US/godot-3d-game/references/alignment-gates.md +26 -0
  224. package/dist/skills/en-US/godot-3d-game/references/appearance-alignment.md +54 -0
  225. package/dist/skills/en-US/godot-3d-game/references/build-and-qa.md +22 -0
  226. package/dist/skills/en-US/godot-3d-game/references/canonical-asset-runtime.md +27 -0
  227. package/dist/skills/en-US/godot-3d-game/references/import-and-wrapper-scenes.md +20 -0
  228. package/dist/skills/en-US/godot-3d-game/references/physics-colliders.md +13 -0
  229. package/dist/skills/en-US/godot-3d-game/references/project-structure.md +22 -0
  230. package/dist/skills/en-US/godot-3d-game/references/skeleton-animation.md +16 -0
  231. package/dist/skills/en-US/phaser-2d-game/SKILL.md +127 -0
  232. package/dist/skills/en-US/phaser-2d-game/agents/openai.yaml +8 -0
  233. package/dist/skills/en-US/phaser-2d-game/references/engine-performance.md +64 -0
  234. package/dist/skills/en-US/phaser-2d-game/references/engine-physics.md +139 -0
  235. package/dist/skills/en-US/phaser-2d-game/references/engine-runtime.md +129 -0
  236. package/dist/skills/en-US/phaser-2d-game/references/engine-spritesheets.md +99 -0
  237. package/dist/skills/en-US/phaser-2d-game/references/engine-tilemaps.md +80 -0
  238. package/dist/skills/en-US/phaser-2d-game/references/file-structure.md +94 -0
  239. package/dist/skills/en-US/phaser-2d-graphics-builder/SKILL.md +68 -0
  240. package/dist/skills/en-US/phaser-2d-graphics-builder/agents/openai.yaml +7 -0
  241. package/dist/skills/en-US/threejs-3d-game/SKILL.md +117 -0
  242. package/dist/skills/en-US/threejs-3d-game/agents/openai.yaml +7 -0
  243. package/dist/skills/en-US/threejs-3d-game/references/engine-asset-pipeline.md +155 -0
  244. package/dist/skills/en-US/threejs-3d-game/references/engine-audio.md +70 -0
  245. package/dist/skills/en-US/threejs-3d-game/references/engine-input-camera.md +84 -0
  246. package/dist/skills/en-US/threejs-3d-game/references/engine-performance.md +96 -0
  247. package/dist/skills/en-US/threejs-3d-game/references/engine-physics.md +142 -0
  248. package/dist/skills/en-US/threejs-3d-game/references/engine-runtime.md +199 -0
  249. package/dist/skills/en-US/threejs-3d-game/references/file-structure.md +353 -0
  250. package/dist/skills/en-US/threejs-aaa-graphics-builder/SKILL.md +62 -0
  251. package/dist/skills/en-US/threejs-aaa-graphics-builder/agents/openai.yaml +7 -0
  252. package/{skills → dist/skills/zh-CN}/evomeme-cloud-fs/SKILL.md +33 -6
  253. package/{skills → dist/skills/zh-CN}/evomeme-cover/SKILL.md +7 -8
  254. package/{skills → dist/skills/zh-CN}/evomeme-game-maker/SKILL.md +91 -88
  255. package/{skills → dist/skills/zh-CN}/evomeme-login/SKILL.md +5 -0
  256. package/dist/skills/zh-CN/evomeme-mcp/SKILL.md +71 -0
  257. package/dist/skills/zh-CN/evomeme-mcp/agents/openai.yaml +7 -0
  258. package/dist/skills/zh-CN/evomeme-mcp/references/troubleshooting.md +55 -0
  259. package/{skills → dist/skills/zh-CN}/evomeme-tos-upload/SKILL.md +6 -5
  260. package/dist/skills/zh-CN/evomeme-ui/SKILL.md +123 -0
  261. package/dist/skills/zh-CN/evomeme-ui/agents/openai.yaml +4 -0
  262. package/dist/skills/zh-CN/evomeme-ui/references/host-layout.md +69 -0
  263. package/dist/skills/zh-CN/game-asset-align/SKILL.md +47 -0
  264. package/dist/skills/zh-CN/game-asset-align/agents/openai.yaml +8 -0
  265. package/dist/skills/zh-CN/game-asset-align/references/alignment-contract.md +61 -0
  266. package/dist/skills/zh-CN/game-asset-align/references/blender-scene-pipeline.md +35 -0
  267. package/dist/skills/zh-CN/game-asset-align/references/canonical-package.md +26 -0
  268. package/dist/skills/zh-CN/game-asset-align/references/engine-capabilities.md +14 -0
  269. package/dist/skills/zh-CN/game-asset-align/references/lightweight-review.md +34 -0
  270. package/dist/skills/zh-CN/game-asset-align/references/repair-policy.md +30 -0
  271. package/dist/skills/zh-CN/game-asset-align/references/tool-contract.md +69 -0
  272. package/dist/skills/zh-CN/game-asset-align/references/visual-review.md +26 -0
  273. package/dist/skills/zh-CN/game-asset-batch-generator/SKILL.md +117 -0
  274. package/dist/skills/zh-CN/game-asset-batch-generator/agents/openai.yaml +7 -0
  275. package/dist/skills/zh-CN/game-asset-image-generator/SKILL.md +86 -0
  276. package/{skills → dist/skills/zh-CN}/game-asset-image-generator/references/background.md +3 -1
  277. package/{skills → dist/skills/zh-CN}/game-asset-image-generator/references/static-game-assets.md +2 -2
  278. package/dist/skills/zh-CN/game-asset-mesh-generator/SKILL.md +35 -0
  279. package/dist/skills/zh-CN/game-asset-mesh-generator/agents/openai.yaml +7 -0
  280. package/dist/skills/zh-CN/game-asset-mesh-generator/references/animation-presets.md +313 -0
  281. package/dist/skills/zh-CN/game-asset-mesh-generator/references/generation-contracts.md +131 -0
  282. package/dist/skills/zh-CN/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
  283. package/dist/skills/zh-CN/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
  284. package/dist/skills/zh-CN/game-asset-mesh-generator/references/threejs-integration.md +113 -0
  285. package/dist/skills/zh-CN/game-asset-mesh-generator/references/tool-reference.md +67 -0
  286. package/dist/skills/zh-CN/game-audio/SKILL.md +121 -0
  287. package/dist/skills/zh-CN/game-audio/agents/openai.yaml +4 -0
  288. package/dist/skills/zh-CN/game-audio/references/bgm-cdn.md +123 -0
  289. package/dist/skills/zh-CN/game-audio/references/sfx-cdn.md +79 -0
  290. package/dist/skills/zh-CN/game-character-view-generator/SKILL.md +110 -0
  291. package/dist/skills/zh-CN/game-character-view-generator/agents/openai.yaml +7 -0
  292. package/dist/skills/zh-CN/game-image-layer-decomposer/SKILL.md +99 -0
  293. package/dist/skills/zh-CN/game-image-layer-decomposer/agents/openai.yaml +7 -0
  294. package/dist/skills/zh-CN/game-image-splitter/SKILL.md +114 -0
  295. package/dist/skills/zh-CN/game-image-splitter/agents/openai.yaml +7 -0
  296. package/dist/skills/zh-CN/game-perfect-pixel/SKILL.md +123 -0
  297. package/dist/skills/zh-CN/game-perfect-pixel/agents/openai.yaml +7 -0
  298. package/dist/skills/zh-CN/game-scroll-generator/SKILL.md +110 -0
  299. package/dist/skills/zh-CN/game-scroll-generator/agents/openai.yaml +7 -0
  300. package/dist/skills/zh-CN/game-test/references/checklists/playtest-qa.md +12 -0
  301. package/dist/skills/zh-CN/game-tile-generator/SKILL.md +151 -0
  302. package/dist/skills/zh-CN/game-tile-generator/agents/openai.yaml +7 -0
  303. package/{skills → dist/skills/zh-CN}/game-ui/SKILL.md +9 -1
  304. package/dist/skills/zh-CN/game-ui/references/checklists/game-ui-quality.md +14 -0
  305. package/dist/skills/zh-CN/game-ui/references/checklists/hud-readability.md +12 -0
  306. package/dist/skills/zh-CN/game-ui/references/checklists/mobile-input.md +12 -0
  307. package/dist/skills/zh-CN/game-ui/references/checklists/responsive-ui-fit.md +12 -0
  308. package/dist/skills/zh-CN/game-ui/references/ui-patterns.md +140 -0
  309. package/dist/skills/zh-CN/game-ui/references/ui-prompt-templates.md +63 -0
  310. package/dist/skills/zh-CN/game-ui-generator/SKILL.md +82 -0
  311. package/dist/skills/zh-CN/game-ui-generator/agents/openai.yaml +7 -0
  312. package/dist/skills/zh-CN/game-ui-generator/references/component-patterns.md +65 -0
  313. package/dist/skills/zh-CN/game-ui-generator/references/runtime-integration.md +45 -0
  314. package/dist/skills/zh-CN/game-video-cycle/SKILL.md +98 -0
  315. package/dist/skills/zh-CN/game-video-cycle/agents/openai.yaml +7 -0
  316. package/dist/skills/zh-CN/game-video-generator/SKILL.md +78 -0
  317. package/dist/skills/zh-CN/game-video-generator/agents/openai.yaml +7 -0
  318. package/dist/skills/zh-CN/godot-3d-game/SKILL.md +41 -0
  319. package/dist/skills/zh-CN/godot-3d-game/agents/openai.yaml +8 -0
  320. package/dist/skills/zh-CN/godot-3d-game/references/alignment-gates.md +26 -0
  321. package/dist/skills/zh-CN/godot-3d-game/references/appearance-alignment.md +54 -0
  322. package/dist/skills/zh-CN/godot-3d-game/references/build-and-qa.md +22 -0
  323. package/dist/skills/zh-CN/godot-3d-game/references/canonical-asset-runtime.md +27 -0
  324. package/dist/skills/zh-CN/godot-3d-game/references/import-and-wrapper-scenes.md +20 -0
  325. package/dist/skills/zh-CN/godot-3d-game/references/physics-colliders.md +13 -0
  326. package/dist/skills/zh-CN/godot-3d-game/references/project-structure.md +22 -0
  327. package/dist/skills/zh-CN/godot-3d-game/references/skeleton-animation.md +16 -0
  328. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  329. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  330. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  331. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/compose-quality.md +10 -0
  332. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +9 -0
  333. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +15 -0
  334. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +10 -0
  335. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +9 -0
  336. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +9 -0
  337. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/compose-recipes.md +71 -0
  338. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +33 -0
  339. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/implementation-blueprint.md +82 -0
  340. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/sprite-recipes.md +90 -0
  341. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/technical-art.md +67 -0
  342. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/vfx-recipes.md +67 -0
  343. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/visual-scorecard.md +129 -0
  344. package/{skills → dist/skills/zh-CN}/threejs-3d-game/SKILL.md +6 -2
  345. package/dist/skills/zh-CN/threejs-3d-game/references/engine-asset-pipeline.md +155 -0
  346. package/{skills → dist/skills/zh-CN}/threejs-aaa-graphics-builder/SKILL.md +6 -6
  347. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  348. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  349. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  350. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +23 -0
  351. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +19 -0
  352. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +14 -0
  353. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +14 -0
  354. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +15 -0
  355. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +17 -0
  356. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +236 -0
  357. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/implementation-blueprint.md +161 -0
  358. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/model-recipes.md +146 -0
  359. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/render-recipes.md +90 -0
  360. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/shader-cookbook.md +292 -0
  361. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/technical-art.md +167 -0
  362. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/visual-scorecard.md +147 -0
  363. package/dist/stage-blender/bake_materials.py +102 -0
  364. package/dist/stage-blender/export_scene.py +275 -0
  365. package/dist/stage-blender/preview_camera.py +90 -0
  366. package/dist/stage-blender/repair_mesh.py +112 -0
  367. package/dist/stage-public/app.js +759 -0
  368. package/dist/stage-public/baked/bakePilot.js +44 -0
  369. package/dist/stage-public/baked/bakedScene.js +129 -0
  370. package/dist/stage-public/baked/sceneAppearance.js +225 -0
  371. package/dist/stage-public/baked/sceneCamera.js +45 -0
  372. package/dist/stage-public/comparison-contract.js +28 -0
  373. package/dist/stage-public/delivery.html +4 -0
  374. package/dist/stage-public/delivery.js +17 -0
  375. package/dist/stage-public/godot/appearance.gd +170 -0
  376. package/dist/stage-public/godot/preview.gd +79 -0
  377. package/dist/stage-public/index.html +141 -0
  378. package/dist/stage-public/review-app.js +289 -0
  379. package/dist/stage-public/review-runtime.js +63 -0
  380. package/dist/stage-public/review.css +24 -0
  381. package/dist/stage-public/review.html +47 -0
  382. package/dist/stage-public/scene-runtime.js +212 -0
  383. package/dist/stage-public/sceneContractRuntime.js +235 -0
  384. package/dist/stage-public/styles.css +328 -0
  385. package/dist/stage-public/three-appearance.js +104 -0
  386. package/dist/stage-public/vendor/three170/LICENSE +21 -0
  387. package/dist/stage-public/vendor/three170/build/three.module.js +54571 -0
  388. package/dist/stage-public/vendor/three170/examples/jsm/controls/OrbitControls.js +1523 -0
  389. package/dist/stage-public/vendor/three170/examples/jsm/environments/RoomEnvironment.js +144 -0
  390. package/dist/stage-public/vendor/three170/examples/jsm/libs/fflate.module.js +2672 -0
  391. package/dist/stage-public/vendor/three170/examples/jsm/loaders/EXRLoader.js +2563 -0
  392. package/dist/stage-public/vendor/three170/examples/jsm/loaders/GLTFLoader.js +4727 -0
  393. package/dist/stage-public/vendor/three170/examples/jsm/utils/BufferGeometryUtils.js +1373 -0
  394. package/dist/tile-transition-mask.svg +40 -0
  395. package/package.json +24 -8
  396. package/dist/canvas-public/cycle-detect.js +0 -166
  397. package/dist/canvas-public/smart-split.js +0 -185
  398. package/skills/game-asset-image-generator/SKILL.md +0 -104
  399. package/skills/game-audio/SKILL.md +0 -82
  400. package/skills/game-audio/agents/openai.yaml +0 -4
  401. package/skills/threejs-3d-game/references/engine-asset-pipeline.md +0 -116
  402. /package/{skills → dist/skills/en-US}/game-test/references/checklists/playtest-qa.md +0 -0
  403. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/game-ui-quality.md +0 -0
  404. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/hud-readability.md +0 -0
  405. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/mobile-input.md +0 -0
  406. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/responsive-ui-fit.md +0 -0
  407. /package/{skills → dist/skills/en-US}/game-ui/references/ui-patterns.md +0 -0
  408. /package/{skills → dist/skills/en-US}/game-ui/references/ui-prompt-templates.md +0 -0
  409. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  410. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  411. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  412. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/compose-quality.md +0 -0
  413. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +0 -0
  414. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +0 -0
  415. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +0 -0
  416. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +0 -0
  417. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +0 -0
  418. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/compose-recipes.md +0 -0
  419. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +0 -0
  420. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/implementation-blueprint.md +0 -0
  421. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/sprite-recipes.md +0 -0
  422. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/technical-art.md +0 -0
  423. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/vfx-recipes.md +0 -0
  424. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/visual-scorecard.md +0 -0
  425. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  426. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  427. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  428. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +0 -0
  429. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +0 -0
  430. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +0 -0
  431. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +0 -0
  432. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +0 -0
  433. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +0 -0
  434. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +0 -0
  435. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/implementation-blueprint.md +0 -0
  436. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/model-recipes.md +0 -0
  437. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/render-recipes.md +0 -0
  438. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/shader-cookbook.md +0 -0
  439. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/technical-art.md +0 -0
  440. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/visual-scorecard.md +0 -0
  441. /package/{skills → dist/skills/zh-CN}/evomeme-canvas/SKILL.md +0 -0
  442. /package/{skills → dist/skills/zh-CN}/evomeme-canvas/agents/openai.yaml +0 -0
  443. /package/{skills → dist/skills/zh-CN}/evomeme-cloud-fs/agents/openai.yaml +0 -0
  444. /package/{skills → dist/skills/zh-CN}/evomeme-cover/agents/openai.yaml +0 -0
  445. /package/{skills → dist/skills/zh-CN}/evomeme-game-maker/agents/openai.yaml +0 -0
  446. /package/{skills → dist/skills/zh-CN}/evomeme-login/agents/openai.yaml +0 -0
  447. /package/{skills → dist/skills/zh-CN}/evomeme-native-bridge-sdk/SKILL.md +0 -0
  448. /package/{skills → dist/skills/zh-CN}/evomeme-native-bridge-sdk/agents/openai.yaml +0 -0
  449. /package/{skills → dist/skills/zh-CN}/evomeme-tos-upload/agents/openai.yaml +0 -0
  450. /package/{skills → dist/skills/zh-CN}/game-art/SKILL.md +0 -0
  451. /package/{skills → dist/skills/zh-CN}/game-art/agents/openai.yaml +0 -0
  452. /package/{skills → dist/skills/zh-CN}/game-asset-image-generator/agents/openai.yaml +0 -0
  453. /package/{skills → dist/skills/zh-CN}/game-demo/SKILL.md +0 -0
  454. /package/{skills → dist/skills/zh-CN}/game-demo/agents/openai.yaml +0 -0
  455. /package/{skills → dist/skills/zh-CN}/game-maker/SKILL.md +0 -0
  456. /package/{skills → dist/skills/zh-CN}/game-maker/agents/openai.yaml +0 -0
  457. /package/{skills → dist/skills/zh-CN}/game-test/SKILL.md +0 -0
  458. /package/{skills → dist/skills/zh-CN}/game-test/agents/openai.yaml +0 -0
  459. /package/{skills → dist/skills/zh-CN}/game-ui/agents/openai.yaml +0 -0
  460. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/SKILL.md +0 -0
  461. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/agents/openai.yaml +0 -0
  462. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-performance.md +0 -0
  463. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-physics.md +0 -0
  464. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-runtime.md +0 -0
  465. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-spritesheets.md +0 -0
  466. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-tilemaps.md +0 -0
  467. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/file-structure.md +0 -0
  468. /package/{skills → dist/skills/zh-CN}/phaser-2d-graphics-builder/SKILL.md +0 -0
  469. /package/{skills → dist/skills/zh-CN}/phaser-2d-graphics-builder/agents/openai.yaml +0 -0
  470. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/agents/openai.yaml +0 -0
  471. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-audio.md +0 -0
  472. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-input-camera.md +0 -0
  473. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-performance.md +0 -0
  474. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-physics.md +0 -0
  475. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-runtime.md +0 -0
  476. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/file-structure.md +0 -0
  477. /package/{skills → dist/skills/zh-CN}/threejs-aaa-graphics-builder/agents/openai.yaml +0 -0
@@ -0,0 +1,44 @@
1
+ import * as THREE from 'three';
2
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
3
+ import { batchStaticScene } from './sceneAppearance.js';
4
+
5
+ export function annotateSourceNames(gltf) {
6
+ gltf.scene.traverse(object => {
7
+ const index = gltf.parser.associations.get(object)?.nodes;
8
+ if (index !== undefined) object.userData.canonicalSourceName = gltf.parser.json.nodes[index].name;
9
+ });
10
+ }
11
+
12
+ export async function loadBakePilot(manifest, readVerified, renderer) {
13
+ if (manifest.schema !== 'evomeme.cycles_bake_pilot.v1' || manifest.display.runtime_tone_mapped !== false || manifest.display.runtime_lighting !== false) throw new Error('Unsupported bake display contract');
14
+ const model = await readVerified('runtime/bake-pilot/pilot.glb');
15
+ const gltf = await new GLTFLoader().parseAsync(model, '');
16
+ const textures = new Map();
17
+ try {
18
+ for (const group of manifest.groups) {
19
+ const bytes = await readVerified(group.display_texture);
20
+ const url = URL.createObjectURL(new Blob([bytes], { type: 'image/png' }));
21
+ let texture;
22
+ try { texture = await new THREE.TextureLoader().loadAsync(url); } finally { URL.revokeObjectURL(url); }
23
+ texture.colorSpace = THREE.SRGBColorSpace;
24
+ texture.flipY = false;
25
+ texture.anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy());
26
+ textures.set(group.id, texture);
27
+ }
28
+ gltf.scene.traverse(object => {
29
+ if (!object.isMesh) return;
30
+ let id;
31
+ for (let node = object; node; node = node.parent) if (node.userData.bake_group) { id = node.userData.bake_group; break; }
32
+ if (!textures.has(id) || !object.geometry.attributes.uv) throw new Error('Pilot mesh has no atlas mapping');
33
+ object.material.dispose();
34
+ object.material = new THREE.MeshBasicMaterial({ name: `Cycles baked diffuse | ${id}`, map: textures.get(id), toneMapped: false, fog: false });
35
+ });
36
+ const batch = batchStaticScene(gltf.scene);
37
+ batch.root.name = 'cycles_bake_pilot_identity_root';
38
+ batch.root.traverse(object => { if (object.isMesh) { object.castShadow = true; object.receiveShadow = false; } });
39
+ return { root: batch.root, stats: batch.sourceStats, dispose() {
40
+ batch.root.traverse(o => { o.geometry?.dispose(); o.material?.dispose(); });
41
+ textures.forEach(t => t.dispose());
42
+ } };
43
+ } catch (error) { textures.forEach(t => t.dispose()); throw error; }
44
+ }
@@ -0,0 +1,129 @@
1
+ import * as THREE from 'three';
2
+ import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
3
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
4
+ import { batchStaticScene } from './sceneAppearance.js';
5
+
6
+ export function exrTexture(bytes, atlas = false) {
7
+ const d = new EXRLoader().parse(bytes);
8
+ const t = new THREE.DataTexture(d.data,d.width,d.height,d.format,d.type);
9
+ t.colorSpace = THREE.LinearSRGBColorSpace;
10
+ t.minFilter = THREE.LinearMipmapLinearFilter; t.magFilter = THREE.LinearFilter;
11
+ t.generateMipmaps = true;
12
+ // EXRLoader returns bottom-first rows; glTF reverses Blender's original UV V.
13
+ if (atlas) { t.repeat.y = -1; t.offset.y = 1; }
14
+ t.needsUpdate = true;
15
+ return t;
16
+ }
17
+
18
+ export async function loadBakedScene(manifest, readVerified, renderer) {
19
+ if (manifest.schema !== 'evomeme.cycles_baked_scene.v1' || manifest.display.texture_color_space !== 'Linear Rec.709') throw new Error('Unsupported linear scene bake');
20
+ const ownedTextures = [], ownedMaterials = new Set();
21
+ let root;
22
+ try {
23
+ const gltf = await new GLTFLoader().parseAsync(await readVerified(manifest.model_file),'');
24
+ const groupMaterials = new Map();
25
+ for (const group of manifest.groups) {
26
+ let map = null;
27
+ if (group.encoding === 'linear_exr') {
28
+ map = exrTexture(await readVerified(group.linear_master),true);
29
+ map.anisotropy = Math.min(8,renderer.capabilities.getMaxAnisotropy());
30
+ ownedTextures.push(map);
31
+ } else if (!group.encoding.startsWith('linear_vertex_color')) throw new Error('Unsupported surface encoding');
32
+ const material = new THREE.MeshBasicMaterial({name:`Cycles scene | ${group.id}`,map,vertexColors:group.encoding.startsWith('linear_vertex_color'),toneMapped:false,fog:false,side:THREE.DoubleSide});
33
+ if(group.encoding==='linear_vertex_color_two_sided'){
34
+ material.onBeforeCompile=shader=>{
35
+ shader.vertexShader='attribute vec3 _backlight;varying vec3 vBackLight;\n'+shader.vertexShader;
36
+ shader.vertexShader=shader.vertexShader.replace('#include <begin_vertex>','#include <begin_vertex>\nvBackLight=_backlight;');
37
+ shader.fragmentShader='varying vec3 vBackLight;\n'+shader.fragmentShader;
38
+ shader.fragmentShader=shader.fragmentShader.replace('#include <color_fragment>','#include <color_fragment>\nif(!gl_FrontFacing) diffuseColor.rgb=vBackLight;');
39
+ };
40
+ material.customProgramCacheKey=()=> 'baked-grass-two-sided-v1';
41
+ }
42
+ ownedMaterials.add(material); groupMaterials.set(group.id,material);
43
+ }
44
+ gltf.scene.traverse(o => {
45
+ if (!o.isMesh) return;
46
+ let id;
47
+ for (let p=o;p;p=p.parent) if (p.userData.bake_group) {id=p.userData.bake_group;break;}
48
+ const m=groupMaterials.get(id);
49
+ if (!m || (m.map && !o.geometry.attributes.uv) || (m.vertexColors && !o.geometry.attributes.color)) throw new Error(`Missing baked channel: ${id}`);
50
+ o.material.dispose(); o.material=m;
51
+ });
52
+ const batch=batchStaticScene(gltf.scene); root=batch.root;
53
+ root.name='full_baked_scene_identity_root';
54
+ root.traverse(o=>{o.castShadow=false;o.receiveShadow=false;});
55
+ const sky=exrTexture(await readVerified(manifest.atmosphere.sky_file)); ownedTextures.push(sky);
56
+ const lutUrl=URL.createObjectURL(new Blob([await readVerified(manifest.atmosphere.display_lut.file)],{type:'image/png'}));
57
+ let lut;
58
+ try {lut=await new THREE.TextureLoader().loadAsync(lutUrl);} finally {URL.revokeObjectURL(lutUrl);}
59
+ lut.colorSpace=THREE.NoColorSpace;lut.flipY=true;lut.generateMipmaps=false;
60
+ lut.minFilter=THREE.LinearFilter;lut.magFilter=THREE.LinearFilter;ownedTextures.push(lut);
61
+ const skyMaterial=new THREE.ShaderMaterial({side:THREE.BackSide,depthWrite:false,toneMapped:false,
62
+ uniforms:{radiance:{value:sky}},
63
+ vertexShader:'varying vec3 direction;void main(){direction=position;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}',
64
+ fragmentShader:'varying vec3 direction;uniform sampler2D radiance;void main(){vec3 d=normalize(direction);vec2 uv=vec2(atan(d.x,-d.z)/6.28318530718+.5,asin(clamp(d.y,-1.,1.))/3.14159265359+.5);gl_FragColor=vec4(texture2D(radiance,uv).rgb,1.);}'
65
+ });
66
+ const skyMesh=new THREE.Mesh(new THREE.SphereGeometry(340,40,24),skyMaterial);
67
+ skyMesh.frustumCulled=false;skyMesh.renderOrder=-100;skyMesh.name='Cycles authored radiance sky';
68
+ return {root,sky:skyMesh,lut,stats:batch.sourceStats,update(camera){skyMesh.position.copy(camera.position);},dispose(){
69
+ root.traverse(o=>o.geometry?.dispose());skyMesh.geometry.dispose();skyMaterial.dispose();
70
+ ownedMaterials.forEach(m=>m.dispose());ownedTextures.forEach(t=>t.dispose());
71
+ }};
72
+ } catch(error) {root?.traverse(o=>o.geometry?.dispose());ownedMaterials.forEach(m=>m.dispose());ownedTextures.forEach(t=>t.dispose());throw error;}
73
+ }
74
+
75
+ /** Single scene-linear HDR pass, bounded depth fog, then the source Blender display LUT. */
76
+ export function createBakedPipeline(renderer, baked, manifest) {
77
+ const target=new THREE.WebGLRenderTarget(1,1,{type:THREE.HalfFloatType,format:THREE.RGBAFormat,samples:4});
78
+ target.depthTexture=new THREE.DepthTexture(1,1,THREE.UnsignedIntType);
79
+ const postScene=new THREE.Scene();
80
+ const postCamera=new THREE.OrthographicCamera(-1,1,1,-1,0,1);
81
+ const a=manifest.atmosphere;
82
+ const mist=a.local_mist||[];
83
+ const uniforms={source:{value:target.texture},depth:{value:target.depthTexture},lut:{value:baked.lut},
84
+ invProjection:{value:new THREE.Matrix4()},cameraWorld:{value:new THREE.Matrix4()},cameraPositionWorld:{value:new THREE.Vector3()},
85
+ fogMin:{value:new THREE.Vector3().fromArray(a.fog.bounds_min)},fogMax:{value:new THREE.Vector3().fromArray(a.fog.bounds_max)},
86
+ fogDensity:{value:a.fog.density},fogLight:{value:new THREE.Color().fromArray(a.fog.inscatter_linear)},
87
+ lutSize:{value:a.display_lut.size},lutMax:{value:a.display_lut.domain_max},saturation:{value:a.compositor.saturation},
88
+ mistTransform:{value:mist.map(m=>new THREE.Matrix4().fromArray(m.world_to_unit))},mistDensity:{value:mist.map(m=>m.mean_density)},
89
+ mistLight:{value:mist.map(m=>new THREE.Vector3(...m.color_linear.map((c,i)=>c/a.fog.color_linear[i]*a.fog.inscatter_linear[i])))},
90
+ texel:{value:new THREE.Vector2()},glowThreshold:{value:a.compositor.glow_threshold},glowStrength:{value:a.compositor.glow_strength},displayCheck:{value:false},surfaceOnly:{value:false}};
91
+ const material=new THREE.ShaderMaterial({depthTest:false,depthWrite:false,toneMapped:false,uniforms,
92
+ vertexShader:'varying vec2 vUv;void main(){vUv=uv;gl_Position=vec4(position.xy,0.,1.);}',
93
+ fragmentShader:`varying vec2 vUv;uniform sampler2D source,depth,lut;uniform mat4 invProjection,cameraWorld;uniform vec3 cameraPositionWorld,fogMin,fogMax,fogLight;
94
+ uniform mat4 mistTransform[${Math.max(1,mist.length)}];uniform float mistDensity[${Math.max(1,mist.length)}];uniform vec3 mistLight[${Math.max(1,mist.length)}];
95
+ uniform float fogDensity,lutSize,lutMax,saturation,glowThreshold,glowStrength;uniform vec2 texel;uniform bool displayCheck,surfaceOnly;
96
+ vec3 sourceView(vec3 c){vec3 q=sqrt(clamp(c/lutMax,0.,1.))*(lutSize-1.);float b=floor(q.b);float next=min(b+1.,lutSize-1.);
97
+ vec2 uv0=vec2((q.r+b*lutSize+.5)/(lutSize*lutSize),(q.g+.5)/lutSize);
98
+ vec2 uv1=vec2((q.r+next*lutSize+.5)/(lutSize*lutSize),(q.g+.5)/lutSize);
99
+ return mix(texture2D(lut,uv0).rgb,texture2D(lut,uv1).rgb,fract(q.b));}
100
+ vec3 saturateHSV(vec3 c,float s){float hi=max(c.r,max(c.g,c.b));return max(vec3(0.),vec3(hi)+(c-vec3(hi))*s);}
101
+ void main(){vec3 c=texture2D(source,vUv).rgb;float z=texture2D(depth,vUv).r;
102
+ if(!surfaceOnly && z<.999999){vec4 v=invProjection*vec4(vUv*2.-1.,z*2.-1.,1.);v/=v.w;vec3 p=(cameraWorld*v).xyz;
103
+ vec3 delta=p-cameraPositionWorld;float distance=length(delta);vec3 d=delta/max(distance,.00001);
104
+ vec3 safeD=mix(vec3(-1.),vec3(1.),step(vec3(0.),d))*max(abs(d),vec3(.00001));vec3 t0=(fogMin-cameraPositionWorld)/safeD;vec3 t1=(fogMax-cameraPositionWorld)/safeD;
105
+ vec3 lo=min(t0,t1),hi=max(t0,t1);float enter=max(0.,max(lo.x,max(lo.y,lo.z)));float leave=min(distance,min(hi.x,min(hi.y,hi.z)));
106
+ float travel=max(0.,leave-enter);float T=exp(-fogDensity*travel);c=c*T+fogLight*(1.-T);
107
+ for(int i=0;i<${mist.length};i++){
108
+ vec3 origin=(mistTransform[i]*vec4(cameraPositionWorld,1.)).xyz;vec3 ray=(mistTransform[i]*vec4(d,0.)).xyz;
109
+ float A=dot(ray,ray),B=dot(origin,ray),C=dot(origin,origin)-1.;float det=B*B-A*C;
110
+ if(det>0.){float root=sqrt(det);float entry=max(0.,(-B-root)/A);float exit=min(distance,(-B+root)/A);
111
+ float span=max(0.,exit-entry);float localT=exp(-mistDensity[i]*span);c=c*localT+mistLight[i]*(1.-localT);}}
112
+ }
113
+ if(!surfaceOnly){vec3 glow=vec3(0.);for(int x=-2;x<=2;x++){for(int y=-2;y<=2;y++){
114
+ vec3 s=texture2D(source,vUv+vec2(float(x),float(y))*texel*3.).rgb;glow+=max(s-vec3(glowThreshold),vec3(0.));}}
115
+ c+=glow*glowStrength/25.;c=saturateHSV(c,saturation);}
116
+ if(displayCheck){int i=int(floor(vUv.x*8.));if(i==0)c=vec3(0.);else if(i==1)c=vec3(.002,.004,.008);else if(i==2)c=vec3(.02,.05,.1);else if(i==3)c=vec3(.1,.2,.3);else if(i==4)c=vec3(.5);else if(i==5)c=vec3(1.);else if(i==6)c=vec3(2.,1.,.5);else c=vec3(4.,8.,16.);}
117
+ gl_FragColor=vec4(sourceView(c),1.);}`});
118
+ const quad=new THREE.Mesh(new THREE.PlaneGeometry(2,2),material);postScene.add(quad);
119
+ const size=new THREE.Vector2();
120
+ return {render(scene,camera,{displayCheck=false,surfaceOnly=false}={}){
121
+ renderer.getDrawingBufferSize(size);
122
+ if(target.width!==size.x||target.height!==size.y)target.setSize(size.x,size.y);
123
+ uniforms.invProjection.value.copy(camera.projectionMatrixInverse);uniforms.cameraWorld.value.copy(camera.matrixWorld);
124
+ uniforms.cameraPositionWorld.value.copy(camera.position);uniforms.texel.value.set(1/size.x,1/size.y);uniforms.displayCheck.value=displayCheck;uniforms.surfaceOnly.value=surfaceOnly;
125
+ const autoReset=renderer.info.autoReset;renderer.info.autoReset=false;renderer.info.reset();
126
+ try {renderer.setRenderTarget(target);renderer.render(scene,camera);renderer.setRenderTarget(null);renderer.render(postScene,postCamera);}
127
+ finally {renderer.setRenderTarget(null);renderer.info.autoReset=autoReset;}
128
+ },dispose(){target.dispose();quad.geometry.dispose();material.dispose();}};
129
+ }
@@ -0,0 +1,225 @@
1
+ import * as THREE from 'three';
2
+ import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
3
+ import { blenderLightDirection } from './sceneCamera.js';
4
+
5
+ const noiseGLSL = `
6
+ float hash31(vec3 p){ p=fract(p*0.1031); p+=dot(p,p.yzx+33.33); return fract((p.x+p.y)*p.z); }
7
+ float noise3(vec3 x){ vec3 i=floor(x), f=fract(x); f=f*f*(3.0-2.0*f);
8
+ return mix(mix(mix(hash31(i),hash31(i+vec3(1,0,0)),f.x),mix(hash31(i+vec3(0,1,0)),hash31(i+vec3(1,1,0)),f.x),f.y),
9
+ mix(mix(hash31(i+vec3(0,0,1)),hash31(i+vec3(1,0,1)),f.x),mix(hash31(i+vec3(0,1,1)),hash31(i+vec3(1,1,1)),f.x),f.y),f.z); }
10
+ float fbm(vec3 p){ return noise3(p)*0.53+noise3(p*2.03+17.1)*0.27+noise3(p*4.11+5.3)*0.13+noise3(p*8.21)*0.07; }
11
+ `;
12
+
13
+ function randomSequence(seed) {
14
+ return () => { seed |= 0; seed = seed + 0x6D2B79F5 | 0; let t = Math.imul(seed ^ seed >>> 15, 1 | seed); t ^= t + Math.imul(t ^ t >>> 7, 61 | t); return ((t ^ t >>> 14) >>> 0) / 4294967296; };
15
+ }
16
+
17
+ /** Static batching transforms cloned vertices, never the canonical root or source file. */
18
+ export function batchStaticScene(source, { splitSourceNames = [], splitSceneNames = [] } = {}) {
19
+ source.updateMatrixWorld(true);
20
+ const selected = new Set(splitSourceNames);
21
+ const sceneSelected = new Set(splitSceneNames);
22
+ const groups = new Map();
23
+ let meshes = 0, triangles = 0;
24
+ source.traverse(object => {
25
+ if (!object.isMesh) return;
26
+ if (object.isSkinnedMesh || Array.isArray(object.material)) throw new Error('Scene batcher expects static single-material GLB meshes');
27
+ meshes++;
28
+ triangles += (object.geometry.index?.count || object.geometry.attributes.position.count) / 3;
29
+ const geometry = object.geometry.index ? object.geometry.toNonIndexed() : object.geometry.clone();
30
+ geometry.applyMatrix4(object.matrixWorld);
31
+ // UV channels and tangents are part of the render contract, including baked atlases.
32
+ for (const attribute of Object.keys(geometry.attributes)) if (!['position', 'normal', 'color', 'uv', 'uv1', 'uv2', 'uv3', 'tangent', '_backlight'].includes(attribute)) geometry.deleteAttribute(attribute);
33
+ let pilotSource = false, bakedSceneSource = false;
34
+ for (let parent = object; parent; parent = parent.parent) {
35
+ if (selected.has(parent.userData.canonicalSourceName)) pilotSource = true;
36
+ if (sceneSelected.has(parent.userData.canonicalSourceName)) bakedSceneSource = true;
37
+ }
38
+ const key = object.material.uuid + Object.keys(geometry.attributes).sort().join(',') + pilotSource + bakedSceneSource;
39
+ if (!groups.has(key)) groups.set(key, { material: object.material, geometries: [], names: [], pilotSource, bakedSceneSource });
40
+ groups.get(key).geometries.push(geometry);
41
+ groups.get(key).names.push(object.name);
42
+ });
43
+ const root = new THREE.Group();
44
+ root.name = 'canonical_identity_root';
45
+ for (const { material, geometries, names, pilotSource, bakedSceneSource } of groups.values()) {
46
+ const geometry = mergeGeometries(geometries, false);
47
+ if (!geometry) throw new Error(`Cannot batch ${material.name}`);
48
+ const mesh = new THREE.Mesh(geometry, material);
49
+ mesh.name = material.name;
50
+ mesh.userData.sourceNodes = names;
51
+ mesh.userData.bakePilotSource = pilotSource;
52
+ mesh.userData.bakedSceneSource = bakedSceneSource;
53
+ mesh.receiveShadow = true;
54
+ // Low-poly distant cliffs receive architectural shadows; disabling their
55
+ // huge self-caster avoids shadow-map acne without changing their geometry.
56
+ mesh.castShadow = !/Meadow|Airborne|Atmospheric far|Distant summit|Distant blue schist/i.test(material.name);
57
+ root.add(mesh);
58
+ geometries.forEach(g => g.dispose());
59
+ }
60
+ source.traverse(o => o.geometry?.dispose());
61
+ return { root, sourceStats: { meshes, triangles, batches: groups.size } };
62
+ }
63
+
64
+ export function createAppearance(scene, root, renderer, contract, profile) {
65
+ if (profile.schema !== 'evomeme.three_scene_profile.v1' || profile.asset_id !== contract.asset_id) throw new Error('Unsupported or mismatched Three.js appearance profile');
66
+ const time = { value: 0 };
67
+ const p = profile;
68
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
69
+ renderer.toneMapping = THREE.AgXToneMapping;
70
+ renderer.toneMappingExposure = p.render.exposure;
71
+ renderer.shadowMap.enabled = true;
72
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
73
+ renderer.shadowMap.autoUpdate = false;
74
+ renderer.shadowMap.needsUpdate = true;
75
+ scene.fog = new THREE.Fog(p.atmosphere.fog_color, p.atmosphere.fog_near, p.atmosphere.fog_far);
76
+ const atmosphere = new THREE.Group();
77
+ atmosphere.name = 'contract_runtime_atmosphere';
78
+ scene.add(atmosphere);
79
+
80
+ const sunEntry = contract.blender_lights.find(l => l.type === 'SUN');
81
+ if (!sunEntry) throw new Error('Contract has no authored SUN');
82
+ const sun = new THREE.DirectionalLight(new THREE.Color().fromArray(sunEntry.color_linear), p.lighting.sun_intensity);
83
+ const sunDirection = blenderLightDirection(sunEntry.rotation_xyz_rad);
84
+ sun.target.position.fromArray(p.lighting.sun_target);
85
+ sun.position.copy(sun.target.position).addScaledVector(sunDirection, -130);
86
+ sun.castShadow = true;
87
+ const extent = p.lighting.shadow_extent;
88
+ Object.assign(sun.shadow.camera, { left: -extent, right: extent, top: extent, bottom: -extent, near: 1, far: 270 });
89
+ sun.shadow.mapSize.setScalar(p.render.shadow_map_size);
90
+ sun.shadow.normalBias = .10;
91
+ sun.shadow.bias = -.00012;
92
+ sun.shadow.camera.updateProjectionMatrix();
93
+ atmosphere.add(sun, sun.target);
94
+ atmosphere.add(new THREE.HemisphereLight(p.lighting.sky_color, p.lighting.ground_color, p.lighting.hemisphere_intensity));
95
+ const fill = new THREE.DirectionalLight(p.lighting.fill_color, p.lighting.fill_intensity);
96
+ fill.position.fromArray(p.lighting.fill_position);
97
+ fill.target.position.set(0, 4, -12);
98
+ atmosphere.add(fill, fill.target);
99
+
100
+ const skyMaterial = new THREE.ShaderMaterial({
101
+ side: THREE.BackSide, depthWrite: false, fog: false,
102
+ uniforms: {
103
+ uTime: time,
104
+ uLow: { value: new THREE.Color().fromArray(p.atmosphere.sky_low_linear) },
105
+ uHigh: { value: new THREE.Color().fromArray(p.atmosphere.sky_high_linear) },
106
+ uShadow: { value: new THREE.Color().fromArray(p.atmosphere.cloud_shadow_linear) },
107
+ uLight: { value: new THREE.Color().fromArray(p.atmosphere.cloud_light_linear) },
108
+ uScale: { value: p.atmosphere.cloud_scale }, uSpeed: { value: p.atmosphere.cloud_speed }
109
+ },
110
+ vertexShader: `varying vec3 vDirection; void main(){ vDirection=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }`,
111
+ fragmentShader: `varying vec3 vDirection; uniform float uTime,uScale,uSpeed; uniform vec3 uLow,uHigh,uShadow,uLight;
112
+ ${noiseGLSL}
113
+ void main(){
114
+ vec3 d=normalize(vDirection); vec3 q=d*vec3(1.0,1.8,1.0)*uScale+vec3(uTime*uSpeed,0.0,1.7);
115
+ float n=fbm(q+fbm(q*0.6)*1.8);
116
+ float cloud=smoothstep(0.38,0.70,n);
117
+ vec3 sky=mix(uLow,uHigh,smoothstep(0.0,0.8,d.y));
118
+ vec3 bank=mix(uShadow,uLight,smoothstep(0.40,0.73,n));
119
+ // A soft cloud tower behind the western gate, in world direction space.
120
+ float tower=exp(-pow((d.x+0.22)/0.09,2.0)-pow((d.y-0.26)/0.27,2.0));
121
+ tower*=smoothstep(0.3,0.64,fbm(q*1.4+2.0));
122
+ sky=mix(sky,bank,cloud*.92); sky=mix(sky,uLight,tower*.62);
123
+ gl_FragColor=vec4(sky,1.0);
124
+ #include <tonemapping_fragment>
125
+ #include <colorspace_fragment>
126
+ }`
127
+ });
128
+ const sky = new THREE.Mesh(new THREE.SphereGeometry(340, 32, 20), skyMaterial);
129
+ sky.frustumCulled = false;
130
+ sky.renderOrder = -100;
131
+ atmosphere.add(sky);
132
+
133
+ const materialSet = new Set();
134
+ root.traverse(o => { if (o.isMesh) materialSet.add(o.material); });
135
+ for (const material of materialSet) {
136
+ const stone = /Limestone|limestone|slate|schist|cliffs|stone joints/.test(material.name);
137
+ const meadow = /Meadow|soil/.test(material.name);
138
+ const cliff = /schist|cliffs/.test(material.name);
139
+ if (cliff) material.color.multiplyScalar(p.materials.cliff_value);
140
+ material.roughness = .91;
141
+ if (material.name.includes('Airborne')) { material.emissiveIntensity = .6; continue; }
142
+ material.onBeforeCompile = shader => {
143
+ Object.assign(shader.uniforms, { uTime: time, uHaze: { value: new THREE.Color(p.atmosphere.fog_color) } });
144
+ shader.vertexShader = 'varying vec3 vSceneWorld;\n' + shader.vertexShader;
145
+ shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', '#include <begin_vertex>\nvSceneWorld=(modelMatrix*vec4(position,1.0)).xyz;');
146
+ shader.fragmentShader = `varying vec3 vSceneWorld; uniform float uTime; uniform vec3 uHaze;\n${noiseGLSL}\n` + shader.fragmentShader;
147
+ shader.fragmentShader = shader.fragmentShader.replace('#include <color_fragment>', `#include <color_fragment>
148
+ float surfaceNoise=fbm(vSceneWorld*${Number(p.materials.stone_scale).toFixed(3)});
149
+ diffuseColor.rgb*=1.0+(surfaceNoise-.5)*${stone ? p.materials.stone_variation * 2 : p.materials.grass_variation};
150
+ ${stone ? 'float grain=noise3(vSceneWorld*25.0); diffuseColor.rgb*=.95+grain*.1;' : ''}
151
+ ${meadow ? `float cloudMask=exp(-pow((vSceneWorld.x+23.0)/18.0,2.0)-pow((vSceneWorld.z-4.0)/27.0,2.0)); diffuseColor.rgb*=1.0-cloudMask*${p.materials.cloud_shadow_strength};` : ''}
152
+ `);
153
+ shader.fragmentShader = shader.fragmentShader.replace('#include <opaque_fragment>', `
154
+ float heightHaze=exp(-max(vSceneWorld.y,0.0)*${p.atmosphere.height_haze_falloff})*smoothstep(40.0,135.0,length(vViewPosition))*${p.atmosphere.height_haze_strength};
155
+ outgoingLight=mix(outgoingLight,uHaze,heightHaze);
156
+ #include <opaque_fragment>`);
157
+ shader.fragmentShader = shader.fragmentShader.replace('#include <tonemapping_fragment>', `#include <tonemapping_fragment>
158
+ float luminance=dot(gl_FragColor.rgb,vec3(.2126,.7152,.0722));
159
+ gl_FragColor.rgb=max(vec3(0.0),mix(vec3(luminance),gl_FragColor.rgb,${p.render.saturation}));
160
+ `);
161
+ };
162
+ material.customProgramCacheKey = () => `sanctuary-v2-${stone}-${meadow}`;
163
+ material.needsUpdate = true;
164
+ }
165
+
166
+ const effects = new THREE.Group();
167
+ effects.name = 'light_threads_and_wind';
168
+ atmosphere.add(effects);
169
+ const threadMaterial = new THREE.MeshBasicMaterial({ color: p.effects.thread_color, transparent: true, opacity: p.effects.thread_opacity, depthWrite: false, toneMapped: false });
170
+ const groundRay = new THREE.Raycaster();
171
+ const meadowMesh = root.children.find(o => o.name.includes('Meadow ·'));
172
+ root.updateMatrixWorld(true);
173
+ for (const t of p.effects.light_threads) {
174
+ const base = new THREE.Vector3().fromArray(t.base);
175
+ if (meadowMesh) {
176
+ groundRay.set(new THREE.Vector3(base.x, 70, base.z), new THREE.Vector3(0, -1, 0));
177
+ const hit = groundRay.intersectObject(meadowMesh)[0];
178
+ if (hit) base.y = hit.point.y + .3;
179
+ }
180
+ const curve = new THREE.CatmullRomCurve3([base, base.clone().add(new THREE.Vector3(-.03, t.height * .53, 0)), base.clone().add(new THREE.Vector3(.1, t.height, 0))]);
181
+ effects.add(new THREE.Mesh(new THREE.TubeGeometry(curve, 12, t.radius, 4, false), threadMaterial));
182
+ }
183
+ const windMaterial = new THREE.MeshBasicMaterial({ color: '#d2e7d6', transparent: true, opacity: p.effects.wind_opacity, depthWrite: false, toneMapped: false });
184
+ for (const points of p.effects.wind_paths) {
185
+ const curve = new THREE.CatmullRomCurve3(points.map(p => new THREE.Vector3().fromArray(p)));
186
+ effects.add(new THREE.Mesh(new THREE.TubeGeometry(curve, 70, .018, 3, false), windMaterial));
187
+ }
188
+
189
+ const random = randomSequence(p.seed);
190
+ const count = p.effects.mote_count;
191
+ const positions = new Float32Array(count * 3);
192
+ for (let i = 0; i < count; i++) positions.set([(random() - .5) * 80, 3 + random() * 27, 15 - random() * 90], i * 3);
193
+ const moteGeometry = new THREE.BufferGeometry();
194
+ moteGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
195
+ const moteMaterial = new THREE.ShaderMaterial({
196
+ transparent: true, depthWrite: false,
197
+ uniforms: { uTime: time, uDpr: { value: renderer.getPixelRatio() } },
198
+ vertexShader: `uniform float uTime,uDpr; varying float vOpacity; void main(){ vec3 p=position; p.x+=sin(uTime*.16+position.z)*.6; p.y+=sin(uTime*.31+position.x)*.18; vec4 mv=modelViewMatrix*vec4(p,1.0); gl_Position=projectionMatrix*mv; gl_PointSize=clamp(100.0/max(6.0,-mv.z),1.0,3.0)*uDpr; vOpacity=.3+.25*sin(position.x+uTime*.8); }`,
199
+ fragmentShader: `varying float vOpacity; void main(){ vec2 uv=gl_PointCoord-.5; float a=1.0-smoothstep(.2,.5,length(uv*vec2(.8,1.5))); gl_FragColor=vec4(.86,.94,.85,a*vOpacity); }`
200
+ });
201
+ effects.add(new THREE.Points(moteGeometry, moteMaterial));
202
+ return {
203
+ sunDirection: sunDirection.toArray(),
204
+ focusCharacterShadow(position, localExtent = 4) {
205
+ // A tight source-sun shadow map resolves the character on the static baked plaza.
206
+ const extent = position ? localExtent : p.lighting.shadow_extent;
207
+ if (position) sun.target.position.copy(position);
208
+ else sun.target.position.fromArray(p.lighting.sun_target);
209
+ sun.position.copy(sun.target.position).addScaledVector(sunDirection, -130);
210
+ Object.assign(sun.shadow.camera,{left:-extent,right:extent,top:extent,bottom:-extent});
211
+ sun.shadow.normalBias = position ? .008 : .10;
212
+ sun.shadow.camera.updateProjectionMatrix();
213
+ renderer.shadowMap.needsUpdate = true;
214
+ },
215
+ setIsolated(isolated) { sky.visible = !isolated; effects.visible = !isolated; },
216
+ setAuthoredSky(enabled) { sky.visible = !enabled; },
217
+ update(seconds, camera) { time.value = seconds; sky.position.copy(camera.position); threadMaterial.opacity = p.effects.thread_opacity * (.95 + .05 * Math.sin(seconds * .6)); moteMaterial.uniforms.uDpr.value = renderer.getPixelRatio(); },
218
+ dispose() {
219
+ scene.remove(atmosphere);
220
+ const geometries = new Set(), materials = new Set();
221
+ atmosphere.traverse(o => { if (o.geometry) geometries.add(o.geometry); if (o.material) materials.add(o.material); });
222
+ geometries.forEach(g => g.dispose()); materials.forEach(m => m.dispose()); sun.shadow.dispose();
223
+ }
224
+ };
225
+ }
@@ -0,0 +1,45 @@
1
+ import * as THREE from 'three';
2
+
3
+ export function applySceneCamera(camera, controls, definition) {
4
+ if (!definition) throw new Error('Missing contract camera');
5
+ camera.position.fromArray(definition.position);
6
+ camera.quaternion.fromArray(definition.quaternion_xyzw).normalize();
7
+ camera.up.fromArray(definition.up || [0, 1, 0]);
8
+ camera.fov = definition.vertical_fov_rad * 180 / Math.PI;
9
+ camera.aspect = definition.aspect_ratio;
10
+ camera.near = definition.near;
11
+ camera.far = definition.far;
12
+ camera.updateProjectionMatrix();
13
+ camera.updateMatrixWorld(true);
14
+ if (controls) controls.target.fromArray(definition.look_at);
15
+ }
16
+
17
+ export function fitFrame(width, height, aspect) {
18
+ const w = Math.min(width, height * aspect);
19
+ return { width: Math.max(1, w), height: Math.max(1, w / aspect) };
20
+ }
21
+
22
+ export function inspectScene(root, camera, contract, cameraId) {
23
+ root.updateMatrixWorld(true);
24
+ const near = (a, b, tolerance = 0.0001) => a.length === b.length && a.every((v, i) => Number.isFinite(v) && Math.abs(v - b[i]) < tolerance);
25
+ const box = new THREE.Box3().setFromObject(root, true);
26
+ const c = contract.cameras.find(c => c.id === cameraId);
27
+ const identity = new THREE.Matrix4();
28
+ const results = [
29
+ { name: '根矩阵保持原点 / 米制 / 原轴向', passed: near(root.matrixWorld.elements, identity.elements) },
30
+ { name: '实际 GLB 包围盒符合源契约', passed: near(box.min.toArray(), contract.coordinates.bounds_gltf_metres.min, .002) && near(box.max.toArray(), contract.coordinates.bounds_gltf_metres.max, .002) }
31
+ ];
32
+ if (c) {
33
+ const q = new THREE.Quaternion().fromArray(c.quaternion_xyzw).normalize();
34
+ results.push({ name: `${c.id} 相机位置及旋转`, passed: near(camera.position.toArray(), c.position) && 1 - Math.abs(camera.quaternion.dot(q)) < 1e-7 });
35
+ results.push({ name: '投影 / 裁剪面 / 1.88 画幅', passed: near([camera.fov, camera.aspect, camera.near, camera.far], [c.vertical_fov_degrees, c.aspect_ratio, c.near, c.far]) });
36
+ }
37
+ return results;
38
+ }
39
+
40
+ // Blender Euler XYZ applies fixed X, then Y, then Z: Rz * Ry * Rx.
41
+ // Three's intrinsic ZYX produces that matrix (intrinsic XYZ does not).
42
+ export function blenderLightDirection(rotation) {
43
+ const d = new THREE.Vector3(0, 0, -1).applyEuler(new THREE.Euler(...rotation, 'ZYX'));
44
+ return new THREE.Vector3(d.x, d.z, -d.y).normalize();
45
+ }
@@ -0,0 +1,28 @@
1
+ const close = (actual, expected, tolerance = .001) => Array.isArray(actual) && Array.isArray(expected) && actual.length === expected.length && actual.every((v, i) => Number.isFinite(v) && Number.isFinite(expected[i]) && Math.abs(v - expected[i]) <= tolerance);
2
+ const result = (state, label, detail) => ({ state, label, detail });
3
+
4
+ // Check only requirements actually declared by this asset. No inferred gameplay requirements.
5
+ export function compareSpatial(contract, stats, recipe) {
6
+ const c = contract?.coordinates;
7
+ if (!c) return result('warn', '未声明约束', '资产契约未声明空间要求。');
8
+ const failures = [];
9
+ if (c.root_transform) {
10
+ if (!close(recipe.translation_metres, c.root_transform.position) || !close(recipe.scale_xyz, c.root_transform.scale)) failures.push('根位置或尺寸');
11
+ const q = c.root_transform.quaternion_xyzw;
12
+ if (!close(recipe.rotation_quaternion_xyzw, q) && !close(recipe.rotation_quaternion_xyzw, q?.map(v => -v))) failures.push('根旋转');
13
+ }
14
+ if (c.bounds_gltf_metres) for (const key of ['min', 'max']) if (!close(stats.bounds[key], c.bounds_gltf_metres[key], .01)) failures.push(`场景边界 ${key}`);
15
+ if (failures.length) return result('error', '存在偏差', `${failures.join('、')}与契约不符。`);
16
+ if (!c.root_transform && !c.bounds_gltf_metres) return result('warn', '部分已检查', '轴向元数据已检查;契约未声明根变换或边界。');
17
+ return result('ok', '符合声明约束', `${stats.size.map(n => n.toFixed(2)).join(' × ')} m · 保留场景原点`);
18
+ }
19
+ export function compareStructural(contract, stats) {
20
+ const contents = contract?.contents;
21
+ if (!contents) return result('warn', '未声明数量', `已载入 ${stats.meshes} 网格;契约未声明结构数量。`);
22
+ const mappings = { mesh_count: 'meshes', node_count: 'nodes', material_count: 'materials', animations: 'animations', camera_count: 'cameras', skin_count: 'skins' };
23
+ const declared = Object.entries(mappings).filter(([k]) => typeof contents[k] === 'number');
24
+ const failures = declared.filter(([k, stat]) => contents[k] !== stats[stat]).map(([k]) => k);
25
+ if (failures.length) return result('error', '存在偏差', `与契约不符:${failures.join('、')}`);
26
+ if (!declared.length) return result('warn', '未声明数量', '已载入原始层级,尚无可比对的结构数量约束。');
27
+ return result('ok', '符合声明数量', `${stats.nodes} 节点 · ${stats.meshes} 网格 · ${stats.materials} 材质`);
28
+ }
@@ -0,0 +1,4 @@
1
+ <!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>已确认资产 · Three.js</title>
2
+ <style>html,body{margin:0;background:#151d20;color:#cce2dc;font:14px system-ui;height:100%}header{height:50px;display:flex;align-items:center;justify-content:space-between;padding:0 24px;border-bottom:1px solid #344044}#view{height:calc(100% - 51px);display:grid;place-items:center}.render-frame canvas{display:block}a{color:#a5dec9}</style>
3
+ <script type="importmap">{"imports":{"three":"./vendor/three170/build/three.module.js","three/addons/":"./vendor/three170/examples/jsm/"}}</script>
4
+ <header><strong id="title">读取已确认资产…</strong><span id="status">校验版本与显示资源</span><a href="./HANDOFF.md">交互设计接入说明</a></header><div id="view"></div><script type="module" src="./delivery.js"></script></html>
@@ -0,0 +1,17 @@
1
+ import { createReviewView } from './review-runtime.js';
2
+ try {
3
+ const delivery = await fetch('./delivery.json').then(r => r.json());
4
+ const bytes = await Promise.all(Object.entries(delivery.integrity).map(async ([file, expected]) => {
5
+ const r = await fetch(`./${file}`); if (!r.ok) throw new Error(`Missing asset: ${file}`);
6
+ const data = await r.arrayBuffer();
7
+ const hash = [...new Uint8Array(await crypto.subtle.digest('SHA-256', data))].map(n => n.toString(16).padStart(2, '0')).join('');
8
+ if (hash !== expected) throw new Error(`Delivery file changed: ${file}`); return [file, ['confirmation.json', 'asset/asset.json', 'asset/scene.contract.json'].includes(file) ? data : null];
9
+ }));
10
+ const resources = Object.fromEntries(bytes), json = key => JSON.parse(new TextDecoder().decode(resources[key]));
11
+ const record = json('confirmation.json');
12
+ const view = await createReviewView({ container: document.querySelector('#view'), assetBase: new URL('./asset/', location.href), asset: json('asset/asset.json'), contract: resources['asset/scene.contract.json'] ? json('asset/scene.contract.json') : null,
13
+ files: record.files, presentation: delivery.presentation, profile: record.target_profile, onProgress: message => document.querySelector('#status').textContent = message });
14
+ document.querySelector('#title').textContent = `${delivery.asset_id} · 已确认版本`;
15
+ document.querySelector('#status').textContent = '同一显示实现 · 可拖动观察 · 交互尚未设计';
16
+ window.addEventListener('pagehide', () => view.dispose());
17
+ } catch (error) { document.querySelector('#status').textContent = error.message; console.error(error); }