間取り図PDFをAIに渡すだけで自分の家を3D化する手順書|Claude Code / Codex用
自分の家の間取り図を、ブラウザでぐるぐる回せる3Dモデルにしてみませんか。
しかも、あなたがやることは実質3ステップだけ。図面のPDFを用意して、AIを起動して、この記事のURLを渡す。それだけです。
実際に私の家(一条工務店 GRAND SMART 平屋)でやってみたところ、間取りはもちろん、招き屋根の形・ハイドロテクトタイルの黒い外壁・太陽光パネル・大開口の窓まで、図面どおりの3Dが出来上がりました。
図面PDFだけで生成した3Dモデル(南面)。招き屋根・黒いタイル外壁・大開口の窓・ウッドデッキまで再現されています
📖 この記事の使い方(人間が読むのはここだけ)
正直に言うと、この記事の本体は人間向けに書かれていません。AI(Claude Code / Codex)がそのまま読んで実行するための手順書です。
なので、あなたが読むのはこのセクションだけで大丈夫です。
用意するもの
| 必要なもの | 説明 |
|---|---|
| 間取り図のPDF | 一条工務店の契約者なら、貸与タブレットの図面アプリから書き出せます。平面図・立面図・電気図面があると精度が上がります |
| Claude Code または Codex | AIにパソコンを操作してもらうためのツール。どちらか片方でOK |
| パソコン | Mac / Windows / Linux どれでも。3Dが動く程度のスペックがあれば十分 |
プログラミングの知識、有料の3Dソフト、CADの経験、どれもいりません。
やること(3ステップ)
1. 図面PDFを手元に置く
一条工務店のタブレットから図面PDFを書き出して、パソコンのわかりやすい場所(デスクトップなど)に保存します。ファイル名は 図面.pdf のように日本語でも大丈夫です。
2. Claude Code または Codex を起動する
ターミナル(Macなら「ターミナル」アプリ、Windowsなら「PowerShell」)を開いて、AIを起動します。
3. AIに「この記事を読んで」と伝える
起動したAIに、次のようにお願いするだけです。
https://roresan.com/blog/floorplan-3d-ai/ この記事を読んで、
その手順どおりに私の家の3Dモデルを作ってください。
図面PDFはここにあります: ~/Desktop/図面.pdf
あとはAIが、3Dエディタのダウンロードから、図面の読み取り、3Dデータの生成、ブラウザでの表示確認まで全部やってくれます。所要時間は1〜2時間くらいです。
できあがるもの
- ブラウザでぐるぐる回せる自分の家の3Dモデル
- 部屋の中を歩き回れるウォークスルー表示
- 共有できるJSONファイル(家族や友人に渡せます)
- GLB形式の3Dモデル(動画制作やBlenderにも持ち込めます)
つまずいたときは
AIが途中で失敗したら、そのエラーメッセージをそのままAIに見せて「この記事の『よくある失敗と対処』を見て直して」と伝えてください。想定される失敗パターンと対処法も記事内に書いてあるので、AIが自力で復旧できます。
以下は AI向けの手順書 です。人間が読む必要はありませんが、興味があれば折りたたみを開いてみてください。
🤖 AI向け完全手順書
AIへの指示: あなたはこれから、ユーザーの家の間取り図PDFを読み取り、Webブラウザで動作する3Dモデルを生成します。以下のセクションを 上から順に 実行してください。各セクションの折りたたみ(details)の中身がすべて実行内容です。省略・要約せずに読んでください。
成果物: ~/Documents/floorplan-3d/ 配下に生成スクリプトとシーンJSON、~/Documents/pascal-editor/ に3Dエディタ本体。
前提の確認: 作業開始前に node --version(v20以上)と python3 --version(3.9以上)を確認し、Bun未導入なら Step 1 でインストールしてください。
Step 0. 全体像とゴール設定
AIはここを開いて読む: 作業の全体設計
このタスクの本質
間取り図PDF → 3Dモデルの変換は、画像解析AIに丸投げするのではなく、「図面を人間と同じように読んで、座標テーブルに転記し、コードで3Dシーンを生成する」 というアプローチを取ります。理由は次のとおりです。
- 日本の住宅図面(特に一条工務店)は910mmモジュールのグリッドに完全に乗っている。グリッド番号を数えるだけで座標が確定するため、画像から寸法を推定するより圧倒的に正確
- 既存のAI自動変換ツール(FloorplanToBlender3d、CubiCasa5K等)は欧米の間取りで学習されており、日本の図面記号・尺モジュールでの精度が低い
- 生成スクリプトを経由することで、後から一部だけ修正・再生成が容易になる
使用ツール
| ツール | 用途 | ライセンス |
|---|---|---|
| pascalorg/editor | React Three Fiber + WebGPU製の3D建築エディタ。壁・床・屋根・建具・家具をJSONで定義できる | MIT |
| Bun | pascal-editorのビルド・実行 | MIT |
| Python 3 | シーンJSON生成スクリプト | PSF |
| pdftoppm (poppler) | PDFを高解像度PNG化 | GPL |
作業フロー
図面PDF
↓ ① pdftoppmで200dpi PNG化 + 4分割クロップ
図面画像(精読可能な解像度)
↓ ② グリッド番号で壁・建具・部屋を座標テーブルに転記
座標テーブル(Pythonコード内)
↓ ③ generate_scene.py 実行
シーンJSON({nodes, rootNodeIds}形式)
↓ ④ pascal-editorにLoad Build
ブラウザ3Dモデル
↓ ⑤ 2D/3D表示を図面と突き合わせて差分修正(2〜3周)
完成品質基準(これを満たすまで完成としない)
- 全部屋の位置・広さが平面図と一致(帖数の検算が合う)
- 全開口部(窓・ドア)が図面の位置・サイズと一致
- 屋根形状が立面図4面すべてと一致
- 外装の色・素材が立面図(カラー)と一致
- 図面に描かれている設備(キッチン・浴槽・トイレ等)が配置されている
Step 1. 環境構築
AIはここを開いて読む: ツールのインストールと起動
1-1. 必要なコマンドの確認とインストール
# バージョン確認
node --version # v20以上が必要
python3 --version # 3.9以上が必要
bun --version # 未インストールなら次のコマンドBunが未インストールの場合:
# macOS / Linux
curl -fsSL https://bun.sh/install | bash
# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"pdftoppm(poppler)が未インストールの場合:
# macOS
brew install poppler
# Ubuntu / Debian
sudo apt install poppler-utils
# Windows: https://github.com/oschwartz10612/poppler-windows/releases からダウンロード1-2. 3Dエディタのクローンとセットアップ
重要: ユーザーのプロジェクトリポジトリの 外側 にクローンしてください。中に置くとgitが入れ子になります。
cd ~/Documents
git clone --depth 1 https://github.com/pascalorg/editor.git pascal-editor
cd pascal-editor
bun install # 数分かかる。node_modulesは1GB超になる1-3. 開発サーバー起動
cd ~/Documents/pascal-editor
bun run dev # → http://localhost:3002 で起動バックグラウンド実行し、curl -s -o /dev/null -w "%{http_code}" http://localhost:3002 が200を返すまで待機してください。
1-4. 作業ディレクトリの作成
mkdir -p ~/Documents/floorplan-3dつまずきポイント
bun installが失敗する場合、Node v20未満の可能性。nodebrew/nvm等でv20以上に切り替える- ポート3002が使用中なら
lsof -ti:3002 | xargs kill -9で解放 - WebGPU非対応ブラウザ(古いSafari等)では表示されない。Chrome最新版を推奨
Step 2. 図面PDFの画像化と精読
AIはここを開いて読む: PDFの扱いと図面の読み取り手順
2-1. PDFの構成を把握する
まずPDFの各ページに何が描かれているかを一覧化します。
# 全ページのテキストから図面種別を抽出
for i in $(seq 1 $(pdfinfo ~/Desktop/図面.pdf | grep Pages | awk '{print $2}')); do
echo "=== Page $i ==="
pdftotext -f $i -l $i ~/Desktop/図面.pdf - 2>/dev/null | grep -E "平面図|立面図|電気|矩計|配置図|仕上表" | head -3
done一条工務店の図面PDFの典型的な構成:
| ページ | 内容 | 3D化での用途 |
|---|---|---|
| 平面図 | 間取り・寸法・グリッド番号 | 最重要。壁・部屋・建具の座標源 |
| 立面図(4面) | 東西南北の外観・屋根形状・軒の出 | 重要。屋根と高さの決定 |
| 電気図面 | コンセント・スイッチ・照明の位置と高さ | 設備配置(発展) |
| カラー立面図 | 外壁・屋根の実際の色 | マテリアル設定 |
| 矩計図 | 天井高・階高の断面 | 高さの検証 |
| 仕上表 | 内外装の材料一覧 | マテリアル設定 |
2-2. 精読可能な解像度で画像化
重要: デフォルトの低解像度PNGでは寸法線や記号が読み取れません。必ず200dpiで書き出してください。
cd ~/Documents/floorplan-3d
# 平面図(ページ番号は2-1で特定したもの)
pdftoppm -f 3 -l 3 -r 200 -png ~/Desktop/図面.pdf plan
# 立面図
pdftoppm -f 4 -l 4 -r 150 -png ~/Desktop/図面.pdf elev
# 電気図面
pdftoppm -f 2 -l 2 -r 200 -png ~/Desktop/図面.pdf elec2-3. 4分割クロップして読む
200dpiの全体画像(3300×2300px程度)をそのまま読むと、AIの画像認識では細部が潰れます。必ず4分割して2倍に拡大してから読んでください。
from PIL import Image
im = Image.open('plan-03.png')
# 図面本体の領域を推定クロップ(余白・図枠を除く)
body = im.crop((350, 100, 2150, 2150))
bw, bh = body.size
quadrants = {
'nw': (0, 0, bw//2+100, bh//2+100),
'ne': (bw//2-100, 0, bw, bh//2+100),
'sw': (0, bh//2-100, bw//2+100, bh),
'se': (bw//2-100, bh//2-100, bw, bh),
}
for k, box in quadrants.items():
c = body.crop(box)
c.resize((c.width*2, c.height*2), Image.LANCZOS).save(f'plan-{k}.png')各象限を Read ツールで画像として読み込み、記号・寸法・室名を読み取ります。
Step 3. 図面の読み解き方(日本の住宅図面の癖)
AIはここを開いて読む: 910mmグリッドと図面記号の読み方
3-1. 910mmモジュール(尺モジュール)が最重要
日本の在来工法・一条工務店の図面は、910mm(3尺)のグリッドに完全に乗っています。図面の外周に X0 X1 X2 ... Y0 Y1 Y2 ... という通り芯番号が振られており、これが910mmピッチです。
つまり、寸法を測る必要はありません。グリッドを数えるだけです。
| 図面上の値 | 意味 |
|---|---|
| 1グリッド | 910mm(3尺) |
| 0.5グリッド | 455mm(1.5尺)。トイレ・廊下幅などで頻出 |
| 0.25グリッド | 227.5mm。壁のずれ・ニッチで登場 |
| 1帖 | 0.5グリッド × 1グリッド(910×1820mm) |
座標変換は単純です。
M = 0.91 # 1グリッド = 910mm = 0.91m
def p(mx, my):
return [mx * M, -my * M] # 北をマイナスZにする(pascal-editorの座標系)3-2. 検算式(転記ミスの早期発見)
読み取ったグリッド数が正しいか、図面に書かれた面積表と突き合わせます。
- 建物寸法の検算:
10,010mm = 11グリッド × 910、8,190mm = 9グリッド × 910 - 床面積の検算:
グリッドマス数 × 0.8281㎡(0.91² = 0.8281) - 帖数の検算:
グリッドマス数 ÷ 2(例: 9マス = 4.5帖)
図面の「延床面積 69.66㎡ / 21.07坪」のような記載と一致すれば転記成功です。
3-3. 一条工務店の図面記号の読み方
建具記号(窓・ドア)
一条の窓記号は J JK JF VS などの記号 + 4桁数字で構成されます。数字は 幅×高さを尺貫法または実寸で表現 しています。
| 記号例 | 読み方 |
|---|---|
J8371 | 大開口スライディング。幅約2,515mm × 高さ約2,151mm |
JF5915 +1677 | 横すべり出し窓。幅約1,790 × 高さ455、+1677 は床からの取付高さ |
JK2430N × 2 | たてすべり出し窓を2連。+1238 が付けば取付高さ1,238mm |
VS1030 (B) | 縦すべり出し窓。(B) は網戸・仕様の区別 |
E-31ER | 玄関ドア(電子錠付きの型番) |
SG311-1 SG300 SG33B | 室内引戸・開き戸。数字が違えば別サイズ |
SGC-30B SGC-45E SGC-60E | クローゼット。数字が幅(30=900mm、45=1,350mm、60=1,800mm 目安) |
重要: 数字から寸法を推定する際、確信が持てないものは図面の建具表(別ページにあることが多い)で確認してください。推定値を使う場合は、その旨をユーザーに報告します。
設備記号
| 記号例 | 意味 |
|---|---|
KG19-27WDR | キッチン(グレイスキッチン系)。27 = 幅2,700mm |
CS19-B180D | カップボード。180 = 幅1,800mm |
GM243R / GM2430 | グレイスドレッサー(洗面台)。24 = 幅1,200mm前後 |
1.25坪 1616 | 浴室サイズ。1.25坪 = 1坪半サイズ |
TD-12 | 玄関ドア・土間まわりの記号 |
AC①(セ) AC②(ス) | エアコン設置位置と種類 |
SA1 RA1 VA1 | 換気の給気口(SA)・還気口(RA)・排気(VA) |
床暖房1-A | 床暖房のゾーン区分 |
CH=2200 | その部屋の天井高(標準は2,400mm) |
天井下地(G) | 天井への下地補強(照明・物干し設置用) |
壁下地 H=0〜1820 | 壁への下地補強と範囲(TV壁掛け等) |
図面の線種
- 太い実線: 壁(外壁は特に太い)
- 細い一点鎖線: 通り芯(グリッド線)
- 破線: 上部にあるもの(吊戸棚・下がり天井・小屋裏)
- ハッチング(斜線): 土間・タイル・デッキなどの床仕上げ違い
3-4. 電気図面の読み方(コンセント・スイッチ・照明)
電気図面は平面図に赤字で設備が重ねて描かれています。位置と高さの両方が読み取れます。
| 記号 | 意味 | 標準取付高さ |
|---|---|---|
| ⊐ または二重丸 | コンセント(アース無は3口が標準) | 床から250mm(記載なき場合) |
H=900 | その設備の取付高さ指定 | 図に明記された値を優先 |
H=1300 | キッチン腰上コンセント等 | 1,300mm |
| ● / ○ | ダウンライト・シーリングライト | 天井 |
WTC57583W 等の型番 | パナソニック製スイッチ・リモコン | 図のH=指定に従う |
とったらリモコン | 着脱式リモコン(床暖房・給湯) | H=1300前後 |
E付 | アース付きコンセント(洗濯機・冷蔵庫・エアコン) | 設備に応じる |
200V AEYH用 | エコキュート等の200V専用回路 | H=1FL+40 |
専用E付 | 電子レンジ・食洗機等の専用回路 | H=900〜1,300 |
情報ボックス PC用ルーター | 弱電盤・LAN機器 | H=2100等 |
分電盤用 | 分電盤 | H=1800前後 |
火災警報器 | 煙感知器(壁/天井) | 天井または壁上部 |
電気設備を3Dに反映する方法
pascal-editorでは、コンセント・スイッチは 壁に取り付くitem として配置できます。
# 壁付けアイテムの配置例
# wallId で壁を指定し、wallT (0〜1) で壁上の位置、position[1] で高さを指定
{
"type": "item",
"parentId": "<wall_id>",
"wallId": "<wall_id>",
"wallT": 0.35, # 壁の始点から35%の位置
"position": [0.35 * wall_length, 0.25, 0], # [壁沿い距離, 高さ, 壁からのオフセット]
"asset": {
"id": "power-outlet",
"src": "/items/power-outlet/model.glb",
"thumbnail": "/items/power-outlet/thumbnail.webp",
"dimensions": [0.12, 0.12, 0.03],
"attachTo": "wall",
...
}
}利用可能なGLBアセットは ~/Documents/pascal-editor/apps/editor/public/items/ を ls して確認してください。照明系は ceiling-light ceiling-lamp circular-ceiling-light floor-lamp などがあります。
注意: コンセントは1軒で50〜80箇所あるため、全部配置すると視認性が落ちます。ユーザーに「全部配置するか、主要なものだけにするか」を確認してください。
3-5. 図面読み取りの落とし穴(実際にハマったもの)
落とし穴1: 窓がない壁に窓を付けてしまう
平面図の壁の切れ目や、設備の記号線を窓と誤認しやすいです。窓・ドアは必ず 立面図と照合 してください。立面図に描かれていない開口部は存在しません。逆に立面図にあって平面図で見落としているケースもあります。
落とし穴2: 設備の出っ張り形状を単純化しすぎる
洗面台やカウンターは、図面上では「壁から何mm出っ張っているか」が正確に描かれています。GLBアセットのデフォルトサイズをそのまま使うと形が合いません。scale で図面の寸法に合わせ、L字・段差がある場合は複数のアイテムやslabを組み合わせてください。
落とし穴3: 室名の隣が別室
「押入」の隣が「洗面台」など、狭い区画が連続する水回りは特に取り違えやすいです。室名テキストがどの区画に属しているかは、囲んでいる壁線をたどって確認してください。
落とし穴4: 通り芯と壁面を混同する
図面の寸法は原則「壁芯(かべしん)」基準です。壁の内法(部屋の実際の広さ)は壁厚の半分ずつ小さくなります。3Dの壁も壁芯をstart/endに指定すれば整合します。
落とし穴5: 「上部アケル」「フキアゲ」等の注記を見落とす
天井の抜け・吹き上げ・下がり天井は3D形状に影響します。CH=2200 上部アケル 天井下地 などの注記は必ず拾ってください。
Step 4. 座標テーブルへの転記とシーンJSON生成
AIはここを開いて読む: 生成スクリプトのテンプレートと書き方
4-1. pascal-editorのシーンJSON仕様
シーン全体は {"nodes": {id: node}, "rootNodeIds": [...]} という形式です。
座標系: メートル単位、Y-up(Yが高さ)、右手系。2D座標は [x, z] で、北が −Z 方向。
ノード階層: site → building → level → 壁・床・部屋・屋根・家具
ID規則(重要): IDは型プレフィックス必須です。間違えるとインポートが拒否されます。
| ノード種別 | IDプレフィックス |
|---|---|
| 壁 | wall_ |
| 窓 | window_ |
| ドア | door_ |
| 床スラブ | slab_ |
| 部屋(ゾーン) | zone_ |
| 屋根グループ | roof_ |
| 屋根セグメント | rseg_ |
| 家具 | item_ |
| 階 | level_ |
| 建物 | building_ |
| 敷地 | site_ |
4-2. 生成スクリプトのテンプレート
~/Documents/floorplan-3d/generate_scene.py として作成します。以下は実際に動作した完全なテンプレートです。座標テーブル部分(壁・窓・部屋の定義)だけをユーザーの図面に合わせて書き換えてください。
#!/usr/bin/env python3
"""間取り図 → pascal-editor シーンJSON 生成
座標系: メートル, Y-up。x = 東(+), z = 南(+)(北 = -z)
モジュール座標 (mx, my): グリッド番号。my は北向きに増加
変換: x = mx * M, z = -my * M
"""
import json
M = 0.91 # 1モジュール = 910mm(メーターモジュールなら 1.0)
EXT_T = 0.16 # 外壁厚
INT_T = 0.12 # 間仕切壁厚
WALL_H = 2.40 # 天井高(一条標準 2400)
nodes = {}
def p(mx, my):
return [round(mx * M, 4), round(-my * M, 4)]
def add(node):
nodes[node["id"]] = node
return node["id"]
def base(id_, type_, name, parent, **extra):
n = {"object": "node", "id": id_, "type": type_, "name": name,
"parentId": parent, "visible": True, "metadata": {}}
n.update(extra)
return n
LEVEL = "level_1f"
# ============ 壁 ============
# 【向きの規約・重要】進行方向の左手が屋外になる順序で描く
# (back面 = 左手側 = exterior 扱いのため。逆だと外装が室内側に付く)
walls = []
def wall(id_, s, e, t, name):
walls.append((id_, s, e, t, name))
# 外周壁(グリッド座標で指定。ここをユーザーの図面に合わせて書き換える)
wall("wall_ext_s", (11, 1), (0, 1), EXT_T, "外壁 南")
wall("wall_ext_e", (11, 8), (11, 1), EXT_T, "外壁 東")
wall("wall_ext_n", (0, 8), (11, 8), EXT_T, "外壁 北")
wall("wall_ext_w", (0, 1), (0, 8), EXT_T, "外壁 西")
# 間仕切壁
wall("wall_p1", (0, 5.5), (2, 5.5), INT_T, "浴室 南")
wall_children = {w[0]: [] for w in walls}
# ============ 開口部(窓・ドア)============
# dist = 壁の start からの中心距離[m]
def window(id_, wall_id, dist_m, width, height, center_y, wtype="sliding", name=""):
add(base(id_, "window", name or id_, wall_id,
position=[round(dist_m, 4), round(center_y, 4), 0],
rotation=[0, 0, 0], wallId=wall_id,
width=width, height=height,
windowType=wtype, openingKind="window"))
wall_children[wall_id].append(id_)
def door(id_, wall_id, dist_m, width, height, dtype="hinged", name=""):
add(base(id_, "door", name or id_, wall_id,
position=[round(dist_m, 4), round(height / 2, 4), 0],
rotation=[0, 0, 0], wallId=wall_id,
width=width, height=height, doorType=dtype))
wall_children[wall_id].append(id_)
# 例: 南面の大開口(start が X11 なので dist は X11 からの距離)
window("window_main", "wall_ext_s", (11 - 5.65) * M, 2.51, 2.15, 0.05 + 2.15/2,
"sliding", "大開口スライディング")
door("door_entrance", "wall_ext_n", 1.5 * M, 0.98, 2.30, "hinged", "玄関ドア")
# ============ 壁ノードの登録 ============
EXT_TILE = {"properties": {"color": "#3d4045", "roughness": 0.65, "metalness": 0.05}}
for wid, s, e, t, name in walls:
extra, front, back = {}, "unknown", "unknown"
if wid.startswith("wall_ext_"):
extra["exteriorMaterial"] = EXT_TILE
front, back = "interior", "exterior"
add(base(wid, "wall", name, LEVEL,
start=p(*s), end=p(*e), thickness=t, height=WALL_H,
children=wall_children[wid],
frontSide=front, backSide=back, **extra))
# ============ 床スラブ ============
outline = [(0, 1), (11, 1), (11, 8), (0, 8)]
add(base("slab_floor", "slab", "1階床", LEVEL,
polygon=[p(*pt) for pt in outline], holes=[], holeMetadata=[],
elevation=0.02, thickness=0.2, autoFromWalls=False))
# ============ ゾーン(部屋)============
zone_defs = [
("zone_ldk", "LDK (22.42帖)", [(0,1),(8,1),(8,8),(0,8)], "#f59e0b"),
]
for zid, name, poly, color in zone_defs:
add(base(zid, "zone", name, LEVEL,
polygon=[p(*pt) for pt in poly],
autoFromWalls=False, boundaryWallIds=[],
spaceRole="room", roomNumber="", enclosureStatus="auto",
floorFinish="", wallFinish="", ceilingFinish="",
ceilingHeight=WALL_H, occupancy="",
clearDimensionPolicy="none", color=color))
# ============ 屋根 ============
# 【重要】roof-segment は必ず roof ノードの子にする
PITCH = 19.29 # 3.5寸勾配 = atan(3.5/10)。4寸なら21.8、5寸なら26.57
PI = 3.14159265
SOLAR_TOP = {"properties": {"color": "#1f2630", "roughness": 0.3, "metalness": 0.25}}
ROOF_EDGE = {"properties": {"color": "#2a2d31", "roughness": 0.5, "metalness": 0.1}}
DARK_TILE = {"properties": {"color": "#3d4045", "roughness": 0.65, "metalness": 0.05}}
def rseg(id_, name, mx0, mx1, my0, my1, rot, pos_y, wall_h, **extra):
cx, cz = (mx0 + mx1) / 2 * M, -(my0 + my1) / 2 * M
return add(base(id_, "roof-segment", name, "roof_1",
position=[round(cx, 3), pos_y, round(cz, 3)], rotation=rot,
width=round((mx1 - mx0) * M, 3), depth=round((my1 - my0) * M, 3),
roofType="shed", pitch=PITCH, wallHeight=wall_h, overhang=0.25,
wallThickness=0.1, deckThickness=0.1, shingleThickness=0.05,
children=[], **extra))
# shed(片流れ)は local -Z 側が高い。rotation=PI で南向きに反転
rseg("rseg_main", "大屋根", 0, 11, 0.5, 5.75, 0.0, 2.40, 0.10)
rseg("rseg_north", "北下屋", 0, 11, 5.75, 8.5, PI, 2.40, 0.62)
add(base("roof_1", "roof", "屋根", LEVEL, position=[0,0,0], rotation=0,
topMaterial=SOLAR_TOP, edgeMaterial=ROOF_EDGE, wallMaterial=DARK_TILE,
children=["rseg_main", "rseg_north"]))
# ============ 家具・設備 ============
# 【重要】asset.thumbnail が必須。無いとインポート拒否される
ITEM_IDS = []
def item(id_, asset_id, dims, offset, mx, my, ry=0.0, scale=(1,1,1), name=""):
ITEM_IDS.append(id_)
add(base(id_, "item", name or asset_id, LEVEL,
position=[round(mx*M, 3), 0, round(-my*M, 3)],
rotation=[0, ry, 0], scale=list(scale), children=[],
asset={"id": asset_id, "name": name or asset_id, "category": "furniture",
"thumbnail": f"/items/{asset_id}/thumbnail.webp",
"src": f"/items/{asset_id}/model.glb",
"dimensions": dims, "offset": offset,
"rotation": [0,0,0], "scale": [1,1,1]}))
HPI = 1.5708 # 90度
item("item_kitchen", "kitchen", [2.5,1.1,1], [0,0,0], 5.5, 5.85, 0, (1.08,1,0.75), "キッチン")
# ============ 階層 ============
add(base(LEVEL, "level", "1階", "building_1", level=0, height=3.0,
children=[w[0] for w in walls] + ["slab_floor"]
+ [z[0] for z in zone_defs] + ["roof_1"] + ITEM_IDS))
add(base("building_1", "building", "自邸", "site_1",
position=[0,0,0], rotation=[0,0,0], children=[LEVEL]))
add(base("site_1", "site", "敷地", None,
polygon={"type": "polygon",
"points": [p(-3,-3), p(14,-3), p(14,14), p(-3,14)]},
children=["building_1"]))
graph = {"nodes": nodes, "rootNodeIds": ["site_1"]}
import os
out = os.path.expanduser("~/Documents/floorplan-3d/my-house.json")
with open(out, "w", encoding="utf-8") as f:
json.dump(graph, f, ensure_ascii=False, indent=1)
print(f"nodes: {len(nodes)} -> {out}")4-3. 家具アセットの一覧を確認する
ls ~/Documents/pascal-editor/apps/editor/public/items/主要なアセットと標準寸法 [幅, 高さ, 奥行]:
| asset_id | dimensions | 用途 |
|---|---|---|
kitchen | [2.5, 1.1, 1] | システムキッチン |
kitchen-counter | [2, 0.8, 1] | カップボード |
fridge | [1, 2, 1] | 冷蔵庫 |
dining-table | [2.5, 0.8, 1] | ダイニングテーブル |
dining-chair | [0.5, 1, 0.5] | 椅子 |
sofa | [2.5, 0.8, 1.5] | ソファ |
coffee-table | [2, 0.4, 1.5] | ローテーブル |
tv-stand | [2, 0.4, 0.5] | TVボード |
double-bed | [2, 0.8, 2.5] | ダブルベッド |
closet | [2, 2.5, 1] | クローゼット |
toilet | [1, 0.9, 1] | トイレ |
bathroom-sink | [2, 1, 1.5] | 洗面台 |
bathtub | [2.5, 0.8, 1.5] | 浴槽 |
washing-machine | [1, 1, 1] | 洗濯機 |
drying-rack | [2, 1.1, 1] | 物干し |
piano | - | ピアノ |
indoor-plant | - | 観葉植物 |
dimensions はアセット固有の値なので上表のとおりに指定し、実際のサイズ調整は scale で行います(例: 幅1,800mmのカップボードなら scale=(0.9, 1, 0.5))。
Step 5. インポートと検証ループ
AIはここを開いて読む: ブラウザでの読み込みと図面との突き合わせ
5-1. シーンの生成とインポート
cd ~/Documents/floorplan-3d && python3 generate_scene.pyブラウザで http://localhost:3002 を開き、次の手順でインポートします。
- 左サイドバーの 歯車アイコン(設定)をクリック
- SAVE & LOAD セクションの Load Build をクリック
- 生成した
my-house.jsonを選択 - スキーマ検証ダイアログが出る → エラーがなければ Replace current scene
AIが自動でこれを行う場合: chrome-devtools MCPが使える環境なら、Load Build ボタンのuidに対して upload_file を実行することでファイル選択ダイアログを経由せず直接インポートできます。ブラウザ操作MCPがない場合は、ユーザーに手動操作を依頼してください。
5-2. 検証の2軸
| 検証軸 | 操作 | 照合先 |
|---|---|---|
| 平面の正確さ | 上部の 2D ボタン | 平面図 |
| 立体の正確さ | 3D + 画面下の Orbit Left/Right(45°刻み) | 立面図4面 |
| 内観 | Preview ボタン → ウォークスルー | 完成イメージ |
| 屋根内部 | Full height トグルで屋根を透過 | 平面図 |
5-3. 修正ループ
差分を見つけたら generate_scene.py の座標テーブルを修正 → 再生成 → 再インポート、を繰り返します。2〜3周は必ず必要です。
チェックリスト:
- 全部屋の位置・広さが平面図と一致しているか
- 帖数の検算が合っているか
- 窓・ドアの位置とサイズが図面どおりか
- 立面図に描かれていない窓を付けていないか(頻出ミス)
- 屋根の勾配・段差・軒の出が立面図4面と一致しているか
- 外壁・屋根の色がカラー立面図と一致しているか
- 設備(キッチン・浴槽・トイレ・洗面台)が図面の位置にあるか
- 設備の出っ張り・向きが図面どおりか
Step 6. よくある失敗と対処
AIはここを開いて読む: エラー別トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
インポートで Invalid input エラー(roof-segment) | IDプレフィックスが roofseg_ になっている | rseg_ に修正 |
インポートで asset.thumbnail エラー | itemノードにthumbnailがない | asset.thumbnail を追加(/items/<id>/thumbnail.webp) |
| 屋根が全く表示されない | roof-segmentをlevel直下に置いている | 必ず roof ノードを作りその子にする。levelのchildrenにはroofのIDだけ入れる |
| 外壁の色が室内側に付く | 壁の描画方向が逆 | 壁のstart/endを入れ替える(進行方向の左手が屋外) |
| 窓・ドアが表示されない | 壁のchildrenに登録していない | parentId設定に加え、親壁のchildren配列にもIDを追加 |
| 建物が真っ黒で何も見えない | カメラが屋根の中に入っている | ホイールでズームアウト、またはTop Viewボタン |
| 家具が床にめり込む/浮く | scaleのY値が不適切 | Y方向のscaleは1.0を基本にし、幅・奥行だけ調整 |
bun run devが起動しない | ポート3002が使用中 | lsof -ti:3002 | xargs kill -9 |
| 3D画面が真っ白 | WebGPU非対応 | Chrome最新版を使う |
| ブラウザ操作で要素が見つからない | ページ再描画でuidが変わった | snapshotを取り直してから操作 |
カメラ操作(AIがスクリプトで操作する場合)
// ズーム
const canvas = document.querySelector('canvas');
const r = canvas.getBoundingClientRect();
canvas.dispatchEvent(new WheelEvent('wheel', {
clientX: r.left + r.width/2, clientY: r.top + r.height/2,
deltaY: 120, bubbles: true, cancelable: true // 正=ズームアウト
}));回転は画面下部の Orbit Left / Orbit Right ボタン(45°刻み)を使うのが確実です。ポインタイベントでのドラッグ回転は座標計算が煩雑になります。
Step 7. 完成データの活用
AIはここを開いて読む: エクスポートと共有
すべてのエクスポートは 設定(歯車)→ EXPORT セクションから行えます。
| 形式 | 用途 |
|---|---|
| Save Build(JSON) | 編集後のシーンを保存。このファイルを渡せば誰でも同じ3Dを再現できる |
| Export GLB | 3Dモデル標準形式。Blender・Three.js・動画制作(Remotion等)に持ち込める |
| Export STL | 3Dプリント用 |
| Export OBJ | 汎用3D形式 |
| Full floor plan | 平面図として書き出し |
発展: さらに精度を上げるには
- 電気設備の配置 — Step 3-4の電気図面の読み方に従い、コンセント・スイッチ・照明を壁付けitemとして配置
- 下がり天井の再現 —
CH=2200などの記載がある部屋はceilingノードで個別に天井を作る - 敷地・外構 — 配置図から敷地形状・駐車場・アプローチを
slabとfenceで追加 - 太陽光パネルの分割表現 —
solar-panelノードで実際の枚数・配列を再現 - 家具の実物寸法化 — 実際に購入する家具のサイズで
scaleを調整すれば、家具配置シミュレーションになる
実際に作ってみた結果
私の家(一条工務店 GRAND SMART 平屋・埼玉県狭山市)で実際にやってみた結果です。
北面(玄関側)。玄関ポーチの下屋、招き屋根の段差、窓の位置と大きさも図面どおりです
2D表示に切り替えると平面図として確認できます。部屋名・帖数・天井高・家具・建具まで入っています
壁を下げる表示にすると内部が見えます。図面の設備記号に対応する家具を配置しています
再現できたもの
- 全部屋の位置と広さ(LDK 22.42帖、防音室・洋室 各4.5帖、1.25坪バス、脱衣室、玄関ホール、トイレ、押入、収納)
- 全開口部(大開口スライディング、高窓、たてすべり出し窓×4、玄関ドア、室内建具6箇所)
- 3.5寸勾配の招き屋根(段違い片流れ)、軒の出、妻壁
- ハイドロテクトタイル・ブラックの外壁、太陽光一体屋根
- ウッドデッキ、玄関ポーチ
- 家具・設備21点(キッチン、カップボード、冷蔵庫、浴槽、洗面台、トイレ、洗濯機、ピアノ、ベッド、ソファ等)
ノード数: 78(壁21・ドア7・窓10・部屋9・屋根3・家具21ほか) 所要時間: 環境構築込みで約2時間
まだできていないこと
- 電気設備(コンセント・スイッチ・照明)の配置
- 下がり天井(CH=2200等)・垂れ壁
- 太陽光パネルの枚数分割表現
なぜこの方法がいいのか
「間取り図 → 3D」はAIで自動変換するツールもありますが(FloorplanToBlender3d、CubiCasa5Kなど)、いずれも欧米の間取りで学習されており、日本の尺モジュール・一条の図面記号では精度が出ません。
一方この方法は、910mmグリッドという日本の住宅図面の性質を利用して、寸法推定を一切せずに座標を確定させるアプローチです。グリッドを数えるだけなので、AIが図面を読み間違えるリスクが大幅に下がります。
商用ツールではFloorplanner(FML形式のAPI公開)が唯一の対抗馬ですが、無料枠は透かし付き・低解像度で、本格利用は有料前提です。今回使ったpascalorg/editorはMITライセンスの完全無料OSSで、すべてローカルで完結します。
おわりに
家づくり中の方にとって、間取り図って何度も見返すものだと思います。でも2次元の図面だけだと、実際の空間の広がりや、窓から見える景色、家具を置いたときの余白って、なかなかイメージしきれないんですよね。
3Dにしてぐるぐる回してみると、「ここの動線ちょっと狭いかも」「この窓の位置だと隣家と目線が合うな」みたいな気づきが出てきます。打ち合わせ前にやっておくと、質問の質が変わってくるはずです。
この記事のURLをAIに渡すだけなので、よかったら試してみてください。
よくある質問
- プログラミングの知識がなくても作れますか?
- 作れます。この記事はAIが読んで実行するための手順書なので、あなたがやるのは3つだけです。図面PDFを用意する、Claude CodeまたはCodexを起動する、「この記事のURLとPDFを読んで3D化して」と伝える。コードを書くのも環境構築もAIが全部やります。
- 費用はかかりますか?
- 3Dエディタ(pascalorg/editor)はMITライセンスの無料OSSで、すべてローカル環境で動くため利用料はかかりません。必要なのはClaude CodeまたはCodexの利用料だけです。図面データも契約者なら一条工務店のタブレットから無料で取得できます。
- 一条工務店以外の家でも作れますか?
- 作れます。ただしこの手順書は一条工務店の910mmモジュール(尺モジュール)を前提に最適化しているため、他社の図面ではグリッドの読み取り方が変わります。メーターモジュールの会社なら1000mm、ツーバイフォーなら図面の寸法線を直接読む方式にAIへ指示してください。
- 作った3Dモデルは他の人に共有できますか?
- できます。完成データはJSONファイル1つにまとまっているので、そのファイルを渡せば相手も同じ3Dを再現できます。GLB形式で書き出せばBlenderや動画編集ソフト、Three.jsを使ったWebサイトにも持ち込めます。
- 図面PDFはどこから手に入りますか?
- 一条工務店の契約者なら、貸与されているタブレット(i-tablet)の図面閲覧アプリからPDFを書き出せます。打ち合わせ資料としてもらった紙図面しかない場合は、スマホのスキャンアプリでPDF化しても構いませんが、寸法線が読み取れる解像度が必要です。