| name | mmd-builder |
| description | Generic MMD→Blender builder (mmd_tools + Blender MCP). Use for PMX/PMD import, material conversion (Principled/SSS/anisotropy/alpha/emission), VMD motion/camera, hold-pose before motion start, 30→60fps retiming, NLA layering, assemble/bake rigid bodies, breast/skirt soft-body tuning, Cloth alternative, Surface Deform accessories, mid-shot visibility, outdoor lighting, pack-all broken Map# paths, headless/remote PNG sequence render, Linux server mmd_tools install, and EEVEE reflection/puddle flicker on animation. Trigger on: MMD, PMX, PMD, VMD, mmd_tools, import model/motion/camera, assemble_all, convert materials, Principled SSS, rigid body bake, breast/skirt physics, breast jitter/bounce, Surface Deform, clip_end gray plate, overexposure, pack resources, Map # missing path, hide mid animation, headless render, blender -b, remote AutoDL, lens=1, Division by Zero Driver tan(angle), only clothes no body, fake orthographic, opencc, reflection flicker, puddle flash, SSR, denoise temporal, EEVEE raytracing — even for a single sub-task.
|
| triggers | ["mmd","pmx","pmd","vmd","mmd_tools","mmd builder","import model","import motion","import camera","assemble_all","rigid body","breast physics","breast jitter","skirt physics","Surface Deform","pack all","Map","clip_end","headless","blender -b","remote render","opencc","reflection flicker","puddle","SSR","EEVEE raytracing","convert materials","Principled","SSS","subsurface","MMDBridge","cloth physics"] |
MMD Builder (Blender + mmd_tools)
Generic workflow. Prefer mcp__blender__execute_blender_code. Do not hardcode character/motion names — resolve from scene and user paths.
Success criteria
- Motion starts at agreed frame; pre-roll hold pose does not drift
- At 60fps, wall-clock duration matches 30fps VMD (key times ×2)
- Accessories that must follow body/breast do not double-deform
- Mid-shot hide works in render (Blender 5: prefer scale 0/1)
- Pack-all has no ghost
Map #… / C:\Map #… paths
- Physics param changes are followed by free bake + resim; save to a named
.blend
- Headless/remote:
mmd_tools enabled, camera PERSP with sane lens (~25–55), body not hide_render
- Animation: wet ground / metal reflections do not flash on/off frame-to-frame
- Materials: photoreal path uses Principled + hand-tuned skin SSS/IOR; toon path may keep MMD shaders
0. Environment
Addon: mmd_tools (import_model, import_vmd, assemble_all, ptcache bake ops)
import_vmd.poll → needs len(context.selected_objects) > 0
Camera VMD → select a Camera object (converts to MMD camera empty + child)
| Pitfall | Fix |
|---|
import_vmd.poll() failed | Select model root or camera first |
| Camera motion applied to model | New Camera only selected, then import camera VMD |
| Huge existing scene | Snapshot object names before/after import |
| Crash on long bake | save_as named blend often; short bake range first |
Same blend OK locally, broken on remote -b | Almost always missing mmd_tools on server — see §9 |
1. Import model
bpy.ops.mmd_tools.import_model(
filepath=pmx_or_pmd,
scale=0.08,
types={'MESH', 'ARMATURE', 'PHYSICS', 'DISPLAY', 'MORPHS'},
clean_model=True,
rename_bones=True,
dictionary='DISABLED',
)
Then bpy.ops.mmd_tools.assemble_all() → root.mmd_root.is_built == True.
Scrubbing the timeline does not simulate rigid bodies. Play or bake.
As of mmd_tools v4.5.x, Rigid Body World is enabled when assembling physics, not merely on VMD import. Assembly→Physics is opt-in convenience — not MMD-faithful soft physics (see §5).
1b. Materials (skin / cloth / hair)
Default import shaders
mmd_tools builds MMDBasicShader / MMDShaderDev: Diffuse + Anisotropic glossy mix (reflection Fac ≈ 0.02). MMD props (ambient / diffuse / alpha / specular / shininess / double-sided / sphere map type / …) live on the material’s MMD Material / MMD Texture panels and sync into the node graph via update callbacks.
| Edit where | Effect |
|---|
| MMD Material / MMD Texture panels | Updates Blender nodes and PMX custom props |
| Blender native BSDF fields only | Nodes change; PMX export props do not follow |
Convert for Cycles / Principled
bpy.ops.mmd_tools.convert_materials()
bpy.ops.mmd_tools.convert_materials_for_cycles()
Verified converter behavior (mmd_tools main / cycles_converter.py):
| Mapping | Result |
|---|
| Base Color | diffuse / Base Tex |
| Alpha | Principled Alpha (or Transmission=1-alpha if no Alpha socket) |
| Non-Principled transparency | MMDAlphaShader = Transparent BSDF mixed by Alpha Fac |
| Subsurface Weight | default 0.001 on all converted materials (not skin-only) |
| IOR | hardcoded 1.0 after Principled switch — must hand-tune |
Photoreal hand-tune (after convert)
| Target | Practical knobs |
|---|
| Skin | SSS Weight/Radius (skin scale, not 0.001 forever); IOR ≈ 1.35–1.45; slight roughness; avoid plastic specular |
| Cloth fabric | Base Color + roughness; sheen/coat if available; disable leftover MMD sphere-map glints if muddy |
| Hair / silk | keep or rebuild anisotropy (MMD default is anisotropic glossy); tangent along hair flow |
| Transparent clothes / lashes | Alpha clip vs blend; sort/shadow modes per engine; hair cards often Alpha Hashed (EEVEE) |
| Self-glow (eyes, LEDs) | Principled Emission Strength + color; or add Emission mix — MMD self-illum is not full bloom by default |
| Toon look | often skip Principled convert; keep MMDShaderDev + lighting; or use shader-to-RGB / ramp workflows |
Do not treat “Convert Materials” as final skin/cloth look — it is a seed. Zero SSS on hard props after convert so only skin keeps subsurface.
2. Motion + camera
Start offset + hold pose
User often wants motion to begin at frame S (e.g. 6). With frame_current in {0,1}:
margin = S - 1
bpy.ops.mmd_tools.import_vmd(filepath=motion_vmd, scale=0.08, margin=margin,
create_new_action=True, update_scene_settings=True)
Hold pose (critical): copy values from first motion keys at frame S onto frame 1 (or start), set interpolation='CONSTANT' for keys ≤ S, delete keys strictly between hold and S. Verify bone world pos: hold frames equal; S→S+1 moves.
Camera VMD
bpy.ops.object.camera_add()
bpy.ops.mmd_tools.import_vmd(filepath=camera_vmd, scale=0.08, margin=margin,
create_new_action=True, detect_camera_changes=True)
cam.data.clip_start = 0.01
cam.data.clip_end = 5000.0
MMD camera rig: root Empty (mmd_type == 'CAMERA') + child Camera. Drivers (need mmd_tools RNA):
| Driver | Expression (mmd_tools) | Depends on |
|---|
lens | sensor_height/tan(angle/2)/2 (often 24.0/… when sensor_height=24) | MMD_Camera.mmd_camera.angle |
type | not is_perspective | mmd_camera.is_perspective |
ortho_scale | 25*abs(empty_distance)/45 | empty distance |
| non-persp Y rot | pi when distance > 1e-5 | distance |
Near-zero angle → extreme/invalid lens (not a freehand F-curve). Without the addon, mmd_camera.angle is missing → driver sees 0 → Division by Zero → lens≈1 (ultra-wide, “fake ortho”). Local GUI with mmd_tools looks fine; remote -b without addon looks wrong — same file.
Optional hardening: patch lens driver to sensor_height/tan(max(angle,1e-4)/2)/2 (or 24.0/…).
60fps keep realtime duration
VMD is authored at 30fps. Setting render.fps=60 without retiming plays 2× speed.
- Scale all related action key times by 2 (bone, facial/morph, display, camera, extra VMD)
- Rebuild NLA strip frame ranges
- Scene range:
start' = S*2, end' = end*2
- Do not use
frame_map_old/new as a duration hack (clips timeline)
Layer second motion mid-way (generic)
Primary dance: NLA REPLACE full range
Secondary (props/toys/extra bones): NLA COMBINE from mid = start + (end-start)//2
Import secondary with use_nla=True / create_new_action as needed, then rebuild strips
Only shift/layer bones that belong to the secondary clip
3. Accessories stick to body / breast
When cloth must look non-simulated but follow breast jiggle:
- Ensure accessory mesh is under armature hierarchy
- Mute all Armature modifiers (including body) → bind at rest topology
Surface Deform target = body mesh (not skirt), strength 1, confirm is_bound
- On accessory: keep Armature off forever (SD only)
| State | Result |
|---|
| Armature only | Follows bones, not breast RB |
| SD → body only | Stick + follow breast |
| SD + Armature both on | Double deform / flies away |
| SD → skirt | Follows skirt physics chaos |
If model was joined into one mesh: separate by material first.
4. Visibility (Blender 5)
hide_render / hide_viewport fcurves may evaluate correctly but not drive the property (multi-slot actions).
Reliable render hide: animate scale to (0,0,0) / (1,1,1) with CONSTANT keys. Optionally also key hide_*.
Rules:
- Match user’s current viewport hide state for “first half costume”
- Mid-shot undress list: scale 0 from mid frame
- Exclude items user wants always on (e.g. metal belt)
Headless “only clothes, no body/face/hair”: check hide_render on body meshes and camera lens — both can stack.
5. Physics: breast / skirt / hair / cloth
Fidelity limits (read first)
Blender rigid body is transform-only (position/orientation of solid meshes) — good for MMD bone/collider chains (hair sticks, skirt bones, breast spheres). It does not mesh-deform fabric.
mmd_tools cannot faithfully match MMD/PMX soft constraints (esp. breast): MMD uses Bullet soft limits; Blender uses hard constraints; collision masks are emulated with many constraints. Assembly → Physics will not closely match MMD breast.
| Goal | Prefer |
|---|
| Exact MMD physics + fewer Blender RB crashes | MMDBridge + disable Blender Rigid Body World (rintrint/mmdbridge); trade-off: own ecosystem (e.g. MME) |
| “Good enough” breast/skirt in pure Blender | Tune RB springs (below) or Soft Body on mesh |
| Cloth surface folds | Cloth modifier (not MMD RB) |
| Hair cards as bones | MMD RB chain on hair bones; optional hair dynamics for strands |
Topology (typical MMD RB)
- type 0 kinematic anchors parented to bones
- type 1 dynamic spheres +
GENERIC_SPRING joints
- After param edit: free bake or params ignored
- Muted
Child Of (mmd_tools_rigid_parent) still creates dependency cycles — delete them if cycles spam + jitter
- Only mesh objects participate in Blender RB; Active=simulated, Passive=static (both can use Animated)
- Bake RB World in Object Mode; save the
.blend first — unsaved caches stay in memory only
Collision policy (squeeze without self-explode)
Overlapping same-side breast spheres on one collision bit → perpetual jitter.
| Pair | Desired |
|---|
| Same-side breast spheres | No collision (disable_collisions on joints / NC constraints) |
| Left ↔ right (cleavage) | Yes collision |
| Breast ↔ body (group 0: arms/torso) | Yes collision |
Practical Blender setup:
- Put dynamic breasts on a shared collision collection bit (e.g. bit 3)
- Body group-0 colliders: bit 0 and bit 3
- Only outer spheres (main + front) need collision; helper spheres can be collision-off (springs only) for stability
- Keep L–R joint/NC constraints with
disable_collisions=False; same-side True
Breast “too bouncy” → overdamped preset
| Param | Soft default | Stiffer render-safe | Why |
|---|
| mass | ~0.9 | ~1.5–2.0 | heavier = less twitch |
| linear / angular damping | 0.97 / 0.99 | 0.999 | kill velocity |
| spring stiff/damp lin | 90 / 50 | 5000 / 3000 | overdamped |
| spring stiff/damp ang | 45 / 40 | 3000 / 2000 | avoid underdamped 120/15 |
| ang limit | ±2.2° | ±0.5° (~0.008 rad) | short travel |
| lin limit | ±0.003 | ±0.0008 | |
| restitution | 0 | 0 | no contact bounce |
| substeps / solver | 10 / 10 | 30–40 / 20–40 | stability |
After any change: bpy.ops.ptcache.free_bake_all() then resim/bake.
Skirt too wide: raise mass/damping, halve joint ang limits, slightly scale leg collision bodies.
Bake vs live sim
| Workflow | Need bake? |
|---|
Sequential blender -b … -a full range | Live sim usually OK |
Mid-range only (-s/-e or -f mid clip) | Bake — missing history |
| Multi-machine per-frame split | Bake mandatory |
Bake: many ACTIVE bodies × long range is crash-prone; trial-bake short range first. rigidbody_world.enabled=True.
Cloth (clothing alternative)
When skirt/dress needs surface folds instead of bone RB:
- Parent cloth mesh to armature; Armature modifier first, then Cloth, smooth/subsurf after
- Pin vertex group on waist/belt; weight paint freely moving hem
- Character body: Collision modifier
- First playthrough auto-caches; for render-stable cache set Start/End → Bake (save blend first)
Do not run MMD skirt RB and Cloth on the same mesh without a clear stack plan — double motion.
6. Lighting (outdoor reference)
- One clear key sun (upper-left / through foliage toward stage); classic three-point = key + soft fill + hair/rim
- Kill conflicting bright area/point lights
- World HDR moderate; if character overexposes, lower sun/exposure first, not world to black
- HDRI-only outdoor: raising a single Background strength softens contrast — split lighting vs camera-visible with two Backgrounds mixed by Light Path Is Camera Ray (lighting/reflections on one input, visible plate on the other)
- EEVEE: enable world/HDRI shadow options if the HDRI should cast shadows
- Dense world volume scatter blacks out the scene
- Puddles: mask only wet areas; wet low roughness + white glints; dry high roughness
- Leaves: alpha color-ramp near 0.9–1.0 makes leaves vanish; open ramp; density = slight scale, not endless duplicates
- Puddle “reflection on/off” flicker is usually EEVEE SSR/temporal denoise, not the material — see §10
7. Pack / ghost textures
Errors like missing source path C:\Map #1 (Chinese UI may show 找不到源路径 'C:\Map #1'):
- Usually 3ds Max leftover image names
Map #… with invalid filepath
- Do not only
images.remove while materials still point at them
- Disconnect
TEX_IMAGE or replace with GENERATED image (has_data=True) then pack
- Find users by scanning material node trees + object slots
Cold headless: packed=True but has_data=False until image.reload() / first use. Prefer diagnosing lens + hide_render before chasing textures.
8. Default pipeline order
- Import PMX/PMD → assemble_all (physics only if you accept Blender RB limits)
- Materials: keep MMD shaders (toon) or Convert Materials → hand-tune skin SSS/IOR / cloth / emission
- Motion VMD (margin) + hold pose
- Camera VMD (camera selected) + clip_end; confirm lens≠1
- Optional 60fps ×2 retiming + frame range
- Optional secondary VMD via NLA COMBINE mid
- Accessory separate if needed → SD body → Armature off
- Physics path: MMDBridge or tune breast/skirt RB or Cloth — then free bake → trial bake
- Visibility scale keys
- Lighting vs reference (sun/exposure before killing world)
- Fix Map# → pack_all → named save
- Remote: install+enable mmd_tools → headless PNG sequence
9. Headless / remote render (Linux server)
CLI PNG sequence (no bpy required)
blender -b scene.blend -o //render_png/frame_ -F PNG -a
blender -b scene.blend -o //render_png/frame_ -F PNG -f 12
blender -b scene.blend -o //render_png/frame_ -F PNG -s 12 -e 100 -a
Resolution/FPS/frame range come from the .blend unless overridden.
Symptom triad (same file, remote only)
| Log / image | Cause |
|---|
Division by Zero in Driver: '24.0/tan(angle/2)/2' | mmd_tools not loaded |
lens=1.0 while type claims PERSP | angle=0 after failed driver |
| Ultra-wide / “ortho-like” plate | lens≈1, not true ORTHO |
| Only dress/empty shell, no body | body hide_render=True and/or bad camera |
Shadow buffer full | EEVEE shadow budget — quality only, not missing character |
Do not blame file transfer first. Confirm addon on server:
import addon_utils, bpy
print(addon_utils.check("mmd_tools"))
empty = bpy.data.objects.get("MMD_Camera")
print(hasattr(empty, "mmd_camera"), empty.mmd_camera.angle if hasattr(empty, "mmd_camera") else None)
Install mmd_tools on headless Blender 5.2 (AutoDL-style)
Path A — git + pip (scripts/addons):
mkdir -p ~/.config/blender/5.2/scripts/addons
cd ~/.config/blender/5.2/scripts/addons
git clone --depth 1 https://github.com/UuuNyaa/blender_mmd_tools.git mmd_tools
PY=/path/to/blender-5.2.*/5.2/python/bin/python3.13
$PY -m ensurepip --upgrade
$PY -m pip install opencc-python-reimplemented
blender -b --python-expr "import addon_utils,bpy; addon_utils.enable('mmd_tools',default_set=True,persistent=True); bpy.ops.wm.save_userpref(); print(addon_utils.check('mmd_tools'))"
Path B — copy local Extension (often cleaner):
Local: %APPDATA%/Blender Foundation/Blender/5.2/extensions/user_default/mmd_tools
Remote: ~/.config/blender/5.2/extensions/user_default/mmd_tools
enable: bl_ext.user_default.mmd_tools
Shell gotchas
- Multi-line
--python-expr with indented lines → IndentationError. Use one line or cat > /tmp/x.py << 'EOF' + blender -b -P /tmp/x.py.
missing 'bl_info' warning on extension-style repo in scripts/addons — noisy but OK if check (True, True) and has mmd_camera True.
- Clone alone does nothing until
enable + save_userpref.
Post-install smoke test
has mmd_camera True
angle ~0.4+ , persp True
lens ~25–55 (not 1.0)
Body hide_render False
then: blender -b file.blend -o //render_png/frame_ -F PNG -f <start>
Optional thin CLI wrapper (no bpy)
import subprocess, sys
from pathlib import Path
blend = Path(__file__).resolve().parent / "scene.blend"
cmd = ["blender", "-b", str(blend), "-o", "//render_png/frame_", "-F", "PNG"]
if len(sys.argv) == 2:
cmd += ["-f", sys.argv[1]]
elif len(sys.argv) >= 3:
cmd += ["-s", sys.argv[1], "-e", sys.argv[2], "-a"]
else:
cmd += ["-a"]
raise SystemExit(subprocess.call(cmd))
10. EEVEE animation: reflection / puddle flicker
Symptom: consecutive frames alternate strong mirror puddles vs dull wet ground (or metal flashes), especially with moving MMD camera.
Root cause (Blender 5 EEVEE): screen-space raytracing + temporal denoise. History is invalid when the camera moves → denoise drops/keeps hits inconsistently → “has reflection / no reflection” pop. Half-res RT (resolution_scale=2) + low TAA (16) makes it worse. Broken reflection planes (tiny influence, huge negative scale) add instability.
Fix preset (prefer in order):
ee = scene.eevee
rt = ee.ray_tracing_options
ee.taa_render_samples = 64
ee.use_raytracing = True
ee.ray_tracing_method = "SCREEN"
rt.resolution_scale = "1"
rt.screen_trace_quality = 1.0
rt.screen_trace_thickness = 0.5
rt.use_denoise = True
rt.denoise_temporal = False
rt.denoise_spatial = True
rt.denoise_bilateral = True
ee.fast_gi_ray_count = 4
ee.fast_gi_step_count = 12
ee.shadow_pool_size = "2048"
| Still flickers? | Next step |
|---|
| Mild noise | TAA 96–128 |
| Hard on/off | use_raytracing=False; rely on reflection planes only (duller but stable) |
| Shadow holes + flicker | raise shadow_pool_size; cut unused shadow casters |
Verify: render 3 consecutive frames with camera motion; puddle highlight should not vanish wholesale.
Verification
Reference
Load when needed:
references/pitfalls.md — failure modes / quick fixes
references/craft-quality.md — how to do it well: copyable params + QC; body has no links; provenance in end Sources
Upstream sources (verified mid-2026)