1 # SPDX-FileCopyrightText: 2016-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # ----------------------------------------------------------
6 # Automatic generation of books
7 # Author: Antonio Vazquez (antonioya)
9 # ----------------------------------------------------------
10 # noinspection PyUnresolvedReferences
12 from math
import cos
, sin
, radians
13 from random
import randint
15 from colorsys
import rgb_to_hsv
, hsv_to_rgb
16 from bpy
.types
import Operator
17 from bpy
.props
import BoolProperty
, IntProperty
, FloatProperty
, FloatVectorProperty
18 from .achm_tools
import *
21 # ------------------------------------------------------------------
24 # ------------------------------------------------------------------
25 class ARCHIMESH_OT_Books(Operator
):
26 bl_idname
= "mesh.archimesh_books"
28 bl_description
= "Books Generator"
30 bl_options
= {'REGISTER', 'UNDO'}
33 name
='Width', min=0.001, max=1, default
=0.045, precision
=3,
34 description
='Bounding book width',
37 name
='Depth', min=0.001, max=1, default
=0.22, precision
=3,
38 description
='Bounding book depth',
40 height
: FloatProperty(
41 name
='Height', min=0.001, max=1, default
=0.30, precision
=3,
42 description
='Bounding book height',
45 name
='Number of books', min=1, max=100, default
=5,
46 description
='Number total of books',
50 name
='X', min=0.000, max=0.999, default
=0, precision
=3,
51 description
='Randomness for X axis',
54 name
='Y', min=0.000, max=0.999, default
=0, precision
=3,
55 description
='Randomness for Y axis',
58 name
='Z', min=0.000, max=0.999, default
=0, precision
=3,
59 description
='Randomness for Z axis',
63 name
='Rotation', min=0.000, max=1, default
=0, precision
=3,
64 description
='Randomness for vertical position (0-> All straight)',
67 name
='Affinity', min=0, max=10, default
=5,
68 description
='Number of books with same rotation angle',
72 crt_mat
: BoolProperty(
73 name
="Create default Cycles materials",
74 description
="Create default materials for Cycles render",
77 objcol
: FloatVectorProperty(
79 description
="Color for material",
80 default
=(1.0, 1.0, 1.0, 1.0),
87 min=0.000, max=1, default
=0, precision
=3,
88 description
='Randomness for color ',
91 # -----------------------------------------------------
92 # Draw (create UI interface)
93 # -----------------------------------------------------
94 # noinspection PyUnusedLocal
95 def draw(self
, context
):
97 space
= bpy
.context
.space_data
98 if not space
.local_view
:
99 # Imperial units warning
100 if bpy
.context
.scene
.unit_settings
.system
== "IMPERIAL":
102 row
.label(text
="Warning: Imperial units not supported", icon
='COLOR_RED')
105 box
.label(text
="Book size")
107 row
.prop(self
, 'width')
108 row
.prop(self
, 'depth')
109 row
.prop(self
, 'height')
111 row
.prop(self
, 'num', slider
=True)
114 box
.label(text
="Randomness")
116 row
.prop(self
, 'rX', slider
=True)
117 row
.prop(self
, 'rY', slider
=True)
118 row
.prop(self
, 'rZ', slider
=True)
120 row
.prop(self
, 'rot', slider
=True)
121 row
.prop(self
, 'afn', slider
=True)
124 if not context
.scene
.render
.engine
in {'CYCLES', 'BLENDER_EEVEE'}:
126 box
.prop(self
, 'crt_mat')
129 row
.prop(self
, 'objcol')
131 row
.prop(self
, 'rC', slider
=True)
134 row
.label(text
="Warning: Operator does not work in local view mode", icon
='ERROR')
136 # -----------------------------------------------------
138 # -----------------------------------------------------
139 # noinspection PyUnusedLocal
140 def execute(self
, context
):
141 if bpy
.context
.mode
== "OBJECT":
143 create_book_mesh(self
)
146 self
.report({'WARNING'}, "Archimesh: Option only valid in Object mode")
150 # ------------------------------------------------------------------------------
152 # All custom values are passed using self container (self.myvariable)
153 # ------------------------------------------------------------------------------
154 def create_book_mesh(self
):
156 for o
in bpy
.data
.objects
:
157 if o
.select_get() is True:
159 bpy
.ops
.object.select_all(action
='DESELECT')
165 # ------------------------------------------------------------------------------
167 # All custom values are passed using self container (self.myvariable)
168 # ------------------------------------------------------------------------------
169 def generate_books(self
):
171 location
= bpy
.context
.scene
.cursor
.location
172 myloc
= copy(location
) # copy location to keep 3D cursor position
181 for x
in range(self
.num
):
187 mydata
= create_book("Book" + str(x
),
188 self
.width
, self
.depth
, self
.height
,
189 lastx
, myloc
.y
, myloc
.z
,
190 self
.crt_mat
if bpy
.context
.scene
.render
.engine
in {'CYCLES', 'BLENDER_EEVEE'} else False,
191 self
.rX
, self
.rY
, self
.rZ
, self
.rot
, ox
, oy
, oz
, ot
,
192 self
.objcol
, self
.rC
)
193 boxes
.extend([mydata
[0]])
196 # calculate rotation using previous book
201 # calculate x size after rotation
205 size
= 0.0003 + cos(radians(90 - bookdata
[3])) * bookdata
[2] # the height is the radius
208 lastx
= lastx
+ bookdata
[0] + size
216 for o
in bpy
.data
.objects
:
217 if o
.select_get() is True:
220 boxes
[0].select_set(True)
221 bpy
.context
.view_layer
.objects
.active
= boxes
[0]
226 # ------------------------------------------------------------------------------
229 # objName: Name for the new object
230 # thickness: wood thickness (sides)
234 # pX: position X axis
235 # pY: position Y axis
236 # pZ: position Z axis
237 # mat: Flag for creating materials
238 # frX: Random factor X
239 # frY: Random factor Y
240 # frZ: Random factor Z
241 # frR: Random factor Rotation
242 # oX: override x size
243 # oY: override y size
244 # oZ: override z size
245 # oR: override rotation
247 # frC: color randomness factor
248 # ------------------------------------------------------------------------------
249 def create_book(objname
, sx
, sy
, sz
, px
, py
, pz
, mat
, frx
,
250 fry
, frz
, frr
, ox
, oy
, oz
, ot
, objcol
, frc
):
252 ri
= randint(10, 150)
256 ri
= randint(0, int(frx
* 1000))
266 ri
= randint(0, int(fry
* 1000))
276 ri
= randint(0, int(frz
* 1000))
284 # Randomness rotation
286 if frr
> 0 and ot
!= -1:
288 ri
= randint(0, int(frr
* 1000))
294 # Randomness color (only hue)
295 hsv
= rgb_to_hsv(objcol
[0], objcol
[1], objcol
[2])
298 rc1
= randint(0, int(hue
* 1000)) # 0 to hue
299 rc2
= randint(int(hue
* 1000), 1000) # hue to maximum
300 rc3
= randint(0, 1000) # sign
302 if rc3
>= hue
* 1000:
303 hue
+= (rc2
* frc
) / 1000
305 hue
-= (rc1
* frc
) / 1000
306 # Convert random color
307 objcol
= hsv_to_rgb(hue
, hsv
[1], hsv
[2])
313 myvertex
.extend([(x
, -sy
, 0), (0, 0, 0), (x
, 0, sz
), (x
, -sy
, sz
)])
314 myfaces
.extend([(0, 1, 2, 3)])
316 myvertex
.extend([(x
+ gap
, -sy
+ gap
, 0), (x
+ gap
, 0, 0), (x
+ gap
, 0, sz
),
317 (x
+ gap
, -sy
+ gap
, sz
)])
318 myfaces
.extend([(4, 5, 6, 7)])
322 myvertex
.extend([(x
, -sy
+ gap
, 0), (x
, 0, 0), (x
, 0, sz
), (x
, -sy
+ gap
, sz
)])
323 myfaces
.extend([(8, 9, 10, 11)])
325 myvertex
.extend([(x
+ gap
, -sy
, 0), (x
+ gap
, 0, 0), (x
+ gap
, 0, sz
), (x
+ gap
, -sy
, sz
)])
326 myfaces
.extend([(12, 13, 14, 15)])
329 [(0, 12, 15, 3), (4, 8, 11, 7), (3, 15, 11, 7), (0, 12, 8, 4), (0, 1, 5, 4),
330 (8, 9, 13, 12), (3, 2, 6, 7),
331 (11, 10, 14, 15), (1, 2, 6, 5), (9, 10, 14, 13)])
334 myvertex
.extend([(gap
, -sy
+ gap
, sz
- gap
), (gap
, -gap
, sz
- gap
), (sx
- gap
, -gap
, sz
- gap
),
335 (sx
- gap
, -sy
+ gap
, sz
- gap
)])
336 myfaces
.extend([(16, 17, 18, 19)])
338 # bottom inside and front face
339 myvertex
.extend([(gap
, -sy
+ gap
, gap
), (gap
, -gap
, gap
), (sx
- gap
, -gap
, gap
), (sx
- gap
, -sy
+ gap
, gap
)])
340 myfaces
.extend([(20, 21, 22, 23), (17, 18, 22, 21)])
342 mymesh
= bpy
.data
.meshes
.new(objname
)
343 mybook
= bpy
.data
.objects
.new(objname
, mymesh
)
345 mybook
.location
[0] = px
346 mybook
.location
[1] = py
347 mybook
.location
[2] = pz
+ sin(radians(rot
)) * sx
348 bpy
.context
.collection
.objects
.link(mybook
)
350 mymesh
.from_pydata(myvertex
, [], myfaces
)
351 mymesh
.update(calc_edges
=True)
353 # ---------------------------------
354 # Materials and UV Maps
355 # ---------------------------------
356 if mat
and bpy
.context
.scene
.render
.engine
in {'CYCLES', 'BLENDER_EEVEE'}:
359 mat
= create_diffuse_material(objname
+ "_material", True,
360 rgb
[0], rgb
[1], rgb
[2], rgb
[0], rgb
[1], rgb
[2], 0.05)
361 set_material(mybook
, mat
)
363 select_faces(mybook
, 0, True)
364 select_faces(mybook
, 3, False)
365 select_faces(mybook
, 4, False)
366 unwrap_mesh(mybook
, False)
368 mat
= create_diffuse_material(objname
+ "_side_material", True, 0.5, 0.5, 0.5, 0.5, 0.5, 0.3, 0.03)
369 mybook
.data
.materials
.append(mat
)
370 select_faces(mybook
, 14, True)
371 select_faces(mybook
, 15, False)
372 select_faces(mybook
, 16, False)
373 set_material_faces(mybook
, 1)
375 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
376 bpy
.ops
.mesh
.select_all(action
='DESELECT')
377 bpy
.ops
.object.mode_set(mode
='OBJECT')
378 select_faces(mybook
, 14, True)
379 select_faces(mybook
, 15, False)
380 select_faces(mybook
, 16, False)
381 unwrap_mesh(mybook
, False)
383 # ---------------------------------
385 # ---------------------------------
386 mybook
.rotation_euler
= (0.0, radians(rot
), 0.0) # radians
388 # add some gap to the size between books
389 return mybook
, (sx
, sy
, sz
, rot
)