D-20261021-01|場地碰撞體分段與邊界法線機制¶
2026-10-26 關聯追補:後果段的已知限制(正式 hard CCD 截斷滾動接地,軌跡判準只覆蓋事件前的自由段,001050) 已由 D-20261026-02 修正:持續接觸 pair 改掃核心球,軌跡判準可涵蓋事件後。
背景與驅動力¶
八車 step 成本的主體是 gameplay hard CCD 對場地 trimesh 的 TOI 掃掠(彎道 CCD 佔 step 九成以上), 成本與單顆 collider 被掃掠的三角數近似線性(issue 001038)。三個 60 分公版場地是單 solid region、單材質、牆地不分的地形雕塑網格(約 44k 三角),八車基線每幀 9.8–12.4 秒,多車完全 不可玩(issue 001042)。
同一組三角切成數百顆小塊即可降兩個數量級,但前提是塊邊界的法線可被正確推定:trimesh 對自由 邊界邊採邊或頂點法線,車輪跨縫會被看不見的稜頂起或偏航。研究輪 v15633–v15639 的結論:補丁 旗標把邊界邊夾成單一鄰面法線,只在塊內平面、鄰塊共面時成立(坡道位差 30–85 cm、yaw 差達 108°);鬼三角(鄰接一環只進擬法線)讓地面塊取得真鄰面法線,8 m 格的跨界流形拆分軌跡差 ≤ 0.7 mm,但用在牆段會抬車。finalizer 對地形雕塑網格推導不出可用的牆段與地面塊(v15637), 路線結構只有作者端(生成器、編輯器)知道。
正式化過程另揭露一個求解層缺口:分塊後輪子跨在兩塊共面地面的縫上,每塊各成一個 contact pair,同一接觸平面變成兩個冗餘約束,能力邊界(最大坡度、剛好功率、靜止起步)的 13-gon 案原地 不動(單顆 0.83 m、分塊 0.00037 m)。原生 contact clustering 只在單一 contact pair 內合併 manifold,鬼三角與旗標都無關。
考慮過的選項¶
- 維持每 region 單顆 trimesh:八車不可玩,棄。
- 只切牆:只拿回四至五成成本,棄。
- 地面切塊+邊界邊旗標:坡道與粗糙地形不通過,棄;旗標只留給牆段。
- 地面切塊+鬼三角、牆段+旗標(採納):鬼三角用在牆會抬車,兩機制依 piece 種類分用。
- finalizer 自動推導牆段:對雕塑網格不可靠;改為作者宣告環序列、finalizer 只驗不推(採納)。
- 宣告與幾何不吻合時靜默退回單顆 trimesh:作者看不到錯誤、成本無聲倒退,棄。
- 宣告不吻合一律拒收並指出節點與三角索引:作者端生成器或編輯器的宣告一有偏差,整個場地就送不 出去,而單顆碰撞體仍是可運作的正確幾何,棄。
- 宣告不吻合時該 region 退回單顆碰撞體並給警告(採納,2026-10-22 使用者裁決):成本倒退可見 (Stage 3 警告+Stage 2 成本等級),不擋送出。
- 13-gon 起步卡死:TS 端以 canonical key 聚合只修得到事件與帳本,修不到冗餘求解約束;compound 不能包 trimesh;把 clustering 全面改成以 body pair 為單位會改變車身多 collider 的行為。採限定 範圍的 piece-group 跨 pair clustering,group 0 行為與原碼逐位元同(採納)。
- 品質閘以單次「≤ 基線」與「碰縫前逐位元同基線」比較:基線本身對 1 µm 起點擾動即自散、三角序 雜訊使穿入差達 ±4×,單次比較無鑑別力,棄;改為擾動集合包絡、絕對上限與縫附近起算的視界。
決定¶
- Kernel 三機制並存(vendored
rapier-deterministic-15370source-kit 兩代重建;REPAIR-OBLIGATIONS新增四條義務;兩機制關且無 group 時 canonical golden 與 revision-14 逐位元同、Node 與 Chromium 一致,u8 八車逐幀行為零差): - 牆段 trimesh 旗標
BOUNDARY_EDGES_AS_INTERNAL(256,與FIX_INTERNAL_EDGES併用):邊界邊 (含條帶真開口端)夾成唯一鄰面法線。 - 地面塊
ghost_indices:鄰接一環三角只進擬法線,絕不進 BVH、接觸或拓樸鍵;隨快照序列化, 還原後世界與 live 等價(pre_launch 無舊快照要保,每顆 trimesh 多一個長度欄)。 - piece-group 跨 contact pair clustering:同一動態 collider × 同一固定 body × 同一非 0 piece group 的多顆 collider,其 manifold 合併為一個 solver cluster;group 由 runtime 依 canonical key 派發、不進快照、restore 後重派。
- 現行 manifest(baseline 1):track manifest 新增必填
collision(groundCellM6–16、wallStrips環序列、singleColliderNodes退回單顆的節點名)。pre_launch 期間 current baseline 一律維持 1(專案生命週期.md 第 3 節),schema 變動不升版號:缺collision的舊形由 schema 以physics manifest collision missing拒收,嵌入版本不等於 1 以physics manifest version embedded invalid拒收。無遷移、無雙讀,公版 27 顆單批重烘(場地groundCellM8、wallStrips空)。作者 root extrascollision可省,省略= 預設格邊長、無牆條帶。 - 宣告規模與 piece 上限在 admission 檢查:每環點數 ≤
TRACK_WALL_RING_POINTS_MAX(64)、每條 環數 ≤TRACK_COLLIDER_PIECE_COUNT_MAX+ 1、所有條帶段數合計 ≤TRACK_COLLIDER_PIECE_COUNT_MAX, 推導端在三角歸屬之前依宣告順序檢查,超限的條帶退回單顆(歸屬判準成本為三角數×段數,累計段數 先受限);manifest 的wallStrips違反上限則拒收。piece 總數由 admission 與 Stage 3 以 runtime 同一套切分純函數計數(地面依質心 XZ 格去重、牆依段長合併後的段組), 超限拒收(Stage 3 拒收碼collider-piece-count-exceeded),載入時另有 fail closed 守門。 - 核心身份進入配對比對:
rapier_version改為 runtime 完整核心版本(含內嵌 WASM digest,與rapierVersion()全等),同套件版號下重建出不同 WASM 的核心不可混場;wire/Ledger 上限放寬到 128 UTF-8 bytes。 - 作者規則 R1–R6(現況規格見 建模參數/場地.md 第 8.7 節):R1 牆地分離 (宣告的牆須為獨立 solid 節點)、R2 牆為環序列條帶、R3 地面依 XZ 格分塊、R4 道具另立 entity、R5 段序列由作者宣告且 finalizer 只驗不推、R6 粒度常數化。未宣告牆的 solid region 一律 走地面分塊。
- 宣告不吻合退回單顆+警告(2026-10-22 使用者裁決):admission 推導與 Stage 3 共用同一個純函數
判定(
resolveWallStrips,依宣告順序)。節點是靜態 solid region 但環形狀不合法、規模超限或有三角 不歸屬 → 該 region 不切段,整顆一個 piece(FIX_INTERNAL_EDGES、無鬼三角、不分塊),節點名寫入 manifestcollision.singleColliderNodes;節點不存在、是 entity 子節點或 fluid → 沒有 region 可退回, 宣告作廢。兩者都給 Stage 3 警告wall-strip-fallback(paramsnodeName、reason=原三個拒收碼之一), 原因不進 manifest。單顆計入 piece 總數,canonical key 不變,piece group 照常派發(單一成員); runtime 只讀 manifest、不重判。 - Stage 2 碰撞 region 分類:場地統計分頁逐靜態 solid 節點列出法線分類(|ny| < 0.5 的三角占比
≥ 90%=牆、≤ 10%=地、其餘=混合)、三角數、依實際處置的預估 piece 數,以及每顆平均三角數的
八車成本等級(< 2000 低、2000–20000 中、> 20000 高);常數在
ui/asset-policy,只供作者參考、 不擋送出。分級點是裁決刻度,依據是 v15632 剖析(CCD 成本隨每顆 collider 的三角數成長)與上列 formal-v3-cost 實測,非由實測擬合。 - 三角歸屬判準(finalizer、admission、runtime 共用同一模組):每環平面過質心、法向取 Newell
面積法向並統一朝條帶前進;三頂點皆落在相鄰兩環平面楔形內的段為候選,多候選時取三角質心到
段中心線最近者(同距取最小段號),側向距離不得超過兩環較大環半徑的 2 倍。容差
1e-5 + maxAbs × 2^-22 + q:q為 Draco 14 bit 位置量化的半對角上界(節點世界 AABB 對角線 ÷(2^14−1)×√3/2,無 Draco 時為 0)。作者環是未量化座標、canonical 牆頂點是量化解碼值,缺q時每個宣告都會被誤拒。 - runtime 聚合語意:canonical key 維持
track:material:sourceNode;damage、wear 與材質診斷依 鍵聚合;碰撞 started 事件以(trackId, canonical key)聚合:依原生事件順序維護每組接觸配對數, 計數 0→1 才發 started(同配對同幀分離後重新接觸不被吞,換手屬延續),同幀同組只留最大撞擊 能量。region、接觸處理、manifest 材質清單、傷害同分與磁力結算順序一律用碼元序比較:localeCompare依執行環境 collation 而異,是 lockstep 分歧風險。 - 常數(protocol
ugc):TRACK_GROUND_CELL_M = 8、TRACK_WALL_SEGMENT_LENGTH_M = 0.25、TRACK_WALL_SEGMENT_LENGTH_MAX_M = 0.5、TRACK_COLLIDER_PIECE_COUNT_MAX = 8192(含)、TRACK_WALL_RING_POINTS_MAX = 64(含);TRACK_COLLIDER_REGION_COUNT_MAX = 256不變。 - 品質閘(
src/physics-engine/track-collision-gates.spec.ts;pnpm test:gates以研究旗標 跑重格):基線=同一份焊接+正規化幾何的單顆 collider;基線與 pieces 各跑決定性擾動集合 (研究 N=9、CI N=5);共同暖機到首次碰縫或碰牆前 20 幀取快照 S,自散視界自 S 起算。硬判準: 無抬車(|ny|=0 的接觸 ΔY < 1 cm;只計全部執行都還在場上的幀,牆格與拒絕統計同一個窗,地面格不截在 首次碰牆、不短於判準窗,截止幀列入輸出)、法線偏差 P95 ≤ 3°、兩顆同時接觸幀的法線差 ≤ 3°、穿入 絕對 ≤ 2 mm、拒絕 ≤ 基線集合最大值 ×1.6(只計全部執行都還在場上的幀:車心仍在地面上方, 與地面格判準窗同一判定,基線與分段同規則,截止幀列入輸出;基線 0 時允許 ≤ 1;騎縫例外集合 {B 地面沿格線、 D 坡道沿格線上坡} 上限 8,屬分塊固有、鬼三角把 47 降到 5)、兩重播逐位元、非騎縫地面格 (B 直行 0° 不壓縫、B 斜向 15° 不壓縫、B 斜向 45° 不壓縫、D 直行)碰牆前全程逐位元同基線、 牆格位差與 yaw 相對 S 的增量 ≤ 2 cm/≤ 1°(視界內)。不壓縫格的前提改為判準窗(≥ 60 幀)內 全部樣本未碰地面縫;斜向 45° 兩格(騎縫與不壓縫)用車道半寬 3 m 的直線場地(車速為指令值時, 半寬 1 m 車道內 45° 判準窗不足 60 幀);直行 0° 不碰牆、判準窗只能止於幀數, 兩種模式都跑 180 幀(600 幀必跨下一條 z 格線)。B 斜向 15°/45° 碰牆前斜跨格線,001078(cylinder 質量性質座標系)修正使軌跡在碰縫時與基線分歧;B 直行 0° 在研究閘 600 幀內漂移、第 290 幀壓上 x=0 縱向格線後分歧。三格 2026-10-26 起歸為騎縫格,以騎縫判準(位差、yaw、穿入、抬車)守門, 判準數字不變。拒絕只計在場上的幀:A 牆端的車滑出牆端、離開場地後已無場地接觸,拒絕來自旋轉 中的輪胎關節,擴大樣本分段與單顆分布重合;抬車亦同:滑出牆端後失去牆支撐,側壓使車在單一地面塊內 側傾或外側輪越過場地外緣,兩版擴大樣本(各 21 個)都有超過 1 cm 者(單顆最大 14.9 mm、分段最大 27.3 mm),接觸只有地面塊內部或場地外緣、與牆段無關;A 貼牆與 C 弧牆不離場,統計窗等於全程。成本守門(src/testing/performance/track-pieces-performance.spec.ts):八車 heavy U 弧牆 120 幀 step P95 ≤ 90 ms、save P50 比值 ≤ 1.5。
後果與影響¶
- 實測(研究層
formal-v3-cost-001038同場次、八車 heavy 120 幀 step P95,單顆→地面分塊→ 牆宣告):u8 256–292→57–62→63–67 ms;urban v6 1056–1108→71–79→95 ms;snowy v4 1993–2659→97–99→113–115 ms;jungle v5 540–548→112–132→108 ms。遊戲性欄(位移、yaw、 碰撞數、拒絕、底盤高)牆版與地面版相同、兩重播逐位元。地面分塊是主要槓桿;牆宣告為選配, 長直牆多段反而較貴,段長策略待調。成本守門實測 step P95 約 70 ms(單顆約 370 ms)、save P50 比值 1.25–1.33。 - 快照位元組與 manifest digest 與分段前不相容;determinism golden 升
canonical-two-car-revision-15(Node 與 Chromium 一致);conformancephysics/track-entity-snapshot只改 snapshotSha256。 - 已知限制(閘的偵測力邊界):正式 hard CCD 把每幀位移截斷在首次撞擊、滾動接地也被截, 軌跡判準只覆蓋事件前的自由段(001050);穿入相對比較在三角序雜訊下無鑑別力,改用絕對 上限後,閘偵測不到此雜訊以下的穿入差;CCD 全關的觀察組顯示分段弧牆有 yaw 差、待歸因 (001051);牆末端被旗標夾成面法線,留有穿入尾巴(001048);leader 換手的 warm start 是 近似(001049);鬼一環只收共享邊三角,塊角頂點擬法線與整片略異(曲面 point-containment 微差);非等比縮放或 shear 的牆節點量化項會低估而誤拒(fail-closed)。
- 同期登錄:
takeSnapshot就地重算質量,拍不拍快照會改變原生狀態(001046);U 形牆三角歸屬 跑到遠段(001047,已改為最近段中心線+側向守門);urban RP1 up 向量與 finalizer 邊界法線 穩健性(001052);builtin route 正規化不冪等(001053)。 - 001028 原生質量 getter
RigidBody.nativeMassProperties()併入第一代重建:質量來源驗證直讀原生 值,active 快取核對改在拋棄式 scratch 世界進行,不碰被採用的世界。 - 草案處置分級的落地範圍(使用者 2026-10-22 裁決範圍 B,追蹤於 issue 001038):R2/R3 不符時 退回單顆並給警告、Stage 2 分類與成本等級 UX 已落地(見上列兩條決定)。R1(牆地分離)與 R4 (道具另立)維持現況:不設硬檢核,牆地未分離就不宣告、與地面一起分塊;道具沿用 entity 機制。 長直牆的段長策略(例如直段不切或放寬單段上限)仍待調。
- 修訂關係:D-20260811-04 的「每 region 一個 fixed trimesh collider」改為 region 內多 piece,逐 region 材質與 canonical key 語意不變; D-20260524-03 的 Rapier 版本鎖定延伸到 vendored fork 的 repair 代識別與 finite-equivalence 義務。