#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Blender 4.2兼容版自动化脚本(白模版本) 自动创建等轴测房间并渲染白模 """ import subprocess import os import sys # Blender可执行文件路径 BLENDER_PATH = r"D:\Program Files\Blender Foundation\blender-4.2.11-windows-x64\blender.exe" # 简化版Blender脚本(只渲染白模) BLENDER_SIMPLE_SCRIPT = ''' import bpy import bmesh import os import math import time def disable_problematic_addons(): """禁用可能导致问题的插件""" try: # 禁用可能冲突的插件 problematic_addons = ['bl_tool'] for addon in problematic_addons: if addon in bpy.context.preferences.addons: bpy.ops.preferences.addon_disable(module=addon) print(f"已禁用插件: {addon}") except: pass def create_isometric_room(): """创建等轴测房间,使用isometric_room_gen插件""" try: # 禁用问题插件 #disable_problematic_addons() # 清除默认立方体 if "Cube" in bpy.data.objects: bpy.data.objects.remove(bpy.data.objects["Cube"], do_unlink=True) # 设置3D游标位置到原点 bpy.context.scene.cursor.location = (0.0, 0.0, 0.0) # 检查isometric_room_gen插件 if not hasattr(bpy.context.scene, 'sna_room_width'): print("错误: isometric_room_gen插件未正确加载") return False # 设置isometric_room_gen插件参数 # 房间设置 bpy.context.scene.sna_wall_thickness = 0.10 # 墙体厚度10cm bpy.context.scene.sna_room_width = 5.0 # 房间宽度8米 bpy.context.scene.sna_room_depth = 8.0 # 房间深度4米 bpy.context.scene.sna_room_height = 3.0 # 房间高度3米 # 窗户设置 - 不设置窗户 bpy.context.scene.sna_windows_enum = 'NONE' # 无窗户 # 设置房间类型为方形 bpy.context.scene.sna_style = 'Square' # 找到3D视图区域 view3d_area = None for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': view3d_area = area break if view3d_area is None: print("错误: 找不到3D视图区域") return False # 临时切换到3D视图上下文 with bpy.context.temp_override(area=view3d_area): # 使用isometric_room_gen插件创建房间 result = bpy.ops.sna.gen_room_1803a() if result == {'FINISHED'}: print("等轴测房间创建完成!") print(f"房间尺寸: {bpy.context.scene.sna_room_width}m x {bpy.context.scene.sna_room_depth}m x {bpy.context.scene.sna_room_height}m") print(f"墙体厚度: {bpy.context.scene.sna_wall_thickness}m") # 将生成的房间在Z轴上向下移动墙体厚度的距离 wall_thickness = bpy.context.scene.sna_wall_thickness # 查找生成的房间对象 room_object = None for obj in bpy.data.objects: if obj.name == "IRG_IsoRoom_WithCeiling": room_object = obj break if room_object: # 获取当前位置 current_location = room_object.location.copy() # 在Z轴上向下移动墙体厚度的距离 room_object.location.z = current_location.z - wall_thickness print(f"已将房间在Z轴上向下移动 {wall_thickness}m") print(f"房间新位置: {room_object.location}") else: print("警告: 未找到生成的房间对象") return True else: print("房间创建失败") return False except Exception as e: print(f"创建房间时出现错误: {str(e)}") import traceback traceback.print_exc() return False def create_window_with_archimesh(): """使用archimesh插件创建落地窗""" try: print("开始创建archimesh落地窗...") # 检查archimesh插件是否可用 if not hasattr(bpy.ops.mesh, 'archimesh_winpanel'): print("错误: archimesh插件未正确加载") print("请确保archimesh插件已安装并启用") return False # 设置3D游标到原点 bpy.context.scene.cursor.location = (0.0, 0.0, 0.0) # 找到3D视图区域 view3d_area = None for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': view3d_area = area break if view3d_area is None: print("错误: 找不到3D视图区域") return False # 临时切换到3D视图上下文 with bpy.context.temp_override(area=view3d_area): # 创建窗户 result = bpy.ops.mesh.archimesh_winpanel() if result == {'FINISHED'}: print("archimesh落地窗创建完成!") # 等待一帧以确保对象创建完成 bpy.context.view_layer.update() # 找到刚创建的窗户组对象(Window_Group) window_group = None for obj in bpy.data.objects: if obj.name == "Window_Group": window_group = obj break if window_group: print(f"找到窗户组: {window_group.name}") # 查找主窗户对象(Window) main_window_obj = None for obj in bpy.data.objects: if obj.name == "Window": main_window_obj = obj break if main_window_obj and hasattr(main_window_obj, 'WindowPanelGenerator'): # 获取窗户属性 window_props = main_window_obj.WindowPanelGenerator[0] print("设置窗户属性...") print(f"找到主窗户对象: {main_window_obj.name}") # 设置窗户的基本属性 window_props.gen = 4 # 4扇窗户(水平) window_props.yuk = 1 # 1行(垂直) window_props.kl1 = 5 # 外框厚度5cm window_props.kl2 = 5 # 竖框厚度5cm window_props.fk = 2 # 内框厚度2cm # 设置窗户尺寸 window_props.gnx0 = 80 # 第1扇窗户宽度80cm window_props.gnx1 = 80 # 第2扇窗户宽度80cm window_props.gnx2 = 80 # 第3扇窗户宽度80cm window_props.gnx3 = 80 # 第4扇窗户宽度80cm window_props.gny0 = 250 # 窗户高度250cm(落地窗) # 设置窗户类型为平窗 window_props.UST = '1' # 平窗 window_props.r = 0 # 旋转角度0度 # 设置窗台 window_props.mr = True # 启用窗台 window_props.mr1 = 4 # 窗台高度4cm window_props.mr2 = 4 # 窗台深度4cm window_props.mr3 = 20 # 窗台宽度20cm window_props.mr4 = 0 # 窗台延伸0cm # 设置材质 window_props.mt1 = '1' # 外框材质PVC window_props.mt2 = '1' # 内框材质塑料 # 设置窗户开启状态 window_props.k00 = True # 第1扇窗户开启 window_props.k01 = True # 第2扇窗户开启 window_props.k02 = True # 第3扇窗户开启 window_props.k03 = True # 第4扇窗户开启(如果有的话) print(f"窗户属性设置完成:") print(f" - 水平扇数: {window_props.gen}") print(f" - 垂直行数: {window_props.yuk}") print(f" - 外框厚度: {window_props.kl1}cm") print(f" - 竖框厚度: {window_props.kl2}cm") print(f" - 内框厚度: {window_props.fk}cm") print(f" - 窗户高度: {window_props.gny0}cm") print(f" - 窗户类型: 平窗") print(f" - 窗台: 启用") print(f" - 窗户开启状态: k00={window_props.k00}, k01={window_props.k01}, k02={window_props.k02}, k03={window_props.k03}") # 修复窗户玻璃材质 print("修复窗户玻璃材质...") fix_window_glass_material() # 等待更新完成 bpy.context.view_layer.update() # 强制触发更新(如果需要) try: # 重新设置属性以触发更新 current_gen = window_props.gen current_gny0 = window_props.gny0 window_props.gen = 2 # 临时设置为其他值 bpy.context.view_layer.update() window_props.gen = current_gen # 设置回目标值 bpy.context.view_layer.update() print("已强制触发窗户更新") except Exception as update_error: print(f"强制更新时出现错误: {update_error}") # 在修改落地窗全部属性后进行位移和旋转 print("开始移动和旋转窗户组...") try: window_group1 = None for obj in bpy.data.objects: if obj.name == "Window_Group": window_group1 = obj break # 设置位置 window_group1.location = (5, 4.0, 0.0) print(f"已将窗户组移动到位置: {window_group1.location}") # 设置旋转 window_group1.rotation_euler = (math.radians(0), math.radians(0), math.radians(90)) # 旋转(0, 0, 90度) print(f"已将窗户组旋转到: (0°, 0°, 90°)") # 强制更新 bpy.context.view_layer.update() print("窗户组位移和旋转完成") except Exception as move_error: print(f"移动和旋转窗户组时出现错误: {move_error}") else: print("警告: 未找到主窗户对象或WindowPanelGenerator属性") # 尝试查找其他可能的窗户属性 for obj in bpy.data.objects: if hasattr(obj, 'archimesh_window'): print(f"找到archimesh_window属性在对象: {obj.name}") # 打印窗户组的所有子对象信息 print("窗户组包含的对象:") for obj in bpy.data.objects: if obj.parent == window_group: print(f" - {obj.name} (位置: {obj.location})") return True else: print("警告: 未找到Window_Group对象") # 尝试查找其他可能的窗户对象 window_objects = [obj for obj in bpy.data.objects if "Window" in obj.name] if window_objects: print("找到的窗户相关对象:") for obj in window_objects: print(f" - {obj.name} (类型: {obj.type})") # 尝试移动第一个找到的窗户对象 first_window = window_objects[0] first_window.location = (0.0, 1.96, 0.0) print(f"已移动 {first_window.name} 到位置: {first_window.location}") return True else: print("未找到任何窗户相关对象") return False else: print("窗户创建失败") return False except Exception as e: print(f"创建窗户时出现错误: {str(e)}") import traceback traceback.print_exc() return False def fix_window_glass_material(): """修复窗户玻璃材质,将其改为高透BSDF""" try: print("开始修复窗户玻璃材质...") # 查找Window_Group下的Window对象 window_group = None for obj in bpy.data.objects: if obj.name == "Window_Group": window_group = obj break if not window_group: print("未找到Window_Group对象") return False # 查找Window对象(Window_Group的子对象) window_obj = None for obj in bpy.data.objects: if obj.name == "Window" and obj.parent == window_group: window_obj = obj break if not window_obj: print("未找到Window对象") return False print(f"找到Window对象: {window_obj.name}") # 检查Window对象的材质 if not window_obj.material_slots: print("Window对象没有材质槽") return False # 遍历所有材质槽 for slot in window_obj.material_slots: if slot.material and "Glass" in slot.material.name: print(f"找到Glass材质: {slot.material.name}") # 启用节点编辑 slot.material.use_nodes = True # 清除所有现有节点 slot.material.node_tree.nodes.clear() # 创建半透BSDF节点 translucent_bsdf = slot.material.node_tree.nodes.new(type='ShaderNodeBsdfTranslucent') translucent_bsdf.location = (0, 0) # 设置半透BSDF参数 translucent_bsdf.inputs['Color'].default_value = (0.95, 0.98, 1.0, 1.0) # 几乎无色 # 半透BSDF节点没有Weight参数,只有Color参数 # 创建材质输出节点 material_output = slot.material.node_tree.nodes.new(type='ShaderNodeOutputMaterial') material_output.location = (300, 0) # 连接节点 slot.material.node_tree.links.new( translucent_bsdf.outputs['BSDF'], material_output.inputs['Surface'] ) # 设置材质混合模式为Alpha Blend slot.material.blend_method = 'BLEND' print(f"已成功修改Glass材质为半透BSDF") print(f" - 半透颜色: 几乎无色") print(f" - 混合模式: Alpha Blend") return True print("未找到Glass材质") return False except Exception as e: print(f"修复玻璃材质时出现错误: {str(e)}") import traceback traceback.print_exc() return False def setup_render_settings(): """设置渲染参数(EEVEE渲染,优化速度)""" scene = bpy.context.scene # 设置渲染引擎为EEVEE scene.render.engine = 'BLENDER_EEVEE_NEXT' print("已设置渲染引擎为EEVEE") # 设置世界环境 if hasattr(scene, 'world') and scene.world: # 启用世界节点 scene.world.use_nodes = True # 清除现有节点 scene.world.node_tree.nodes.clear() # 创建自发光节点 emission_node = scene.world.node_tree.nodes.new(type='ShaderNodeEmission') emission_node.location = (0, 0) # 设置HSV颜色:色相0,饱和度0,明度0.051,Alpha 1 # 转换为RGB:HSV(0, 0, 0.051) = RGB(0.051, 0.051, 0.051) emission_node.inputs['Color'].default_value = (0.051, 0.051, 0.051, 1.0) # 深灰色 emission_node.inputs['Strength'].default_value = 7.0 # 强度7 # 创建世界输出节点 world_output = scene.world.node_tree.nodes.new(type='ShaderNodeOutputWorld') world_output.location = (300, 0) # 连接节点 scene.world.node_tree.links.new( emission_node.outputs['Emission'], world_output.inputs['Surface'] ) print("已设置世界环境为自发光") print(f" - 颜色: HSV(0, 0, 0.051) = RGB(0.051, 0.051, 0.051)") print(f" - 强度: 7.0") # 设置EEVEE采样(降低采样数提高速度) try: if hasattr(scene.eevee, 'taa_render_samples'): scene.eevee.taa_render_samples = 32 # 从64降低到32 print(f"已设置EEVEE渲染采样: {scene.eevee.taa_render_samples}") elif hasattr(scene.eevee, 'taa_samples'): scene.eevee.taa_samples = 32 # 从64降低到32 print(f"已设置EEVEE采样: {scene.eevee.taa_samples}") else: print("警告: 无法找到EEVEE采样设置,使用默认值") # 启用屏幕空间反射(简化设置) if hasattr(scene.eevee, 'use_ssr'): scene.eevee.use_ssr = True print("已启用屏幕空间反射") # 启用环境光遮蔽(简化设置) if hasattr(scene.eevee, 'use_gtao'): scene.eevee.use_gtao = True print("已启用环境光遮蔽") # 启用透明渲染(必要) if hasattr(scene.eevee, 'use_transparent'): scene.eevee.use_transparent = True print("已启用透明渲染") # 设置透明混合模式(必要) if hasattr(scene.render, 'film_transparent'): scene.render.film_transparent = True print("已启用透明背景") except AttributeError as e: print(f"EEVEE设置警告: {e}") print("使用默认EEVEE渲染设置") # 设置分辨率 scene.render.resolution_x = 1080 # 宽度:1080px scene.render.resolution_y = 2400 # 高度:2400px scene.render.resolution_percentage = 100 # 设置输出格式 scene.render.image_settings.file_format = 'PNG' scene.render.image_settings.color_mode = 'RGBA' # 支持透明 # 设置输出路径到桌面 desktop_path = os.path.join(os.path.expanduser("~"), "Desktop") timestamp = time.strftime("%m%d_%H%M%S") # 日期_时分秒格式 filename = f"isometric_{timestamp}.png" scene.render.filepath = os.path.join(desktop_path, filename) print(f"EEVEE渲染设置完成,输出路径: {scene.render.filepath}") return scene.render.filepath def setup_camera_and_lighting(): """设置摄像机和照明(160W点光源 + 150W日光)""" # 计算灯光高度(房间高度减一米) room_height = 3.0 light_height = room_height - 1.0 # 设置摄像机 camera = None if "Camera" in bpy.data.objects: camera = bpy.data.objects["Camera"] elif "Isometric Camera" in bpy.data.objects: camera = bpy.data.objects["Isometric Camera"] else: # 创建新摄像机 bpy.ops.object.camera_add(location=(7, -7, 5)) camera = bpy.context.active_object camera.name = "Isometric Camera" # 设置摄像机位置和旋转 camera.location = (1, 1, 1.3) # 相机位置 camera.rotation_euler = (math.radians(90), math.radians(0), math.radians(45)) # 相机旋转 # 设置为透视投影 camera.data.type = 'PERSP' camera.data.lens = 35.0 # 35mm焦距 camera.data.sensor_width = 35.0 # 35mm传感器 camera.data.sensor_fit = 'AUTO' # 自动适配 # 设置为场景的活动摄像机 bpy.context.scene.camera = camera print("摄像机设置完成") # 隐藏ISO Emission灯光(如果存在) light_objects = ["ISO Emission Left", "ISO Emission Right"] for obj_name in light_objects: if obj_name in bpy.data.objects: light_obj = bpy.data.objects[obj_name] # 隐藏发光对象 light_obj.hide_render = True light_obj.hide_viewport = True print(f"已隐藏 {obj_name}") # 创建第一个160W点光源(房间中心) # try: # # 计算点光源位置(房间中心上方) # room_length = 4.0 # Y轴 # room_width = 8.0 # X轴(4+4) # # 第一个点光源位置:房间中心上方 # light_x = 0 # X轴中心 # light_y = 0 # Y轴中心 # light_z = light_height # # 创建第一个点光源 # bpy.ops.object.light_add(type='POINT', location=(light_x, light_y, light_z)) # point_light1 = bpy.context.active_object # point_light1.name = "Main Point Light" # point_light1.data.energy = 160 # 160W # # 设置软衰减(简化设置) # point_light1.data.shadow_soft_size = 0.5 # 0.5米半径,产生柔和阴影 # point_light1.data.use_shadow = True # 启用阴影 # print(f"已创建第一个点光源(160W)") # print(f"点光源位置: x={light_x}, y={light_y}, z={light_z}") # except Exception as e: # print(f"创建第一个点光源时出错: {e}") # 创建第二个150W日光(位置12, 7, 6) try: # 第二个光源位置 - 调整到窗户后方更远的位置 light2_x = 12 light2_y = 7 # 让日光更远 light2_z = 6 # 提高高度 # 创建日光 bpy.ops.object.light_add(type='SUN', location=(light2_x, light2_y, light2_z)) sun_light = bpy.context.active_object sun_light.name = "Sun Light" sun_light.data.energy = 20 # 调整日光旋转角度,让光线更直接地照射窗户 sun_light.rotation_euler = (math.radians(0), math.radians(60), math.radians(0)) # 简化旋转角度 # 设置日光属性(简化设置) sun_light.data.angle = 0.05 # 从0.1改为0.05,让阴影更锐利 sun_light.data.use_shadow = True # 启用阴影 print(f"已创建日光(20W)") print(f"日光位置: x={light2_x}, y={light2_y}, z={light2_z}") print(f"日光旋转: x={-70}°, y={0}°, z={0}°") except Exception as e: print(f"创建日光时出错: {e}") print("照明设置完成") print("灯光类型: 点光源 + 日光") print(f"衰减类型: 点光源软衰减 + 日光平行光") print(f"阴影设置: 启用,柔和阴影") print(f"主灯光高度: {light_height}米 (房间高度减一米)") print(f"日光位置: (0, 10, 6)米,旋转: (-70°, 0°, 0°)") def render_scene(): """渲染场景""" print("开始EEVEE渲染...") # 执行渲染 bpy.ops.render.render(write_still=True) print(f"EEVEE渲染完成! 图片保存在: {bpy.context.scene.render.filepath}") return bpy.context.scene.render.filepath def main(): """主函数""" print("=" * 60) print("开始自动创建等轴测房间并EEVEE渲染") print("=" * 60) try: # 1. 创建房间 print("1. 创建等轴测房间...") if not create_isometric_room(): print("房间创建失败,停止执行") return False # 2. 创建落地窗 print("2. 创建archimesh落地窗...") if not create_window_with_archimesh(): print("落地窗创建失败,但继续执行") # 不中断执行,窗户创建失败不影响渲染 # 3. 设置渲染参数(EEVEE) print("3. 设置EEVEE渲染参数...") output_path = setup_render_settings() # 4. 设置摄像机和照明 print("4. 设置摄像机和照明...") setup_camera_and_lighting() # 5. 渲染场景 print("5. 开始EEVEE渲染...") final_path = render_scene() print("=" * 60) print("EEVEE渲染完成!") print(f"渲染图片已保存到桌面: {final_path}") print("=" * 60) return True except Exception as e: print(f"执行过程中出现错误: {str(e)}") import traceback traceback.print_exc() return False # 执行主函数 if __name__ == "__main__": main() ''' def check_blender_exists(): """检查Blender是否存在于指定路径""" if not os.path.exists(BLENDER_PATH): print(f"错误: 在路径 '{BLENDER_PATH}' 找不到Blender") print("请检查Blender安装路径是否正确") return False return True def create_temp_script(): """创建临时的Blender脚本文件""" script_path = os.path.join(os.getcwd(), "temp_simple_blender_script.py") with open(script_path, 'w', encoding='utf-8') as f: f.write(BLENDER_SIMPLE_SCRIPT) return script_path def launch_blender_simple(): """启动Blender并执行简化版自动化脚本""" if not check_blender_exists(): return False # 创建临时脚本文件 script_path = create_temp_script() try: print("正在启动Blender(白模版本)...") print(f"Blender路径: {BLENDER_PATH}") print("特点:") print("- 不设置材质,使用默认白色材质") print("- 快速渲染") print("- 兼容Blender 4.2") print("- 自动保存到桌面") # 构建命令行参数 cmd = [ BLENDER_PATH, "--background", # 后台运行模式 "--disable-crash-handler", # 禁用崩溃处理器 "--python", script_path # 执行Python脚本 ] print("\n开始执行白模渲染流程...") # 启动Blender并等待完成 process = subprocess.run( cmd, capture_output=True, text=True, encoding='utf-8', timeout=300 # 5分钟超时 ) # 输出结果 if process.stdout: print("Blender输出:") # 过滤掉重复的错误信息 lines = process.stdout.split('\n') filtered_lines = [] for line in lines: if not ("AttributeError: 'NoneType' object has no attribute 'idname'" in line or "Error in bpy.app.handlers.depsgraph_update_post" in line): filtered_lines.append(line) print('\n'.join(filtered_lines)) if process.stderr and not "rotate_tool.py" in process.stderr: print("重要错误信息:") print(process.stderr) if process.returncode == 0: print("\n✅ 白模渲染执行成功!") print("白模图片应该已经保存到桌面") return True else: print(f"\n❌ 执行失败,返回码: {process.returncode}") return False except subprocess.TimeoutExpired: print("\n⏰ 执行超时(5分钟),可能Blender仍在运行") print("请检查桌面是否有生成的图片") return False except Exception as e: print(f"启动Blender时出现错误: {str(e)}") return False finally: # 清理临时文件 try: if os.path.exists(script_path): os.remove(script_path) print(f"已清理临时文件: {script_path}") except: pass def main(): """主函数""" print("=" * 70) print("Blender 4.2兼容版自动化脚本 - 白模渲染") print("=" * 70) print("特点:") print("✓ 移除所有材质设置") print("✓ 使用默认白色材质") print("✓ 快速渲染") print("✓ 兼容Blender 4.2") print("✓ 自动保存到桌面") print("=" * 70) success = launch_blender_simple() if success: print("\n🎉 白模渲染完成!") print("请检查桌面上的渲染图片") print("图片文件名格式: isometric_room_white_[时间戳].png") else: print("\n❌ 执行失败!") print("可能的解决方案:") print("1. 确保Isometquick插件已正确安装") print("2. 禁用可能冲突的其他插件") print("3. 检查磁盘空间是否足够") if __name__ == "__main__": main()