Skip to content

fix(render3d): 补齐三渲二出动作这一环的四个根因 - #835

Open
johnnyzhang-eng wants to merge 1 commit into
1024XEngineer:mainfrom
johnnyzhang-eng:fix/rig-motion
Open

fix(render3d): 补齐三渲二出动作这一环的四个根因#835
johnnyzhang-eng wants to merge 1 commit into
1024XEngineer:mainfrom
johnnyzhang-eng:fix/rig-motion

Conversation

@johnnyzhang-eng

Copy link
Copy Markdown
Contributor

三渲二在生产里一次都没成功过 —— 查 windup_character 全表,有 3D 资产的造型 0 个。链上有四个各自独立、各自致命的缺陷,全在「不报错、只出错东西」这一类。

四个根因与实测

# 缺陷 证据
rig() 不传 motion → 请求体无 MotionType → 产物零动画 带 MotionType 的三份绑骨产物各有 1 个 AnimationStack,不带的那份 0 个;原始混元 GLB 9 份全 0
MediaCategory 里没有 model-3d_publish_model 恒抛 ValidationError → 模型从没上传成功过model_3d_url 永远 None → 三渲二永远不被选中 直接复现:Input should be 'reference-image','outfit-preview','action-frame' or 'general'
产物是 FBX 却恒按 .glb 发布 → 两个出帧台都按后缀挑 loader → GLTFLoader → "Bad glTF" 归档里每份绑骨产物都是 FBX
片段名对不上:出帧台找 "walk",模型里那个叫 Armature|<资产id>_remap 用真的 FBXLoader.parse() 解三份真实产物;08-19 与 08-27 两次独立任务的字符串完全一致,所以是资产 id 不是任务哈希

②是「生产里 3D 资产 0 个」的直接原因。

Changes

  • 绑骨带 MotionType产物没有动画片段就抛、不落点 —— 否则 10 积分买来的哑模型会被挂成 READY。
  • MediaCategoryMODEL_3D
  • 发布按 magic bytes 定后缀与 content-type。
  • 片段解析:恰好一个片段时用它;多于一个还对不上名字仍然抛。两个宿主(bake_driver.mjs / stage.ts)同一条规则。
  • 派单前加闸:有 3D 资产但动作不在可渲集合里当场抛。不加的话这次修改会把「响亮的失败」变成「静默的错交付」——attack 任务收到一段走路,帧数/时长/朝向/成色全部自洽。

attack / custom 显式不接:预设库只有 thrust(16)/kick(18),而攻击按运动拓扑分四型,拿 thrust 顶 sweep 是另一种静默错;custom 是用户自述动作,映射到任何预设都是拿别的动作冒充。

⚠️ openapi.json 多一行

           "action-frame",
+          "model-3d",
           "general"

备选是改用 GENERAL(零 diff),但会让 3D 模型和杂项混在 media/general/ 下,且与代码里每处 media/model-3d/ 引用矛盾。选补枚举。

Verification

  • 7 条新用例。其中「绑骨请求体确实带 MotionType」那条刻意用真的 TencentAutoRigProvider、只换掉网络层 —— 替身只能证明「传了个 motion 参数」,证不了它变成了请求体里的 MotionType,而链上任何一环断掉症状都一样。
  • 两个既有替身原本比真接口宽容(恒回 motion=None / 根本没有 motion 字段),「忘了传动作」在所有用例里都不可见 —— 已改成照真行为。
  • 逐条把修改还原掉验证会红(try/finally + 结束校验 sha256,全部已回滚且哈希一致)。
  • uv run ruff check . / export_openapi rc=0 / lint-imports 2 kept 0 broken / pytest 1853 passed, 0 failed
  • 前端 tsc -b 通过、vitest 1255 passed (81 files)
  • 端到端零成本验证:拿真实绑骨产物喂给生产同一份 LocalSpriteRenderProvider,出 6 帧、sha 全不同、帧间差异 1.4–1.7 万像素、双腿左右交替,是完整走路循环。

已知未做

  • 追加第二个动作的入口(需要「每动作一份资产 URL」的契约改动)。
  • attack 的 sweep 类预设编号未实测(要花 10 积分)。
  • bake_driver.mjs 的片段解析规则没有自动化测试(要 node + three + playwright,CI 不跑),靠上面那次真实产物端到端跑通。

Closes #834

三渲二在生产里一次都没成功过 —— 查 windup_character 全表,有 3D 资产的造型 0 个。
四个缺陷各自独立、各自致命,且全在"不报错、只出错东西"这一类:

① rig() 不传 motion → 请求体无 MotionType → 产物零动画。实测对照:带 MotionType 的
   三份绑骨产物各有 1 个 AnimationStack,不带的那份是 0 个。

② **MediaCategory 里没有 model-3d** → _publish_model 恒抛 ValidationError → 绑骨模型
   从来没上传成功过 → outfits[].model_3d_url 永远是 None → 三渲二永远不被选中。
   这是"生产里 3D 资产 0 个"的直接原因。

③ 绑骨产物是 FBX 却恒按 .glb 发布 → 两个出帧台宿主都按后缀挑 loader → 走 GLTFLoader
   → Bad glTF。归档里每一份绑骨产物都是 FBX。

④ 片段名对不上:出帧台按 clip="walk" 找,而模型里唯一那个片段叫
   Armature|<资产id>_remap。用真的 FBXLoader.parse() 解了三个真实产物确认;
   08-19 与 08-27 两次独立任务拿到的字符串完全一致,所以它是动作资产自己的 id,
   不是每次任务的哈希。

配套:一份资产只带一个动作,所以在**派单之前**加闸 —— 不加的话这次修改会把"响亮的
失败"(没有任何动画片段)变成"静默的错交付"(attack 任务收到一段走路,帧数/时长/朝向/
成色全部自洽)。attack 与 custom 显式不接:预设库里只有 thrust/kick,拿它顶 sweep 是
另一种静默错;custom 是用户自述动作,映射到任何预设都是拿别的动作冒充。

两个替身原本比真接口宽容(恒回 motion=None / 根本没有 motion 字段),"忘了传动作"
在所有用例里都不可见 —— 已改成照真行为。

Closes 1024XEngineer#834
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

Deployment failed for project windup with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/huyan-s-projects1?upgradeToPro=build-rate-limit

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #835      +/-   ##
==========================================
+ Coverage   86.47%   86.49%   +0.01%     
==========================================
  Files         296      296              
  Lines       22136    22150      +14     
  Branches     3356     3357       +1     
==========================================
+ Hits        19142    19158      +16     
+ Misses       2055     2052       -3     
- Partials      939      940       +1     
Flag Coverage Δ
backend 91.48% <100.00%> (+0.04%) ⬆️
frontend 80.51% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...app/src/windup_app/server/orchestrator/executor.py 77.64% <100.00%> (+0.10%) ⬆️
.../windup_app/server/orchestrator/render3d_assets.py 100.00% <100.00%> (ø)
...windup_app/server/orchestrator/render3d_service.py 81.60% <100.00%> (+1.95%) ⬆️
...d/packages/common/src/windup_common/enums/media.py 100.00% <100.00%> (ø)
frontend/src/features/client-bake/index.ts 96.77% <100.00%> (-0.11%) ⬇️
frontend/src/features/client-bake/stage.ts 2.89% <100.00%> (+1.42%) ⬆️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend motion/publishing and both bake-host clip-resolution paths are covered by the changed code and pass the available syntax/diff checks. One API contract synchronization issue remains inline below. The repository-specific test runners could not execute because this checkout has no uv and no installed/executable frontend vitest.

Comment thread openapi.json
"reference-image",
"outfit-preview",
"action-frame",
"model-3d",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep the frontend media-category union in sync

The backend enum and this OpenAPI schema now advertise model-3d, but frontend/src/entities/media/index.ts still defines MediaCategory without that value. Any frontend code that needs to call MediaApis.upload(file, 'model-3d') is therefore rejected by TypeScript even though the backend accepts the category, and the frontend comment explicitly says the union mirrors the backend enum. Add the new literal to the frontend union (and its related contract test/generated type if applicable) so the client and published schema remain consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 三渲二出不了动作 —— 链上四个各自致命的缺陷

1 participant