irg/__init__.py

1268 lines
62 KiB
Python
Raw Permalink Normal View History

2025-07-18 16:42:22 +08:00
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import math
import bmesh
import bpy.utils.previews
import bpy
bl_info = {
"name": "Isometric Room Gen",
"author": "SkdSam & Mr Steve",
"description": "Generate Isometric Rooms",
"blender": (3, 6, 0),
"version": (1, 0, 6),
"location": "",
"warning": "",
"doc_url": "https://superhivemarket.com/creators/skdsam",
"tracker_url": "",
"category": "3D View"
}
def string_to_int(value):
if value.isdigit():
return int(value)
return 0
def string_to_icon(value):
if value in bpy.types.UILayout.bl_rna.functions["prop"].parameters["icon"].enum_items.keys():
return bpy.types.UILayout.bl_rna.functions["prop"].parameters["icon"].enum_items[value].value
return string_to_int(value)
addon_keymaps = {}
_icons = None
class SNA_PT_IRG_3542C(bpy.types.Panel):
bl_label = 'IRG'
bl_idname = 'SNA_PT_IRG_3542C'
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_context = ''
bl_category = 'IRG'
bl_order = 0
bl_ui_units_x = 0
@classmethod
def poll(cls, context):
return not (False)
def draw_header(self, context):
layout = self.layout
def draw(self, context):
layout = self.layout
layout.prop(bpy.context.scene, 'sna_style',
text=bpy.context.scene.sna_style, icon_value=0, emboss=True, expand=True)
if bpy.context.scene.sna_style == "Square":
col_F2E32 = layout.column(heading='', align=False)
col_F2E32.alert = False
col_F2E32.enabled = True
col_F2E32.active = True
col_F2E32.use_property_split = False
col_F2E32.use_property_decorate = False
col_F2E32.scale_x = 1.0
col_F2E32.scale_y = 1.0
col_F2E32.alignment = 'Expand'.upper()
col_F2E32.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_8A9B7 = col_F2E32.row(heading='', align=True)
row_8A9B7.alert = False
row_8A9B7.enabled = True
row_8A9B7.active = True
row_8A9B7.use_property_split = False
row_8A9B7.use_property_decorate = False
row_8A9B7.scale_x = 1.0
row_8A9B7.scale_y = 1.0
row_8A9B7.alignment = 'Expand'.upper()
row_8A9B7.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_8A9B7.prop(bpy.context.scene, 'sna_room_settings', text='Room Settings ', icon_value=(
11 if bpy.context.scene.sna_room_settings else 10), emboss=False, toggle=True)
row_8A9B7.prop(bpy.context.scene, 'sna_room_settings', text='', icon_value=string_to_icon(
'MATCUBE'), emboss=bpy.context.scene.sna_room_settings, toggle=True)
col_AF92C = col_F2E32.column(heading='', align=False)
col_AF92C.alert = False
col_AF92C.enabled = True
col_AF92C.active = True
col_AF92C.use_property_split = False
col_AF92C.use_property_decorate = False
col_AF92C.scale_x = 1.0
col_AF92C.scale_y = 1.0
col_AF92C.alignment = 'Expand'.upper()
col_AF92C.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
if bpy.context.scene.sna_room_settings:
box_480DE = col_AF92C.box()
box_480DE.alert = False
box_480DE.enabled = True
box_480DE.active = True
box_480DE.use_property_split = False
box_480DE.use_property_decorate = False
box_480DE.alignment = 'Expand'.upper()
box_480DE.scale_x = 1.0
box_480DE.scale_y = 1.0
if not True:
box_480DE.operator_context = "EXEC_DEFAULT"
box_480DE.prop(bpy.context.scene, 'sna_room_width',
text='Room Width', icon_value=0, emboss=True)
box_480DE.prop(bpy.context.scene, 'sna_room_depth',
text='Room Depth', icon_value=0, emboss=True)
box_480DE.prop(bpy.context.scene, 'sna_room_height',
text='Room Height', icon_value=0, emboss=True)
box_480DE.prop(bpy.context.scene, 'sna_wall_thickness',
text='Wall Thickness', icon_value=0, emboss=True)
row_C816B = col_F2E32.row(heading='', align=True)
row_C816B.alert = False
row_C816B.enabled = True
row_C816B.active = True
row_C816B.use_property_split = False
row_C816B.use_property_decorate = False
row_C816B.scale_x = 1.0
row_C816B.scale_y = 1.0
row_C816B.alignment = 'Expand'.upper()
row_C816B.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_C816B.prop(bpy.context.scene, 'sna_winows_settings', text='Window Settings', icon_value=(
11 if bpy.context.scene.sna_winows_settings else 10), emboss=False, toggle=True)
row_C816B.prop(bpy.context.scene, 'sna_winows_settings', text='', icon_value=string_to_icon(
'MESH_PLANE'), emboss=bpy.context.scene.sna_winows_settings, toggle=True)
if bpy.context.scene.sna_winows_settings:
box_7092C = col_F2E32.box()
box_7092C.alert = False
box_7092C.enabled = True
box_7092C.active = True
box_7092C.use_property_split = False
box_7092C.use_property_decorate = False
box_7092C.alignment = 'Expand'.upper()
box_7092C.scale_x = 1.0
box_7092C.scale_y = 1.0
if not True:
box_7092C.operator_context = "EXEC_DEFAULT"
box_7092C.prop(bpy.context.scene, 'sna_windows_enum',
text='Window Placement', icon_value=0, emboss=True)
if (bpy.context.scene.sna_windows_enum != 'NONE'):
col_8CB93 = box_7092C.column(heading='', align=False)
col_8CB93.alert = False
col_8CB93.enabled = True
col_8CB93.active = True
col_8CB93.use_property_split = False
col_8CB93.use_property_decorate = False
col_8CB93.scale_x = 1.0
col_8CB93.scale_y = 1.0
col_8CB93.alignment = 'Expand'.upper()
col_8CB93.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
col_8CB93.prop(bpy.context.scene, 'sna_window_style',
text='Type', icon_value=0, emboss=True)
col_8CB93.prop(bpy.context.scene, 'sna_windows_count',
text='Window Count', icon_value=0, emboss=True)
col_8CB93.prop(bpy.context.scene, 'sna_windows_width',
text='Window Width %', icon_value=0, emboss=True)
col_8CB93.prop(bpy.context.scene, 'sna_windows_height',
text='Window Height %', icon_value=0, emboss=True)
# 添加拱门设置
row_arch = col_F2E32.row(heading='', align=True)
row_arch.alert = False
row_arch.enabled = True
row_arch.active = True
row_arch.use_property_split = False
row_arch.use_property_decorate = False
row_arch.scale_x = 1.0
row_arch.scale_y = 1.0
row_arch.alignment = 'Expand'.upper()
row_arch.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_arch.prop(bpy.context.scene, 'sna_arch_settings', text='Arch Settings ', icon_value=(
11 if bpy.context.scene.sna_arch_settings else 10), emboss=False, toggle=True)
row_arch.prop(bpy.context.scene, 'sna_arch_settings', text='', icon_value=string_to_icon(
'MESH_CUBE'), emboss=bpy.context.scene.sna_arch_settings, toggle=True)
if bpy.context.scene.sna_arch_settings:
box_arch = col_F2E32.box()
box_arch.alert = False
box_arch.enabled = True
box_arch.active = True
box_arch.use_property_split = False
box_arch.use_property_decorate = False
box_arch.alignment = 'Expand'.upper()
box_arch.scale_x = 1.0
box_arch.scale_y = 1.0
if not True:
box_arch.operator_context = "EXEC_DEFAULT"
box_arch.prop(bpy.context.scene, 'sna_arch_placement',
text='Arch Placement', icon_value=0, emboss=True)
if bpy.context.scene.sna_arch_placement != 'NONE':
box_arch.prop(bpy.context.scene, 'sna_arch_width',
text='Arch Width', icon_value=0, emboss=True)
box_arch.prop(bpy.context.scene, 'sna_arch_height',
text='Arch Height', icon_value=0, emboss=True)
box_arch.prop(bpy.context.scene, 'sna_arch_thickness',
text='Arch Thickness', icon_value=0, emboss=True)
col_F2E32.separator(factor=1.0000016689300537)
row_A6551 = col_F2E32.row(heading='', align=False)
row_A6551.alert = False
row_A6551.enabled = True
row_A6551.active = True
row_A6551.use_property_split = False
row_A6551.use_property_decorate = False
row_A6551.scale_x = 1.0
row_A6551.scale_y = 2.440000057220459
row_A6551.alignment = 'Expand'.upper()
row_A6551.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
op = row_A6551.operator('sna.gen_room_1803a', text='Generate Room', icon_value=string_to_icon(
'SHAPEKEY_DATA'), emboss=True, depress=False)
elif bpy.context.scene.sna_style == "Round":
col_05349 = layout.column(heading='', align=False)
col_05349.alert = False
col_05349.enabled = True
col_05349.active = True
col_05349.use_property_split = False
col_05349.use_property_decorate = False
col_05349.scale_x = 1.0
col_05349.scale_y = 1.0
col_05349.alignment = 'Expand'.upper()
col_05349.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_31CFF = col_05349.row(heading='', align=True)
row_31CFF.alert = False
row_31CFF.enabled = True
row_31CFF.active = True
row_31CFF.use_property_split = False
row_31CFF.use_property_decorate = False
row_31CFF.scale_x = 1.0
row_31CFF.scale_y = 1.0
row_31CFF.alignment = 'Expand'.upper()
row_31CFF.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_31CFF.prop(bpy.context.scene, 'sna_round_room_settings', text='Room Settings ', icon_value=(
11 if bpy.context.scene.sna_round_room_settings else 10), emboss=False, toggle=True)
row_31CFF.prop(bpy.context.scene, 'sna_round_room_settings', text='', icon_value=string_to_icon(
'MATCUBE'), emboss=bpy.context.scene.sna_round_room_settings, toggle=True)
col_549D5 = col_05349.column(heading='', align=False)
col_549D5.alert = False
col_549D5.enabled = True
col_549D5.active = True
col_549D5.use_property_split = False
col_549D5.use_property_decorate = False
col_549D5.scale_x = 1.0
col_549D5.scale_y = 1.0
col_549D5.alignment = 'Expand'.upper()
col_549D5.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
if bpy.context.scene.sna_round_room_settings:
box_B3F1F = col_549D5.box()
box_B3F1F.alert = False
box_B3F1F.enabled = True
box_B3F1F.active = True
box_B3F1F.use_property_split = False
box_B3F1F.use_property_decorate = False
box_B3F1F.alignment = 'Expand'.upper()
box_B3F1F.scale_x = 1.0
box_B3F1F.scale_y = 1.0
if not True:
box_B3F1F.operator_context = "EXEC_DEFAULT"
box_B3F1F.prop(bpy.context.scene, 'sna_room_diameter_round',
text='Room Diameter', icon_value=0, emboss=True)
box_B3F1F.prop(bpy.context.scene, 'sna_room_floor_thickness_round',
text='Floor Depth', icon_value=0, emboss=True)
box_B3F1F.prop(bpy.context.scene, 'sna_room_height_round',
text='Room Height', icon_value=0, emboss=True)
box_B3F1F.prop(bpy.context.scene, 'sna_wall_thickness_round',
text='Wall Thickness', icon_value=0, emboss=True)
row_6F6A0 = col_05349.row(heading='', align=True)
row_6F6A0.alert = False
row_6F6A0.enabled = True
row_6F6A0.active = True
row_6F6A0.use_property_split = False
row_6F6A0.use_property_decorate = False
row_6F6A0.scale_x = 1.0
row_6F6A0.scale_y = 1.0
row_6F6A0.alignment = 'Expand'.upper()
row_6F6A0.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
row_6F6A0.prop(bpy.context.scene, 'sna_round_window_settings', text='Window Settings', icon_value=(
11 if bpy.context.scene.sna_round_window_settings else 10), emboss=False, toggle=True)
row_6F6A0.prop(bpy.context.scene, 'sna_round_window_settings', text='', icon_value=string_to_icon(
'MESH_PLANE'), emboss=bpy.context.scene.sna_round_window_settings, toggle=True)
if bpy.context.scene.sna_round_window_settings:
box_E2A99 = col_05349.box()
box_E2A99.alert = False
box_E2A99.enabled = True
box_E2A99.active = True
box_E2A99.use_property_split = False
box_E2A99.use_property_decorate = False
box_E2A99.alignment = 'Expand'.upper()
box_E2A99.scale_x = 1.0
box_E2A99.scale_y = 1.0
if not True:
box_E2A99.operator_context = "EXEC_DEFAULT"
box_E2A99.prop(bpy.context.scene, 'sna_window_style_round',
text='Window Placement', icon_value=0, emboss=True)
if (bpy.context.scene.sna_window_style_round != 'NONE'):
col_765D0 = box_E2A99.column(heading='', align=False)
col_765D0.alert = False
col_765D0.enabled = True
col_765D0.active = True
col_765D0.use_property_split = False
col_765D0.use_property_decorate = False
col_765D0.scale_x = 1.0
col_765D0.scale_y = 1.0
col_765D0.alignment = 'Expand'.upper()
col_765D0.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
col_765D0.prop(bpy.context.scene, 'sna_round_window_amount',
text='Window Count', icon_value=0, emboss=True)
col_765D0.prop(bpy.context.scene, 'sna_round_window_width',
text='Window Width %', icon_value=0, emboss=True)
col_765D0.prop(bpy.context.scene, 'sna_round_window_height',
text='Window Height %', icon_value=0, emboss=True)
col_05349.separator(factor=1.0000016689300537)
row_63B4A = col_05349.row(heading='', align=False)
row_63B4A.alert = False
row_63B4A.enabled = True
row_63B4A.active = True
row_63B4A.use_property_split = False
row_63B4A.use_property_decorate = False
row_63B4A.scale_x = 1.0
row_63B4A.scale_y = 2.440000057220459
row_63B4A.alignment = 'Expand'.upper()
row_63B4A.operator_context = "INVOKE_DEFAULT" if True else "EXEC_DEFAULT"
op = row_63B4A.operator('sna.gen_round_room_a43ca', text='Generate Room', icon_value=string_to_icon(
'SHAPEKEY_DATA'), emboss=True, depress=False)
class SNA_OT_Gen_Room_1803A(bpy.types.Operator):
bl_idname = "sna.gen_room_1803a"
bl_label = "Gen room"
bl_description = "Create room"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
if bpy.app.version >= (3, 0, 0) and True:
cls.poll_message_set('')
return not False
def execute(self, context):
room_width = bpy.context.scene.sna_room_width
room_depth = bpy.context.scene.sna_room_depth
room_height = bpy.context.scene.sna_room_height
wall_thickness = bpy.context.scene.sna_wall_thickness
window_placement = bpy.context.scene.sna_windows_enum
window_count = bpy.context.scene.sna_windows_count
window_width_pct = bpy.context.scene.sna_windows_width
window_height_pct = bpy.context.scene.sna_windows_height
window_type = bpy.context.scene.sna_window_style
import math
# 确保大的值为room_depth小的值为room_width
if room_width > room_depth:
# 交换值
room_width, room_depth = room_depth, room_width
print(f"自动调整房间尺寸:宽度={room_width}m深度={room_depth}m")
# 在generate_room函数中将拱门生成函数定义在调用之前
def generate_room(
room_width, room_depth, room_height, wall_thickness,
window_placement, window_count, window_width_pct, window_height_pct,
window_type='SQUARE', window_divisions=2,
sash_bottom_pct=0.55):
"""
1) Make floor & walls as separate cubes (scale applied)
2) Carve windows from each wall (cutters & panes all apply scale)
3) Boolean-union walls into the floor
4) Join sash-frame bits, then weld stray verts
5) Add ceiling (天花板)
6) Add arch door (拱门)
"""
extra = 0.01
inset = (wall_thickness - extra) / 2.0
glass_thickness = extra
ceiling_thickness = 0.10 # 天花板厚度10cm
# 拱门生成函数定义在generate_room内部但在使用之前
def add_arch_to_room(room_obj, room_width, room_depth, room_height, wall_thickness):
"""创建独立的拱门墙面"""
arch_placement = bpy.context.scene.sna_arch_placement
# 如果选择"无",则不生成拱门
if arch_placement == 'NONE':
print("未选择拱门位置,跳过拱门生成")
return
# 根据规则计算拱门参数
# 基础空间5x3x2.8拱门距背景墙1米空间每加1米长距离增加30cm
base_distance = 1.0 # 基础距离1米
distance_increase = 0.3 # 每增加1米距离增加30cm
extra_length = room_depth - 5.0 # 超出基础5米的长度不是3米
arch_distance = base_distance + \
(extra_length * distance_increase)
# 门洞宽度基础1.2米空间宽度每加1米门洞宽度+30cm
base_width = 1.2 # 基础宽度1.2米
width_increase = 0.3 # 每增加1米宽度增加30cm
extra_width = room_width - 3.0 # 超出基础3米的宽度不是5米
arch_width = base_width + (extra_width * width_increase)
# 门洞高度:根据层高确定
arch_height = 2.4 # 默认2.4米
if room_height >= 3.2:
arch_height = 2.7
elif room_height >= 3.0:
arch_height = 2.6
elif room_height >= 2.8:
arch_height = 2.4
# 拱门墙面厚度
arch_wall_thickness = 0.10 # 10cm厚
print(f"拱门墙面参数计算:")
print(f" 房间尺寸: {room_width}x{room_depth}x{room_height}")
print(f" 拱门墙面距离背景墙: {arch_distance}m")
print(f" 拱门宽度: {arch_width}m")
print(f" 拱门高度: {arch_height}m")
print(f" 拱门墙面厚度: {arch_wall_thickness}m")
# 创建拱门墙面(复制并移动选择的墙面)
arch_wall = create_arch_wall_by_copy(
room_width, room_depth, room_height, arch_wall_thickness, arch_placement, arch_distance)
if arch_wall:
# 在拱门墙面上挖洞
# 增加切割器厚度,确保能完全穿透拱门墙面
arch_cutter = create_arch_cutter(
arch_width, arch_height, arch_wall_thickness + 0.05) # 增加厚度确保穿透
if arch_cutter:
# 设置拱门切割器位置(相对于拱门墙面)
if arch_placement == 'BACK':
# 在后墙前面创建拱门墙面,在墙面上挖洞
# 拱门墙面位置:(0, room_depth - distance - wall_thickness, 0)
# 切割器应该在拱门墙面中心:(room_width/2, room_depth - distance - wall_thickness, 0)
arch_cutter.location = (
room_width/2, # X居中
room_depth - arch_distance - arch_wall_thickness +
arch_wall_thickness/2, # Y在拱门墙面中心多加半个墙体厚度
0 # Z从地面开始
)
# 参考COPY4的旋转设置绕Y轴旋转90度使其垂直于墙面
arch_cutter.rotation_euler = (0, 0, 0)
# 使用布尔运算在拱门墙面上挖洞
print(
f"开始布尔运算,拱门墙面: {arch_wall.name}, 切割器: {arch_cutter.name}")
print(
f"拱门墙面位置: {arch_wall.location}, 切割器位置: {arch_cutter.location}")
print(
f"拱门墙面尺寸: {arch_wall.dimensions}, 切割器尺寸: {arch_cutter.dimensions}")
# 确保切割器完全穿透拱门墙面
mod = arch_wall.modifiers.new("ArchCut", 'BOOLEAN')
mod.operation = 'DIFFERENCE'
mod.object = arch_cutter
bpy.context.view_layer.objects.active = arch_wall
bpy.ops.object.modifier_apply(modifier=mod.name)
print(f"布尔运算完成")
# 删除拱门切割器对象
bpy.data.objects.remove(arch_cutter, do_unlink=True)
print(f"拱门墙面已创建并挖洞,位置: {arch_placement}")
def create_arch_wall_by_copy(room_width, room_depth, room_height, wall_thickness, placement, distance):
"""通过复制墙面创建拱门墙面"""
# 创建墙面几何体
bm = bmesh.new()
# 房间外轮廓尺寸(包含墙体)
room_outer_width = room_width # 直接使用房间宽度,不加墙体厚度
room_outer_depth = room_depth + wall_thickness
room_inner_height = room_height
if placement == 'BACK':
# 复制后墙并前移
# 后墙尺寸:宽度=房间宽度,高度=房间内高度,厚度=10cm
wall_width = room_outer_width
wall_height = room_inner_height
wall_depth = wall_thickness
# 拱门墙面位置在Y=distance处距背景墙distance米
# 创建墙面顶点(从原点开始,然后移动到正确位置)
verts = [
(0, 0, 0), # 左下后(地面顶面)
(wall_width, 0, 0), # 右下后(地面顶面)
(wall_width, wall_depth, 0), # 右下前(地面顶面)
(0, wall_depth, 0), # 左下前(地面顶面)
(0, 0, room_inner_height), # 左上后(天花板底面)
(wall_width, 0, room_inner_height), # 右上后(天花板底面)
(wall_width, wall_depth, room_inner_height), # 右上前(天花板底面)
(0, wall_depth, room_inner_height) # 左上前(天花板底面)
]
# 添加顶点到bmesh
bm_verts = [bm.verts.new(v) for v in verts]
bm.verts.ensure_lookup_table()
# 创建面
faces = [
[0, 1, 2, 3], # 底面
[4, 5, 6, 7], # 顶面
[0, 4, 7, 3], # 左面
[1, 5, 6, 2], # 右面
[3, 7, 6, 2], # 前面
[0, 4, 5, 1] # 后面
]
for face_verts in faces:
bm.faces.new([bm_verts[i] for i in face_verts])
# 创建网格对象
mesh = bpy.data.meshes.new("ArchWallMesh")
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new("ArchWall", mesh)
bpy.context.collection.objects.link(obj)
# 设置拱门墙面位置
if placement == 'BACK':
# 背景墙位置Y=room_depth
# 拱门墙面位置Y=room_depth - distance
# 调整向原点方向移动半个墙面厚度使背面距背景墙前面distance米
obj.location = (0, room_depth - distance -
wall_thickness, 0)
return obj
def create_arch_cutter(width, height, thickness, segments=32):
"""创建拱门切割器垂直XY面"""
bm = bmesh.new()
radius = width / 2
wall_height = height - radius # 墙体部分高度
# 创建拱门轮廓点在XZ平面上
profile_verts = []
# 底部矩形部分 - 从左下角开始,逆时针,从地面以下开始,确保完全穿透
profile_verts.extend([
(-radius, 0, -thickness/2), # 左下(地面以下)
(radius, 0, -thickness/2), # 右下(地面以下)
(radius, 0, wall_height), # 右上(墙体顶部)
(-radius, 0, wall_height) # 左上(墙体顶部)
])
# 顶部半圆部分 - 从右到左创建半圆顶点
# 注意这里需要从右到左所以角度从0到π
for i in range(segments + 1):
angle = math.pi * i / segments # 从0到π
x = radius * math.cos(angle)
z = wall_height + radius * math.sin(angle)
profile_verts.append((x, 0, z))
# 拉伸创建3D几何体沿Y轴拉伸
extrude_profile_to_3d_vertical(
bm, profile_verts, thickness, radius, wall_height)
# 创建网格对象
mesh = bpy.data.meshes.new("ArchCutterMesh")
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new("ArchCutter", mesh)
bpy.context.collection.objects.link(obj)
return obj
def extrude_profile_to_3d_vertical(bm, profile_verts, thickness, radius, wall_height):
"""将2D轮廓沿Y轴拉伸为3D几何体垂直XY面"""
# 创建前后面
front_verts = []
back_verts = []
for x, y, z in profile_verts:
front_verts.append(bm.verts.new((x, thickness/2, z)))
back_verts.append(bm.verts.new((x, -thickness/2, z)))
bm.verts.ensure_lookup_table()
# 创建前面
bm.faces.new(front_verts)
# 创建后面
bm.faces.new(back_verts[::-1]) # 反转顺序以保持法向一致
# 创建侧面 - 但不创建矩形和半圆之间的连接面
for i in range(len(profile_verts) - 1):
v1_front = front_verts[i]
v2_front = front_verts[i + 1]
v1_back = back_verts[i]
v2_back = back_verts[i + 1]
# 检查是否是矩形和半圆之间的连接
# 矩形顶部Z = wall_height, X = ±radius
# 半圆底部Z = wall_height, X = ±radius
# 如果两个顶点都在矩形顶部,则不创建面
if (abs(v1_front.co.z - wall_height) < 0.001 and
abs(v2_front.co.z - wall_height) < 0.001 and
abs(v1_front.co.x) < radius + 0.001 and
abs(v2_front.co.x) < radius + 0.001):
continue # 跳过这个面
bm.faces.new([v1_front, v2_front, v2_back, v1_back])
# 连接首尾
v1_front = front_verts[-1]
v2_front = front_verts[0]
v1_back = back_verts[-1]
v2_back = back_verts[0]
bm.faces.new([v1_front, v2_front, v2_back, v1_back])
def create_semicircle_arch_geometry(width, height, thickness, segments=32):
"""创建半圆拱门几何体(保留用于其他用途)"""
bm = bmesh.new()
radius = width / 2
wall_height = height - radius # 墙体部分高度
# 创建拱门轮廓点
profile_verts = []
# 底部矩形部分
profile_verts.extend([
(-radius, 0, 0), (radius, 0, 0),
(radius, wall_height, 0), (-radius, wall_height, 0)
])
# 顶部半圆部分
for i in range(segments + 1):
angle = math.pi * i / segments
x = radius * math.cos(angle)
y = wall_height + radius * math.sin(angle)
profile_verts.append((x, y, 0))
# 拉伸创建3D几何体
extrude_profile_to_3d(bm, profile_verts, thickness)
# 创建网格对象
mesh = bpy.data.meshes.new("SemicircleArchMesh")
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new("SemicircleArch", mesh)
bpy.context.collection.objects.link(obj)
return obj
def extrude_profile_to_3d(bm, profile_verts, thickness):
"""将2D轮廓拉伸为3D几何体保留用于其他用途"""
# 创建前后面
front_verts = []
back_verts = []
for x, y, z in profile_verts:
front_verts.append(bm.verts.new((x, y, thickness/2)))
back_verts.append(bm.verts.new((x, y, -thickness/2)))
bm.verts.ensure_lookup_table()
# 创建前面
bm.faces.new(front_verts)
# 创建后面
bm.faces.new(back_verts[::-1]) # 反转顺序以保持法向一致
# 创建侧面
for i in range(len(profile_verts) - 1):
v1_front = front_verts[i]
v2_front = front_verts[i + 1]
v1_back = back_verts[i]
v2_back = back_verts[i + 1]
bm.faces.new([v1_front, v2_front, v2_back, v1_back])
# 连接首尾
v1_front = front_verts[-1]
v2_front = front_verts[0]
v1_back = back_verts[-1]
v2_back = back_verts[0]
bm.faces.new([v1_front, v2_front, v2_back, v1_back])
# — Glass material setup —
def get_glass_mat():
mat = bpy.data.materials.get("IRG_GlassMaterial")
if not mat:
mat = bpy.data.materials.new("IRG_GlassMaterial")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
for n in list(nodes):
nodes.remove(n)
out = nodes.new(type='ShaderNodeOutputMaterial')
mix = nodes.new(type='ShaderNodeMixShader')
fres = nodes.new(type='ShaderNodeFresnel')
refr = nodes.new(type='ShaderNodeBsdfRefraction')
gloss = nodes.new(type='ShaderNodeBsdfGlossy')
fres.inputs['IOR'].default_value = 1.5
refr.inputs['IOR'].default_value = 1.5
refr.inputs['Roughness'].default_value = 0.0
gloss.inputs['Roughness'].default_value = 0.0
fres.location = (-400, 200)
refr.location = (-200, 300)
gloss.location = (-200, 100)
mix.location = (0, 200)
out.location = (200, 200)
links.new(fres.outputs['Fac'], mix.inputs['Fac'])
links.new(refr.outputs['BSDF'], mix.inputs[1])
links.new(gloss.outputs['BSDF'], mix.inputs[2])
links.new(mix.outputs['Shader'], out.inputs['Surface'])
return mat
glass_mat = get_glass_mat()
# 1) Create floor & walls, then apply scale
def make_cube(name, loc, scale):
bpy.ops.mesh.primitive_cube_add(location=loc)
o = bpy.context.active_object
o.name = name
o.scale = scale
bpy.ops.object.transform_apply(
location=False, rotation=False, scale=True)
return o
# 房间内空尺寸(用户输入的尺寸)
room_inner_width = room_width
room_inner_depth = room_depth
room_inner_height = room_height
# 房间外轮廓尺寸(包含墙体)
room_outer_width = room_inner_width + wall_thickness # 只有一边有墙
room_outer_depth = room_inner_depth + wall_thickness # 只有一边有墙
room_outer_height = room_inner_height + 2 * wall_thickness # 上下都有墙(地面+天花板)
floor = make_cube("Floor",
# 地面顶面在Z=0底面在Z=-wall_thickness
(room_outer_width/2,
room_outer_depth/2, -wall_thickness/2),
(room_outer_width/2, room_outer_depth/2, wall_thickness/2))
# 创建天花板
ceiling = make_cube("Ceiling",
(room_outer_width/2, room_outer_depth/2,
# 天花板底面在Z=room_height顶面在Z=room_height+wall_thickness
room_height + wall_thickness/2),
(room_outer_width/2, room_outer_depth/2, wall_thickness/2))
back_wall = make_cube("BackWall",
(room_outer_width/2, room_outer_depth -
wall_thickness/2, room_height/2),
(room_outer_width/2, wall_thickness/2, room_height/2))
# 生成X=0位置的侧墙垂直XZ面
side_wall = make_cube("SideWall",
(wall_thickness/2,
room_outer_depth/2, room_height/2),
(wall_thickness/2, room_outer_depth/2, room_height/2))
# 2) Window-cut helpers with scaleapply
def cube_cut(loc, sx, sy, sz):
bpy.ops.mesh.primitive_cube_add(location=loc)
o = bpy.context.active_object
o.scale = (sx, sy, sz)
bpy.ops.object.transform_apply(
location=False, rotation=False, scale=True)
return o
def cyl_cut(loc, dia, depth, rot):
bpy.ops.mesh.primitive_cylinder_add(
vertices=64, radius=dia/2, depth=depth,
location=loc, rotation=rot
)
o = bpy.context.active_object
# scale is baked into radius/depth above, so just apply
bpy.ops.object.transform_apply(
location=False, rotation=False, scale=True)
return o
# carve windows out of a wall
def add_windows(wall, axis, wall_len):
frames = []
glasses = []
margin = wall_len * 0.1
slot = (wall_len - 2*margin) / window_count
for i in range(window_count):
win_h = room_height * window_height_pct
z0 = room_height/2
if axis == 'X':
x0 = margin + (i+0.5)*slot
y0 = room_depth - wall_thickness/2
else:
y0 = margin + (i+0.5)*slot
x0 = room_width - wall_thickness/2
win_w = slot * window_width_pct
pos = (x0, y0, z0)
depth_cut = wall_thickness + extra
# Boolean cutter
if window_type == 'ROUND':
dia = min(win_w, win_h)
rot = (math.pi/2, 0, 0) if axis == 'X' else (
math.pi/2, 0, math.pi/2)
cutter = cyl_cut(pos, dia, depth_cut, rot)
else:
sx = win_w/2 if axis == 'X' else depth_cut/2
sy = depth_cut/2 if axis == 'X' else win_w/2
sz = win_h/2
cutter = cube_cut(pos, sx, sy, sz)
# subtract cutter
mod = wall.modifiers.new(f"Win_{axis}_{i}", 'BOOLEAN')
mod.operation = 'DIFFERENCE'
mod.object = cutter
bpy.context.view_layer.objects.active = wall
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(cutter, do_unlink=True)
# add glass panes, name & apply scale
if window_type == 'ROUND':
glass_loc = (
x0, y0-(wall_thickness/2-glass_thickness/2), z0
) if axis == 'X' else (
x0-(wall_thickness/2-glass_thickness/2), y0, z0
)
g = cyl_cut(glass_loc, dia, glass_thickness, rot)
g.name = "IRG_IsoWindow"
g.data.materials.append(glass_mat)
glasses.append(g)
continue
# sash or single pane
if window_type == 'SASH' and window_divisions == 2:
ft = min(win_w, win_h)*0.08
bottom_h = win_h * sash_bottom_pct
top_h = win_h - bottom_h - ft
# bottom pane
bot_cz = z0 - win_h/2 + bottom_h/2
loc_b = (x0, y0-(wall_thickness/2-glass_thickness/2), bot_cz) \
if axis == 'X' else \
(x0-(wall_thickness/2-glass_thickness/2), y0, bot_cz)
g_bot = cube_cut(loc_b,
*((win_w/2, glass_thickness/2, bottom_h/2)
if axis == 'X'
else (glass_thickness/2, win_w/2, bottom_h/2)))
g_bot.name = "IRG_IsoWindow"
g_bot.data.materials.append(glass_mat)
glasses.append(g_bot)
# divider
z_div = z0 - win_h/2 + bottom_h + ft/2
d = cube_cut((x0, y0, z_div),
*((win_w/2, depth_cut/2, ft/2)
if axis == 'X'
else (depth_cut/2, win_w/2, ft/2)))
frames.append(d)
# top pane
top_cz = z0 - win_h/2 + bottom_h + ft + top_h/2
loc_t = (x0, y0-(wall_thickness/2-glass_thickness/2), top_cz) \
if axis == 'X' else \
(x0-(wall_thickness/2-glass_thickness/2), y0, top_cz)
g_top = cube_cut(loc_t,
*((win_w/2, glass_thickness/2, top_h/2)
if axis == 'X'
else (glass_thickness/2, win_w/2, top_h/2)))
g_top.name = "IRG_IsoWindow"
g_top.data.materials.append(glass_mat)
glasses.append(g_top)
else:
# single pane
loc_g = (x0, y0-(wall_thickness/2-glass_thickness/2), z0) \
if axis == 'X' else \
(x0-(wall_thickness/2-glass_thickness/2), y0, z0)
g = cube_cut(loc_g,
*((win_w/2, glass_thickness/2, win_h/2)
if axis == 'X'
else (glass_thickness/2, win_w/2, win_h/2)))
g.name = "IRG_IsoWindow"
g.data.materials.append(glass_mat)
glasses.append(g)
return frames, glasses
# carve windows
frame_objs = []
if window_placement in {'BACK', 'BOTH'}:
f, _ = add_windows(back_wall, 'X', room_width)
frame_objs += f
if window_placement in {'SIDE', 'BOTH'}:
# 改为'X'轴因为对面墙也是X轴方向
f, _ = add_windows(side_wall, 'X', room_width)
frame_objs += f
# 3) 取消墙面与地面合并,保持独立对象
# for wall in (back_wall, side_wall): # 注释掉墙面合并
# mod = floor.modifiers.new(f"Union_{wall.name}", 'BOOLEAN')
# mod.operation = 'UNION'
# mod.object = wall
# bpy.context.view_layer.objects.active = floor
# bpy.ops.object.modifier_apply(modifier=mod.name)
# bpy.data.objects.remove(wall, do_unlink=True)
# 4) 取消窗户框架合并,保持独立对象
# bpy.ops.object.select_all(action='DESELECT')
# floor.select_set(True)
# for o in frame_objs:
# o.select_set(True)
# bpy.context.view_layer.objects.active = floor
# bpy.ops.object.join()
# floor.name = "IRG_IsoRoom"
# 5) Final weld + normals (只对地面进行)
bpy.context.view_layer.objects.active = floor
bpy.ops.object.mode_set(mode='EDIT')
try:
bpy.ops.mesh.merge_by_distance(distance=0.001)
except AttributeError:
bpy.ops.mesh.remove_doubles(
threshold=0.001, use_unselected=False)
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set(mode='OBJECT')
# 6) 取消天花板与房间主体合并,保持独立对象
# bpy.ops.object.select_all(action='DESELECT')
# floor.select_set(True)
# ceiling.select_set(True)
# bpy.context.view_layer.objects.active = floor
# bpy.ops.object.join()
# floor.name = "IRG_IsoRoom_WithCeiling"
# 7) 添加拱门
if bpy.context.scene.sna_arch_settings:
add_arch_to_room(floor, room_width, room_depth,
room_height, wall_thickness)
# 8) 最终清理(只对地面进行)
bpy.context.view_layer.objects.active = floor
bpy.ops.object.mode_set(mode='EDIT')
try:
bpy.ops.mesh.merge_by_distance(distance=0.001)
except AttributeError:
bpy.ops.mesh.remove_doubles(
threshold=0.001, use_unselected=False)
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set(mode='OBJECT')
# 9) 将整个房间在X轴负方向上位移墙厚度的距离
floor.location.x -= wall_thickness
back_wall.location.x -= wall_thickness
side_wall.location.x -= wall_thickness
ceiling.location.x -= wall_thickness
print(f"房间已向X轴负方向位移 {wall_thickness}m")
generate_room(room_width, room_depth, room_height,
wall_thickness, window_placement,
window_count, window_width_pct,
window_height_pct, window_type,
window_divisions=2, sash_bottom_pct=0.55)
return {"FINISHED"}
def invoke(self, context, event):
return self.execute(context)
class SNA_OT_Gen_Round_Room_A43Ca(bpy.types.Operator):
bl_idname = "sna.gen_round_room_a43ca"
bl_label = "Gen Round Room"
bl_description = "Create Round Iso Room"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
if bpy.app.version >= (3, 0, 0) and True:
cls.poll_message_set('')
return not False
def execute(self, context):
diameter = bpy.context.scene.sna_room_diameter_round
floor_thickness = bpy.context.scene.sna_room_floor_thickness_round
wall_height = bpy.context.scene.sna_room_height_round
wall_thickness = bpy.context.scene.sna_wall_thickness_round
window_count = bpy.context.scene.sna_round_window_amount
window_width_pct = bpy.context.scene.sna_round_window_width
window_height_pct = bpy.context.scene.sna_round_window_height
window_type = bpy.context.scene.sna_window_style_round
def get_glass_mat():
mat = bpy.data.materials.get("IRG_GlassMaterial")
if not mat:
mat = bpy.data.materials.new("IRG_GlassMaterial")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
for n in list(nodes):
nodes.remove(n)
out = nodes.new('ShaderNodeOutputMaterial')
mix = nodes.new('ShaderNodeMixShader')
fres = nodes.new('ShaderNodeFresnel')
refr = nodes.new('ShaderNodeBsdfRefraction')
gloss = nodes.new('ShaderNodeBsdfGlossy')
fres.inputs['IOR'].default_value = 1.5
refr.inputs['IOR'].default_value = 1.5
refr.inputs['Roughness'].default_value = 0.0
gloss.inputs['Roughness'].default_value = 0.0
links.new(fres.outputs['Fac'], mix.inputs['Fac'])
links.new(refr.outputs['BSDF'], mix.inputs[1])
links.new(gloss.outputs['BSDF'], mix.inputs[2])
links.new(mix.outputs['Shader'], out.inputs['Surface'])
return mat
def make_curved_glass(name, θ_center, arc_angle, radius, height, segments, z_offset, mat=None):
bm = bmesh.new()
verts_top = []
verts_bottom = []
for i in range(segments + 1):
θ = θ_center - arc_angle / 2 + i * (arc_angle / segments)
x = radius * math.cos(θ)
y = radius * math.sin(θ)
verts_top.append(bm.verts.new((x, y, z_offset + height / 2)))
verts_bottom.append(bm.verts.new(
(x, y, z_offset - height / 2)))
bm.verts.ensure_lookup_table()
for i in range(segments):
bm.faces.new(
(verts_top[i], verts_bottom[i], verts_bottom[i+1], verts_top[i+1]))
mesh = bpy.data.meshes.new(name)
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new(name, mesh)
bpy.context.collection.objects.link(obj)
obj.location.z = height / 2
if mat:
obj.data.materials.append(mat)
return obj
def generate_round_room(
diameter, floor_thickness, wall_height, wall_thickness,
window_count, window_width_pct, window_height_pct,
window_type='SQUARE', sash_bottom_pct=0.55
):
extra = 0.01
R_out = diameter / 2.0
R_mid = R_out - wall_thickness / 2.0
glass_off = extra / 2.0
glass_mat = get_glass_mat()
def make_cylinder(name, radius, depth, loc, rot=(0, 0, 0), verts=64):
bpy.ops.mesh.primitive_cylinder_add(
vertices=verts, radius=radius, depth=depth, location=loc, rotation=rot)
o = bpy.context.active_object
o.name = name
bpy.ops.object.transform_apply(
location=False, rotation=False, scale=True)
return o
def make_cube(name, loc, half_extents, rot=(0, 0, 0)):
bpy.ops.mesh.primitive_cube_add(location=loc)
o = bpy.context.active_object
o.name = name
o.scale = half_extents
o.rotation_euler = rot
bpy.ops.object.transform_apply(
location=False, rotation=True, scale=True)
return o
floor = make_cylinder(
"Floor", R_out, floor_thickness, (0, 0, floor_thickness/2))
wall = make_cylinder("WallOuter", R_out,
wall_height, (0, 0, wall_height/2))
inner = make_cylinder(
"WallInner", R_out-wall_thickness, wall_height+extra, (0, 0, wall_height/2))
mod = wall.modifiers.new("HollowWall", 'BOOLEAN')
mod.operation, mod.object = 'DIFFERENCE', inner
bpy.context.view_layer.objects.active = wall
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(inner, do_unlink=True)
cutter = make_cube(
"FrontCutter", (0, R_out, wall_height/2), (2*R_out, R_out, wall_height))
mod = wall.modifiers.new("CutFront", 'BOOLEAN')
mod.operation, mod.object = 'DIFFERENCE', cutter
bpy.context.view_layer.objects.active = wall
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(cutter, do_unlink=True)
panes = []
# Only generate windows if applicable
if window_type.upper() in {'SQUARE', 'SASH'} and window_count > 0:
start_ang, end_ang = math.pi, 2*math.pi
seg = (end_ang - start_ang) / window_count
for i in range(window_count):
θ = start_ang + (i+0.5)*seg
x, y = R_mid * math.cos(θ), R_mid * math.sin(θ)
z = wall_height / 2
win_h = wall_height * window_height_pct
win_ang = seg * window_width_pct
chord = 2 * R_mid * math.sin(win_ang / 2)
cut = make_cube(
f"Cut_{i}", (x, y, z), (wall_thickness, chord/2 + extra, win_h/2 + extra), rot=(0, 0, θ))
mod = wall.modifiers.new(f"WinBool_{i}", 'BOOLEAN')
mod.operation, mod.object = 'DIFFERENCE', cut
bpy.context.view_layer.objects.active = wall
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(cut, do_unlink=True)
objs = []
if window_type.upper() == 'SASH':
ft = min(chord, win_h) * 0.08
bot_h = win_h * sash_bottom_pct
top_h = win_h - bot_h - ft
bz = z - win_h/2
bot = make_curved_glass(
f"IRG_IsoRoom_Window_SashBot_{i}", θ, win_ang, R_out - glass_off, bot_h, 16, bz, glass_mat)
objs.append(bot)
dz = bz + bot_h
bar = make_curved_glass(
f"IRG_IsoRoom_Window_SashBar_{i}", θ, win_ang, R_out - glass_off, ft, 16, dz, None)
objs.append(bar)
tz = dz + ft
top = make_curved_glass(
f"IRG_IsoRoom_Window_SashTop_{i}", θ, win_ang, R_out - glass_off, top_h, 16, tz, glass_mat)
objs.append(top)
if window_type.upper() == 'SQUARE':
pane = make_curved_glass(
f"IRG_IsoRoom_Window_Glass_{i}", θ, win_ang, R_out - glass_off, win_h, 16, z - win_h/2, glass_mat)
objs.append(pane)
panes.extend(objs)
mod = floor.modifiers.new("WallUnion", 'BOOLEAN')
mod.operation, mod.object = 'UNION', wall
bpy.context.view_layer.objects.active = floor
bpy.ops.object.modifier_apply(modifier=mod.name)
bpy.data.objects.remove(wall, do_unlink=True)
me = floor.data
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
bm.normal_update()
bm.to_mesh(me)
bm.free()
floor.name = "IRG_IsoRoom_Round"
return floor, panes
generate_round_room(
diameter,
floor_thickness,
wall_height,
wall_thickness,
window_count,
window_width_pct,
window_height_pct,
window_type
)
return {"FINISHED"}
def invoke(self, context, event):
return self.execute(context)
def register():
global _icons
_icons = bpy.utils.previews.new()
bpy.types.Scene.sna_room_width = bpy.props.FloatProperty(
name='Room Width', description='', default=1.0, subtype='NONE', unit='NONE', min=0.10000000149011612, step=3, precision=2)
bpy.types.Scene.sna_room_depth = bpy.props.FloatProperty(
name='Room Depth', description='', default=1.0, subtype='NONE', unit='NONE', min=0.10000000149011612, step=3, precision=2)
bpy.types.Scene.sna_room_height = bpy.props.FloatProperty(
name='Room Height', description='', default=1.0, subtype='NONE', unit='NONE', min=0.10000000149011612, step=3, precision=2)
bpy.types.Scene.sna_wall_thickness = bpy.props.FloatProperty(
name='Wall Thickness', description='', default=0.10000000149011612, subtype='NONE', unit='NONE', min=0.009999999776482582, step=3, precision=2)
bpy.types.Scene.sna_windows_enum = bpy.props.EnumProperty(name='Windows Enum', description='', items=[('NONE', 'NONE', 'None', 0, 0), (
'BACK', 'BACK', 'Back Wall Only', 0, 1), ('SIDE', 'SIDE', 'Side Wall Only', 0, 2), ('BOTH', 'BOTH', 'Both Walls', 0, 3)])
bpy.types.Scene.sna_windows_count = bpy.props.IntProperty(
name='Windows Count', description='', default=1, subtype='NONE', min=1, max=5)
bpy.types.Scene.sna_windows_width = bpy.props.FloatProperty(
name='Windows Width', description='', default=0.25, subtype='NONE', unit='NONE', min=0.10000000149011612, max=1.0, step=3, precision=2)
bpy.types.Scene.sna_windows_height = bpy.props.FloatProperty(
name='Windows Height', description='', default=0.25, subtype='NONE', unit='NONE', min=0.10000000149011612, max=1.0, step=3, precision=2)
bpy.types.Scene.sna_window_style = bpy.props.EnumProperty(name='Window Style', description='', items=[(
'SQUARE', 'SQUARE', 'SQUARE', 0, 0), ('ROUND', 'ROUND', 'ROUND', 0, 1), ('SASH', 'SASH', 'SASH', 0, 2)])
bpy.types.Scene.sna_room_settings = bpy.props.BoolProperty(
name='Room Settings', description='', default=False)
bpy.types.Scene.sna_winows_settings = bpy.props.BoolProperty(
name='Winows settings', description='', default=False)
bpy.types.Scene.sna_style = bpy.props.EnumProperty(name='Style', description='', items=[(
'Square', 'Square', 'Square Room', 0, 0), ('Round', 'Round', 'Round Room', 0, 1)])
# 添加拱门相关属性(移除前墙选项)
bpy.types.Scene.sna_arch_settings = bpy.props.BoolProperty(
name='Arch Settings', description='', default=False)
bpy.types.Scene.sna_arch_placement = bpy.props.EnumProperty(
name='Arch Placement', description='',
items=[('NONE', 'None', 'No Arch', 0, 0),
('BACK', 'Back Wall', 'Back Wall', 0, 1)])
bpy.types.Scene.sna_arch_width = bpy.props.FloatProperty(
name='Arch Width', description='', default=1.2, subtype='NONE', unit='NONE', min=0.5, max=3.0, step=3, precision=2)
bpy.types.Scene.sna_arch_height = bpy.props.FloatProperty(
name='Arch Height', description='', default=2.4, subtype='NONE', unit='NONE', min=1.5, max=4.0, step=3, precision=2)
bpy.types.Scene.sna_arch_thickness = bpy.props.FloatProperty(
name='Arch Thickness', description='', default=0.10, subtype='NONE', unit='NONE', min=0.05, max=0.5, step=3, precision=2)
bpy.types.Scene.sna_round_room_settings = bpy.props.BoolProperty(
name='Round Room Settings', description='', default=False)
bpy.types.Scene.sna_round_window_settings = bpy.props.BoolProperty(
name='Round Window Settings', description='', default=False)
bpy.types.Scene.sna_room_diameter_round = bpy.props.FloatProperty(
name='Room diameter Round', description='', default=10.0, subtype='NONE', unit='NONE', min=0.10000000149011612, step=3, precision=2)
bpy.types.Scene.sna_room_floor_thickness_round = bpy.props.FloatProperty(
name='Room Floor thickness Round', description='', default=0.10000000149011612, subtype='NONE', unit='NONE', min=0.10000000149011612, step=3, precision=2)
bpy.types.Scene.sna_room_height_round = bpy.props.FloatProperty(
name='Room Height Round', description='', default=3.0, subtype='NONE', unit='NONE', min=0.10000000149011612, step=3, precision=2)
bpy.types.Scene.sna_wall_thickness_round = bpy.props.FloatProperty(
name='Wall Thickness round', description='', default=0.20000000298023224, subtype='NONE', unit='NONE', min=0.009999999776482582, step=3, precision=2)
bpy.types.Scene.sna_window_style_round = bpy.props.EnumProperty(name='Window Style Round', description='', items=[(
'NONE', 'NONE', 'No Windows', 0, 0), ('SQUARE', 'SQUARE', 'Square Windows', 0, 1), ('SASH', 'SASH', 'Sash Windows', 0, 2)])
bpy.types.Scene.sna_round_window_amount = bpy.props.IntProperty(
name='Round Window Amount', description='', default=6, subtype='NONE', min=1)
bpy.types.Scene.sna_round_window_width = bpy.props.FloatProperty(
name='Round Window Width', description='', default=0.699999988079071, subtype='NONE', unit='NONE', min=0.10000000149011612, max=1.0, step=3, precision=2)
bpy.types.Scene.sna_round_window_height = bpy.props.FloatProperty(
name='Round Window Height', description='', default=0.6000000238418579, subtype='NONE', unit='NONE', min=0.10000000149011612, max=1.0, step=3, precision=2)
bpy.utils.register_class(SNA_PT_IRG_3542C)
bpy.utils.register_class(SNA_OT_Gen_Room_1803A)
bpy.utils.register_class(SNA_OT_Gen_Round_Room_A43Ca)
def unregister():
global _icons
bpy.utils.previews.remove(_icons)
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
for km, kmi in addon_keymaps.values():
km.keymap_items.remove(kmi)
addon_keymaps.clear()
# 清理拱门相关属性
del bpy.types.Scene.sna_arch_thickness
del bpy.types.Scene.sna_arch_height
del bpy.types.Scene.sna_arch_width
del bpy.types.Scene.sna_arch_placement
del bpy.types.Scene.sna_arch_settings
del bpy.types.Scene.sna_round_window_height
del bpy.types.Scene.sna_round_window_width
del bpy.types.Scene.sna_round_window_amount
del bpy.types.Scene.sna_window_style_round
del bpy.types.Scene.sna_wall_thickness_round
del bpy.types.Scene.sna_room_height_round
del bpy.types.Scene.sna_room_floor_thickness_round
del bpy.types.Scene.sna_room_diameter_round
del bpy.types.Scene.sna_round_window_settings
del bpy.types.Scene.sna_round_room_settings
del bpy.types.Scene.sna_style
del bpy.types.Scene.sna_winows_settings
del bpy.types.Scene.sna_room_settings
del bpy.types.Scene.sna_window_style
del bpy.types.Scene.sna_windows_height
del bpy.types.Scene.sna_windows_width
del bpy.types.Scene.sna_windows_count
del bpy.types.Scene.sna_windows_enum
del bpy.types.Scene.sna_wall_thickness
del bpy.types.Scene.sna_room_height
del bpy.types.Scene.sna_room_depth
del bpy.types.Scene.sna_room_width
bpy.utils.unregister_class(SNA_PT_IRG_3542C)
bpy.utils.unregister_class(SNA_OT_Gen_Room_1803A)
bpy.utils.unregister_class(SNA_OT_Gen_Round_Room_A43Ca)