1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you may redistribute it, and/or
4 # modify it, under the terms of the GNU General Public License
5 # as published by the Free Software Foundation - either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, write to:
16 # the Free Software Foundation Inc.
17 # 51 Franklin Street, Fifth Floor
18 # Boston, MA 02110-1301, USA
20 # or go online at: http://www.gnu.org/licenses/ to view license options.
22 # ##### END GPL LICENCE BLOCK #####
24 # authors: dudecon, jambay
26 # This module contains the UI definition, display,
27 # and processing (create mesh) functions.
28 # The routines to generate the vertices for the wall
29 # are found in the "Blocks" module.
33 from bpy
.types
import Operator
34 from bpy
.props
import (
57 from bpy_extras
import object_utils
59 class add_mesh_wallb(Operator
):
60 bl_idname
= "mesh.wall_add"
61 bl_label
= "Add a Masonry Wall"
62 bl_description
= "Create a block (masonry) wall mesh"
63 bl_options
= {'REGISTER', 'UNDO'}
65 # UI items - API for properties - User accessible variables...
66 # not all options are via UI, and some operations just don't work yet
68 Wall
: BoolProperty(name
= "Wall",
72 #### change properties
73 name
: StringProperty(name
= "Name",
76 change
: BoolProperty(name
= "Change",
78 description
= "change Wall")
80 # only create object when True
81 # False allows modifying several parameters without creating object
82 ConstructTog
: BoolProperty(
84 description
="Generate the object",
87 # need to modify so radial makes a tower (normal);
88 # want "flat" setting to make disk (alternate)
89 # make the wall circular - if not sloped it's a flat disc
90 RadialTog
: BoolProperty(
92 description
="Make masonry radial",
95 # curve the wall - if radial creates dome.
96 SlopeTog
: BoolProperty(
98 description
="Make masonry sloped, or curved",
101 # need to review defaults and limits for all of these UI objects
104 WallStart
: FloatProperty(
106 description
="Left side, or start angle",
110 WallEnd
: FloatProperty(
112 description
="Right side, or end angle",
116 WallBottom
: FloatProperty(
118 description
="Lower height or radius",
122 WallTop
: FloatProperty(
124 description
="Upper height or radius",
128 EdgeOffset
: FloatProperty(
130 description
="Block staggering on wall sides",
131 default
=0.6, min=0.0, max=100.0
134 Width
: FloatProperty(
136 description
="Average width of each block",
140 WidthVariance
: FloatProperty(
142 description
="Random variance of block width",
146 WidthMinimum
: FloatProperty(
148 description
="Absolute minimum block width",
152 Height
: FloatProperty(
154 description
="Average Height of each block",
158 HeightVariance
: FloatProperty(
160 description
="Random variance of block Height",
164 HeightMinimum
: FloatProperty(
166 description
="Absolute minimum block Height",
170 Depth
: FloatProperty(
172 description
="Average Depth of each block",
176 DepthVariance
: FloatProperty(
178 description
="Random variance of block Depth",
182 DepthMinimum
: FloatProperty(
184 description
="Absolute minimum block Depth",
188 MergeBlock
: BoolProperty(
190 description
="Make big blocks (merge closely adjoining blocks)",
194 Grout
: FloatProperty(
196 description
="Distance between blocks",
200 GroutVariance
: FloatProperty(
202 description
="Random variance of block Grout",
206 GroutDepth
: FloatProperty(
208 description
="Grout Depth from the face of the blocks",
212 GroutDepthVariance
: FloatProperty(
214 description
="Random variance of block Grout Depth",
218 GroutEdge
: BoolProperty(
220 description
="Grout perimiter",
223 # properties for openings
224 Opening1Tog
: BoolProperty(
226 description
="Make windows or doors",
229 Opening1Width
: FloatProperty(
231 description
="The Width of the first opening",
235 Opening1Height
: FloatProperty(
237 description
="The Height of the first opening",
241 Opening1X
: FloatProperty(
243 description
="The x position or spacing of the first opening",
247 Opening1Z
: FloatProperty(
249 description
="The z position of the First opening",
253 Opening1Repeat
: BoolProperty(
255 description
="make multiple openings, with spacing X1",
258 Opening1TopArchTog
: BoolProperty(
260 description
="Add an arch to the top of the first opening",
263 Opening1TopArch
: FloatProperty(
265 description
="Height of the arch on the top of the opening",
269 Opening1TopArchThickness
: FloatProperty(
271 description
="Thickness of the arch on the top of the opening",
275 Opening1BtmArchTog
: BoolProperty(
277 description
="Add an arch to the bottom of opening 1",
280 Opening1BtmArch
: FloatProperty(
282 description
="Height of the arch on the bottom of the opening",
286 Opening1BtmArchThickness
: FloatProperty(
288 description
="Thickness of the arch on the bottom of the opening",
292 Opening1Bevel
: FloatProperty(
294 description
="Angle block face",
298 # openings on top of wall
299 CrenelTog
: BoolProperty(
301 description
="Make openings along top of wall",
304 CrenelXP
: FloatProperty(
306 description
="Gap width in wall based the percentage of wall width",
311 CrenelZP
: FloatProperty(
313 description
="Crenel Height as the percentage of wall height",
318 # narrow openings in wall.
319 # need to prevent overlap with arch openings - though inversion is an interesting effect.
320 SlotTog
: BoolProperty(
322 description
="Make narrow openings in wall",
325 SlotRpt
: BoolProperty(
327 description
="Repeat slots along wall",
330 SlotWdg
: BoolProperty(
332 description
="Bevel edges of slots",
335 SlotX
: FloatProperty(
337 description
="The x position or spacing of slots",
338 default
=0.0, min=-100, max=100.0
340 SlotGap
: FloatProperty(
342 description
="The opening size of slots",
343 default
=0.5, min=0.10, max=100.0
347 description
="Vertical slots",
350 SlotVH
: FloatProperty(
352 description
="Height of vertical slot",
356 SlotVBtm
: FloatProperty(
358 description
="Z position for slot",
360 min=-100.0, max=100.0
364 description
="Horizontal slots",
367 SlotHW
: FloatProperty(
369 description
="Width of horizontal slot",
373 # this should offset from VBtm... maybe make a % like crenels?
374 SlotHBtm
: FloatProperty(
376 description
="Z position for horizontal slot",
378 min=-100.0, max=100.0
380 # properties for shelf (extend blocks in area)
381 ShelfTog
: BoolProperty(
383 description
="Add blocks in area by depth to make shelf/platform",
386 ShelfX
: FloatProperty(
388 description
="The x position of Shelf",
392 ShelfZ
: FloatProperty(
394 description
="The z position of Shelf",
398 ShelfH
: FloatProperty(
400 description
="The Height of Shelf area",
404 ShelfW
: FloatProperty(
406 description
="The Width of shelf area",
410 ShelfD
: FloatProperty(
412 description
="Depth of each block for shelf (from cursor + 1/2 wall depth)",
416 ShelfBack
: BoolProperty(
418 description
="Shelf on backside of wall",
421 # properties for steps (extend blocks in area, progressive width)
422 StepTog
: BoolProperty(
424 description
="Add blocks in area by depth with progressive width to make steps",
427 StepX
: FloatProperty(
429 description
="The x position of steps",
433 StepZ
: FloatProperty(
435 description
="The z position of steps",
439 StepH
: FloatProperty(
441 description
="The Height of step area",
445 StepW
: FloatProperty(
447 description
="The Width of step area",
451 StepD
: FloatProperty(
453 description
="Depth of each block for steps (from cursor + 1/2 wall depth)",
457 StepV
: FloatProperty(
459 description
="Height of each step",
463 StepT
: FloatProperty(
465 description
="Width of each step",
469 StepLeft
: BoolProperty(
471 description
="If checked, flip steps direction towards the -X axis",
474 StepOnly
: BoolProperty(
476 description
="Steps only, no supporting blocks",
479 StepBack
: BoolProperty(
481 description
="Steps on backside of wall",
485 # Display the toolbox options
486 def draw(self
, context
):
490 box
.prop(self
, 'ConstructTog')
492 # Wall area (size/position)
494 box
.label(text
="Wall Size (area)")
496 col
= box
.column(align
=True)
497 col
.prop(self
, "WallStart")
498 col
.prop(self
, "WallEnd")
500 col
= box
.column(align
=True)
501 col
.prop(self
, "WallBottom")
502 col
.prop(self
, "WallTop")
503 box
.prop(self
, "EdgeOffset")
507 box
.label(text
="Block Sizing")
508 box
.prop(self
, "MergeBlock")
510 # add checkbox for "fixed" sizing (ignore variance) a.k.a. bricks
511 col
= box
.column(align
=True)
512 col
.prop(self
, "Width")
513 col
.prop(self
, "WidthVariance")
514 col
.prop(self
, "WidthMinimum")
516 col
= box
.column(align
=True)
517 col
.prop(self
, "Height")
518 col
.prop(self
, "HeightVariance")
519 col
.prop(self
, "HeightMinimum")
521 col
= box
.column(align
=True)
522 col
.prop(self
, "Depth")
523 col
.prop(self
, "DepthVariance")
524 col
.prop(self
, "DepthMinimum")
528 box
.label(text
="Grout")
530 col
= box
.column(align
=True)
531 col
.prop(self
, "Grout")
532 col
.prop(self
, "GroutVariance")
534 col
= box
.column(align
=True)
535 col
.prop(self
, "GroutDepth")
536 col
.prop(self
, "GroutDepthVariance")
538 # Wall shape modifiers
540 box
.label(text
="Wall Shape")
541 row
= box
.row(align
=True)
542 row
.prop(self
, "RadialTog", toggle
=True)
543 row
.prop(self
, "SlopeTog", toggle
=True)
545 # Openings (doors, windows; arched)
547 box
.prop(self
, 'Opening1Tog')
549 col
= box
.column(align
=True)
550 col
.prop(self
, "Opening1Width")
551 col
.prop(self
, "Opening1Height")
552 col
.prop(self
, "Opening1X")
553 col
.prop(self
, "Opening1Z")
554 col
.prop(self
, "Opening1Bevel")
556 box
.prop(self
, "Opening1Repeat", toggle
=True)
559 sub_box
.prop(self
, "Opening1TopArchTog")
560 if self
.Opening1TopArchTog
:
561 col
= sub_box
.column(align
=True)
562 col
.prop(self
, "Opening1TopArch")
563 col
.prop(self
, "Opening1TopArchThickness")
566 sub_box
.prop(self
, "Opening1BtmArchTog")
567 if self
.Opening1BtmArchTog
:
568 col
= sub_box
.column(align
=True)
569 col
.prop(self
, "Opening1BtmArch")
570 col
.prop(self
, "Opening1BtmArchThickness")
572 # Slots (narrow openings)
574 box
.prop(self
, "SlotTog")
576 col
= box
.column(align
=True)
577 col
.prop(self
, "SlotX")
578 col
.prop(self
, "SlotGap")
580 box
.prop(self
, "SlotRpt", toggle
=True)
583 sub_box
.prop(self
, "SlotV")
585 col
= sub_box
.column(align
=True)
586 col
.prop(self
, "SlotVH")
587 col
.prop(self
, "SlotVBtm")
590 sub_box
.prop(self
, "SlotH")
592 col
= sub_box
.column(align
=True)
593 col
.prop(self
, "SlotHW")
594 col
.prop(self
, "SlotHBtm")
596 # Crenels, gaps in top of wall
598 box
.prop(self
, "CrenelTog")
600 col
= box
.column(align
=True)
601 col
.prop(self
, "CrenelXP")
602 col
.prop(self
, "CrenelZP")
604 # Shelfing (protrusions)
606 box
.prop(self
, 'ShelfTog')
608 col
= box
.column(align
=True)
609 col
.prop(self
, "ShelfX")
610 col
.prop(self
, "ShelfZ")
612 col
= box
.column(align
=True)
613 col
.prop(self
, "ShelfW")
614 col
.prop(self
, "ShelfH")
615 col
.prop(self
, "ShelfD")
617 box
.prop(self
, "ShelfBack")
621 box
.prop(self
, 'StepTog')
623 col
= box
.column(align
=True)
624 col
.prop(self
, "StepX")
625 col
.prop(self
, "StepZ")
627 col
= box
.column(align
=True)
628 col
.prop(self
, "StepH")
629 col
.prop(self
, "StepW")
630 col
.prop(self
, "StepD")
632 col
= box
.column(align
=True)
633 col
.prop(self
, "StepV")
634 col
.prop(self
, "StepT")
636 col
= box
.column(align
=True)
637 row
= col
.row(align
=True)
638 row
.prop(self
, "StepLeft", toggle
=True)
639 row
.prop(self
, "StepOnly", toggle
=True)
640 col
.prop(self
, "StepBack", toggle
=True)
642 # Respond to UI - get the properties set by user.
643 # Check and process UI settings to generate masonry
645 def execute(self
, context
):
657 # Create the wall when enabled (skip regen iterations when off)
658 if not self
.ConstructTog
:
661 # enter the settings for the wall dimensions (area)
662 # start can't be zero - min/max don't matter [if max less than end] but zero don't workie.
663 # start can't exceed end.
664 if not self
.WallStart
or self
.WallStart
>= self
.WallEnd
:
665 self
.WallStart
= NOTZERO
# Reset UI if input out of bounds...
667 dims
['s'] = self
.WallStart
668 dims
['e'] = self
.WallEnd
669 dims
['b'] = self
.WallBottom
670 dims
['t'] = self
.WallTop
672 settings
['eoff'] = self
.EdgeOffset
674 # retrieve the settings for the wall block properties
675 settings
['w'] = self
.Width
676 settings
['wv'] = self
.WidthVariance
677 settings
['wm'] = self
.WidthMinimum
680 settings
['sdv'] = settings
['w']
682 settings
['sdv'] = 0.12
684 settings
['h'] = self
.Height
685 settings
['hv'] = self
.HeightVariance
686 settings
['hm'] = self
.HeightMinimum
688 settings
['d'] = self
.Depth
689 settings
['dv'] = self
.DepthVariance
690 settings
['dm'] = self
.DepthMinimum
697 settings
['g'] = self
.Grout
698 settings
['gv'] = self
.GroutVariance
699 settings
['gd'] = self
.GroutDepth
700 settings
['gdv'] = self
.GroutDepthVariance
707 # set wall shape modifiers
710 # eliminate to allow user control for start/completion?
711 dims
['s'] = 0.0 # complete radial
712 if dims
['e'] > PI
* 2:
713 dims
['e'] = PI
* 2 # max end for circle
714 if dims
['b'] < settings
['g']:
715 dims
['b'] = settings
['g'] # min bottom for grout extension
727 # Add shelf if enabled
730 shelfSpecs
['h'] = self
.ShelfH
731 shelfSpecs
['w'] = self
.ShelfW
732 shelfSpecs
['d'] = self
.ShelfD
733 shelfSpecs
['x'] = self
.ShelfX
734 shelfSpecs
['z'] = self
.ShelfZ
743 # Make steps if enabled
746 stepSpecs
['x'] = self
.StepX
747 stepSpecs
['z'] = self
.StepZ
748 stepSpecs
['h'] = self
.StepH
749 stepSpecs
['w'] = self
.StepW
750 stepSpecs
['d'] = self
.StepD
751 stepSpecs
['v'] = self
.StepV
752 stepSpecs
['t'] = self
.StepT
763 # enter the settings for the openings
764 # when openings overlap they create inverse stonework - interesting but not the desired effect :)
765 # if opening width == indent * 2 the edge blocks fail (row of blocks cross opening) - bug.
767 openingIdx
= 0 # track opening array references for multiple uses
769 # general openings with arch options - can be windows or doors.
772 openingSpecs
+= [{'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1,
773 'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
775 openingSpecs
[openingIdx
]['w'] = self
.Opening1Width
776 openingSpecs
[openingIdx
]['h'] = self
.Opening1Height
777 openingSpecs
[openingIdx
]['x'] = self
.Opening1X
778 openingSpecs
[openingIdx
]['z'] = self
.Opening1Z
779 openingSpecs
[openingIdx
]['rp'] = self
.Opening1Repeat
781 if self
.Opening1TopArchTog
:
782 openingSpecs
[openingIdx
]['v'] = self
.Opening1TopArch
783 openingSpecs
[openingIdx
]['t'] = self
.Opening1TopArchThickness
785 if self
.Opening1BtmArchTog
:
786 openingSpecs
[openingIdx
]['vl'] = self
.Opening1BtmArch
787 openingSpecs
[openingIdx
]['tl'] = self
.Opening1BtmArchThickness
789 openingSpecs
[openingIdx
]['b'] = self
.Opening1Bevel
791 openingIdx
+= 1 # count window/door/arch openings
793 # Slots (narrow openings)
796 if self
.SlotV
: # vertical slots
798 openingSpecs
+= [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
799 'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
801 openingSpecs
[openingIdx
]['w'] = self
.SlotGap
802 openingSpecs
[openingIdx
]['h'] = self
.SlotVH
803 openingSpecs
[openingIdx
]['x'] = self
.SlotX
804 openingSpecs
[openingIdx
]['z'] = self
.SlotVBtm
805 openingSpecs
[openingIdx
]['rp'] = self
.SlotRpt
807 # make them pointy...
808 openingSpecs
[openingIdx
]['v'] = self
.SlotGap
809 openingSpecs
[openingIdx
]['t'] = self
.SlotGap
/ 2
810 openingSpecs
[openingIdx
]['vl'] = self
.SlotGap
811 openingSpecs
[openingIdx
]['tl'] = self
.SlotGap
/ 2
813 openingIdx
+= 1 # count vertical slot openings
815 # need to handle overlap of H and V slots...
817 if self
.SlotH
: # Horizontal slots
819 openingSpecs
+= [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
820 'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
822 openingSpecs
[openingIdx
]['w'] = self
.SlotHW
823 openingSpecs
[openingIdx
]['h'] = self
.SlotGap
824 openingSpecs
[openingIdx
]['x'] = self
.SlotX
825 openingSpecs
[openingIdx
]['z'] = self
.SlotHBtm
826 # horizontal repeat isn't same spacing as vertical...
827 openingSpecs
[openingIdx
]['rp'] = self
.SlotRpt
829 # make them pointy...
830 openingIdx
+= 1 # count horizontal slot openings
832 # Crenellations (top row openings)
835 # add bottom arch option?
836 # perhaps a repeat toggle...
837 # if crenel opening overlaps with arch opening it fills with blocks...
840 openingSpecs
+= [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 1,
841 'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
843 wallW
= self
.WallEnd
- self
.WallStart
844 crenelW
= wallW
* self
.CrenelXP
# Width % opening.
846 wallH
= self
.WallTop
- self
.WallBottom
847 crenelH
= wallH
* self
.CrenelZP
# % proportional height.
849 openingSpecs
[openingIdx
]['w'] = crenelW
850 openingSpecs
[openingIdx
]['h'] = crenelH
852 # calculate the spacing between openings.
853 # this isn't the absolute start (left),
854 # it's opening center offset relative to cursor (space between openings)...
855 openingSpecs
[openingIdx
]['x'] = crenelW
* 2 - 1 # assume standard spacing
857 if not radialized
: # normal wall?
858 # set indent 0 (center) if opening is 50% or more of wall width, no repeat.
859 if crenelW
* 2 >= wallW
:
860 openingSpecs
[openingIdx
]['x'] = 0
861 openingSpecs
[openingIdx
]['rp'] = 0
862 # set bottom of opening (center of hole)
863 openingSpecs
[openingIdx
]['z'] = self
.WallTop
- (crenelH
/ 2)
865 openingIdx
+= 1 # count crenel openings
867 # Process the user settings to generate a wall
868 # generate the list of vertices for the wall...
869 verts_array
, faces_array
= createWall(
870 radialized
, slope
, openingSpecs
, bigBlock
,
871 shelfExt
, shelfBack
, stepMod
, stepLeft
, stepOnly
,
875 if bpy
.context
.mode
== "OBJECT":
876 if self
.change
== True and self
.change
!= None:
877 obj
= context
.active_object
879 oldmeshname
= obj
.data
.name
880 mesh
= bpy
.data
.meshes
.new("Wall")
881 mesh
.from_pydata(verts_array
, [], faces_array
)
883 for material
in oldmesh
.materials
:
884 obj
.data
.materials
.append(material
)
885 bpy
.data
.meshes
.remove(oldmesh
)
886 obj
.data
.name
= oldmeshname
888 mesh
= bpy
.data
.meshes
.new("Wall")
889 mesh
.from_pydata(verts_array
, [], faces_array
)
890 obj
= object_utils
.object_data_add(context
, mesh
, operator
=None)
894 obj
.data
["Wall"] = True
895 obj
.data
["change"] = False
896 for prm
in WallParameters():
897 obj
.data
[prm
] = getattr(self
, prm
)
899 if bpy
.context
.mode
== "EDIT_MESH":
900 active_object
= context
.active_object
901 name_active_object
= active_object
.name
902 bpy
.ops
.object.mode_set(mode
='OBJECT')
903 mesh
= bpy
.data
.meshes
.new("TMP")
904 mesh
.from_pydata(verts_array
, [], faces_array
)
905 obj
= object_utils
.object_data_add(context
, mesh
, operator
=None)
907 active_object
.select_set(True)
908 bpy
.ops
.object.join()
909 context
.active_object
.name
= name_active_object
910 bpy
.ops
.object.mode_set(mode
='EDIT')
914 def WallParameters():
937 "GroutDepthVariance",
945 "Opening1TopArchTog",
947 "Opening1TopArchThickness",
948 "Opening1BtmArchTog",
950 "Opening1BtmArchThickness",
984 return WallParameters