import bpy, os, math
from mathutils import Vector

EVAL = "/Users/danicosta/Desktop/Avatar-Platon/3d_pipeline/head_clay"
os.makedirs(EVAL, exist_ok=True)

bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
coll = scene.collection

# convencion: up=+Z, cara mira a -Y (frente=-Y), derecha=+X
def sphere(name, loc, scale, seg=48, ring=24):
    bpy.ops.mesh.primitive_uv_sphere_add(segments=seg, ring_count=ring, location=loc)
    o = bpy.context.active_object
    o.name = name
    o.scale = scale
    return o

def cyl(name, loc, r, depth, scale=(1,1,1)):
    bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=r, depth=depth, location=loc)
    o = bpy.context.active_object
    o.name = name
    o.scale = scale
    return o

parts = []
# craneo liso (alto, algo mas profundo que ancho)
parts.append(sphere("cranium", (0, 0.03, 1.05), (0.60, 0.70, 0.82)))
# masa facial frontal (rellena la cara sin bultos)
parts.append(sphere("facemass", (0, -0.18, 0.80), (0.46, 0.42, 0.50)))
# occipucio (nuca)
parts.append(sphere("occiput", (0, 0.22, 0.98), (0.52, 0.46, 0.60)))
# mandibula y menton integrados
parts.append(sphere("jaw", (0, -0.12, 0.52), (0.44, 0.50, 0.40)))
parts.append(sphere("chin", (0, -0.22, 0.40), (0.20, 0.26, 0.20)))
# orejas leves
parts.append(sphere("earL", (0.585, 0.06, 0.84), (0.06, 0.14, 0.18)))
parts.append(sphere("earR", (-0.585, 0.06, 0.84), (0.06, 0.14, 0.18)))
# cuello
parts.append(cyl("neck", (0, 0.06, 0.16), 0.26, 0.7, (1.0, 1.0, 1.0)))
# hombros (ancho en x, fino en y)
parts.append(sphere("shoulders", (0, 0.0, -0.30), (1.15, 0.52, 0.34)))
# torso leve
parts.append(sphere("torso", (0, 0.0, -0.52), (0.82, 0.46, 0.30)))

# unir todo
for o in parts:
    o.select_set(True)
bpy.context.view_layer.objects.active = parts[0]
bpy.ops.object.join()
head = bpy.context.active_object
head.name = "Plato_Bust"

# aplicar escala antes de remesh
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

# voxel remesh -> fusiona en una superficie unica y limpia
head.data.remesh_voxel_size = 0.025
head.data.remesh_voxel_adaptivity = 0.0
bpy.ops.object.voxel_remesh()

# suavizado
for p in head.data.polygons:
    p.use_smooth = True
mod = head.modifiers.new("Smooth", 'SMOOTH')
mod.factor = 0.5; mod.iterations = 10
mod2 = head.modifiers.new("Subsurf", 'SUBSURF')
mod2.levels = 1; mod2.render_levels = 1

# centrar origen
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
head.location = (0, 0, 0)
print("dims:", tuple(round(d, 3) for d in head.dimensions),
      "| verts:", len(head.data.vertices))

# material clay
mat = bpy.data.materials.new("Clay"); mat.use_nodes = True
b = mat.node_tree.nodes["Principled BSDF"]
b.inputs["Base Color"].default_value = (0.55, 0.52, 0.5, 1)
b.inputs["Roughness"].default_value = 0.6
head.data.materials.append(mat)

# mundo neutro
world = bpy.data.worlds.new("W"); scene.world = world; world.use_nodes = True
bgn = world.node_tree.nodes["Background"]
bgn.inputs["Color"].default_value = (0.2, 0.2, 0.22, 1)
bgn.inputs["Strength"].default_value = 0.6

tgt = bpy.data.objects.new("Tgt", None); coll.objects.link(tgt); tgt.location = (0, 0, 0)
ld = bpy.data.lights.new("Key", 'AREA'); ld.energy = 900; ld.size = 5
lo = bpy.data.objects.new("Key", ld); coll.objects.link(lo); lo.location = (3, -4, 4)
lo.constraints.new('TRACK_TO').target = tgt
ld2 = bpy.data.lights.new("Fill", 'AREA'); ld2.energy = 300; ld2.size = 6
lo2 = bpy.data.objects.new("Fill", ld2); coll.objects.link(lo2); lo2.location = (-4, -3, 1)
lo2.constraints.new('TRACK_TO').target = tgt

cam_data = bpy.data.cameras.new("Cam"); cam_data.lens = 70
cam = bpy.data.objects.new("Cam", cam_data); coll.objects.link(cam)
cam.constraints.new('TRACK_TO').target = tgt
scene.camera = cam

scene.render.engine = 'BLENDER_EEVEE_NEXT'
try: scene.eevee.taa_render_samples = 32
except Exception: pass
scene.render.resolution_x = 480; scene.render.resolution_y = 600
scene.render.image_settings.file_format = 'PNG'

# vistas: frente(-Y), 3/4, perfil(+X), nuca(+Y)
R = 4.2; elev = 1.1
views = {"front": (0, -R), "tq": (-R*0.7, -R*0.7), "profile": (R, 0), "back": (0, R)}
for nm, (x, y) in views.items():
    cam.location = (x, y, elev)
    scene.render.filepath = os.path.join(EVAL, f"clay_{nm}.png")
    bpy.ops.render.render(write_still=True)
    print("rendered", nm)
print("DONE")
