feat(render3d): 出帧台读到的骨架事实与位移轨落进造型与动作 - #777
Conversation
|
Deployment failed for project windup with the following error: Learn More: https://vercel.com/huyan-s-projects1?upgradeToPro=build-rate-limit |
|
✅ 此 PR 已关联 issue,之前的提醒已自动标记为已解决。 |
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #777 +/- ##
==========================================
- Coverage 86.47% 86.45% -0.03%
==========================================
Files 296 296
Lines 22136 22232 +96
Branches 3356 3359 +3
==========================================
+ Hits 19142 19220 +78
- Misses 2055 2072 +17
- Partials 939 940 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
| # 出帧台读到的骨架事实与根骨位移轨(#774)。三渲二独有,i2v 路线恒为 None。 | ||
| # 此前这两样每渲一段都算一遍、算完即丢。骨架事实是**每造型一次性**的, | ||
| # 随第一个动作带上来即可;位移轨是每动作一份。 | ||
| if generated.rig is not None: |
There was a problem hiding this comment.
[P1] Preserve rig metadata through task deserialization
这里把 rig_facts 和 root_motion 写入了动作结果 JSON,但 task_repo._deserialize_result() 重建 CharacterActionOutput 时没有读取这两个键;因此通过任务查询接口或断线重连拿到的已完成任务会静默丢掉新字段(实时事件与持久化读取的结果也会不一致)。前端的 mapActionResult 同样只映射 frames/geometry,所以这些数据最终无法到达动作结果消费者。请把字段加入动作输出模型及两条反序列化/映射路径,确保结果持久化后仍完整。
| # 出帧台读到的骨架事实与根骨位移轨此前算完即丢(#774)。留在实例上由 | ||
| # CharacterGenerator._finish 取走 —— derive 的返回类型是帧列表,是两个入口 | ||
| # (服务端渲 / 浏览器渲)共用的形状,不为这一条改它。 | ||
| self._last_rig = RigFacts( |
There was a problem hiding this comment.
[P1] Avoid shared mutable metadata between concurrent renders
ActionTaskExecutor 是进程级单例,缓存的 CharacterGenerator/RenderFrameStrategy 会被多个任务线程共享;本 PR 将每次渲染的结果写入 _last_rig 与 _last_root_motion,再由调用方稍后从 strategy 读取。两个三渲二任务可在 A 的 derive() 写入后、A 的 _finish() 读取前交错,导致 A 的动作帧携带 B 模型的骨架事实和位移轨(反之亦然),并将错误元数据持久化。请让派生结果与帧一起按调用返回,或以请求局部状态传递,不能依赖共享 strategy 实例字段。
此前每渲一段都算一遍、算完即丢。骨架事实每造型一份,位移轨每动作一份, 取根骨动画轨而非交付帧像素反推。服务端渲与浏览器渲两条路交回同一批数。 Closes #774
三条都属于"任务照常 COMPLETED、帧数时长成色全对、零报错,只是产出的东西是错的"。
两位审查者(FennoAI + 内部审查)独立指出了其中两条。
① **位移轨恒为 None。** bake_driver.mjs 交回 meta 时已按片段名拆过一层
(root_motion: rootMotion[clip] ?? null),sprite.py 原样透传 —— _root_motion_of
再 .get(clip) 一次恒得 None。服务端渲那条 100% 走这里。
顺带修脏数据守卫:原来只校验 x 且解包先于守卫,长度不对照样 ValueError、z 是脏值
直接抛;守卫命中时又只丢那一条,让位移轨比帧数短、索引静默错位。改成整条作废。
② **并发串味。** ActionTaskExecutor 是进程级单例,strategy 被所有任务线程共用而
action 并发默认 8:A 的 derive 写完、A 的 _finish 读之前,B 的 derive 会覆盖它。
改用 ContextVar 按请求隔离,取完即清。不改 derive 的返回形状 —— 那是两个入口共用的。
③ **落库丢字段。** rig_facts / root_motion 只写进结果 JSON,没进 CharacterActionOutput
也没进 _deserialize_result:查询接口与断线重连拿到的已完成任务缺这两样,而实时事件
那条路有,两条路给出不同结果。同一个坑 geometry 已经踩过一次,注释就在它旁边。
配套修正测试:那个桩用的是 {"walk": {...}},比生产多包一层 —— ① 那个真 bug 就是这么
合进来的。改成 bake_driver 实际交回的形状。
另外 rebase 到 main(#771 已合)时解冲突,顺手清掉两处重复的 return 与一处把 strategy
当 render_spec 位置实参传的写法。
4ae065d to
c82ecb9
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Summary
出帧台每渲一段动作都从绑骨模型里读出骨架事实、根骨位移轨与可用片段表,此前三样全部算完即丢(
sheet.rig/available_clips在 main 上各有 0 处消费)。本 PR 给它们落点。本 PR 基于 #771(
feat/518-render3d-entry),diff 只含本条改动。落点
CharacterOutfit.rig_facts—— 骨数、根骨名、骨名列表、蒙皮网格数、顶点数、可用片段表。每造型一份,与model_3d_url同批产出。CharacterAction.root_motion—— 逐帧(dx, dz),单位「1.0 = 角色总高」。每动作一份。两个字段都可空、纯加法,存量数据原样解析(有用例锁)。不引入迁移机制,
schema_sync巡检能覆盖;真正的迁移机制是 #633。位移轨取哪一份
取出帧台从根骨动画轨读出的那份,不取
postprocess.rootmotion从交付帧像素反推的那份。后者是在帧已被对齐成原地之后再猜,信息在对齐那一步就损失了;前者是作者意图的精确值,且在三渲二这条路上是白送的。两份不混装在同一字段里——i2v 路线没有骨架,只能用反推的,那条不写进本字段,免得同一个字段装着两种精度的数还分不出来。
两条路必须交回同样的东西
服务端渲:strategy 在
derive里留下,_finish取走。浏览器渲:此前
completeBake只回传clip与sampleTimes,rigInfo与根骨轨在浏览器里算完就随页面销毁——这是 #714 引入的缺口,本 PR 一并收。现在BakeStage在压平根骨 XZ 之前先把位移抽出来存着(压平是为了让帧原地不动,位移本身仍是产物的一部分),随交齐一起回传,服务端存进登记、续跑时带进出参。有用例锁住「同一造型走哪条路存下来的资产一致」。
撤掉了原方案里的一条硬校验
issue 原方案写了「拿到骨架事实之后,
RigInfodocstring 里那条『28 骨 · humanoid 命名 · 无mixamorig前缀』才有地方执行,不满足时不静默继续」。实测推翻(量全部归档绑骨产物):
mixamorig前缀「28 骨」只有 1/5 命中,而 24 与 27 正是混元自己绑的四足——同一条链路出来的骨数就不是常数;带前缀那个是 Mixamo 绑的,属重定向路线的正常输入。据此当闸会把自家产物与重定向输入一起挡掉。
RigInfo的 docstring 自称「已确立(不必每次重验)」,重验一次即翻。已改判并写明依据,免得下一个人接着信。这几个数改为记录用,不做拒绝。顺带更正一处 issue 表述
#192 的子项把这条写成「无处存放就等于每补一个动作都重做一遍前四段,直接抹掉本路线的成本优势」。该表述不成立:
_produce_action见到model_3d_url就直接走出帧,绑骨只在建资产路径上调,动作生成路径碰不到它——成本复用在 #277 落model_3d_url时就解决了。本 PR 的实际收益是「已经算出来的数据不再被丢掉」,不是省钱。已写进 #774 备注。不包含
postprocess.rootmotion(i2v 路线仍只能像素反推)。Test plan
ruff/export_openapi(openapi.json 已随之更新 +179 行)/lint-imports/pytest -q:1780 passed, 14 skippedformat:check/lint/typecheck/test:coverage/build:1213 passed命令都跑在最后一次编辑之后。
Closes #774