evomeme 0.1.9 → 0.1.10

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 (405) hide show
  1. package/dist/canvas-public/app.js +509 -5999
  2. package/dist/canvas-public/assets/presets/catalog.json +47 -0
  3. package/dist/canvas-public/assets/presets/tiles/A_01_CaoDi.png +0 -0
  4. package/dist/canvas-public/assets/presets/tiles/A_02_ShaMo.png +0 -0
  5. package/dist/canvas-public/assets/presets/tiles/A_03_TianKong.png +0 -0
  6. package/dist/canvas-public/assets/presets/tiles/A_04_ShuiBo.png +0 -0
  7. package/dist/canvas-public/assets/presets/tiles/A_05_HaiMian.png +0 -0
  8. package/dist/canvas-public/assets/presets/tiles/A_06_MuDiBan.png +0 -0
  9. package/dist/canvas-public/assets/presets/tiles/A_07_ShiZhuan.png +0 -0
  10. package/dist/canvas-public/assets/presets/tiles/A_08_AnRongYan.png +0 -0
  11. package/dist/canvas-public/assets/presets/tiles/A_09_QingTaiShi.png +0 -0
  12. package/dist/canvas-public/assets/presets/tiles/A_10_ShenHai.png +0 -0
  13. package/dist/canvas-public/assets/presets/tiles/A_11_HuiYanShi.png +0 -0
  14. package/dist/canvas-public/assets/presets/tiles/A_12_HongTu.png +0 -0
  15. package/dist/canvas-public/assets/presets/tiles/A_13_GengDi.png +0 -0
  16. package/dist/canvas-public/assets/presets/tiles/A_14_HuoShanYan.png +0 -0
  17. package/dist/canvas-public/assets/presets/tiles/A_15_NiTu.png +0 -0
  18. package/dist/canvas-public/assets/presets/tiles/A_16_HuangSha.png +0 -0
  19. package/dist/canvas-public/assets/presets/tiles/A_17_CongLin.png +0 -0
  20. package/dist/canvas-public/assets/presets/tiles/A_18_ZiYanShi.png +0 -0
  21. package/dist/canvas-public/assets/presets/tiles/A_19_MoGuTu.png +0 -0
  22. package/dist/canvas-public/assets/presets/tiles/A_20_HuaDian.png +0 -0
  23. package/dist/canvas-public/assets/presets/tiles/A_21_HaiLang.png +0 -0
  24. package/dist/canvas-public/assets/presets/tiles/A_22_GanTu.png +0 -0
  25. package/dist/canvas-public/assets/presets/tiles/A_23_QingTaiShiBan.png +0 -0
  26. package/dist/canvas-public/assets/presets/tiles/A_24_XingKong.png +0 -0
  27. package/dist/canvas-public/assets/presets/tiles/A_25_MoFaFuWen.png +0 -0
  28. package/dist/canvas-public/assets/presets/tiles/A_26_ShiTuYeDi.png +0 -0
  29. package/dist/canvas-public/assets/presets/tiles/A_27_LanZhuan.png +0 -0
  30. package/dist/canvas-public/assets/presets/tiles/A_28_HongZhuan.png +0 -0
  31. package/dist/canvas-public/assets/presets/tiles/A_29_YueQiu.png +0 -0
  32. package/dist/canvas-public/assets/presets/tiles/A_30_JinShuGe.png +0 -0
  33. package/dist/canvas-public/assets/presets/tiles/A_31_JinShuJingGai.png +0 -0
  34. package/dist/canvas-public/assets/presets/tiles/A_32_ShuZiChuang.png +0 -0
  35. package/dist/canvas-public/canvas/actions.js +143 -0
  36. package/dist/canvas-public/canvas/camera.js +28 -0
  37. package/dist/canvas-public/canvas/chrome.js +13 -0
  38. package/dist/canvas-public/canvas/data.js +123 -0
  39. package/dist/canvas-public/canvas/dependencies.js +19 -0
  40. package/dist/canvas-public/canvas/interactions.js +544 -0
  41. package/dist/canvas-public/canvas/panel-resize.js +5 -0
  42. package/dist/canvas-public/canvas/renderer.js +297 -0
  43. package/dist/canvas-public/canvas/selection.js +62 -0
  44. package/dist/canvas-public/canvas/serialization.js +118 -0
  45. package/dist/canvas-public/canvas/sync.js +202 -0
  46. package/dist/canvas-public/core/api.js +27 -0
  47. package/dist/canvas-public/core/feature-registry.js +29 -0
  48. package/dist/canvas-public/core/feature-state.js +228 -0
  49. package/dist/canvas-public/core/layout.js +133 -0
  50. package/dist/canvas-public/core/operations.js +38 -0
  51. package/dist/canvas-public/core/project.js +203 -0
  52. package/dist/canvas-public/core/state.js +27 -0
  53. package/dist/canvas-public/dialogs/context-menu.js +118 -0
  54. package/dist/canvas-public/elements/base.js +100 -0
  55. package/dist/canvas-public/elements/connections.js +105 -0
  56. package/dist/canvas-public/elements/drag.js +53 -0
  57. package/dist/canvas-public/elements/image.js +259 -0
  58. package/dist/canvas-public/elements/loading-recovery.js +5 -0
  59. package/dist/canvas-public/elements/loading.js +21 -0
  60. package/dist/canvas-public/elements/map.js +64 -0
  61. package/dist/canvas-public/elements/registry.js +58 -0
  62. package/dist/canvas-public/elements/replacement.js +78 -0
  63. package/dist/canvas-public/elements/resize.js +210 -0
  64. package/dist/canvas-public/elements/task-pipeline.js +234 -0
  65. package/dist/canvas-public/elements/types.js +21 -0
  66. package/dist/canvas-public/elements/ui.js +14 -0
  67. package/dist/canvas-public/elements/video.js +64 -0
  68. package/dist/canvas-public/features/assets/local-assets.js +72 -0
  69. package/dist/canvas-public/features/assets/official-assets.js +538 -0
  70. package/dist/canvas-public/features/editing/canvas-size.js +532 -0
  71. package/dist/canvas-public/features/editing/color-key.js +469 -0
  72. package/dist/canvas-public/features/editing/cutout.js +20 -0
  73. package/dist/canvas-public/features/editing/interactive-image-editing.js +981 -0
  74. package/dist/canvas-public/features/editing/layer-decomposition.js +44 -0
  75. package/dist/canvas-public/features/editing/manual-split.js +114 -0
  76. package/dist/canvas-public/features/editing/perfect-pixel.js +336 -0
  77. package/dist/canvas-public/features/editing/scroll-preview.js +129 -0
  78. package/dist/canvas-public/features/editing/scroll-seam-elimination.js +71 -0
  79. package/dist/canvas-public/features/editing/seam-elimination.js +46 -0
  80. package/dist/canvas-public/features/editing/smart-split.js +48 -0
  81. package/dist/canvas-public/features/editing/source-preview.js +43 -0
  82. package/dist/canvas-public/features/editing/video-cycle.js +49 -0
  83. package/dist/canvas-public/features/editing/video-frames.js +35 -0
  84. package/dist/canvas-public/features/frame-editor/frame-editing.js +755 -0
  85. package/dist/canvas-public/features/frame-editor/frame-model.js +63 -0
  86. package/dist/canvas-public/features/frame-editor/playback.js +83 -0
  87. package/dist/canvas-public/features/frame-editor/preview.js +149 -0
  88. package/dist/canvas-public/features/generation/animation.js +272 -0
  89. package/dist/canvas-public/features/generation/asset-batch.js +102 -0
  90. package/dist/canvas-public/features/generation/direction.js +163 -0
  91. package/dist/canvas-public/features/generation/fields.js +73 -0
  92. package/dist/canvas-public/features/generation/game-ui.js +64 -0
  93. package/dist/canvas-public/features/generation/image.js +83 -0
  94. package/dist/canvas-public/features/generation/scroll-image.js +157 -0
  95. package/dist/canvas-public/features/generation/seamless-tile.js +52 -0
  96. package/dist/canvas-public/features/generation/tile-transition-atlas.js +79 -0
  97. package/dist/canvas-public/features/generation/video.js +105 -0
  98. package/dist/canvas-public/features/map/creation.js +73 -0
  99. package/dist/canvas-public/features/map/tile-map-editor.js +2518 -0
  100. package/dist/canvas-public/features/registry.js +59 -0
  101. package/dist/canvas-public/index.html +84 -259
  102. package/dist/canvas-public/inspector/controls.js +49 -0
  103. package/dist/canvas-public/inspector/frame-preview.js +11 -0
  104. package/dist/canvas-public/inspector/inspector.js +190 -0
  105. package/dist/canvas-public/inspector/media-inspector.js +517 -0
  106. package/dist/canvas-public/inspector/media-preview.js +56 -0
  107. package/dist/canvas-public/inspector/number-stepper.js +96 -0
  108. package/dist/canvas-public/inspector/resize.js +88 -0
  109. package/dist/canvas-public/inspector/scroll-preview.js +11 -0
  110. package/dist/canvas-public/inspector/ui-inspector.js +14 -0
  111. package/dist/canvas-public/language/en-US.js +1059 -0
  112. package/dist/canvas-public/language/index.js +79 -0
  113. package/dist/canvas-public/language/zh-CN.js +1059 -0
  114. package/dist/canvas-public/map/export.js +9 -0
  115. package/dist/canvas-public/map/inspector.js +8 -0
  116. package/dist/canvas-public/map/model.js +409 -0
  117. package/dist/canvas-public/map/tile-map-editor.js +2 -0
  118. package/dist/canvas-public/media/metadata.js +183 -0
  119. package/dist/canvas-public/media/support.js +183 -0
  120. package/dist/canvas-public/styles/base.css +49 -0
  121. package/dist/canvas-public/styles/canvas.css +127 -0
  122. package/dist/canvas-public/styles/controls.css +203 -0
  123. package/dist/canvas-public/styles/dialogs.css +521 -0
  124. package/dist/canvas-public/styles/feature-form.css +409 -0
  125. package/dist/canvas-public/styles/inspector.css +441 -0
  126. package/dist/canvas-public/styles/layout.css +344 -0
  127. package/dist/canvas-public/styles/map.css +11 -0
  128. package/dist/canvas-public/styles/nodes.css +636 -0
  129. package/dist/canvas-public/styles.css +9 -2247
  130. package/dist/canvas-public/ui/controls.js +222 -0
  131. package/dist/canvas-public/ui/elements.js +9 -0
  132. package/dist/canvas-public/ui/feature-controller.js +296 -0
  133. package/dist/canvas-public/ui/feature-form.js +401 -0
  134. package/dist/canvas-public/ui/settings.js +83 -0
  135. package/dist/canvas-public/ui/toolbar.js +55 -0
  136. package/dist/canvas-public/vendor/pixi-tilemap.js +126 -0
  137. package/dist/canvas-public/vendor/pixi.min.js +2340 -0
  138. package/dist/canvas-public/workers/color-key-worker.js +289 -0
  139. package/dist/canvas-public/workers/perfect-pixel-worker.js +510 -0
  140. package/dist/index.js +42236 -13382
  141. package/dist/skills/en-US/evomeme-canvas/SKILL.md +52 -0
  142. package/dist/skills/en-US/evomeme-canvas/agents/openai.yaml +7 -0
  143. package/dist/skills/en-US/evomeme-cloud-fs/SKILL.md +176 -0
  144. package/dist/skills/en-US/evomeme-cloud-fs/agents/openai.yaml +7 -0
  145. package/dist/skills/en-US/evomeme-cover/SKILL.md +132 -0
  146. package/dist/skills/en-US/evomeme-cover/agents/openai.yaml +7 -0
  147. package/dist/skills/en-US/evomeme-game-maker/SKILL.md +288 -0
  148. package/dist/skills/en-US/evomeme-game-maker/agents/openai.yaml +4 -0
  149. package/dist/skills/en-US/evomeme-login/SKILL.md +92 -0
  150. package/dist/skills/en-US/evomeme-login/agents/openai.yaml +7 -0
  151. package/dist/skills/en-US/evomeme-native-bridge-sdk/SKILL.md +286 -0
  152. package/dist/skills/en-US/evomeme-native-bridge-sdk/agents/openai.yaml +8 -0
  153. package/dist/skills/en-US/evomeme-tos-upload/SKILL.md +47 -0
  154. package/dist/skills/en-US/evomeme-tos-upload/agents/openai.yaml +5 -0
  155. package/dist/skills/en-US/evomeme-ui/SKILL.md +123 -0
  156. package/dist/skills/en-US/evomeme-ui/agents/openai.yaml +4 -0
  157. package/dist/skills/en-US/evomeme-ui/references/host-layout.md +69 -0
  158. package/dist/skills/en-US/game-art/SKILL.md +121 -0
  159. package/dist/skills/en-US/game-art/agents/openai.yaml +8 -0
  160. package/dist/skills/en-US/game-asset-align/SKILL.md +45 -0
  161. package/dist/skills/en-US/game-asset-align/agents/openai.yaml +8 -0
  162. package/dist/skills/en-US/game-asset-align/references/alignment-contract.md +61 -0
  163. package/dist/skills/en-US/game-asset-align/references/blender-scene-pipeline.md +45 -0
  164. package/dist/skills/en-US/game-asset-align/references/canonical-package.md +26 -0
  165. package/dist/skills/en-US/game-asset-align/references/engine-capabilities.md +14 -0
  166. package/dist/skills/en-US/game-asset-align/references/lightweight-review.md +32 -0
  167. package/dist/skills/en-US/game-asset-align/references/repair-policy.md +30 -0
  168. package/dist/skills/en-US/game-asset-align/references/tool-contract.md +39 -0
  169. package/dist/skills/en-US/game-asset-align/references/visual-review.md +26 -0
  170. package/dist/skills/en-US/game-asset-image-generator/SKILL.md +104 -0
  171. package/dist/skills/en-US/game-asset-image-generator/agents/openai.yaml +7 -0
  172. package/dist/skills/en-US/game-asset-image-generator/references/background.md +32 -0
  173. package/dist/skills/en-US/game-asset-image-generator/references/static-game-assets.md +38 -0
  174. package/dist/skills/en-US/game-audio/SKILL.md +82 -0
  175. package/dist/skills/en-US/game-audio/agents/openai.yaml +4 -0
  176. package/dist/skills/en-US/game-demo/SKILL.md +103 -0
  177. package/dist/skills/en-US/game-demo/agents/openai.yaml +7 -0
  178. package/dist/skills/en-US/game-maker/SKILL.md +111 -0
  179. package/dist/skills/en-US/game-maker/agents/openai.yaml +4 -0
  180. package/dist/skills/en-US/game-test/SKILL.md +58 -0
  181. package/dist/skills/en-US/game-test/agents/openai.yaml +7 -0
  182. package/dist/skills/en-US/game-ui/SKILL.md +177 -0
  183. package/dist/skills/en-US/game-ui/agents/openai.yaml +4 -0
  184. package/dist/skills/en-US/godot-3d-game/SKILL.md +39 -0
  185. package/dist/skills/en-US/godot-3d-game/agents/openai.yaml +8 -0
  186. package/dist/skills/en-US/godot-3d-game/references/alignment-gates.md +26 -0
  187. package/dist/skills/en-US/godot-3d-game/references/appearance-alignment.md +52 -0
  188. package/dist/skills/en-US/godot-3d-game/references/build-and-qa.md +22 -0
  189. package/dist/skills/en-US/godot-3d-game/references/canonical-asset-runtime.md +27 -0
  190. package/dist/skills/en-US/godot-3d-game/references/import-and-wrapper-scenes.md +20 -0
  191. package/dist/skills/en-US/godot-3d-game/references/physics-colliders.md +13 -0
  192. package/dist/skills/en-US/godot-3d-game/references/project-structure.md +22 -0
  193. package/dist/skills/en-US/godot-3d-game/references/skeleton-animation.md +16 -0
  194. package/dist/skills/en-US/phaser-2d-game/SKILL.md +127 -0
  195. package/dist/skills/en-US/phaser-2d-game/agents/openai.yaml +8 -0
  196. package/dist/skills/en-US/phaser-2d-game/references/engine-performance.md +64 -0
  197. package/dist/skills/en-US/phaser-2d-game/references/engine-physics.md +139 -0
  198. package/dist/skills/en-US/phaser-2d-game/references/engine-runtime.md +129 -0
  199. package/dist/skills/en-US/phaser-2d-game/references/engine-spritesheets.md +99 -0
  200. package/dist/skills/en-US/phaser-2d-game/references/engine-tilemaps.md +80 -0
  201. package/dist/skills/en-US/phaser-2d-game/references/file-structure.md +94 -0
  202. package/dist/skills/en-US/phaser-2d-graphics-builder/SKILL.md +68 -0
  203. package/dist/skills/en-US/phaser-2d-graphics-builder/agents/openai.yaml +7 -0
  204. package/dist/skills/en-US/threejs-3d-game/SKILL.md +117 -0
  205. package/dist/skills/en-US/threejs-3d-game/agents/openai.yaml +7 -0
  206. package/dist/skills/en-US/threejs-3d-game/references/engine-asset-pipeline.md +153 -0
  207. package/dist/skills/en-US/threejs-3d-game/references/engine-audio.md +70 -0
  208. package/dist/skills/en-US/threejs-3d-game/references/engine-input-camera.md +84 -0
  209. package/dist/skills/en-US/threejs-3d-game/references/engine-performance.md +96 -0
  210. package/dist/skills/en-US/threejs-3d-game/references/engine-physics.md +142 -0
  211. package/dist/skills/en-US/threejs-3d-game/references/engine-runtime.md +199 -0
  212. package/dist/skills/en-US/threejs-3d-game/references/file-structure.md +353 -0
  213. package/dist/skills/en-US/threejs-aaa-graphics-builder/SKILL.md +62 -0
  214. package/dist/skills/en-US/threejs-aaa-graphics-builder/agents/openai.yaml +7 -0
  215. package/{skills → dist/skills/zh-CN}/evomeme-cloud-fs/SKILL.md +4 -4
  216. package/{skills → dist/skills/zh-CN}/evomeme-cover/SKILL.md +1 -1
  217. package/{skills → dist/skills/zh-CN}/evomeme-game-maker/SKILL.md +91 -88
  218. package/{skills → dist/skills/zh-CN}/evomeme-tos-upload/SKILL.md +6 -5
  219. package/dist/skills/zh-CN/evomeme-ui/SKILL.md +123 -0
  220. package/dist/skills/zh-CN/evomeme-ui/agents/openai.yaml +4 -0
  221. package/dist/skills/zh-CN/evomeme-ui/references/host-layout.md +69 -0
  222. package/dist/skills/zh-CN/game-asset-align/SKILL.md +45 -0
  223. package/dist/skills/zh-CN/game-asset-align/agents/openai.yaml +8 -0
  224. package/dist/skills/zh-CN/game-asset-align/references/alignment-contract.md +61 -0
  225. package/dist/skills/zh-CN/game-asset-align/references/blender-scene-pipeline.md +45 -0
  226. package/dist/skills/zh-CN/game-asset-align/references/canonical-package.md +26 -0
  227. package/dist/skills/zh-CN/game-asset-align/references/engine-capabilities.md +14 -0
  228. package/dist/skills/zh-CN/game-asset-align/references/lightweight-review.md +32 -0
  229. package/dist/skills/zh-CN/game-asset-align/references/repair-policy.md +30 -0
  230. package/dist/skills/zh-CN/game-asset-align/references/tool-contract.md +39 -0
  231. package/dist/skills/zh-CN/game-asset-align/references/visual-review.md +26 -0
  232. package/{skills → dist/skills/zh-CN}/game-asset-image-generator/SKILL.md +1 -1
  233. package/dist/skills/zh-CN/game-audio/references/bgm-cdn.md +127 -0
  234. package/dist/skills/zh-CN/game-audio/references/sfx-cdn.md +77 -0
  235. package/dist/skills/zh-CN/game-test/references/checklists/playtest-qa.md +12 -0
  236. package/{skills → dist/skills/zh-CN}/game-ui/SKILL.md +9 -1
  237. package/dist/skills/zh-CN/game-ui/references/checklists/game-ui-quality.md +14 -0
  238. package/dist/skills/zh-CN/game-ui/references/checklists/hud-readability.md +12 -0
  239. package/dist/skills/zh-CN/game-ui/references/checklists/mobile-input.md +12 -0
  240. package/dist/skills/zh-CN/game-ui/references/checklists/responsive-ui-fit.md +12 -0
  241. package/dist/skills/zh-CN/game-ui/references/ui-patterns.md +140 -0
  242. package/dist/skills/zh-CN/game-ui/references/ui-prompt-templates.md +63 -0
  243. package/dist/skills/zh-CN/godot-3d-game/SKILL.md +39 -0
  244. package/dist/skills/zh-CN/godot-3d-game/agents/openai.yaml +8 -0
  245. package/dist/skills/zh-CN/godot-3d-game/references/alignment-gates.md +26 -0
  246. package/dist/skills/zh-CN/godot-3d-game/references/appearance-alignment.md +52 -0
  247. package/dist/skills/zh-CN/godot-3d-game/references/build-and-qa.md +22 -0
  248. package/dist/skills/zh-CN/godot-3d-game/references/canonical-asset-runtime.md +27 -0
  249. package/dist/skills/zh-CN/godot-3d-game/references/import-and-wrapper-scenes.md +20 -0
  250. package/dist/skills/zh-CN/godot-3d-game/references/physics-colliders.md +13 -0
  251. package/dist/skills/zh-CN/godot-3d-game/references/project-structure.md +22 -0
  252. package/dist/skills/zh-CN/godot-3d-game/references/skeleton-animation.md +16 -0
  253. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  254. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  255. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  256. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/compose-quality.md +10 -0
  257. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +9 -0
  258. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +15 -0
  259. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +10 -0
  260. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +9 -0
  261. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +9 -0
  262. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/compose-recipes.md +71 -0
  263. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +33 -0
  264. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/implementation-blueprint.md +82 -0
  265. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/sprite-recipes.md +90 -0
  266. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/technical-art.md +67 -0
  267. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/vfx-recipes.md +67 -0
  268. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/visual-scorecard.md +129 -0
  269. package/{skills → dist/skills/zh-CN}/threejs-3d-game/SKILL.md +6 -2
  270. package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-asset-pipeline.md +52 -15
  271. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  272. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  273. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  274. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +23 -0
  275. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +19 -0
  276. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +14 -0
  277. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +14 -0
  278. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +15 -0
  279. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +17 -0
  280. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +236 -0
  281. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/implementation-blueprint.md +161 -0
  282. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/model-recipes.md +146 -0
  283. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/render-recipes.md +90 -0
  284. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/shader-cookbook.md +292 -0
  285. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/technical-art.md +167 -0
  286. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/visual-scorecard.md +147 -0
  287. package/dist/stage-blender/bake_materials.py +102 -0
  288. package/dist/stage-blender/export_scene.py +275 -0
  289. package/dist/stage-blender/preview_camera.py +90 -0
  290. package/dist/stage-blender/repair_mesh.py +112 -0
  291. package/dist/stage-public/app.js +759 -0
  292. package/dist/stage-public/baked/bakePilot.js +44 -0
  293. package/dist/stage-public/baked/bakedScene.js +129 -0
  294. package/dist/stage-public/baked/sceneAppearance.js +225 -0
  295. package/dist/stage-public/baked/sceneCamera.js +45 -0
  296. package/dist/stage-public/comparison-contract.js +28 -0
  297. package/dist/stage-public/delivery.html +4 -0
  298. package/dist/stage-public/delivery.js +17 -0
  299. package/dist/stage-public/godot/appearance.gd +170 -0
  300. package/dist/stage-public/godot/preview.gd +79 -0
  301. package/dist/stage-public/index.html +141 -0
  302. package/dist/stage-public/review-app.js +289 -0
  303. package/dist/stage-public/review-runtime.js +63 -0
  304. package/dist/stage-public/review.css +24 -0
  305. package/dist/stage-public/review.html +47 -0
  306. package/dist/stage-public/scene-runtime.js +212 -0
  307. package/dist/stage-public/sceneContractRuntime.js +235 -0
  308. package/dist/stage-public/styles.css +328 -0
  309. package/dist/stage-public/three-appearance.js +104 -0
  310. package/dist/stage-public/vendor/three170/LICENSE +21 -0
  311. package/dist/stage-public/vendor/three170/build/three.module.js +54571 -0
  312. package/dist/stage-public/vendor/three170/examples/jsm/controls/OrbitControls.js +1523 -0
  313. package/dist/stage-public/vendor/three170/examples/jsm/environments/RoomEnvironment.js +144 -0
  314. package/dist/stage-public/vendor/three170/examples/jsm/libs/fflate.module.js +2672 -0
  315. package/dist/stage-public/vendor/three170/examples/jsm/loaders/EXRLoader.js +2563 -0
  316. package/dist/stage-public/vendor/three170/examples/jsm/loaders/GLTFLoader.js +4727 -0
  317. package/dist/stage-public/vendor/three170/examples/jsm/utils/BufferGeometryUtils.js +1373 -0
  318. package/dist/tile-transition-mask.svg +40 -0
  319. package/package.json +21 -7
  320. package/dist/canvas-public/cycle-detect.js +0 -166
  321. package/dist/canvas-public/smart-split.js +0 -185
  322. /package/{skills → dist/skills/en-US}/game-audio/references/bgm-cdn.md +0 -0
  323. /package/{skills → dist/skills/en-US}/game-audio/references/sfx-cdn.md +0 -0
  324. /package/{skills → dist/skills/en-US}/game-test/references/checklists/playtest-qa.md +0 -0
  325. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/game-ui-quality.md +0 -0
  326. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/hud-readability.md +0 -0
  327. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/mobile-input.md +0 -0
  328. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/responsive-ui-fit.md +0 -0
  329. /package/{skills → dist/skills/en-US}/game-ui/references/ui-patterns.md +0 -0
  330. /package/{skills → dist/skills/en-US}/game-ui/references/ui-prompt-templates.md +0 -0
  331. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  332. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  333. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  334. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/compose-quality.md +0 -0
  335. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +0 -0
  336. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +0 -0
  337. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +0 -0
  338. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +0 -0
  339. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +0 -0
  340. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/compose-recipes.md +0 -0
  341. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +0 -0
  342. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/implementation-blueprint.md +0 -0
  343. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/sprite-recipes.md +0 -0
  344. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/technical-art.md +0 -0
  345. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/vfx-recipes.md +0 -0
  346. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/visual-scorecard.md +0 -0
  347. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  348. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  349. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  350. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +0 -0
  351. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +0 -0
  352. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +0 -0
  353. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +0 -0
  354. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +0 -0
  355. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +0 -0
  356. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +0 -0
  357. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/implementation-blueprint.md +0 -0
  358. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/model-recipes.md +0 -0
  359. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/render-recipes.md +0 -0
  360. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/shader-cookbook.md +0 -0
  361. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/technical-art.md +0 -0
  362. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/visual-scorecard.md +0 -0
  363. /package/{skills → dist/skills/zh-CN}/evomeme-canvas/SKILL.md +0 -0
  364. /package/{skills → dist/skills/zh-CN}/evomeme-canvas/agents/openai.yaml +0 -0
  365. /package/{skills → dist/skills/zh-CN}/evomeme-cloud-fs/agents/openai.yaml +0 -0
  366. /package/{skills → dist/skills/zh-CN}/evomeme-cover/agents/openai.yaml +0 -0
  367. /package/{skills → dist/skills/zh-CN}/evomeme-game-maker/agents/openai.yaml +0 -0
  368. /package/{skills → dist/skills/zh-CN}/evomeme-login/SKILL.md +0 -0
  369. /package/{skills → dist/skills/zh-CN}/evomeme-login/agents/openai.yaml +0 -0
  370. /package/{skills → dist/skills/zh-CN}/evomeme-native-bridge-sdk/SKILL.md +0 -0
  371. /package/{skills → dist/skills/zh-CN}/evomeme-native-bridge-sdk/agents/openai.yaml +0 -0
  372. /package/{skills → dist/skills/zh-CN}/evomeme-tos-upload/agents/openai.yaml +0 -0
  373. /package/{skills → dist/skills/zh-CN}/game-art/SKILL.md +0 -0
  374. /package/{skills → dist/skills/zh-CN}/game-art/agents/openai.yaml +0 -0
  375. /package/{skills → dist/skills/zh-CN}/game-asset-image-generator/agents/openai.yaml +0 -0
  376. /package/{skills → dist/skills/zh-CN}/game-asset-image-generator/references/background.md +0 -0
  377. /package/{skills → dist/skills/zh-CN}/game-asset-image-generator/references/static-game-assets.md +0 -0
  378. /package/{skills → dist/skills/zh-CN}/game-audio/SKILL.md +0 -0
  379. /package/{skills → dist/skills/zh-CN}/game-audio/agents/openai.yaml +0 -0
  380. /package/{skills → dist/skills/zh-CN}/game-demo/SKILL.md +0 -0
  381. /package/{skills → dist/skills/zh-CN}/game-demo/agents/openai.yaml +0 -0
  382. /package/{skills → dist/skills/zh-CN}/game-maker/SKILL.md +0 -0
  383. /package/{skills → dist/skills/zh-CN}/game-maker/agents/openai.yaml +0 -0
  384. /package/{skills → dist/skills/zh-CN}/game-test/SKILL.md +0 -0
  385. /package/{skills → dist/skills/zh-CN}/game-test/agents/openai.yaml +0 -0
  386. /package/{skills → dist/skills/zh-CN}/game-ui/agents/openai.yaml +0 -0
  387. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/SKILL.md +0 -0
  388. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/agents/openai.yaml +0 -0
  389. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-performance.md +0 -0
  390. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-physics.md +0 -0
  391. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-runtime.md +0 -0
  392. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-spritesheets.md +0 -0
  393. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-tilemaps.md +0 -0
  394. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/file-structure.md +0 -0
  395. /package/{skills → dist/skills/zh-CN}/phaser-2d-graphics-builder/SKILL.md +0 -0
  396. /package/{skills → dist/skills/zh-CN}/phaser-2d-graphics-builder/agents/openai.yaml +0 -0
  397. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/agents/openai.yaml +0 -0
  398. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-audio.md +0 -0
  399. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-input-camera.md +0 -0
  400. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-performance.md +0 -0
  401. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-physics.md +0 -0
  402. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-runtime.md +0 -0
  403. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/file-structure.md +0 -0
  404. /package/{skills → dist/skills/zh-CN}/threejs-aaa-graphics-builder/SKILL.md +0 -0
  405. /package/{skills → dist/skills/zh-CN}/threejs-aaa-graphics-builder/agents/openai.yaml +0 -0
@@ -0,0 +1,102 @@
1
+ """Optional UV surface baking on an isolated runtime copy, after source capture."""
2
+ import bpy
3
+ import hashlib
4
+
5
+
6
+ def bake_procedural_materials(scene, output, features, size, warnings):
7
+ original_engine = scene.render.engine
8
+ scene.render.engine = 'CYCLES'
9
+ scene.cycles.samples = 16
10
+ for obj in list(scene.objects):
11
+ if obj.type != 'MESH' or obj.hide_render or not obj.data.polygons:
12
+ continue
13
+ originals = [s.material for s in obj.material_slots]
14
+ if not any(m and features.get(m.name, {}).get('procedural') for m in originals):
15
+ continue
16
+ if any(m is None or features[m.name]['unsupported_nodes'] or features[m.name].get('unsupported_inputs') for m in originals):
17
+ warnings.append(obj.name + ':包含不支持的材质闭包,跳过自动表面烘焙。')
18
+ continue
19
+ # Bake each object's complete UV atlas so shared source materials do not
20
+ # accidentally pick up another object's coordinates or transforms.
21
+ obj.data = obj.data.copy()
22
+ obj.data.materials.clear()
23
+ working = [mat.copy() for mat in originals]
24
+ for mat in working:
25
+ obj.data.materials.append(mat)
26
+ bpy.ops.object.select_all(action='DESELECT')
27
+ obj.hide_set(False)
28
+ obj.select_set(True)
29
+ bpy.context.view_layer.objects.active = obj
30
+ if not obj.data.uv_layers:
31
+ bpy.ops.object.mode_set(mode='EDIT')
32
+ bpy.ops.mesh.select_all(action='SELECT')
33
+ bpy.ops.uv.smart_project(island_margin=.03)
34
+ bpy.ops.object.mode_set(mode='OBJECT')
35
+ obj.data.uv_layers.active = obj.data.uv_layers[0]
36
+ obj.data.uv_layers[0].active_render = True
37
+ key = hashlib.sha256(obj.name.encode()).hexdigest()[:12]
38
+ baked = {}
39
+ supported = True
40
+ for mat in working:
41
+ if not mat.use_nodes:
42
+ mat.use_nodes = True
43
+ out = next((n for n in mat.node_tree.nodes if n.type == 'OUTPUT_MATERIAL' and n.is_active_output), None)
44
+ if not out or not out.inputs['Surface'].is_linked or out.inputs['Surface'].links[0].from_node.type != 'BSDF_PRINCIPLED':
45
+ supported = False
46
+ if not supported:
47
+ warnings.append(obj.name + ':自动烘焙要求输出直接连接 Principled BSDF。')
48
+ continue
49
+ for channel, socket_name in [('base_color','Base Color'), ('roughness','Roughness'), ('metallic','Metallic'), ('normal','Normal')]:
50
+ image = bpy.data.images.new('Stage bake '+key+' '+channel, width=size, height=size, alpha=False)
51
+ image.colorspace_settings.name = 'sRGB' if channel == 'base_color' else 'Non-Color'
52
+ restore = []
53
+ for mat in working:
54
+ nodes, links = mat.node_tree.nodes, mat.node_tree.links
55
+ out = next(n for n in nodes if n.type == 'OUTPUT_MATERIAL' and n.is_active_output)
56
+ shader = out.inputs['Surface'].links[0].from_node
57
+ target = nodes.new('ShaderNodeTexImage')
58
+ target.image = image
59
+ nodes.active = target
60
+ emitter = None
61
+ if channel != 'normal':
62
+ emitter = nodes.new('ShaderNodeEmission')
63
+ socket = shader.inputs[socket_name]
64
+ if socket.is_linked:
65
+ links.new(socket.links[0].from_socket, emitter.inputs['Color'])
66
+ else:
67
+ value = socket.default_value
68
+ emitter.inputs['Color'].default_value = tuple(value) if hasattr(value, '__len__') else (value,value,value,1)
69
+ links.new(emitter.outputs[0], out.inputs['Surface'])
70
+ restore.append((mat, out, shader, target, emitter))
71
+ try:
72
+ bpy.ops.object.bake(type='NORMAL' if channel == 'normal' else 'EMIT', normal_space='TANGENT', margin=12, use_clear=True)
73
+ file = f'textures/{key}_{channel}.png'
74
+ image.filepath_raw = str(output/file)
75
+ image.file_format = 'PNG'
76
+ image.save()
77
+ baked[channel] = (image, file)
78
+ finally:
79
+ for mat, out, shader, target, emitter in restore:
80
+ mat.node_tree.links.new(shader.outputs['BSDF'], out.inputs['Surface'])
81
+ mat.node_tree.nodes.remove(target)
82
+ if emitter:
83
+ mat.node_tree.nodes.remove(emitter)
84
+ for source, mat in zip(originals, working):
85
+ mat.name = source.name + '::' + key
86
+ nodes, links = mat.node_tree.nodes, mat.node_tree.links
87
+ out = next(n for n in nodes if n.type == 'OUTPUT_MATERIAL' and n.is_active_output)
88
+ shader = out.inputs['Surface'].links[0].from_node
89
+ for channel, socket in [('base_color','Base Color'), ('roughness','Roughness'), ('metallic','Metallic'), ('normal','Normal')]:
90
+ image, file = baked[channel]
91
+ texture = nodes.new('ShaderNodeTexImage')
92
+ texture.image = image
93
+ if channel == 'normal':
94
+ normal = nodes.new('ShaderNodeNormalMap')
95
+ links.new(texture.outputs['Color'], normal.inputs['Color'])
96
+ links.new(normal.outputs['Normal'], shader.inputs[socket])
97
+ else:
98
+ links.new(texture.outputs['Color'], shader.inputs[socket])
99
+ features[source.name]['baked_channels'][obj.name+':'+channel] = file
100
+ features[source.name]['baked_objects'] = features[source.name].get('baked_objects', []) + [obj.name]
101
+ features[source.name].setdefault('runtime_materials', []).append({'object': obj.name, 'material_name': mat.name})
102
+ scene.render.engine = original_engine
@@ -0,0 +1,275 @@
1
+ """Export a saved source in an isolated Blender process. Never overwrites input.
2
+
3
+ Arguments after --: a JSON request written by the local scene export service.
4
+ The host validates every artifact before atomically promoting the package.
5
+ """
6
+ import bpy
7
+ import hashlib
8
+ import json
9
+ import math
10
+ import struct
11
+ import sys
12
+ sys.dont_write_bytecode = True
13
+ import uuid
14
+ from pathlib import Path
15
+ from mathutils import Vector
16
+
17
+
18
+ def write_json(path, data):
19
+ path.parent.mkdir(parents=True, exist_ok=True)
20
+ path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
21
+
22
+
23
+ def xyz(v):
24
+ return [float(v[0]), float(v[2]), float(-v[1])]
25
+
26
+
27
+ def connected_nodes(tree):
28
+ visited = set()
29
+ found = []
30
+ def walk(node, current_tree):
31
+ key = (current_tree.as_pointer(), node.as_pointer())
32
+ if key in visited:
33
+ return
34
+ visited.add(key)
35
+ found.append(node)
36
+ if node.type == 'GROUP' and node.node_tree:
37
+ for output in node.node_tree.nodes:
38
+ if output.type == 'GROUP_OUTPUT':
39
+ walk(output, node.node_tree)
40
+ for socket in node.inputs:
41
+ for link in socket.links:
42
+ walk(link.from_node, current_tree)
43
+ for node in tree.nodes:
44
+ if node.type in {'OUTPUT_MATERIAL', 'OUTPUT_WORLD'} and getattr(node, 'is_active_output', True):
45
+ walk(node, tree)
46
+ return found
47
+
48
+
49
+ def choose_device():
50
+ try:
51
+ prefs = bpy.context.preferences.addons['cycles'].preferences
52
+ for backend in ['OPTIX', 'CUDA', 'HIP', 'METAL', 'ONEAPI']:
53
+ try:
54
+ prefs.compute_device_type = backend
55
+ prefs.get_devices()
56
+ if any(d.type == backend for d in prefs.devices):
57
+ for d in prefs.devices:
58
+ d.use = d.type == backend
59
+ return 'GPU'
60
+ except Exception:
61
+ continue
62
+ except Exception:
63
+ pass
64
+ return 'CPU'
65
+
66
+
67
+ def make_camera(scene, name, position, target):
68
+ camera = bpy.data.objects.new(name, bpy.data.cameras.new(name))
69
+ scene.collection.objects.link(camera)
70
+ camera.location = position
71
+ camera.rotation_euler = (Vector(target) - camera.location).to_track_quat('-Z', 'Y').to_euler()
72
+ camera.data.lens = 38
73
+ camera.data.clip_end = max(1000, (Vector(target) - camera.location).length * 10)
74
+ return camera
75
+
76
+
77
+ def main(request):
78
+ output = Path(request['output_dir']).resolve()
79
+ source = Path(bpy.data.filepath).resolve()
80
+ if not source.is_file() or source.suffix.lower() != '.blend':
81
+ raise RuntimeError('Save the Blender source .blend before exporting a scene package')
82
+ if source == output / 'source/scene.blend':
83
+ raise RuntimeError('Source and output must differ')
84
+ scene = bpy.data.scenes.get(request.get('scene_name')) if request.get('scene_name') else bpy.context.scene
85
+ if scene is None:
86
+ raise RuntimeError('Requested Blender scene does not exist')
87
+ if abs(scene.unit_settings.scale_length - 1.0) > 1e-6:
88
+ raise RuntimeError('Scene export requires metre-scale coordinates (unit scale 1); convert a source copy first')
89
+ if scene.render.engine not in {'CYCLES', 'BLENDER_EEVEE_NEXT'}:
90
+ raise RuntimeError('Scene preview supports Cycles and Eevee source scenes')
91
+ if bpy.data.libraries:
92
+ raise RuntimeError('Linked libraries must be made local in an export copy before packaging')
93
+ bpy.context.window.scene = scene
94
+ warnings = []
95
+ # Pack images/fonts/sounds and reject unsupported external dependencies instead
96
+ # of creating a package that happens to work only on the author's machine.
97
+ bpy.ops.file.pack_all()
98
+ remaining = [str(p) for p in bpy.utils.blend_paths(absolute=True, packed=False) if p]
99
+ if remaining:
100
+ raise RuntimeError('Unpacked external dependencies (sequences/caches/volumes): ' + ', '.join(remaining[:8]))
101
+ for folder in ['source', 'preview', 'runtime', 'textures']:
102
+ (output / folder).mkdir(parents=True, exist_ok=True)
103
+ render_objects = [o for o in scene.objects if not o.hide_render]
104
+ geometry = [o for o in render_objects if o.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'}]
105
+ if not geometry:
106
+ raise RuntimeError('Scene contains no renderable geometry')
107
+ bounds = [o.matrix_world @ Vector(corner) for o in geometry for corner in o.bound_box]
108
+ low = Vector(tuple(min(p[i] for p in bounds) for i in range(3)))
109
+ high = Vector(tuple(max(p[i] for p in bounds) for i in range(3)))
110
+ center = (low + high) / 2
111
+ radius = max((high-low).length / 2, 1)
112
+ camera_names = request.get('camera_names') or []
113
+ cameras = []
114
+ if camera_names:
115
+ for name in camera_names:
116
+ obj = scene.objects.get(name)
117
+ if not obj or obj.type != 'CAMERA':
118
+ raise RuntimeError('Camera does not exist: ' + name)
119
+ cameras.append(obj)
120
+ else:
121
+ reference = scene.camera or next((o for o in render_objects if o.type == 'CAMERA'), None)
122
+ if reference is None:
123
+ reference = make_camera(scene, 'Stage Reference', center + Vector((1.25, -1.8, 1.05))*radius, center)
124
+ warnings.append('源场景没有相机;参考机位根据模型边界生成。')
125
+ cameras = [reference, make_camera(scene, 'Stage Overview', center + Vector((.65, -.85, 2.4))*radius, center)]
126
+ if len(cameras) > 6 or any(c.data.type != 'PERSP' for c in cameras):
127
+ raise RuntimeError('Select between 1 and 6 perspective cameras')
128
+ scene.camera = cameras[0]
129
+ render_settings = {'engine': scene.render.engine, 'frame': scene.frame_current, 'samples': scene.cycles.samples,
130
+ 'view_transform': scene.view_settings.view_transform, 'look': scene.view_settings.look,
131
+ 'exposure': scene.view_settings.exposure, 'gamma': scene.view_settings.gamma,
132
+ 'resolution': [scene.render.resolution_x, scene.render.resolution_y],
133
+ 'preview_quality': request.get('quality', 'preview')}
134
+ width = request.get('width', 960)
135
+ aspect = scene.render.resolution_x * scene.render.pixel_aspect_x / (scene.render.resolution_y * scene.render.pixel_aspect_y)
136
+ if aspect < .25 or aspect > 4:
137
+ raise RuntimeError('Source camera aspect is outside supported range 0.25..4')
138
+ scene.render.resolution_x = width
139
+ scene.render.resolution_y = max(1, round(width/aspect))
140
+ scene.render.resolution_percentage = 100
141
+ scene.render.pixel_aspect_x = aspect * scene.render.resolution_y / width
142
+ scene.render.pixel_aspect_y = 1
143
+ if scene.render.use_border:
144
+ warnings.append('参考图使用完整画幅,未使用源场景的渲染边框。')
145
+ scene.render.use_border = False
146
+ scene.render.use_crop_to_border = False
147
+ ids = set()
148
+ nodes = []
149
+ materials = {}
150
+ volumes = []
151
+ for obj in scene.objects:
152
+ stable_id = obj.get('evomeme_node_id') or str(uuid.uuid5(uuid.NAMESPACE_URL, request['asset_id']+'/'+obj.name))
153
+ if stable_id in ids:
154
+ raise RuntimeError('Duplicate evomeme_node_id: '+stable_id)
155
+ ids.add(stable_id)
156
+ obj['evomeme_node_id'] = stable_id
157
+ nodes.append({'id': stable_id, 'name': obj.name, 'type': obj.type, 'parent': obj.parent.name if obj.parent else None,
158
+ 'renderable': not obj.hide_render})
159
+ if obj.hide_render:
160
+ continue
161
+ if obj.type == 'VOLUME':
162
+ volumes.append({'object': obj.name, 'type': 'VOLUME'})
163
+ if any(m.type == 'NODES' for m in obj.modifiers):
164
+ warnings.append(obj.name + ':几何节点按导出时的求值结果交付,需复核实例与动画。')
165
+ for slot in obj.material_slots:
166
+ mat = slot.material
167
+ if not mat:
168
+ continue
169
+ if mat.name not in materials:
170
+ linked = connected_nodes(mat.node_tree) if mat.use_nodes else []
171
+ types = sorted(set(n.bl_idname for n in linked))
172
+ unsupported = [t for t in types if t.startswith('ShaderNodeBsdf') and t != 'ShaderNodeBsdfPrincipled']
173
+ unsupported += [t for t in types if t in {'ShaderNodeMixShader', 'ShaderNodeAddShader', 'ShaderNodeScript', 'ShaderNodeShaderToRGB'} or t.startswith('ShaderNodeVolume')]
174
+ simple_nodes = {'ShaderNodeOutputMaterial', 'ShaderNodeBsdfPrincipled', 'ShaderNodeTexImage',
175
+ 'ShaderNodeUVMap', 'ShaderNodeNormalMap', 'ShaderNodeSeparateColor', 'ShaderNodeSeparateRGB',
176
+ 'ShaderNodeRGB', 'ShaderNodeValue'}
177
+ composed = [t for t in types if t not in simple_nodes and t not in unsupported]
178
+ # Four-channel baking cannot resolve procedural opacity,
179
+ # emission, transmission, displacement, or custom closures.
180
+ unbaked_inputs = []
181
+ if composed:
182
+ for node in linked:
183
+ if node.type == 'BSDF_PRINCIPLED':
184
+ unbaked_inputs += [s.name for s in node.inputs if s.is_linked and s.name not in {'Base Color','Roughness','Metallic','Normal'}]
185
+ if node.type == 'OUTPUT_MATERIAL' and node.inputs['Displacement'].is_linked:
186
+ unbaked_inputs.append('Displacement')
187
+ materials[mat.name] = {'id': str(uuid.uuid5(uuid.NAMESPACE_URL, request['asset_id']+'/material/'+mat.name)),
188
+ 'name': mat.name, 'node_types': types, 'procedural': bool(composed),
189
+ 'unsupported_nodes': unsupported, 'unsupported_inputs': sorted(set(unbaked_inputs)), 'objects': [], 'baked_channels': {}}
190
+ if any('Volume' in t for t in types):
191
+ volumes.append({'object': obj.name, 'material': mat.name, 'type': 'SHADER_VOLUME'})
192
+ materials[mat.name]['objects'].append(obj.name)
193
+ world_nodes = connected_nodes(scene.world.node_tree) if scene.world and scene.world.use_nodes else []
194
+ if any('Volume' in n.bl_idname for n in world_nodes):
195
+ volumes.append({'object': 'World', 'type': 'WORLD_VOLUME'})
196
+ lights = []
197
+ for obj in render_objects:
198
+ if obj.type != 'LIGHT':
199
+ continue
200
+ position, rotation, _ = obj.matrix_world.decompose()
201
+ lights.append({'name': obj.name, 'type': obj.data.type, 'position': list(position), 'rotation_xyz_rad': list(rotation.to_euler('XYZ')),
202
+ 'energy': obj.data.energy, 'color_linear': list(obj.data.color), 'shadow_enabled': getattr(obj.data, 'use_shadow', True),
203
+ 'spot_size': getattr(obj.data, 'spot_size', math.pi/2), 'size': getattr(obj.data, 'size', 0)})
204
+ # Source reference remains authored and editable. Runtime-only conversion is
205
+ # performed after this snapshot/reference stage.
206
+ scene.cycles.device = choose_device()
207
+ bpy.ops.wm.save_as_mainfile(filepath=str(output/'source/scene.blend'), copy=True)
208
+ if request.get('quality') != 'final':
209
+ scene.cycles.samples = 32
210
+ scene.cycles.use_denoising = True
211
+ scene.render.image_settings.file_format = 'PNG'
212
+ scene.render.image_settings.color_mode = 'RGBA'
213
+ for index, camera in enumerate(cameras):
214
+ scene.camera = camera
215
+ camera_id = 'reference' if index == 0 else 'overview' if index == 1 else 'inspection_'+str(index)
216
+ scene.render.filepath = str(output/f'preview/blender_{camera_id}.png')
217
+ bpy.ops.render.render(write_still=True, scene=scene.name)
218
+ scene.camera = cameras[0]
219
+ if request.get('bake_materials'):
220
+ from bake_materials import bake_procedural_materials
221
+ bake_procedural_materials(scene, output, materials, request.get('texture_size', 1024), warnings)
222
+ # Exclude hidden helpers from runtime geometry, but leave source unchanged.
223
+ for obj in scene.objects:
224
+ volume_only = bool(obj.material_slots) and all(s.material and s.material.use_nodes and
225
+ any(n.type == 'OUTPUT_MATERIAL' and n.inputs['Volume'].is_linked and not n.inputs['Surface'].is_linked for n in s.material.node_tree.nodes)
226
+ for s in obj.material_slots)
227
+ obj.select_set(not obj.hide_render and not volume_only and obj.type != 'VOLUME')
228
+ for camera in cameras:
229
+ camera.hide_render = False
230
+ camera.select_set(True)
231
+ bpy.ops.export_scene.gltf(filepath=str(output/'model.glb'), export_format='GLB', use_selection=True,
232
+ use_active_scene=True, export_yup=True, export_apply=True, export_cameras=True, export_lights=False, export_extras=True)
233
+ model = (output/'model.glb').read_bytes()
234
+ gltf = json.loads(model[20:20+struct.unpack_from('<I', model, 12)[0]])
235
+ camera_defs = []
236
+ # Matrix world is used to handle parented cameras; glTF projection is the
237
+ # exported, actual vertical FOV rather than a guessed sensor conversion.
238
+ for index, camera in enumerate(cameras):
239
+ node = next((n for n in gltf.get('nodes', []) if n.get('name') == camera.name and 'camera' in n), None)
240
+ if node is None:
241
+ raise RuntimeError('Exported camera missing: '+camera.name)
242
+ projection = gltf['cameras'][node['camera']]['perspective']
243
+ pos, rot, scale = camera.matrix_world.decompose()
244
+ if max(abs(s-1) for s in scale) > .0001 or abs(camera.data.shift_x)+abs(camera.data.shift_y) > .0001:
245
+ raise RuntimeError('Scaled or shifted cameras require conversion to a perspective export camera')
246
+ from mathutils import Matrix
247
+ q = (Matrix.Rotation(-math.pi/2, 4, 'X') @ camera.matrix_world).to_quaternion()
248
+ direction = rot @ Vector((0,0,-1))
249
+ camera_id = 'reference' if index == 0 else 'overview' if index == 1 else 'inspection_'+str(index)
250
+ camera_defs.append({'id': camera_id, 'label': camera.name, 'position': xyz(pos), 'quaternion_xyzw': [q.x,q.y,q.z,q.w],
251
+ 'look_at': xyz(pos+direction*radius), 'vertical_fov_rad': projection['yfov'], 'aspect_ratio': aspect,
252
+ 'near': projection['znear'], 'far': projection.get('zfar', camera.data.clip_end),
253
+ 'reference_render': f'preview/blender_{camera_id}.png'})
254
+ world_color = list(scene.world.color) if scene.world else [.1,.1,.1]
255
+ features = {'schema': 'evomeme.source_scene_features.v1', 'nodes': nodes, 'materials': list(materials.values()), 'volumes': volumes,
256
+ 'world': {'environment_texture': any(n.bl_idname == 'ShaderNodeTexEnvironment' for n in world_nodes), 'color': world_color},
257
+ 'rendering': render_settings, 'export_warnings': warnings}
258
+ contract = {'schema': 'evomeme.scene_contract.v1', 'asset_id': request['asset_id'], 'contract_revision': 1,
259
+ 'source_of_truth': {'authoring_and_cycles_appearance': 'source/scene.blend', 'authoring_scene': scene.name,
260
+ 'reference_camera': 'reference', 'reference_render': 'preview/blender_reference.png'},
261
+ 'coordinates': {'blender_to_gltf': '[x,y,z] -> [x,z,-y]', 'conversion_already_baked': True, 'metres_per_unit': 1,
262
+ 'up_axis': '+y', 'forward_axis': '-z', 'pivot': 'origin', 'scale_hint_metres': None,
263
+ 'root_transform': {'position': [0,0,0], 'quaternion_xyzw': [0,0,0,1], 'scale': [1,1,1]}},
264
+ 'cameras': camera_defs, 'blender_lights': lights, 'source_features': features,
265
+ 'contents': {'mesh_count': len(gltf.get('meshes', [])), 'node_count': len(gltf.get('nodes', [])),
266
+ 'material_count': len(gltf.get('materials', [])), 'camera_count': len(gltf.get('cameras', [])),
267
+ 'skin_count': len(gltf.get('skins', [])), 'animations': len(gltf.get('animations', []))}}
268
+ write_json(output/'export-manifest.json', contract)
269
+ print('EVOMEME_SCENE_EXPORT '+json.dumps({'ok': True, 'asset_id': request['asset_id'], 'cameras': len(camera_defs)}, ensure_ascii=True), flush=True)
270
+
271
+
272
+ if __name__ == '__main__':
273
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
274
+ request_file = Path(sys.argv[sys.argv.index('--')+1])
275
+ main(json.loads(request_file.read_text(encoding='utf-8')))
@@ -0,0 +1,90 @@
1
+ """Read-only, persistent Cycles camera preview worker. Never saves the .blend."""
2
+ import bpy
3
+ import json
4
+ import math
5
+ import sys
6
+ import time
7
+ from mathutils import Matrix, Quaternion, Vector
8
+
9
+
10
+ def emit(value):
11
+ print('EVOMEME_PREVIEW ' + json.dumps(value, ensure_ascii=True), flush=True)
12
+
13
+
14
+ device = 'CPU'
15
+ try:
16
+ prefs = bpy.context.preferences.addons['cycles'].preferences
17
+ for backend in ['OPTIX', 'CUDA', 'HIP', 'METAL', 'ONEAPI']:
18
+ try:
19
+ prefs.compute_device_type = backend
20
+ prefs.get_devices()
21
+ if any(d.type == backend for d in prefs.devices):
22
+ for d in prefs.devices:
23
+ d.use = d.type == backend
24
+ device = backend
25
+ break
26
+ except Exception:
27
+ continue
28
+ except Exception:
29
+ pass
30
+
31
+ # glTF has Y up; Blender has Z up. Local camera axes are shared (-Z, +Y).
32
+ gltf_to_blender = Matrix.Rotation(math.pi / 2, 4, 'X')
33
+ camera_data = bpy.data.cameras.new('EvoMeme comparison preview')
34
+ camera_obj = bpy.data.objects.new('EvoMeme comparison preview', camera_data)
35
+ linked_scene = None
36
+
37
+ for line in sys.stdin:
38
+ try:
39
+ job = json.loads(line)
40
+ started = time.monotonic()
41
+ scene = bpy.data.scenes.get(job['scene'])
42
+ if scene is None:
43
+ raise ValueError('The contracted Blender scene does not exist')
44
+ if scene.render.engine not in {'CYCLES', 'BLENDER_EEVEE_NEXT'}:
45
+ raise ValueError('Native comparison requires a Cycles or Eevee source scene')
46
+ if linked_scene != scene:
47
+ scene.collection.objects.link(camera_obj)
48
+ linked_scene = scene
49
+ c = job['camera']
50
+ x, y, z, w = c['quaternion']
51
+ camera_obj.matrix_world = gltf_to_blender @ Matrix.LocRotScale(
52
+ Vector(c['position']), Quaternion((w, x, y, z)), Vector((1, 1, 1)))
53
+ camera_data.type = 'PERSP'
54
+ camera_data.sensor_fit = 'VERTICAL'
55
+ camera_data.sensor_height = 24
56
+ camera_data.lens = 24 / (2 * math.tan(math.radians(c['fov']) / 2))
57
+ camera_data.clip_start = c['near']
58
+ camera_data.clip_end = c['far']
59
+ camera_data.shift_x = camera_data.shift_y = 0
60
+ camera_data.dof.use_dof = False
61
+ scene.camera = camera_obj
62
+ scene.render.resolution_x = job['width']
63
+ scene.render.resolution_y = job['height']
64
+ scene.render.resolution_percentage = 100
65
+ # Preserve the exact camera aspect despite integer image dimensions.
66
+ scene.render.pixel_aspect_x = c['aspect'] * job['height'] / job['width']
67
+ scene.render.pixel_aspect_y = 1
68
+ scene.render.use_border = False
69
+ scene.render.use_crop_to_border = False
70
+ scene.render.use_persistent_data = True
71
+ scene.cycles.device = 'GPU' if device != 'CPU' else 'CPU'
72
+ scene.cycles.samples = 32
73
+ scene.cycles.use_denoising = True
74
+ scene.render.image_settings.file_format = 'PNG'
75
+ scene.render.image_settings.color_mode = 'RGBA'
76
+ scene.render.filepath = job['output']
77
+ bpy.context.view_layer.update()
78
+ bpy.ops.render.render(write_still=True, scene=scene.name)
79
+ projection = camera_obj.calc_matrix_camera(bpy.context.evaluated_depsgraph_get(),
80
+ x=job['width'], y=job['height'], scale_x=scene.render.pixel_aspect_x,
81
+ scale_y=scene.render.pixel_aspect_y)
82
+ emit({'id': job['id'], 'ok': True, 'seconds': round(time.monotonic() - started, 2),
83
+ 'engine': scene.render.engine, 'device': device, 'samples': 32,
84
+ 'width': job['width'], 'height': job['height'], 'blender': bpy.app.version_string,
85
+ 'camera_matrix_blender': [list(row) for row in camera_obj.matrix_world],
86
+ 'projection': [list(row) for row in projection],
87
+ 'view_transform': scene.view_settings.view_transform,
88
+ 'look': scene.view_settings.look})
89
+ except Exception as error:
90
+ emit({'id': locals().get('job', {}).get('id'), 'ok': False, 'error': str(error)})
@@ -0,0 +1,112 @@
1
+ """
2
+ Minimal Blender repair for evomeme canonical packages.
3
+
4
+ Runs inside Blender or a bpy wheel:
5
+
6
+ blender --background --factory-startup --python repair_mesh.py -- \\
7
+ --src /abs/model.glb --dest /abs/out.glb [--ground] [--report /abs/report.json]
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import sys
14
+ from pathlib import Path
15
+
16
+
17
+ def _bpy():
18
+ try:
19
+ import bpy # type: ignore
20
+ except ImportError as exc: # pragma: no cover
21
+ raise RuntimeError(
22
+ "repair_mesh.py must run inside Blender or an env with the bpy wheel"
23
+ ) from exc
24
+ return bpy
25
+
26
+
27
+ def repair(src: Path, dest: Path, *, ground: bool) -> dict:
28
+ bpy = _bpy()
29
+ bpy.ops.wm.read_factory_settings(use_empty=True)
30
+
31
+ # Clear default cube if present
32
+ for obj in list(bpy.data.objects):
33
+ bpy.data.objects.remove(obj, do_unlink=True)
34
+
35
+ ext = src.suffix.lower()
36
+ if ext in {".glb", ".gltf"}:
37
+ bpy.ops.import_scene.gltf(filepath=str(src))
38
+ elif ext == ".fbx":
39
+ bpy.ops.import_scene.fbx(filepath=str(src))
40
+ elif ext == ".obj":
41
+ bpy.ops.wm.obj_import(filepath=str(src))
42
+ else:
43
+ raise RuntimeError(f"unsupported source suffix: {ext}")
44
+
45
+ imported = [obj for obj in bpy.context.scene.objects if obj.type in {"MESH", "ARMATURE", "EMPTY"}]
46
+ if not imported:
47
+ raise RuntimeError("Blender imported no objects")
48
+
49
+ # Join meshes for a stable single root when grounding
50
+ meshes = [obj for obj in imported if obj.type == "MESH"]
51
+ if ground and meshes:
52
+ bpy.ops.object.select_all(action="DESELECT")
53
+ for obj in meshes:
54
+ obj.select_set(True)
55
+ bpy.context.view_layer.objects.active = meshes[0]
56
+ if len(meshes) > 1:
57
+ bpy.ops.object.join()
58
+ active = bpy.context.view_layer.objects.active
59
+ bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="BOUNDS")
60
+ # Put lowest point on Z=0 (Blender Z-up); glTF export converts to Y-up.
61
+ min_z = min((active.matrix_world @ v.co).z for v in active.data.vertices) if active.data.vertices else 0.0
62
+ active.location.z -= min_z
63
+
64
+ dest.parent.mkdir(parents=True, exist_ok=True)
65
+ bpy.ops.export_scene.gltf(
66
+ filepath=str(dest),
67
+ export_format="GLB",
68
+ use_selection=False,
69
+ )
70
+
71
+ return {
72
+ "ok": True,
73
+ "src": str(src),
74
+ "dest": str(dest),
75
+ "ground": ground,
76
+ "object_count": len(imported),
77
+ "mesh_count": len(meshes),
78
+ }
79
+
80
+
81
+ def main(argv: list[str] | None = None) -> int:
82
+ parser = argparse.ArgumentParser()
83
+ parser.add_argument("--src", required=True)
84
+ parser.add_argument("--dest", required=True)
85
+ parser.add_argument("--ground", action="store_true")
86
+ parser.add_argument("--report", default="")
87
+ # Blender passes everything after `--`; also tolerate stray blender args.
88
+ args, _unknown = parser.parse_known_args(argv)
89
+
90
+ src = Path(args.src).expanduser().resolve()
91
+ dest = Path(args.dest).expanduser().resolve()
92
+ if not src.is_file():
93
+ raise SystemExit(f"source missing: {src}")
94
+
95
+ report = repair(src, dest, ground=bool(args.ground))
96
+ text = json.dumps(report, ensure_ascii=False, indent=2)
97
+ if args.report:
98
+ report_path = Path(args.report).expanduser().resolve()
99
+ report_path.parent.mkdir(parents=True, exist_ok=True)
100
+ report_path.write_text(text + "\n", encoding="utf-8")
101
+ print(text)
102
+ return 0
103
+
104
+
105
+ if __name__ == "__main__":
106
+ # When launched via `blender --python this.py -- ...`, argv includes blender noise.
107
+ argv = sys.argv
108
+ if "--" in argv:
109
+ argv = argv[argv.index("--") + 1 :]
110
+ else:
111
+ argv = argv[1:]
112
+ raise SystemExit(main(argv))