mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-20 13:22:58 +00:00

Fixes T67048: Adding archimesh stairs object to the scene flips normals of all other objects in scene Reviewers: antoniov, brecht Maniphest Tasks: T67048 Differential Revision: https://developer.blender.org/D5282
471 lines
18 KiB
Python
471 lines
18 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# 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
|
|
# MERCHANTABILITY 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, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
# <pep8 compliant>
|
|
|
|
# ----------------------------------------------------------
|
|
# Automatic generation of shelves
|
|
# Author: Antonio Vazquez (antonioya)
|
|
#
|
|
# ----------------------------------------------------------
|
|
import bpy
|
|
from copy import copy
|
|
from bpy.types import Operator, PropertyGroup
|
|
from bpy.props import FloatProperty, BoolProperty, IntProperty, CollectionProperty, EnumProperty
|
|
from .achm_tools import *
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Define property group class for shelves properties
|
|
# ------------------------------------------------------------------
|
|
class ShelvesProperties(PropertyGroup):
|
|
sX: FloatProperty(name='width', min=0.001, max=10, default=1,
|
|
precision=3, description='Furniture width')
|
|
wY: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Modify y size')
|
|
wZ: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Modify z size')
|
|
# Cabinet position shift
|
|
pX: FloatProperty(name='', min=0, max=10, default=0, precision=3, description='Position x shift')
|
|
pY: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Position y shift')
|
|
pZ: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
|
|
# Shelves
|
|
sNum: IntProperty(name='Shelves', min=0, max=12, default=6, description='Number total of shelves')
|
|
|
|
# 12 shelves (shelf)
|
|
Z01: FloatProperty(name='zS1', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z02: FloatProperty(name='zS2', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z03: FloatProperty(name='zS3', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z04: FloatProperty(name='zS4', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z05: FloatProperty(name='zS5', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z06: FloatProperty(name='zS6', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z07: FloatProperty(name='zS7', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z08: FloatProperty(name='zS8', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z09: FloatProperty(name='zS9', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z10: FloatProperty(name='zS10', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z11: FloatProperty(name='zS11', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
Z12: FloatProperty(name='zS12', min=-10, max=10, default=0, precision=3, description='Position z shift')
|
|
|
|
right: BoolProperty(name="Right", description="Create right side", default=True)
|
|
left: BoolProperty(name="Left", description="Create left side", default=True)
|
|
|
|
bpy.utils.register_class(ShelvesProperties)
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Define UI class
|
|
# Shelves
|
|
# ------------------------------------------------------------------
|
|
class ARCHIMESH_OT_Shelves(Operator):
|
|
bl_idname = "mesh.archimesh_shelves"
|
|
bl_label = "Shelves"
|
|
bl_description = "Shelves Generator"
|
|
bl_category = 'View'
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
thickness: FloatProperty(
|
|
name='Side Thickness', min=0.001, max=5,
|
|
default=0.03, precision=3,
|
|
description='Board thickness',
|
|
)
|
|
sthickness: FloatProperty(
|
|
name='Shelves Thickness', min=0.001, max=5,
|
|
default=0.03, precision=3,
|
|
description='Board thickness',
|
|
)
|
|
depth: FloatProperty(
|
|
name='Depth', min=0.001, max=50,
|
|
default=0.28, precision=3,
|
|
description='Default unit depth',
|
|
)
|
|
height: FloatProperty(
|
|
name='Height', min=0.001, max=50,
|
|
default=2, precision=3,
|
|
description='Default unit height',
|
|
)
|
|
top: FloatProperty(
|
|
name='Top', min=0, max=50,
|
|
default=0.03, precision=3,
|
|
description='Default top shelf position',
|
|
)
|
|
bottom: FloatProperty(
|
|
name='Bottom', min=0, max=50,
|
|
default=0.07, precision=3,
|
|
description='Default bottom self position',
|
|
)
|
|
stype: EnumProperty(
|
|
items=(
|
|
('1', "Full side", ""),
|
|
('4', "4 Legs", ""),
|
|
('99', "None", "")),
|
|
name="Sides",
|
|
description="Type of side construction",
|
|
)
|
|
|
|
fitZ: BoolProperty(
|
|
name="Floor origin in Z=0",
|
|
description="Use Z=0 axis as vertical origin floor position",
|
|
default=True,
|
|
)
|
|
|
|
shelves_num: IntProperty(
|
|
name='Number of Units',
|
|
min=1, max=10,
|
|
default=1,
|
|
description='Number total of shelves units',
|
|
)
|
|
shelves: CollectionProperty(type=ShelvesProperties)
|
|
|
|
# Materials
|
|
crt_mat: BoolProperty(
|
|
name="Create default Cycles materials",
|
|
description="Create default materials for Cycles render",
|
|
default=True,
|
|
)
|
|
|
|
# -----------------------------------------------------
|
|
# Draw (create UI interface)
|
|
# -----------------------------------------------------
|
|
# noinspection PyUnusedLocal
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
space = bpy.context.space_data
|
|
if not space.local_view:
|
|
# Imperial units warning
|
|
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
|
row = layout.row()
|
|
row.label(text="Warning: Imperial units not supported", icon='COLOR_RED')
|
|
|
|
box = layout.box()
|
|
row = box.row()
|
|
row.prop(self, 'thickness')
|
|
row.prop(self, 'sthickness')
|
|
row = box.row()
|
|
row.prop(self, 'depth')
|
|
row.prop(self, 'height')
|
|
row = box.row()
|
|
row.prop(self, 'top')
|
|
row.prop(self, 'bottom')
|
|
row = box.row()
|
|
row.prop(self, 'stype')
|
|
row.prop(self, 'fitZ')
|
|
|
|
# Furniture number
|
|
row = layout.row()
|
|
row.prop(self, 'shelves_num')
|
|
# Add menu for shelves
|
|
if self.shelves_num > 0:
|
|
for idx in range(0, self.shelves_num):
|
|
box = layout.box()
|
|
add_shelves(self, box, idx + 1, self.shelves[idx])
|
|
|
|
box = layout.box()
|
|
if not context.scene.render.engine in {'CYCLES', 'BLENDER_EEVEE'}:
|
|
box.enabled = False
|
|
box.prop(self, 'crt_mat')
|
|
else:
|
|
row = layout.row()
|
|
row.label(text="Warning: Operator does not work in local view mode", icon='ERROR')
|
|
|
|
# -----------------------------------------------------
|
|
# Execute
|
|
# -----------------------------------------------------
|
|
# noinspection PyUnusedLocal
|
|
def execute(self, context):
|
|
if bpy.context.mode == "OBJECT":
|
|
# Create all elements
|
|
for i in range(len(self.shelves) - 1, self.shelves_num):
|
|
self.shelves.add()
|
|
|
|
# Create shelves
|
|
create_shelves_mesh(self)
|
|
return {'FINISHED'}
|
|
else:
|
|
self.report({'WARNING'}, "Archimesh: Option only valid in Object mode")
|
|
return {'CANCELLED'}
|
|
|
|
|
|
# -----------------------------------------------------
|
|
# Add shelves parameters
|
|
# -----------------------------------------------------
|
|
def add_shelves(self, box, num, sh):
|
|
row = box.row()
|
|
row.label(text="Unit " + str(num))
|
|
row.prop(sh, 'sX')
|
|
|
|
row = box.row()
|
|
row.prop(sh, 'wY')
|
|
row.prop(sh, 'wZ')
|
|
if self.stype != "99":
|
|
row.prop(sh, 'left')
|
|
row.prop(sh, 'right')
|
|
|
|
row = box.row()
|
|
row.prop(sh, 'pX')
|
|
row.prop(sh, 'pY')
|
|
row.prop(sh, 'pZ')
|
|
|
|
row = box.row()
|
|
row.prop(sh, 'sNum', slider=True)
|
|
|
|
if sh.sNum >= 1:
|
|
row = box.row()
|
|
row.prop(sh, 'Z01')
|
|
if sh.sNum >= 2:
|
|
row.prop(sh, 'Z02')
|
|
if sh.sNum >= 3:
|
|
row.prop(sh, 'Z03')
|
|
|
|
if sh.sNum >= 4:
|
|
row = box.row()
|
|
row.prop(sh, 'Z04')
|
|
if sh.sNum >= 5:
|
|
row.prop(sh, 'Z05')
|
|
if sh.sNum >= 6:
|
|
row.prop(sh, 'Z06')
|
|
|
|
if sh.sNum >= 7:
|
|
row = box.row()
|
|
row.prop(sh, 'Z07')
|
|
if sh.sNum >= 8:
|
|
row.prop(sh, 'Z08')
|
|
if sh.sNum >= 9:
|
|
row.prop(sh, 'Z09')
|
|
|
|
if sh.sNum >= 10:
|
|
row = box.row()
|
|
row.prop(sh, 'Z10')
|
|
if sh.sNum >= 11:
|
|
row.prop(sh, 'Z11')
|
|
if sh.sNum >= 12:
|
|
row.prop(sh, 'Z12')
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Generate mesh data
|
|
# All custom values are passed using self container (self.myvariable)
|
|
# ------------------------------------------------------------------------------
|
|
def create_shelves_mesh(self):
|
|
# deactivate others
|
|
for o in bpy.data.objects:
|
|
if o.select_get() is True:
|
|
o.select_set(False)
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
# Create units
|
|
generate_shelves(self)
|
|
|
|
return
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Generate Units
|
|
# All custom values are passed using self container (self.myvariable)
|
|
# ------------------------------------------------------------------------------
|
|
def generate_shelves(self):
|
|
|
|
boxes = []
|
|
location = bpy.context.scene.cursor.location
|
|
myloc = copy(location) # copy location to keep 3D cursor position
|
|
# Fit to floor
|
|
if self.fitZ:
|
|
myloc[2] = 0
|
|
|
|
# Create units
|
|
lastx = myloc[0]
|
|
# ------------------------------------------------------------------------------
|
|
# Shelves
|
|
# ------------------------------------------------------------------------------
|
|
for i in range(0, self.shelves_num):
|
|
mydata = create_unit(self.stype, "Shelves" + str(i + 1),
|
|
self.thickness, self.sthickness,
|
|
self.shelves[i].sX, self.depth + self.shelves[i].wY, self.height + self.shelves[i].wZ,
|
|
self.shelves[i].pX + lastx, myloc[1] + self.shelves[i].pY, myloc[2] + self.shelves[i].pZ,
|
|
self.shelves[i].left, self.shelves[i].right,
|
|
self.shelves[i].sNum,
|
|
(self.shelves[i].Z01, self.shelves[i].Z02, self.shelves[i].Z03,
|
|
self.shelves[i].Z04, self.shelves[i].Z05, self.shelves[i].Z06,
|
|
self.shelves[i].Z07, self.shelves[i].Z08, self.shelves[i].Z09,
|
|
self.shelves[i].Z10, self.shelves[i].Z11, self.shelves[i].Z12),
|
|
self.top, self.bottom)
|
|
boxes.extend([mydata[0]])
|
|
lastx = mydata[1]
|
|
|
|
# refine units
|
|
for box in boxes:
|
|
remove_doubles(box)
|
|
set_normals(box)
|
|
|
|
# deactivate others
|
|
for o in bpy.data.objects:
|
|
if o.select_get() is True:
|
|
o.select_set(False)
|
|
|
|
boxes[0].select_set(True)
|
|
bpy.context.view_layer.objects.active = boxes[0]
|
|
|
|
# Create materials
|
|
if self.crt_mat and bpy.context.scene.render.engine in {'CYCLES', 'BLENDER_EEVEE'}:
|
|
mat = create_diffuse_material("Shelves_material", False, 0.8, 0.8, 0.8)
|
|
for box in boxes:
|
|
set_material(box, mat)
|
|
|
|
return
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Create shelves unit
|
|
#
|
|
# stype: type of sides
|
|
# objName: Name for the new object
|
|
# thickness: wood thickness (sides)
|
|
# sthickness: wood thickness (shelves)
|
|
# sX: Size in X axis
|
|
# sY: Size in Y axis
|
|
# sZ: Size in Z axis
|
|
# pX: position X axis
|
|
# pY: position Y axis
|
|
# pZ: position Z axis
|
|
# right: True-> create right side
|
|
# left: True-> create left side
|
|
# shelves: Number of shelves
|
|
# zPos: List with z shift for each self
|
|
# top: position of top shelf
|
|
# bottom: position of bottom shelf
|
|
# ------------------------------------------------------------------------------
|
|
def create_unit(stype, objname, thickness, sthickness, sx, sy, sz, px, py, pz, left, right, shelves, zpos,
|
|
top, bottom):
|
|
|
|
myvertex = []
|
|
myfaces = []
|
|
v = 0
|
|
|
|
# no Sides, then no thickness
|
|
if stype == "99":
|
|
thickness = 0
|
|
|
|
# ------------------------------
|
|
# Left side
|
|
# ------------------------------
|
|
if left and stype != "99":
|
|
# Full side
|
|
if stype == "1":
|
|
myvertex.extend([(0, 0, 0), (0, -sy, 0), (0, -sy, sz), (0, 0, sz),
|
|
(thickness, 0, 0), (thickness, -sy, 0), (thickness, -sy, sz), (thickness, 0, sz)])
|
|
myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
|
|
(v, v + 1, v + 5, v + 4),
|
|
(v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
# Four legs
|
|
if stype == "4":
|
|
# back
|
|
myvertex.extend([(0, 0, 0), (0, -thickness, 0), (0, -thickness, sz), (0, 0, sz),
|
|
(thickness, 0, 0), (thickness, -thickness, 0), (thickness, -thickness, sz),
|
|
(thickness, 0, sz)])
|
|
myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
|
|
(v, v + 1, v + 5, v + 4),
|
|
(v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
# Front
|
|
myvertex.extend([(0, -sy + thickness, 0), (0, -sy, 0), (0, -sy, sz), (0, -sy + thickness, sz),
|
|
(thickness, -sy + thickness, 0), (thickness, -sy, 0), (thickness, -sy, sz),
|
|
(thickness, -sy + thickness, sz)])
|
|
myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
|
|
(v, v + 1, v + 5, v + 4),
|
|
(v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
|
|
# -----------------
|
|
# Right side
|
|
# -----------------
|
|
if right and stype != "99":
|
|
width = sx - thickness
|
|
# Full side
|
|
if stype == "1":
|
|
myvertex.extend([(width, 0, 0), (width, -sy, 0), (width, -sy, sz), (width, 0, sz),
|
|
(width + thickness, 0, 0), (width + thickness, -sy, 0), (width + thickness, -sy, sz),
|
|
(width + thickness, 0, sz)])
|
|
myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
|
|
(v, v + 1, v + 5, v + 4), (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
# Four legs
|
|
if stype == "4":
|
|
# back
|
|
myvertex.extend([(width, 0, 0), (width, -thickness, 0), (width, -thickness, sz), (width, 0, sz),
|
|
(width + thickness, 0, 0), (width + thickness, -thickness, 0),
|
|
(width + thickness, -thickness, sz), (width + thickness, 0, sz)])
|
|
myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
|
|
(v, v + 1, v + 5, v + 4), (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
# Front
|
|
myvertex.extend(
|
|
[(width, -sy + thickness, 0), (width, -sy, 0), (width, -sy, sz), (width, -sy + thickness, sz),
|
|
(width + thickness, -sy + thickness, 0), (width + thickness, -sy, 0), (width + thickness, -sy, sz),
|
|
(width + thickness, -sy + thickness, sz)])
|
|
myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
|
|
(v, v + 1, v + 5, v + 4), (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
# -----------------
|
|
# shelves
|
|
# -----------------
|
|
posx = 0
|
|
# calculate width
|
|
width = sx - thickness
|
|
posx = posx + thickness
|
|
|
|
# calculate vertical spaces
|
|
dist = sz - top - bottom - sthickness
|
|
# if only top/bottom the space is not necessary
|
|
if shelves > 2:
|
|
space = dist / (shelves - 1)
|
|
else:
|
|
space = 0
|
|
|
|
posz1 = bottom
|
|
|
|
for x in range(shelves):
|
|
# bottom
|
|
if x == 0:
|
|
posz1 = bottom
|
|
# top
|
|
if x == shelves - 1:
|
|
posz1 = sz - top - sthickness
|
|
|
|
posz2 = posz1 - sthickness
|
|
myvertex.extend([(posx, 0, posz1 + zpos[x]), (posx, -sy, posz1 + zpos[x]),
|
|
(posx, -sy, posz2 + zpos[x]), (posx, 0, posz2 + zpos[x]),
|
|
(width, 0, posz1 + zpos[x]), (width, -sy, posz1 + zpos[x]),
|
|
(width, -sy, posz2 + zpos[x]), (width, 0, posz2 + zpos[x])])
|
|
|
|
myfaces.extend(
|
|
[(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3), (v, v + 1, v + 5, v + 4),
|
|
(v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
|
|
v += 8
|
|
posz1 += space
|
|
|
|
mymesh = bpy.data.meshes.new(objname)
|
|
myobject = bpy.data.objects.new(objname, mymesh)
|
|
|
|
myobject.location[0] = px
|
|
myobject.location[1] = py
|
|
myobject.location[2] = pz
|
|
bpy.context.collection.objects.link(myobject)
|
|
|
|
mymesh.from_pydata(myvertex, [], myfaces)
|
|
mymesh.update(calc_edges=True)
|
|
|
|
return myobject, px + sx - thickness
|