import bpy
import math
# --- Configuration Constants ---
GIRAFFE_COLLECTION_NAME = "Giraffe_Model"
MATERIAL_NAME = "Giraffe_Pattern"
COLOR_BROWN = (0.6, 0.3, 0.1, 1)
COLOR_CREAM = (0.9, 0.8, 0.7, 1)
COLOR_DARK = (0.05, 0.02, 0.01, 1)
def create_giraffe_material():
mat = bpy.data.materials.new(name=MATERIAL_NAME)
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
for n in nodes: nodes.remove(n)
output = nodes.new(type='ShaderNodeOutputMaterial')
bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
ramp = nodes.new(type='ShaderNodeValToRGB')
voronoi = nodes.new(type='ShaderNodeTexVoronoi')
coord = nodes.new(type='ShaderNodeTexCoord')
links.new(coord.outputs['Object'], voronoi.inputs['Vector'])
voronoi.voronoi_dimensions = '3D'
voronoi.feature = 'F1'
if hasattr(voronoi, "distance"):
voronoi.distance = 'EUCLIDEAN'
else:
voronoi.distance_metric = 'EUCLIDEAN'
voronoi.inputs['Scale'].default_value = 4.5
voronoi.inputs['Randomness'].default_value = 0.8
ramp.color_ramp.interpolation = 'CONSTANT'
ramp.color_ramp.elements[0].position = 0.55
ramp.color_ramp.elements[0].color = (0.32, 0.15, 0.05, 1)
ramp.color_ramp.elements[1].position = 0.60
ramp.color_ramp.elements[1].color = (0.9, 0.8, 0.7, 1)
links.new(voronoi.outputs['Distance'], ramp.inputs['Fac'])
links.new(ramp.outputs['Color'], bsdf.inputs['Base Color'])
bsdf.inputs['Roughness'].default_value = 0.3
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
return mat
def create_part(name, scale, location, rotation=(0, 0, 0), color=None, material=None, bevel_width=0.04, shape='CUBE', collection=None):
if shape == 'SPHERE':
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.5, segments=32, ring_count=16)
else:
bpy.ops.mesh.primitive_cube_add(size=1)
obj = bpy.context.active_object
obj.name = name
obj.scale = scale
obj.location = location
obj.rotation_euler = rotation
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
if shape == 'CUBE' and bevel_width > 0:
bevel_mod = obj.modifiers.new(name="Bevel", type='BEVEL')
bevel_mod.width = bevel_width
bevel_mod.segments = 4
bpy.ops.object.shade_smooth()
elif shape == 'SPHERE':
bpy.ops.object.shade_smooth()
if material:
obj.data.materials.append(material)
elif color:
mat = bpy.data.materials.new(name=f"Mat_{name}")
mat.use_nodes = True
bsdf = mat.node_tree.nodes["Principled BSDF"]
bsdf.inputs['Base Color'].default_value = color
bsdf.inputs['Roughness'].default_value = 0.3
obj.data.materials.append(mat)
if collection:
for col in obj.users_collection:
col.objects.unlink(obj)
collection.objects.link(obj)
return obj
def establish_parent(child, parent):
if child and parent:
old_matrix = child.matrix_world.copy()
child.parent = parent
child.matrix_parent_inverse = parent.matrix_world.inverted()
# --- Main Execution ---
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
giraffe_col = bpy.data.collections.new(GIRAFFE_COLLECTION_NAME)
bpy.context.scene.collection.children.link(giraffe_col)
pattern_mat = create_giraffe_material()
torso_rot = math.radians(12)
torso_loc = (0, 0, 3.2)
# 1. Torso (The Root Parent)
torso = create_part("Torso", (1.0, 1.8, 1.2), torso_loc, (torso_rot, 0, 0), material=pattern_mat, bevel_width=0.08, collection=giraffe_col)
# 2. Neck & Head Chain (Torso -> Neck -> Head)
neck_angle, neck_l, neck_loc = math.radians(-25), 3.5, (0, 1.15, 5.2)
neck = create_part("Neck", (0.4, 0.4, neck_l), neck_loc, (neck_angle, 0, 0), material=pattern_mat, bevel_width=0.04, collection=giraffe_col)
establish_parent(neck, torso)
h_y = neck_loc[1] + (math.sin(-neck_angle) * (neck_l/2)) + 0.3
h_z = neck_loc[2] + (math.cos(-neck_angle) * (neck_l/2)) + 0.1
head = create_part("Head", (0.5, 1.0, 0.5), (0, h_y, h_z), (math.radians(10), 0, 0), material=pattern_mat, bevel_width=0.05, collection=giraffe_col)
establish_parent(head, neck)
# 3. Legs & Joints (Parented to Torso)
f_y, f_z = torso_loc[1] + (math.cos(torso_rot) * 0.8), torso_loc[2] + (math.sin(torso_rot) * 0.8)
b_y, b_z = torso_loc[1] - (math.cos(torso_rot) * 0.8), torso_loc[2] - (math.sin(torso_rot) * 0.8)
leg_data = [("FL", -0.4, f_y, f_z, 3.35), ("FR", 0.4, f_y, f_z, 3.35),
("BL", -0.4, b_y, b_z, 3.0), ("BR", 0.4, b_y, b_z, 3.0)]
for name, x, y, az, h in leg_data:
joint_z = az - 0.1
joint = create_part(f"Joint_{name}", (0.35, 0.35, 0.35), (x, y, joint_z), material=pattern_mat, shape='SPHERE', collection=giraffe_col)
establish_parent(joint, torso)
leg = create_part(name, (0.2, 0.2, h), (x, y, joint_z - (h/2)), material=pattern_mat, bevel_width=0.02, collection=giraffe_col)
establish_parent(leg, torso)
# 4. Tail (Parented to Torso)
tail = create_part("Tail", (0.1, 0.1, 1.2), (0, b_y, b_z), (math.radians(-45), 0, 0), color=COLOR_DARK, bevel_width=0.01, collection=giraffe_col)
establish_parent(tail, torso)
# 5. Head Detail Parts (Parented to Head)
head_details = [
("Ear_L", (0.3, 0.1, 0.2), (-0.32, h_y - 0.15, h_z + 0.3), (0, 0, math.radians(30)), COLOR_BROWN, None, 0.02, 'CUBE'),
("Ear_R", (0.3, 0.1, 0.2), (0.32, h_y - 0.15, h_z + 0.3), (0, 0, math.radians(-30)), COLOR_BROWN, None, 0.02, 'CUBE'),
("Ossicone_L", (0.1, 0.1, 0.4), (-0.15, h_y - 0.1, h_z + 0.4), (0, 0, 0), COLOR_DARK, None, 0.01, 'CUBE'),
("Ossicone_R", (0.1, 0.1, 0.4), (0.15, h_y - 0.1, h_z + 0.4), (0, 0, 0), COLOR_DARK, None, 0.01, 'CUBE'),
("Eye_L", (0.1, 0.1, 0.1), (-0.28, h_y + 0.2, h_z + 0.1), (0, 0, 0), COLOR_DARK, None, 0.005, 'CUBE'),
("Eye_R", (0.1, 0.1, 0.1), (0.28, h_y + 0.2, h_z + 0.1), (0, 0, 0), COLOR_DARK, None, 0.005, 'CUBE')
]
for name, scale, loc, rot, col, mat, bevel, shape in head_details:
hd_obj = create_part(name, scale, loc, rot, color=col, material=mat, bevel_width=bevel, shape=shape, collection=giraffe_col)
establish_parent(hd_obj, head)
bpy.ops.object.select_all(action='DESELECT')
Comments
Post a Comment