Community程式設計與開發github.com

bugeng777/teardown

建立一個真實機器或機構(引擎、起落架、步行連桿、泵浦、變速箱)的互動式 3D 模擬器,作為單一離線 HTML 頁面,讓人們可以拆解,具有由相同模型驅動的即時面板、每個子系統的按鈕、剖面圖、攝影機視角以及將控制權交給訪客的自動導覽;然後逐幀錄製成 60 fps 影片,並搭配同步的程序化配樂。當有人詢問機器如何運作、想要「視覺解說器」、「模擬器」、「剖面圖」、「爆炸視圖」或「互動式 3D 課程」,或希望看到 3D 物件實際運作而非僅被描述時使用。

teardown 是什麼?

Teardown 是一個 AI 代理技能,用於從真實參考資料建立單頁、互動式 3D 機器模擬器。它涵蓋從數學運動學精確模型(如 V8 引擎的滑塊-曲柄機構、Cessna 起落架的齒輪齒條、Strandbeest 的 Jansen 連桿)到完整幾何建構、即時重疊面板和自動導覽的完整流程。該技能可在 Cursor、Codex 和 Claude Code 等平台上使用,透過 `build.mjs` 建構離線 HTML 頁面,並使用 `capture.mjs` 和 Python 音效引擎逐幀渲染為 60 fps 影片。其設計核心在於嚴格的規則:一個輸入驅動所有部分、即時數值、基於參考的建構、以及透過 `render(t)` 合約實現的純函數渲染,確保模擬、動畫、配樂和錄製影片之間完美同步。它還包括詳細的審查流程和 three.js 陷阱清單,以確保輸出品質。

相容平台✓Claude Code~Codex CLI✓Cursor
npx skills add bugeng777/teardown-cursor

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

teardown 是做什麼的?

一台機器,一個頁面。訪客看著機器運轉,按下按鈕,正在運作的那個零件就會打開,而周圍的儀表則顯示真實的數值。若無人操作,頁面會自動導覽,並顯示游標。相同的程式碼會逐幀錄製導覽過程,並在同一時間軸上建立配樂。

SKILL_DIR 指的是此檔案所在的資料夾(若是 Cursor 安裝,通常為 ~/.cursor/skills/teardown、~/.agents/skills/teardown 或專案內的 .cursor/skills/teardown)。建構和渲染的輸出會放在您執行指令所在資料夾的 ./teardown-out 中。

首次執行

cd "$SKILL_DIR" && npm install && npx playwright install chromium

頁面不需要其他東西。影片需要 ffmpeg 在 PATH 環境變數中,配樂需要 Python 3(僅需標準函式庫)。設定 FFMPEG、PYTHON 或 CHROMIUM 環境變數可指向其他執行檔。

3 個範例

建構其中一個,開啟 HTML,按下按鈕。當您開始製作新機器時,複製最接近的那個。

機器驅動方式值得參考的部分
v8-*帶有玻璃汽缸襯套的十字曲軸 V8:您所追蹤的汽缸內燃燒、氣門在凸輪上升起、8 個活塞透過精確的滑塊-曲柄機構運動。曲軸角度。點火順序 1-8-4-3-6-5-7-2,用於指示圖的燃燒室壓力模型。剖面模式(玻璃、剖面、實心)、作為汽缸選擇器的點火順序、與曲軸同步的即時圖表。
cessna-*Cessna 337 主起落架,在起飛時折疊進機身,降落時放下。行程 0..1:一個 201 mm 的齒條帶動一個 32 齒的小齒輪(模數 4 mm)精確旋轉 180°。單一自由度驅動整個機構、作為時間函數的飛行階段、在模型下方滾動的跑道。
strandbeest-*Theo Jansen 的 Strandbeest,20 條腿安裝在同一根曲軸上,在沙地上橫向行走。風力。曲軸速度由風力積分而來;每個關節透過 Jansen 的 11 個連桿長度進行圓相交計算來決定位置。僅 67 行程式碼的精確連桿解算器、實例化的管狀零件、腳印軌跡、所有 20 隻腳的接觸圖表。

每個範例包含 3 個資料夾:<名稱>-shared/ 存放機器本身,<名稱>-film/ 存放錄製的導覽,<名稱>-live/ 存放您操作的頁面。

node "$SKILL_DIR/sims/build.mjs" v8-live          # → teardown-out/dist/v8-live.html,離線,約 800 KB
node "$SKILL_DIR/sims/build.mjs"                  # 全部 6 個

如何建構一台機器

每個機器一個資料夾,按職責拆分。範例完全遵循此結構:

檔案職責
kin.js / gear.js / jansen.js數學運算。使用真實尺寸和單位,一個輸入(曲軸角度、行程、風力)以及作為其函數的每個姿勢和讀數。此處不使用 three.js。
engine.js / gear.js / beast.js幾何圖形,透過程式碼建構:車削零件、管狀結構、實例化連桿。update(input) 設定其姿勢。
stage.js場景、燈光、陰影、後製(GTAO、Bloom)。frame(state, cam) 繪製一幀。
console.js覆蓋層:固定 1600 x 900 CSS 版面的面板、圖表、切換開關、帶有引導線的標註。update(state) 根據該幀的狀態重新繪製它。
film.js一個操作者動作的 SCRIPT(視角、隔離、剖面、油門)以及作為時間純函數的 render(t)。
live.js即時狀態下的相同機器:按鈕、滑桿、軌道、縮放、按鍵。若無輸入,它會播放 film.js 的腳本作為帶有游標的自動導覽;任何點擊、拖曳、滾輪或按鍵都會將控制權交給使用者,25 秒無操作後交還。
score.py影片時間軸上的聲音。

對於 SKILL_DIR 之外的機器,建立一個包含 main.js 的資料夾,並透過名稱導入工具包:import {runSim} from 'teardown-kit/runtime.js'。使用 node "$SKILL_DIR/sims/build.mjs" ./my-machine 建構它。

決定成敗的規則

  1. 從參考資料開始,而非憑空想像。 一張照片、一本手冊中的剖面圖、一段真實機器的影片。以其為標準進行建構,並逐幀比較。憑一句話想像出來的機器總是平庸的;而同一個模型在有明確比對目標時,才能精確重建出真實的機器。
  2. 真實數值,單一來源。 首先取得真實尺寸(缸徑、衝程、齒數、連桿長度)。每個儀表、圖表和標籤都讀取自同一個用來設定零件姿勢的函數,因此螢幕上沒有任何東西會與運動不一致。用手工驗證一次:在 Cessna 中,32 齒 × 4 mm 模數的齒輪節圓直徑為 128 mm,其半圓周長即為 201 mm 的行程。
  3. 一個輸入驅動一切。 根據曲軸角度、行程或風力設定姿勢。沒有任何部分是手動設定關鍵影格的,因此影片、即時頁面和配樂永遠不會不同步。
  4. 正在運作的那個零件會打開。 將其隔離、剖開、X 光透視。一個能進入內部觀察的攝影機,比十個外部軌道鏡頭更有價值。
  5. 燈光場景,一個重點色。 淺色背景在資訊流中容易閱讀;深色背景則會流失第一秒的注意力。重點色標示正在變化的東西。注意淺色背景上的近白色零件:為它們加上墨線或較深的地面。
  6. 為機構選擇攝影機。 正面觀看平面連桿機構會使其看起來像一個網格;從 3/4 角度沿著其主軸觀看,則能讓每條腿都清晰可辨。
  7. 用數字修復碰撞。 當一個零件穿入另一個零件時,在參數區塊中縮放或移動它。切勿用攝影機將其隱藏。

即時頁面規則

  • 在整合式 GPU 上測試。 在桌機顯示卡上能以 60 fps 運行的頁面,在筆電內建 GPU 上可能會降至 15 fps。範例中包含了品質等級(live.js 中的 TIERS):無環境光遮蔽、較低的像素比例、較小的陰影貼圖、每隔一幀重新繪製面板。首先找出最昂貴的著色器:每個頂點計算 5 次的置換,成本比所有等級加起來還高。
  • 預熱著色器。 kit/warm.js 在第一幀之前並行編譯所有程式,因此頁面永遠不會在首次繪製時凍結。
  • 保持頁面小於 1 MB。 連結爬蟲(X、Slack)會從 HTML 讀取分享卡片,並放棄超過 15 MB 的頁面。大型模型會作為頁面旁邊的檔案輸出:build.json → {"external": {"heavy.glb": "light.glb"}}。

render(t) 合約

import {runSim} from '../kit/runtime.js';

const DEFAULTS = {duration: 20, fps: 60, hud: {enabled: false}, director: {enabled: false}};

runSim(DEFAULTS, async ({cfg, canvas, capture}) => {
  // 建立場景,預計算任何有狀態的內容
  return {render(t) { /* 從頭開始繪製第 t 幀 */ return {}; }, resize() {}};
});
  • render(t) 是純函數:沒有 Math.random(在設定時使用 kit/util.js 的 rng(seed)),沒有 Date,沒有在幀之間攜帶狀態。capture 會在不定的時間呼叫它。
  • 任何依自身規則運動的東西(代理、粒子)都在載入時模擬一次,存入型別陣列;render 只讀取它們。隨時間積分的量(Strandbeest 在變化風力下的曲軸)會以封閉形式積分或在載入時製成表格。
  • 每個節拍都存入 <名稱>-film/timeline.json,單位為秒。配樂也讀取相同的檔案。
  • 第 0 幀是縮圖。它必須已經顯示機器。

指令

node "$SKILL_DIR/sims/build.mjs" v8-film
node "$SKILL_DIR/sims/capture.mjs" v8-film --size 1600x900 --dpr 1.2 --stills beats --cols 4 --out teardown-out/v8-beats.png
node "$SKILL_DIR/sims/capture.mjs" v8-film --size 1600x900 --dpr 1.2 --still 12.5 --out teardown-out/v8-12.5.png
node "$SKILL_DIR/sims/capture.mjs" v8-film --size 1600x900 --dpr 1.2 --out teardown-out/v8-silent.mp4
python "$SKILL_DIR/run_sfx.py" "$SKILL_DIR/sims/v8-shared/score.py"
ffmpeg -y -i teardown-out/v8-silent.mp4 -i teardown-out/v8-score.wav -map 0:v -map 1:a -c:v copy -c:a aac -b:a 256k -shortest -movflags +faststart teardown-out/v8.mp4
node "$SKILL_DIR/wave.mjs" teardown-out/v8.mp4 --timeline "$SKILL_DIR/sims/v8-film/timeline.json"

--size 是 CSS 版面尺寸,--dpr 是其倍數:1600 x 900 在 1.2 倍下會得到 1920 x 1080。Capture 會列印它使用的 GPU;若出現軟體渲染器,表示 paths.mjs 中的 GPU 旗標未生效。

審查流程

您無法觀看影片;但您可以查看幀畫面。在閱讀當前渲染結果的畫面之前,絕不應報告機器已完成。

  1. --stills beats:時間軸上每個節拍後 0.3 秒處的一格畫面,每個標示 t=。
  2. 根據下方檢查清單閱讀每一格畫面。
  3. 放大:對一個轉場使用 --stills every:0.2 --from A --to B,對一個完整幀使用 --still T。
  4. 修正、重建、返回步驟 1。當完整檢查一輪後找不到任何問題時停止。
  5. 最終:node "$SKILL_DIR/sheet.mjs" --video <最終.mp4> --timeline <timeline.json>,然後也閱讀該輸出:它會顯示編碼器寫入了什麼。

檢查清單:

  • 機構。 每個應該接觸的零件都接觸到了;沒有任何東西穿過另一個物體;面板上的數值與姿勢匹配。
  • 構圖。 每幀一個焦點。沒有重要物體距離邊緣 60 px 以內。標籤從不遮蓋它們所命名的零件。
  • 文字。 沒有被裁切。關鍵讀數在手機寬度下也能讀取。
  • 3D。 Bloom 會吞噬細節:保持自發光圖層低於 1.0。近白色表面會過曝。低光源下的光滑金屬會產生條紋:大表面的粗糙度應 ≥ 0.45,envMapIntensity 應 ≈ 0.5。
  • 時機。 每個狀態停留時間足夠閱讀:一個短語 1 秒,超過三個字後每多一個字加 0.25 秒。

範例已經踩過的 three.js 陷阱

  • 在 a > b 的情況下,smoothstep(a, b, x) 在 Windows(ANGLE/D3D)上會回傳 0。請改寫為 1.0 - smoothstep(b, a, x)。
  • 一個 ShadowMaterial 或一個位於地板上方不到一毫米的貼花,在後處理渲染目標內部會失去深度測試。請為其加上 polygonOffset。
  • 一個使用鏡像映射、平放在地面上的四邊形,會被作為背面剔除。請使用 DoubleSide。
  • 一個 NaN 像素值會透過 Bloom 使整個幀變黑。請在著色器中箝制每個 pow() 的底數。
  • 對已裁剪(剖面)模型使用 GTAO 時,需要在 gtao.normalMaterial 上設定相同的裁剪平面,否則裁剪掉的一半會以蒼白的鬼影形式出現。

聲音

sfx/ 使用純 Python(48 kHz,可重現)合成所有聲音,並讀取影片的時間軸,因此聲音永遠不會與畫面不同步。python "$SKILL_DIR/run_sfx.py" --list 會顯示產生器。sample() 會加入 sfx/samples/ 中錄製的 CC0 聲音。sfx/score_kit.py 包含了三個配樂所使用的背景和重點音效:和弦背景、每個動力行程的輕柔重擊聲、馬達聲、空氣阻力聲。

  • 每個時間值都來自時間軸,絕不來自手動輸入的數字。
  • 母帶處理是自動的:−14 LUFS,真實峰值 ≤ −2 dBFS。
  • 重點音效,而非旁白:連續運動時保持無聲。每個音調聲音只對應一個關鍵事件。

品味,以數字量化

  • 緩動:進入時用 cubic-bezier(0.16, 1, 0.3, 1),退出時用 cubic-bezier(0.6, 0, 0.9, 0.35),持續時間為進入時的一半,狀態間轉換用 cubic-bezier(0.65, 0, 0.35, 1)。僅在連續運動時使用線性緩動。
  • 機器應佔據視埠的 60-70%。在 1080 畫布上,標籤絕不小於 34 px。
  • 一個重點色,並且它必須有意義:代表「現在正在運作的零件」。
  • 沒有任何東西是完全靜止的:總有一個元素是動態的。

致謝

Cursor 移植版本由 bugeng777/teardown-cursor 基於 elvarofficial/teardown(MIT 授權)製作。工具包、幀捕捉、音效引擎和審查規則來自 whaleyxbt/claude-motion(MIT 授權)。錄製的樣本為來自 Freesound 的 CC0 授權聲音,由 Barty-Bart/motion-graphics(MIT 授權)裁剪;來源記錄於 sfx/samples/SOURCES.md。

相關技能