"""LOG496 用：看護師（大城さん）とハコブくんを同じ床に置いて描く（記事の画像とサムネイルの素材）。
python3 scene_render.py <nurse_clay.blend> <hakobu.blend> <出力png> <場面> [幅 高さ]
場面：drawer（引き出しの前でかがむ）・talk（立って話しかける）
背景は透明で描き、あとで #eceef2 の地に重ねる（影は床の影だけ残す）"""
import math
import os
import sys

import bpy
from mathutils import Matrix, Vector

nurse_blend, hakobu_blend, out, scene = sys.argv[1:5]
W, H = (int(sys.argv[5]), int(sys.argv[6])) if len(sys.argv) > 6 else (1920, 1080)

bpy.ops.wm.open_mainfile(filepath=nurse_blend)
for o in [o for o in bpy.data.objects if o.type in ("CAMERA", "LIGHT")]:
    bpy.data.objects.remove(o)
with bpy.data.libraries.load(hakobu_blend) as (src, dst):
    dst.objects = [n for n in src.objects if n.startswith("Hakobu")]
sc = bpy.context.scene
for o in dst.objects:
    sc.collection.objects.link(o)
hk = bpy.data.objects["Hakobu"]
for img in bpy.data.images:  # ハコブくんの画像は blend と同じフォルダにある
    if img.name.startswith("hakobu_"):
        img.filepath = os.path.join(os.path.dirname(os.path.abspath(hakobu_blend)), os.path.basename(img.filepath))
rig = bpy.data.objects["Nurse_Oshiro"]
X, Y, Z = (1, 0, 0), (0, 1, 0), (0, 0, 1)


def upd():
    bpy.context.view_layer.update()


def rot_world(bone, axis, deg):
    pb = rig.pose.bones[bone]
    h = pb.head.copy()
    R = Matrix.Rotation(math.radians(deg), 4, Vector(axis).normalized())
    pb.matrix = Matrix.Translation(h) @ R @ Matrix.Translation(-h) @ pb.matrix
    upd()


def twist(bone, deg):
    pb = rig.pose.bones[bone]
    rot_world(bone, pb.tail - pb.head, deg)


def curl(side, deg, thumb):
    for f in ("Index", "Middle", "Ring", "Little"):
        for seg, k in (("Proximal", 1.0), ("Intermediate", 1.2)):
            pb = rig.pose.bones[side + f + seg]
            pb.rotation_mode = "XYZ"
            pb.rotation_euler.x = math.radians(deg * k)
    for seg in ("Proximal", "Intermediate"):
        pb = rig.pose.bones[side + "Thumb" + seg]
        pb.rotation_mode = "XYZ"
        pb.rotation_euler.x = math.radians(thumb)
    upd()


def mouth(kind):
    for ob in bpy.data.objects:
        if ob.name.startswith("Nurse_Mouth_"):
            ob.hide_render = ob.name != "Nurse_Mouth_" + kind


# 姿勢はすべて、看護師が -Y を向いた作った時の向きで付けてから、体ごと回す
if scene == "drawer":
    drawer_open = 0.26
    rot_world("Spine", X, 16)
    rot_world("Chest", X, 14)
    rot_world("RightUpperArm", X, -48)
    rot_world("RightLowerArm", X, -18)
    rot_world("LeftUpperArm", X, -30)
    rot_world("LeftLowerArm", X, -30)
    curl("Right", 50, 30)
    curl("Left", 20, 10)
    rot_world("Head", X, 14)
    mouth("smile")
    nurse_xy, nurse_rot = (0.98, -0.30), -60
    cam = ((0.55, -3.5, 1.5), (0.5, -0.2, 0.80), 36)
else:  # talk
    drawer_open = 0.0
    rot_world("LeftUpperArm", X, -30)
    rot_world("LeftLowerArm", X, -55)
    twist("LeftLowerArm", -30)
    twist("LeftHand", -30)
    curl("Left", 10, 0)
    rot_world("Head", X, 10)
    rot_world("Head", Z, -6)
    mouth("talk")
    nurse_xy, nurse_rot = (1.05, -0.25), -60
    cam = ((0.55, -3.5, 1.5), (0.5, -0.2, 0.84), 36)

for o in bpy.data.objects:
    if o.name.startswith(("Hakobu_Drawer", "Hakobu_Tray")):
        o.location.y = -drawer_open
# ハコブくんの顔（-Y）を右手前へ、看護師の顔を左手前へ。向かい合いつつ、両方とも 30° カメラへ向ける
hk.rotation_euler = (0, 0, math.radians(60))
rig.location = (*nurse_xy, 0)
rig.rotation_euler = (0, 0, math.radians(nurse_rot))
upd()

# 薬の箱（白に青の帯）をトレーの上に
if drawer_open > 0:
    bpy.ops.mesh.primitive_cube_add(size=1, location=(0.04, -0.50, 0.455))
    box = bpy.context.object
    box.parent = hk
    box.scale = (0.12, 0.08, 0.05)
    box.rotation_euler = (0, 0, math.radians(8))
    mw = bpy.data.materials.new("Box")
    mw.use_nodes = True
    mw.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0.92, 0.92, 0.9, 1)
    box.data.materials.append(mw)
    bpy.ops.mesh.primitive_cube_add(size=1, location=(0.04, -0.50, 0.455))
    band = bpy.context.object
    band.parent = hk
    band.scale = (0.03, 0.082, 0.052)
    band.rotation_euler = box.rotation_euler
    mb = bpy.data.materials.new("Band")
    mb.use_nodes = True
    mb.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0.05, 0.25, 0.75, 1)
    band.data.materials.append(mb)

# 床：影だけを受ける
bpy.ops.mesh.primitive_plane_add(size=12, location=(0, 0, 0))
floor = bpy.context.object
floor.is_shadow_catcher = True

sc.render.engine = "CYCLES"
sc.cycles.device = "CPU"
sc.cycles.samples = 48
sc.cycles.use_denoising = True
sc.render.film_transparent = True
sc.view_settings.view_transform = "Standard"
w = bpy.data.worlds.new("W")
sc.world = w
w.use_nodes = True
bg = w.node_tree.nodes["Background"]
bg.inputs["Color"].default_value = (0.86, 0.88, 0.92, 1)
bg.inputs["Strength"].default_value = 0.8
sun = bpy.data.lights.new("Sun", "SUN")
sun.energy = 1.6
sun.angle = math.radians(25)
so = bpy.data.objects.new("Sun", sun)
so.rotation_euler = (math.radians(50), math.radians(-15), math.radians(-35))
sc.collection.objects.link(so)
L = bpy.data.lights.new("F", "AREA")
L.energy, L.size = 120, 4
o = bpy.data.objects.new("F", L)
o.location = (0.6, -3.0, 1.6)
o.rotation_euler = (Vector((0.5, 0, 0.8)) - Vector(o.location)).to_track_quat("-Z", "Y").to_euler()
sc.collection.objects.link(o)
loc, tgt, lens = cam
c = bpy.data.cameras.new("C")
c.lens = lens
co = bpy.data.objects.new("C", c)
co.location = loc
co.rotation_euler = (Vector(tgt) - Vector(loc)).to_track_quat("-Z", "Y").to_euler()
sc.collection.objects.link(co)
sc.camera = co
sc.render.resolution_x, sc.render.resolution_y = W, H
sc.render.image_settings.color_mode = "RGBA"
sc.render.filepath = out
bpy.ops.render.render(write_still=True)
print("wrote", out)
