Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ unitypackage_loader/ Extension 本体(blender_manifest.toml、Blender 4.5
operators/ File > Import、モデル選択ダイアログ、再構築、アウトライン
ui/ Preferences、サイドバー "UPI" タブ
tests/ unittest(bpy 不要)+ Blender 上の統合テスト+合成パッケージ生成
tools/ GitHub Pages 用サイトの組み立て(build_site.py)、OGP 画像の元(og_card.html)、スクリーンショット撮影(shoot_screenshots.py)
tools/ GitHub Pages 用サイトの組み立て(build_site.py)、OGP 画像の元(og_card.html)、extensions.blender.org の featured image とアイコンの元(listing_card.html)、BOOTH の商品画像の元(booth_card.html)、スクリーンショット撮影(shoot_screenshots.py)
web/ 紹介ページのソース(英語 index.html、日本語 ja/、assets/)。ビルド時に site/ へコピーし {{VERSION}} 等を埋める
.github/workflows/ Pages への Extension Repository 公開(pages.yml)と、単体テスト・Blender 4.5 / 5.2 での統合テスト(tests.yml)
_local/ 検証用データ置き場(gitignore。詳細は CLAUDE.local.md)
Expand Down Expand Up @@ -129,6 +129,9 @@ Blender MCP が接続されている場合は、`addon_utils.disable` → `sys.m
- extensions.blender.org にも同じ `id`・同じ `version` で出す(2026-09-28 に掲載の方針を決めた。手順と審査の要件は `_local/publish_extensions_blender_org.md`)。
main にマージしたら、`extension build` で作った zip を掲載ページから新しいバージョンとしてアップロードする。
同じアドオンを 2 つの入手先から入れると両方が有効になってクラスを上書きし合うので、README と紹介ページには「どれか 1 つから入れる」と書いておく。
- BOOTH(https://utagestudio.booth.pm/items/8910607 、2026-09-28 公開)にも同じ zip を置いている(無料版と、中身が同じ応援版 300 円・1,000 円)。
minor 以上のリリースのときに全バリエーションの zip を差し替え、説明文の「現在のバージョン」と「更新履歴」も直す(fix 版では差し替えなくてよい)。
説明文の元と手順は `_local/booth/description.txt` と `_local/publish_booth.md`。商品画像は `tools/booth_card.html` から書き出す。
- 紹介ページのローカル確認: `blender ... extension build` と `server-generate` を `site/` に出したあと `python3 tools/build_site.py site <base_url>`。
Google Tag Manager は、環境変数 `GTM_ID`(無ければリポジトリ直下の `.env`。gitignore 対象)に `GTM-XXXX` 形式の ID があるときだけ埋め込む。
公開時はリポジトリの Settings > Secrets and variables > Actions > Variables の `GTM_ID` を `pages.yml` が渡す(未設定なら埋め込まない)。
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ search for "Unitypackage" in Preferences > Get Extensions and install it from th

Drag the zip from the website onto the Blender window, or load it with "Install from Disk" in the menu at the
upper right of Preferences > Get Extensions. Installed this way, the extension is not updated automatically.
The same zip is also available for free on [BOOTH](https://utagestudio.booth.pm/items/8910607), with optional support versions that contain the same file.

**Switching sources**

Expand Down
1 change: 1 addition & 0 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Blender 公式の拡張機能サイトへの掲載を申請しています。掲

紹介ページの zip を Blender の画面にドラッグ&ドロップするか、Preferences > Get Extensions の右上のメニューにある
"Install from Disk" で読み込みます。この方法で入れた場合は、新しい版が出ても自動では更新されません。
同じ zip を [BOOTH](https://utagestudio.booth.pm/items/8910607) でも無料で配布しています(中身が同じ応援版もあります)。

**入手先を変えるとき**

Expand Down
17 changes: 17 additions & 0 deletions tests/test_license_copy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent


class LicenseCopyTest(unittest.TestCase):
"""配布 zip は unitypackage_loader/ の中だけを詰めるので、そこに置いた LICENSE がルートのものと同じであること。"""

def test_addon_license_matches_root(self):
root = (ROOT / "LICENSE").read_bytes()
addon = (ROOT / "unitypackage_loader" / "LICENSE").read_bytes()
self.assertEqual(addon, root)


if __name__ == "__main__":
unittest.main()
99 changes: 99 additions & 0 deletions tools/booth_card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<!-- BOOTH の商品画像(1200×1200、正方形)の元。?p=1 がサムネイル、2〜4 が説明の画像。ローカルの Chrome でレンダリングする:
for p in 1 2 3 4; do google-chrome --headless=new --hide-scrollbars --virtual-time-budget=8000 --window-size=1200,1200 --screenshot=_local/booth/image$p.png "file://$PWD/tools/booth_card.html?p=$p"; done
リポジトリのルートで実行すること(画像は web/assets/ を相対パスで読む)。フォントは Google Fonts から読むので、オンラインで実行すること。
デザインは tools/og_card.html に揃えてある。購入アセットが写る画像(use-render / use-props / scene-street)は BOOTH では使わない。 -->
<html lang="ja">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@700;800&family=Zen+Maru+Gothic:wght@700;900&display=swap">
<style>
html, body { margin: 0; width: 1200px; height: 1200px; overflow: hidden; }
body { background: #ffffff; color: #3a322c; font-family: "Zen Maru Gothic", "Nunito", system-ui, sans-serif; position: relative; }
.blob { position: absolute; border-radius: 50%; pointer-events: none; }
.b1 { width: 900px; height: 900px; right: -300px; top: -380px; background: radial-gradient(closest-side, #ffe3cc, rgba(255,227,204,0) 72%); }
.b2 { width: 560px; height: 560px; left: -160px; bottom: -300px; background: radial-gradient(closest-side, #ffd2b0, rgba(255,210,176,0) 72%); }
.page { position: absolute; inset: 0; padding: 64px 60px 56px; display: none; flex-direction: column; box-sizing: border-box; }
.page.on { display: flex; }
.logo { display: flex; align-items: center; gap: 16px; font-family: "Nunito", system-ui, sans-serif; font-weight: 800; font-size: 30px; }
.logo svg { width: 56px; height: 56px; }
.logo .free { margin-left: auto; background: #f07f2a; color: #3a322c; font-family: "Zen Maru Gothic", sans-serif; font-weight: 900; font-size: 30px; padding: 8px 26px; border-radius: 999px; }
h1 { margin: 34px 0 0; font-size: 76px; line-height: 1.28; font-weight: 900; }
h2 { margin: 40px 0 0; font-size: 68px; line-height: 1.3; font-weight: 900; }
em { font-style: normal; background: linear-gradient(90deg, #ad5410, #f07f2a); -webkit-background-clip: text; background-clip: text; color: transparent; }
.nb { white-space: nowrap; }
p { margin: 20px 0 0; font-size: 34px; line-height: 1.5; color: #72665d; font-weight: 700; }
.frame { position: relative; margin-top: auto; border-radius: 36px; overflow: hidden; background: #393939; box-shadow: 0 40px 80px -40px rgba(58,50,44,.55); }
.frame img { display: block; width: 100%; height: 100%; object-fit: cover; }
.credit { margin-top: 14px; text-align: right; font-family: "Nunito", system-ui, sans-serif; font-size: 20px; color: #9a8e85; font-weight: 700; }
.tag { position: absolute; top: 22px; font-size: 30px; font-weight: 900; line-height: 1.3; padding: 8px 22px; border-radius: 999px; }
.tag.l { left: 22px; background: rgba(35,29,25,.82); color: #fff; }
.tag.r { right: 22px; background: #f07f2a; color: #3a322c; }

/* p=1: 前後比較(紹介ページのスライダーと同じく真ん中で分ける) */
.split { height: 700px; }
.split img { position: absolute; inset: 0; object-position: 50% 12%; }
.split img.after { clip-path: inset(0 0 0 50%); }
.split .line { position: absolute; top: 0; bottom: 0; left: 50%; width: 6px; margin-left: -3px; background: #fff; }
.split .knob { position: absolute; top: 50%; left: 50%; width: 80px; height: 80px; margin: -40px 0 0 -40px; border-radius: 50%; background: #f07f2a; border: 6px solid #fff; box-sizing: border-box; display: grid; place-items: center; }
.split .knob svg { width: 40px; height: 40px; }
.page:not([data-p="1"]) { justify-content: center; }
.page:not([data-p="1"]) .frame { margin-top: 56px; }
.page:not([data-p="1"]) h2 { margin-top: 0; }
.wide { height: 610px; flex: none; }
</style>
</head>
<body>
<div class="blob b1"></div>
<div class="blob b2"></div>

<section class="page" data-p="1">
<div class="logo">
<svg viewBox="0 0 64 64" aria-hidden="true"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ffc26a"/><stop offset="1" stop-color="#f07f2a"/></linearGradient></defs><rect x="8" y="12" width="48" height="44" rx="16" fill="url(#g)"/><path d="M32 22v20M24 34l8 8 8-8" fill="none" stroke="#fff" stroke-width="5.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="50" cy="14" r="7" fill="#4fc4a8"/></svg>
Unitypackage Importer
<span class="free">無料</span>
</div>
<h1><span class="nb">.unitypackage を、</span><br><em class="nb">テクスチャ付きで</em><span class="nb"> Blender へ。</span></h1>
<div class="frame split">
<img src="../web/assets/hero-before.webp" alt="">
<img class="after" src="../web/assets/hero-after.webp" alt="">
<span class="line"></span>
<span class="knob"><svg viewBox="0 0 24 24" fill="none" stroke="#3a322c" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="m9 7-5 5 5 5M15 7l5 5-5 5"/></svg></span>
<span class="tag l">FBX だけ</span>
<span class="tag r">このアドオン</span>
</div>
<div class="credit">Model: Tohoku Zunko © SSS LLC.</div>
</section>

<section class="page" data-p="2">
<h2>ドラッグ&ドロップで<br><em>そのまま読み込み</em></h2>
<p>.unitypackage を Blender の画面に落として、Import を押すだけ。Unity は要りません。</p>
<div class="frame wide"><img src="../web/assets/demo-poster.webp" alt=""></div>
<div class="credit">Model: Tohoku Zunko © SSS LLC.</div>
</section>

<section class="page" data-p="3">
<h2>トゥーンでも、<em>PBR でも</em></h2>
<p>lilToon・Poiyomi・MToon などの設定を読んで、トゥーン調か PBR に組み直します。あとから切り替えもできます。</p>
<div class="frame wide">
<img src="../web/assets/shot-modes.webp" alt="">
<span class="tag l">自動(トゥーン)</span>
<span class="tag r">PBR に切り替え</span>
</div>
<div class="credit">Model: Tohoku Zunko © SSS LLC.</div>
</section>

<section class="page" data-p="4">
<h2>改変の前に、<em>形をじっくり確認</em></h2>
<p>ボーンやシェイプキーも入るので、編集モードで網目を見たり、ポーズや表情を付けたりできます。</p>
<div class="frame wide"><img src="../web/assets/use-edit.webp" alt=""></div>
<div class="credit">Model: Tohoku Zunko © SSS LLC.</div>
</section>

<script>
var p = new URLSearchParams(location.search).get("p") || "1";
var page = document.querySelector('.page[data-p="' + p + '"]') || document.querySelector(".page");
page.classList.add("on");
</script>
</body>
</html>
83 changes: 83 additions & 0 deletions tools/listing_card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<!-- extensions.blender.org の featured image(1920×1080、16:9)の元。ローカルの Chrome でレンダリングする:
google-chrome --headless=new --hide-scrollbars --virtual-time-budget=8000 --window-size=1920,1080 --screenshot=_local/listing/featured.png "file://$PWD/tools/listing_card.html"
アイコン(256×256 の PNG、背景は透明)は同じファイルの ?icon で出す:
google-chrome --headless=new --hide-scrollbars --default-background-color=00000000 --window-size=256,256 --screenshot=_local/listing/icon.png "file://$PWD/tools/listing_card.html?icon"
リポジトリのルートで実行すること(前後比較は web/assets/hero-*.webp を相対パスで読む)。
フォントは Google Fonts から読むので、オンラインで実行すること。デザインは tools/og_card.html に揃えてある。
掲載ページの画像に Blender のロゴを使ってはいけない(ToS)。 -->
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@700;800&display=swap">
<style>
html, body { margin: 0; width: 1920px; height: 1080px; overflow: hidden; }
body { background: #ffffff; color: #3a322c; font-family: "Nunito", system-ui, sans-serif; position: relative; }
.blob { position: absolute; border-radius: 50%; pointer-events: none; }
.b1 { width: 1300px; height: 1300px; right: -420px; top: -520px; background: radial-gradient(closest-side, #ffe3cc, rgba(255,227,204,0) 72%); }
.b2 { width: 720px; height: 720px; left: -200px; bottom: -380px; background: radial-gradient(closest-side, #ffd2b0, rgba(255,210,176,0) 72%); }
.card { position: absolute; inset: 0; padding: 90px 120px 90px 132px; display: grid; grid-template-columns: minmax(0, 1fr) 640px; align-items: center; gap: 80px; }
.logo { display: flex; align-items: center; gap: 26px; font-weight: 800; font-size: 44px; margin-bottom: 48px; }
.logo svg { width: 88px; height: 88px; }
h1 { margin: 0 0 40px; font-size: 92px; line-height: 1.1; font-weight: 800; letter-spacing: -0.015em; }
h1 em { font-style: normal; background: linear-gradient(90deg, #ad5410, #f07f2a); -webkit-background-clip: text; background-clip: text; color: transparent; }
p { margin: 0; font-size: 38px; line-height: 1.45; color: #72665d; max-width: 1040px; font-weight: 700; }
.pills { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 48px; }
.pill { display: inline-flex; align-items: center; gap: 14px; background: #fff5ec; color: #ad5410; font-weight: 800; font-size: 34px; padding: 14px 32px; border-radius: 999px; }
.pill::before { content: ""; width: 18px; height: 18px; border-radius: 50%; background: #f07f2a; }

/* before / after, split in the middle like the slider on the website */
.art { display: grid; gap: 16px; justify-items: center; }
.frame { position: relative; width: 640px; height: 800px; border-radius: 48px; overflow: hidden; background: #393939; box-shadow: 0 50px 100px -50px rgba(58,50,44,.55); }
.frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.frame img.after { clip-path: inset(0 0 0 50%); }
.frame .line { position: absolute; top: 0; bottom: 0; left: 50%; width: 6px; margin-left: -3px; background: #fff; }
.frame .knob { position: absolute; top: 50%; left: 50%; width: 88px; height: 88px; margin: -44px 0 0 -44px; border-radius: 50%; background: #f07f2a; border: 6px solid #fff; box-sizing: border-box; display: grid; place-items: center; }
.frame .knob svg { width: 44px; height: 44px; }
.tag { position: absolute; top: 26px; font-size: 30px; font-weight: 800; line-height: 1.3; padding: 8px 22px; border-radius: 999px; }
.tag.before { left: 26px; background: rgba(35,29,25,.82); color: #fff; }
.tag.after { right: 26px; background: #f07f2a; color: #3a322c; }
.credit { font-size: 22px; color: #9a8e85; font-weight: 700; }

/* ?icon: 256×256 のアイコン(紹介ページのロゴをそのまま拡大) */
html.icon, html.icon body { width: 256px; height: 256px; background: transparent; }
html.icon .blob, html.icon .card { display: none; }
.icon-only { display: none; }
html.icon .icon-only { display: block; width: 256px; height: 256px; }
</style>
</head>
<body>
<svg class="icon-only" viewBox="0 0 64 64" aria-hidden="true"><defs><linearGradient id="gi" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ffc26a"/><stop offset="1" stop-color="#f07f2a"/></linearGradient></defs><rect x="8" y="12" width="48" height="44" rx="16" fill="url(#gi)"/><path d="M32 22v20M24 34l8 8 8-8" fill="none" stroke="#fff" stroke-width="5.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="50" cy="14" r="7" fill="#4fc4a8"/></svg>
<div class="blob b1"></div>
<div class="blob b2"></div>
<div class="card">
<div>
<div class="logo">
<svg viewBox="0 0 64 64" aria-hidden="true"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ffc26a"/><stop offset="1" stop-color="#f07f2a"/></linearGradient></defs><rect x="8" y="12" width="48" height="44" rx="16" fill="url(#g)"/><path d="M32 22v20M24 34l8 8 8-8" fill="none" stroke="#fff" stroke-width="5.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="50" cy="14" r="7" fill="#4fc4a8"/></svg>
Unitypackage Importer
</div>
<h1>Import .unitypackage files, <em>materials and textures included.</em></h1>
<p>Drop a package onto the viewport. No Unity needed.</p>
<div class="pills">
<span class="pill">lilToon · Poiyomi · MToon</span>
<span class="pill">Standard · URP · HDRP</span>
<span class="pill">Whole scenes</span>
</div>
</div>
<div class="art">
<div class="frame">
<img src="../web/assets/hero-before.webp" alt="">
<img class="after" src="../web/assets/hero-after.webp" alt="">
<span class="line"></span>
<span class="knob"><svg viewBox="0 0 24 24" fill="none" stroke="#3a322c" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="m9 7-5 5 5 5M15 7l5 5-5 5"/></svg></span>
<span class="tag before">Plain FBX</span>
<span class="tag after">This add-on</span>
</div>
<span class="credit">Model: Tohoku Zunko © SSS LLC.</span>
</div>
</div>
<script>
if (new URLSearchParams(location.search).has("icon")) document.documentElement.classList.add("icon");
</script>
</body>
</html>
Loading
Loading