irg/__init__.py

1268 lines
62 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.

# 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)