見出し画像

Blender 基本シーン設定スクリプト、バージョンアップ版 


"""
タイトル: Blender 基本シーン設定スクリプト
var1.9.0 2024年11月19日
説明:シーン設定を効率化するスクリプト,処理を個別の関数に分割して再利用可能に設計されています。
"""

"""
以下読み込み方書式

import sys
import os

# スクリプトのあるディレクトリをPythonパスに追加
script_dir = os.path.dirname(bpy.data.filepath)
if script_dir not in sys.path:
    sys.path.append(script_dir)

# インポート
from setup_stage import clear_stage,create_camera,create_light,set_background_color
"""

import bpy
import math
import mathutils

# ステージクリア(カメラ・照明を残すかどうかを引数で指定)
def clear_stage(keep_camera=True, keep_lights=True):
    for obj in bpy.data.objects:
        if (obj.type == 'CAMERA' and keep_camera) or (obj.type == 'LIGHT' and keep_lights):
            continue
        bpy.data.objects.remove(obj, do_unlink=True)

# シーン内のすべてのマテリアルを削除します。
def clear_materials():
    try:
        # マテリアルを削除
        for mat in bpy.data.materials:
            bpy.data.materials.remove(mat, do_unlink=True)
        print("すべてのマテリアルを削除しました。")
        return True
    except Exception as e:
        print(f"マテリアル削除中にエラーが発生しました: {e}")
        return False

# カメラを設置する関数
def create_camera(location=(0, 0, 10), rotation=(math.radians(90), 0, 0), name="Camera"):
    bpy.ops.object.camera_add(location=location)
    camera = bpy.context.object
    camera.name = name
    camera.rotation_euler = rotation
    return camera

#   指定したカメラを特定のオブジェクトまたは座標方向に向けます。
def point_camera(camera, target=None, target_location=None):
    """
    Args:
        camera (bpy.types.Object): カメラオブジェクト。
        target (bpy.types.Object): カメラが向く対象のオブジェクト(指定しない場合は target_location を使用)。
        target_location (tuple): カメラが向く座標 (x, y, z)。target が None の場合に有効。
    
    Returns:
        bool: 成功した場合は True、失敗した場合は False。
    """
    try:
        if target is not None:
            # 対象オブジェクトのワールド座標を取得
            target_location = target.matrix_world.translation
        elif target_location is None:
            raise ValueError("target または target_location のいずれかを指定する必要があります。")
        
        # カメラの位置を取得
        camera_location = camera.matrix_world.translation
        
        # カメラの方向ベクトルを計算
        direction = camera_location -mathutils.Vector(target_location)
        direction.normalize()
        
        # 回転マトリックスを計算
        rotation = direction.to_track_quat('Z', 'Y').to_euler()
        
        # カメラの回転を設定
        camera.rotation_euler = rotation
        return True
    except Exception as e:
        print(f"カメラの向き変更中にエラーが発生しました: {e}")
        return False

# 照明を設置する関数
def create_light(location=(10, 10, 10), rotation=(math.radians(0), math.radians(45), 0), light_type='SUN', energy=1000, color=(1.0, 1.0, 1.0), name="Light"):
    # 照明の種類 ('SUN', 'POINT', 'SPOT', 'AREA')
    bpy.ops.object.light_add(type=light_type, location=location)
    light = bpy.context.object
    light.name = name
    light.rotation_euler = rotation
    light.data.energy = energy
    light.data.color = color
    return light

# 照明をターゲットに向ける関数
def point_light(light_object, target_location):
    try:
        if light_object.type != 'LIGHT':
            raise ValueError(f"Object '{light_object.name}' is not a light.")
        light_location = mathutils.Vector(light_object.location)
        direction = mathutils.Vector(target_location) - light_location
        rotation = direction.to_track_quat('-Z', 'Y').to_euler()
        light_object.rotation_euler = rotation
        return True
    except Exception as e:
        print(f"照明の向き変更中にエラーが発生しました: {e}")
        return False

# 背景色を指定する関数(成功したかどうかを返す)
def set_background_color(color=(0.0, 0.0, 0.0, 1.0)):
    bpy.context.scene.world.use_nodes = True
    bg_node = bpy.context.scene.world.node_tree.nodes.get("Background")
    if bg_node:
        bg_node.inputs[0].default_value = color  # 色指定 (RGBA)
        return True
    else:
        print("背景ノードが見つかりません。スキップします。")
        return False

def initialize_stage(
    camera_location=(0, 10, 10),
    camera_rotation=(math.radians(45), math.radians(0), math.radians(180)),
    light_location=(5, 5, 5),
    light_rotation=(math.radians(-45), math.radians(0), 0),
    light_type='SUN',
    light_energy=5,
    light_color=(1.0, 1.0, 1.0),
    background_color=(0.0, 0.0, 0.5, 1.0),
    keep_camera=False,
    keep_lights=False,
    keep_matelials=True):
    
    clear_stage(keep_camera=keep_camera, keep_lights=keep_lights)
    if keep_matelials:
        clear_materials()
    camera = create_camera(location=camera_location, rotation=camera_rotation, name="Camera")
    light = create_light(location=light_location, rotation=light_rotation, light_type=light_type, color=light_color, energy=light_energy, name="Light")
    set_background_color(color=background_color)
    
    return camera,light

# マテリアルを生成する関数
def create_material(
    name="Material", 
    color=(1.0, 1.0, 1.0, 1.0), 
    specular=0.5, 
    roughness=0.5, 
    metallic=0.0,
    emission_color=(0.0, 0.0, 0.0, 1.0), 
    emission_strength=0.0, alpha=1.0, use_alpha=False):
        
    """
    多機能マテリアル生成関数。

    Args:
        name (str): マテリアル名。
        color (tuple): ベースカラー (R, G, B, A)。
        specular (float): 反射強度 (0.0 - 1.0)。
        roughness (float): 表面の粗さ (0.0 - 1.0)。
        metallic (float): 金属度 (0.0 - 1.0)。
        emission_color (tuple): 発光色 (R, G, B, A)。
        emission_strength (float): 発光の強度。
        alpha (float): 透明度 (0.0 - 1.0)。
        use_alpha (bool): 透明度を有効にするかどうか。

    Returns:
        bpy.types.Material: 作成されたマテリアルオブジェクト。
    """
    # マテリアル取得または新規作成
    mat = bpy.data.materials.get(name)
    if not mat:
        mat = bpy.data.materials.new(name=name)
        mat.use_nodes = True

    # ノード設定
    bsdf = mat.node_tree.nodes.get("Principled BSDF")
    if bsdf:
        # ベースカラー
        if "Base Color" in bsdf.inputs:
            bsdf.inputs["Base Color"].default_value = color

        # 反射強度
        if "Specular" in bsdf.inputs:
            bsdf.inputs["Specular"].default_value = specular

        # 粗さ
        if "Roughness" in bsdf.inputs:
            bsdf.inputs["Roughness"].default_value = roughness

        # 金属度
        if "Metallic" in bsdf.inputs:
            bsdf.inputs["Metallic"].default_value = metallic

        # 発光設定
        if emission_strength > 0.0 and "Emission" in bsdf.inputs:
            bsdf.inputs["Emission"].default_value = emission_color
            if "Emission Strength" in bsdf.inputs:
                bsdf.inputs["Emission Strength"].default_value = emission_strength

        # 透明度設定
        if use_alpha and "Alpha" in bsdf.inputs:
            bsdf.inputs["Alpha"].default_value = alpha
            mat.blend_method = 'BLEND'  # ブレンドモードを有効に
            mat.shadow_method = 'HASHED'  # 影の透過設定

    return mat


def create_primitive_object(
    object_type='SPHERE', 
    location=(0, 0, 0), 
    scale=(1, 1, 1), 
    rotation=(0, 0, 0), 
    material=None, 
    name="Object", 
    smooth=False, 
    **kwargs):
        
    """
    高機能プリミティブオブジェクト生成関数。
    
    Args:
        object_type (str): 作成するプリミティブオブジェクトのタイプ。
            サポートされるタイプ: 'SPHERE', 'CUBE', 'CYLINDER', 'CONE', 'PLANE', 'ICO_SPHERE', 'MONKEY'。
        location (tuple): 生成するオブジェクトの座標。
        scale (tuple): オブジェクトのスケール。
        rotation (tuple): オブジェクトの回転 (radians)。
        material (bpy.types.Material): 適用するマテリアル。指定がない場合は新規作成。
        name (str): オブジェクトの名前。
        smooth (bool): スムースシェーディングを適用するかどうか。
        kwargs: オブジェクト固有のオプション(ポリゴン数など)。
            - 'segments' (int): 球体、円柱、円錐のセグメント数。
            - 'ring_count' (int): UV球体のリング数。
            - 'subdivisions' (int): ICO球体の分割数。
    Returns:
        bpy.types.Object: 作成されたオブジェクト。
    """
    obj = None

    # オブジェクト作成
    if object_type.upper() == 'SPHERE':
        segments = kwargs.get('segments', 32)
        ring_count = kwargs.get('ring_count', 16)
        bpy.ops.mesh.primitive_uv_sphere_add(location=location, segments=segments, ring_count=ring_count)
        obj = bpy.context.object

    elif object_type.upper() == 'CUBE':
        bpy.ops.mesh.primitive_cube_add(location=location)
        obj = bpy.context.object

    elif object_type.upper() == 'CYLINDER':
        segments = kwargs.get('segments', 32)
        bpy.ops.mesh.primitive_cylinder_add(location=location, vertices=segments)
        obj = bpy.context.object

    elif object_type.upper() == 'CONE':
        segments = kwargs.get('segments', 32)
        bpy.ops.mesh.primitive_cone_add(location=location, vertices=segments)
        obj = bpy.context.object

    elif object_type.upper() == 'PLANE':
        bpy.ops.mesh.primitive_plane_add(location=location)
        obj = bpy.context.object

    elif object_type.upper() == 'ICO_SPHERE':
        subdivisions = kwargs.get('subdivisions', 2)
        bpy.ops.mesh.primitive_ico_sphere_add(location=location, subdivisions=subdivisions)
        obj = bpy.context.object

    elif object_type.upper() == 'MONKEY':
        bpy.ops.mesh.primitive_monkey_add(location=location)
        obj = bpy.context.object

    else:
        raise ValueError(f"Unsupported object type: {object_type}")

    # オブジェクトのプロパティ設定
    if obj:
        obj.name = name
        obj.scale = scale
        obj.rotation_euler = rotation

        # マテリアルを適用
        if material is None:
            # マテリアルが指定されていない場合、新規作成
            material = create_material(name=f"{name}_Material")
        if material:
            obj.data.materials.append(material)

        # スムースシェーディングを適用
        if smooth:
            bpy.ops.object.shade_smooth()
        else:
            bpy.ops.object.shade_flat()

    return obj


# メイン処理
if __name__ == "__main__":
    # 初期設定
    camera, light = initialize_stage(
        camera_location=(20, 10, 10),
        camera_rotation=(math.radians(90), 0, 0),
        light_location=(0, 10, 10),
        #light_type='SUN',  # 照明の種類 ('SUN', 'POINT', 'SPOT', 'AREA')
        light_type='SPOT',  # 照明の種類 ('SUN', 'POINT', 'SPOT', 'AREA')
        light_energy=5000,
        light_color=(1.0, 1.0, 0.3),
        background_color=(0.0, 0.3, 0.8, 1.0),  # 青い背景
        keep_camera=False,
        keep_lights=False,
        keep_matelials=True
    )

    # 座標に向ける
    target_location = (0, 0, 0)  # 原点に向ける
    point_camera(camera, target_location=(0, -1, 0))
    point_light(light, target_location)

    plane_material = create_material(name="PlaneMaterial", color=(0.5, 0.5, 0.5, 1))
    red_material = create_material(name="RedMaterial", color=(1.0, 0.0, 0.0, 1.0))
    blue_material = create_material(name="BlueMaterial", color=(0.0, 0.0, 1.0, 1.0))
    create_primitive_object(object_type='SPHERE', location=(0, 0, 0), scale=(1, 1, 1), material=red_material, name="RedSphere", smooth=True)
    create_primitive_object(object_type='CUBE', location=(2, 2, 0), scale=(1, 1, 1), material=blue_material, name="BlueCube")
    create_primitive_object(object_type='PLANE', location=(0, 0, -1), scale=(10, 10, 1), material=plane_material, name="Floor")

いいなと思ったら応援しよう!