irg/auto_room copy_window.py

774 lines
30 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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
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 = (0, 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.051Alpha 1
# 转换为RGBHSV(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"
# 设置摄像机位置和旋转
# 房间宽度5.0米,相机位置调整为(房间宽度-1, 1, 1.3)
room_width = 5.0
camera.location = (room_width - 1, 1, 1.3) # 相机位置:(4, 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("摄像机设置完成")
print(f"相机位置: ({camera.location.x}, {camera.location.y}, {camera.location.z})")
# 隐藏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 = -6
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()