Communityコーディング&開発github.com

bugeng777/teardown

実在の機械や機構(エンジン、ランディングギア、歩行リンケージ、ポンプ、ギアボックス)のインタラクティブな3Dシミュレーターを1つのオフラインHTMLページとして構築します。分解して内部を観察でき、部品を動かす同じモデルで駆動するライブパネル、各サブシステムのボタン、カットアウェイ、カメラビュー、訪問者に制御を委ねる自動ツアーを備えています。さらに、60fpsの映像と同期したプロシージャルスコア付きでフレームごとに録画します。

teardown とは?

Teardown は、複雑な機械や機構のインタラクティブな3Dシミュレーターを1つのオフラインHTMLページとして生成するAIエージェントスキルです。エンジン、ランディングギア、歩行リンケージなどの実在する機械を、分解可能な3Dモデルとライブパネル(ゲージ、チャート)で再現します。ユーザーはボタン一つで部品の分解・カットアウェイ表示を行い、内部動作を直感的に理解できます。放置すると自動ツアーが開始され、カーソルが各機能を案内します。また、同じコードベースでツアーの映像を60fpsのプロシージャルスコア付き動画としてフレームごとに録画できます。Claude Code、Cursor、CodexなどのAIエージェントツールで使用することを想定しており、数学的な駆動モデル(kin.js / gear.js / jansen.js)とThree.jsによる3Dレンダリングを分離し、1つの入力(クランク角、ストローク、風力)で全ての動作と読み出しを駆動します。機械の設計は実在のリファレンスに基づき、正確な寸法と物理法則に従います。

対応✓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に質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

teardown は何をしますか?

1つの機械、1つのページ。訪問者は機械が動くのを見て、ボタンを押すと、その部分が開き、周囲のゲージに実際の数値が表示されます。何もしなければ、ページはカーソルと共に自動でツアーします。同じコードで、ツアーの映像を同じタイムライン上のスコアと共にフレームごとに録画します。

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つのサンプル

1つビルドして、HTMLを開き、ボタンを押してください。新しい機械を始めるときは、最も近いものをコピーしてください。

機械駆動方法盗む価値あり
v8-*ガラスライナー付きクロスプレーンV8:追跡するシリンダー内でチャージが燃焼し、バルブがカム上でリフトし、8つのピストンが正確なスライダークランクから動きます。クランク角度。点火順序1-8-4-3-6-5-7-2、インジケーター線図用のチャンバー圧力モデル。カットアウェイモード(ガラス、断面、ソリッド)、シリンダーセレクターとしての点火順序、クランクに同期したライブチャート。
cessna-*セスナ337メインランディングギアが離陸時に機体に格納され、着陸時に降りる機構。ストローク0..1:201mmのラックが32歯のピニオン(モジュール4mm)を正確に180°回転させる。1自由度で機構全体をポーズ制御、時間の関数としての飛行フェーズ、モデルの下で回転する滑走路。
strandbeest-*テオ・ヤンセンのストランドビースト、1つのクランクシャフトに20脚、砂の上を横方向に歩行。風。クランク速度は風から積分され、すべての関節はヤンセンの11リンク長から円交差によって導かれる。67行の正確なリンケージソルバー、インスタンス化されたチューブ、足跡、全20脚の接触チャート。

各サンプルは3つのフォルダに分かれています:<name>-shared/ は機械、<name>-film/ は録画されたツアー、<name>-live/ は操作可能なページを保持します。

node "$SKILL_DIR/sims/build.mjs" v8-live          # → teardown-out/dist/v8-live.html、オフライン、約800KB
node "$SKILL_DIR/sims/build.mjs"                  # 6つすべて

機械の構成方法

機械ごとに1つのフォルダに、役割ごとにファイルを分割します。サンプルは以下に従っています:

ファイル役割
kin.js / gear.js / jansen.js数学。実際の寸法と実単位で、1つの入力(クランク角、ストローク、風力)とそれに依存するすべてのポーズと読み出し。内部にthree.jsはありません。
engine.js / gear.js / beast.jsジオメトリ。コードから構築:旋盤加工部品、チューブ、インスタンス化されたリンク。update(input) でポーズ設定。
stage.jsスタジオ、ライト、影、ポスト処理(GTAO、ブルーム)。frame(state, cam) で1フレームを描画。
console.jsオーバーレイ:固定1600x900 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. 実数、1つのソース。 最初に実際の寸法を入手(ボア、ストローク、歯数、リンク長)。すべてのゲージ、チャート、ラベルは部品をポーズ設定する同じ関数から読み取るため、画面上で動きと矛盾するものは何もない。1つ手計算で確認:セスナでは、32歯×4mmモジュールは128mmのピッチ円直径で、その半円周が201mmのストローク。
  3. 1つの入力がすべてを駆動。 クランク角、ストローク、または風力からポーズ設定する。手動でキーフレームを設定しないため、フィルム、ライブページ、スコアがずれることはない。
  4. 動作する部分を開く。 アイソレート、カット、X線表示。内部に入るカメラは10回のオービットより価値がある。
  5. ライトスタジオ、1つのアクセント。 淡い背景はフィードで読みやすい。暗い背景は最初の1秒を見失う。アクセントは変化しているものを示す。淡い背景の白に近い部分には、インクの輪郭または暗めの地面を与える。
  6. メカニズムに合わせてカメラを選ぶ。 平面リンケージを正面から見ると格子状に見える。脊椎に沿った3/4ビューはすべての脚を読みやすくする。
  7. 衝突は数値で修正。 部品が別の部品を突き抜ける場合、パラメータブロックでスケールまたは移動する。カメラで隠してはいけない。

ライブページのルール

  • 統合GPUでテスト。 デスクトップカードで60fpsのページが、ラップトップの内蔵GPUでは15fpsに低下する可能性がある。サンプルは品質層(live.js の TIERS)を持つ:AOなし、低ピクセル比、小さいシャドウマップ、パネルは1フレームおきに再描画。最初に高価なシェーダーを見つける:頂点ごとに5回計算される変位は、すべての層を合わせたよりもコストがかかる。
  • シェーダーをウォームアップ。 kit/warm.js は最初のフレームの前にすべてのプログラムを並列コンパイルするため、最初の描画時にページがフリーズすることはない。
  • ページを1MB未満に保つ。 リンククローラー(X、Slack)はHTMLから共有カードを読み取り、15MBのページでは諦める。大きなモデルはページの隣にファイルとして出力: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 なし、フレーム間で状態を引き継がない。キャプチャは任意のタイミングで呼び出す。
  • 独自のルールで動くもの(エージェント、パーティクル)はロード時に型付き配列に一度シミュレートされ、render は読み取りのみ行う。時間とともに積分される量(変化する風下でのストランドビーストのクランク)は、閉形式で積分されるか、ロード時に表形式で格納される。
  • すべてのビートは <name>-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 はそれを乗算:1600x900を1.2倍で1920x1080になります。Captureは取得したGPUを表示します。ソフトウェアレンダラーは paths.mjs のGPUフラグが有効にならなかったことを意味します。

レビューループ

動画は見られません。フレームを見ることができます。現在のレンダリングのシートを読まずに機械が完了したと報告しないでください。

  1. --stills beats:タイムラインビートごとに1タイル、その0.3秒後、それぞれ t= でラベル付け。
  2. 各タイルを以下のチェックリストに対して読む。
  3. 拡大:トランジションには --stills every:0.2 --from A --to B、フルフレームには --still T。
  4. 修正、再ビルド、1に戻る。完全なパスで何も見つからなくなるまで。
  5. 最終:node "$SKILL_DIR/sheet.mjs" --video <final.mp4> --timeline <timeline.json> も読み取る:エンコーダーが書き込んだものが表示される。

チェックリスト:

  • 機構: 接触すべきすべての部品が接触している。何も貫通していない。パネルの数値がポーズと一致している。
  • 構成: フレームごとに1つの焦点。重要なものは端から60ピクセル以内にない。ラベルは名前を付けた部品を覆わない。
  • タイポグラフィ: 切り取られていない。主要な読み取り値は電話画面幅でも読める。
  • 3D: ブルームはディテールを食いつぶす:エミッシブレイヤーを1.0未満に保つ。白に近い表面は飛ぶ。低照度下の光沢金属は筋状になる:大きな表面では粗さ ≥ 0.45、envMapIntensity ≈ 0.5。
  • タイミング: 各状態は読むのに十分な長さ保持:短いフレーズで1秒、3語を超える場合は1語あたり0.25秒追加。

three.jsの罠(サンプルですでにヒット済み)

  • smoothstep(a, b, x) で a > b の場合、Windows(ANGLE/D3D)では0を返す。1.0 - smoothstep(b, a, x) と書く。
  • ShadowMaterial や床から数ミリ上のデカールは、ポストプロセッシングレンダーターゲット内で深度テストを失う。polygonOffset を与える。
  • ミラーマッピングで地面に配置されたクワッドは、裏面としてカリングされる。DoubleSide を使用する。
  • 1つのNaNピクセルがブルームを通じてフレーム全体を黒くする。シェーダー内のすべての pow() のベースをクランプする。
  • クリップされた(カットアウェイ)モデル上のGTAOは、gtao.normalMaterial に同じクリッピングプレーンが必要。そうしないと、カットされた半分が淡いゴーストとして戻ってくる。

サウンド

sfx/ はすべてのサウンドを純粋なPython(48kHz、再現可能)で合成し、フィルムのタイムラインを読み取るため、サウンドがシンクからずれることはありません。python "$SKILL_DIR/run_sfx.py" --list でジェネレーターを表示。sample() は sfx/samples/ の録音済みCC0サウンドを追加します。sfx/score_kit.py には3つのスコアが使用するベッドとアクセントがあります:コードベッド、パワーストロークごとの柔らかいドスン、モーター、抗力下の空気。

  • すべてのタイミングはタイムラインから来ており、入力された数字ではない。
  • マスタリングは自動:−14 LUFS、トゥルーピーク ≤ −2 dBFS。
  • アクセント、決してナレーションしない:連続した動きは無音のまま。各トーンサウンドに1つのキー。

テイスト、数字で

  • イージング:出現 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キャンバス上で34px以上。
  • 1つのアクセント、そしてそれは意味を持つ:今動作している部分。
  • 完全に静止しているものはない:1つの要素は常に動いている。

クレジット

Cursor移植: bugeng777/teardown-cursor(elvarofficial/teardown の移植、MIT)。キット、フレームキャプチャ、SFXエンジン、レビュールールは whaleyxbt/claude-motion(MIT)から。録音されたサンプルはFreesoundのCC0で、Barty-Bart/motion-graphics(MIT)でトリミング。ソースは sfx/samples/SOURCES.md にあり。

関連スキル