1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can 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 the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 # ----------------------------------------------------------
22 # Automatic generation of lamps
23 # Author: Antonio Vazquez (antonioya)
25 # ----------------------------------------------------------
27 from math
import cos
, sin
, radians
29 from bpy
.types
import Operator
30 from bpy
.props
import EnumProperty
, FloatProperty
, IntProperty
, BoolProperty
, FloatVectorProperty
31 from .achm_tools
import *
34 # ------------------------------------------------------
35 # set predefined designs
37 # self: self container
38 # ------------------------------------------------------
40 # -----------------------
42 # -----------------------
43 if self
.preset
== "1":
44 self
.base_height
= 0.22
45 self
.base_segments
= 16
62 # -----------------------
64 # -----------------------
65 if self
.preset
== "2":
66 self
.base_height
= 0.20
67 self
.base_segments
= 16
84 # -----------------------
86 # -----------------------
87 if self
.preset
== "3":
88 self
.base_height
= 0.20
89 self
.base_segments
= 8
106 # -----------------------
108 # -----------------------
109 if self
.preset
== "4":
110 self
.base_height
= 0.15
111 self
.base_segments
= 4
114 self
.subdivide
= False
128 # ------------------------------------------------------------------
131 # ------------------------------------------------------------------
132 class AchmLamp(Operator
):
133 bl_idname
= "mesh.archimesh_lamp"
135 bl_description
= "Lamp Generator"
136 bl_category
= 'Archimesh'
137 bl_options
= {'REGISTER', 'UNDO'}
139 preset
= EnumProperty(
145 ('4', "Rectangular", ""),
148 description
="Apply predefined design",
152 base_height
= FloatProperty(
154 min=0.01, max=10, default
=0.20, precision
=3,
155 description
='lamp base height',
157 base_segments
= IntProperty(
159 min=3, max=128, default
=16,
160 description
='Number of segments (vertical)',
162 base_rings
= IntProperty(
164 min=2, max=12, default
=6,
165 description
='Number of rings (horizontal)',
167 holder
= FloatProperty(
169 min=0.001, max=10, default
=0.02, precision
=3,
170 description
='Lampholder height',
172 smooth
= BoolProperty(
174 description
="Use smooth shader",
177 subdivide
= BoolProperty(
179 description
="Add subdivision modifier",
183 bz01
= FloatProperty(name
='S1', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
184 bz02
= FloatProperty(name
='S2', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
185 bz03
= FloatProperty(name
='S3', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
186 bz04
= FloatProperty(name
='S4', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
187 bz05
= FloatProperty(name
='S5', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
188 bz06
= FloatProperty(name
='S6', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
189 bz07
= FloatProperty(name
='S7', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
190 bz08
= FloatProperty(name
='S8', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
191 bz09
= FloatProperty(name
='S9', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
192 bz10
= FloatProperty(name
='S10', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
193 bz11
= FloatProperty(name
='S11', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
194 bz12
= FloatProperty(name
='S12', min=-1, max=1, default
=0, precision
=3, description
='Z shift factor')
196 br01
= FloatProperty(name
='R1', min=0.001, max=10, default
=0.06, precision
=3, description
='Ring radio')
197 br02
= FloatProperty(name
='R2', min=0.001, max=10, default
=0.08, precision
=3, description
='Ring radio')
198 br03
= FloatProperty(name
='R3', min=0.001, max=10, default
=0.09, precision
=3, description
='Ring radio')
199 br04
= FloatProperty(name
='R4', min=0.001, max=10, default
=0.08, precision
=3, description
='Ring radio')
200 br05
= FloatProperty(name
='R5', min=0.001, max=10, default
=0.06, precision
=3, description
='Ring radio')
201 br06
= FloatProperty(name
='R6', min=0.001, max=10, default
=0.03, precision
=3, description
='Ring radio')
202 br07
= FloatProperty(name
='R7', min=0.001, max=10, default
=0.10, precision
=3, description
='Ring radio')
203 br08
= FloatProperty(name
='R8', min=0.001, max=10, default
=0.10, precision
=3, description
='Ring radio')
204 br09
= FloatProperty(name
='R9', min=0.001, max=10, default
=0.10, precision
=3, description
='Ring radio')
205 br10
= FloatProperty(name
='R10', min=0.001, max=10, default
=0.10, precision
=3, description
='Ring radio')
206 br11
= FloatProperty(name
='R11', min=0.001, max=10, default
=0.10, precision
=3, description
='Ring radio')
207 br12
= FloatProperty(name
='R12', min=0.001, max=10, default
=0.10, precision
=3, description
='Ring radio')
209 top_height
= FloatProperty(
210 name
='Height', min=0.01, max=10,
211 default
=0.20, precision
=3,
212 description
='lampshade height',
214 top_segments
= IntProperty(
215 name
='Segments', min=3, max=128,
217 description
='Number of segments (vertical)',
219 tr01
= FloatProperty(
220 name
='R1', min=0.001, max=10,
221 default
=0.16, precision
=3,
222 description
='lampshade bottom radio',
224 tr02
= FloatProperty(name
='R2', min=0.001, max=10,
225 default
=0.08, precision
=3,
226 description
='lampshade top radio')
227 pleats
= BoolProperty(
228 name
="Pleats", description
="Create pleats in the lampshade",
231 tr03
= FloatProperty(
232 name
='R3', min=0.001, max=1,
233 default
=0.01, precision
=3, description
='Pleats size',
235 energy
= FloatProperty(
236 name
='Light', min=0.00, max=1000,
237 default
=15, precision
=3,
238 description
='Light intensity',
240 opacity
= FloatProperty(
241 name
='Translucency', min=0.00, max=1,
242 default
=0.3, precision
=3,
243 description
='Lampshade translucency factor (1 completely translucent)',
247 crt_mat
= BoolProperty(
248 name
="Create default Cycles materials",
249 description
="Create default materials for Cycles render",
252 objcol
= FloatVectorProperty(
254 description
="Color for material",
255 default
=(1.0, 1.0, 1.0, 1.0),
261 # -----------------------------------------------------
262 # Draw (create UI interface)
263 # -----------------------------------------------------
264 # noinspection PyUnusedLocal
265 def draw(self
, context
):
267 space
= bpy
.context
.space_data
268 if not space
.local_view
:
269 # Imperial units warning
270 if bpy
.context
.scene
.unit_settings
.system
== "IMPERIAL":
272 row
.label("Warning: Imperial units not supported", icon
='COLOR_RED')
275 box
.label("Lamp base")
277 row
.prop(self
, 'preset')
279 row
.prop(self
, 'base_height')
280 row
.prop(self
, 'base_segments')
281 row
.prop(self
, 'base_rings')
283 row
.prop(self
, 'smooth')
284 row
.prop(self
, 'subdivide')
286 row
.prop(self
, 'holder')
288 if self
.base_rings
>= 1:
290 row
.prop(self
, 'br01')
291 row
.prop(self
, 'bz01', slider
=True)
292 if self
.base_rings
>= 2:
294 row
.prop(self
, 'br02')
295 row
.prop(self
, 'bz02', slider
=True)
296 if self
.base_rings
>= 3:
298 row
.prop(self
, 'br03')
299 row
.prop(self
, 'bz03', slider
=True)
301 if self
.base_rings
>= 4:
303 row
.prop(self
, 'br04')
304 row
.prop(self
, 'bz04', slider
=True)
305 if self
.base_rings
>= 5:
307 row
.prop(self
, 'br05')
308 row
.prop(self
, 'bz05', slider
=True)
309 if self
.base_rings
>= 6:
311 row
.prop(self
, 'br06')
312 row
.prop(self
, 'bz06', slider
=True)
314 if self
.base_rings
>= 7:
316 row
.prop(self
, 'br07')
317 row
.prop(self
, 'bz07', slider
=True)
318 if self
.base_rings
>= 8:
320 row
.prop(self
, 'br08')
321 row
.prop(self
, 'bz08', slider
=True)
322 if self
.base_rings
>= 9:
324 row
.prop(self
, 'br09')
325 row
.prop(self
, 'bz09', slider
=True)
327 if self
.base_rings
>= 10:
329 row
.prop(self
, 'br10')
330 row
.prop(self
, 'bz10', slider
=True)
331 if self
.base_rings
>= 11:
333 row
.prop(self
, 'br11')
334 row
.prop(self
, 'bz11', slider
=True)
335 if self
.base_rings
>= 12:
337 row
.prop(self
, 'br12')
338 row
.prop(self
, 'bz12', slider
=True)
341 box
.label("Lampshade")
343 row
.prop(self
, 'top_height')
344 row
.prop(self
, 'top_segments')
346 row
.prop(self
, 'tr01')
347 row
.prop(self
, 'tr02')
349 row
.prop(self
, 'energy')
350 row
.prop(self
, 'opacity', slider
=True)
352 row
.prop(self
, 'pleats')
354 row
.prop(self
, 'tr03')
357 if not context
.scene
.render
.engine
== 'CYCLES':
359 box
.prop(self
, 'crt_mat')
362 row
.prop(self
, 'objcol')
365 row
.label("Warning: Operator does not work in local view mode", icon
='ERROR')
367 # -----------------------------------------------------
369 # -----------------------------------------------------
370 # noinspection PyUnusedLocal
371 def execute(self
, context
):
372 if bpy
.context
.mode
== "OBJECT":
373 if self
.oldpreset
!= self
.preset
:
375 self
.oldpreset
= self
.preset
378 create_lamp_mesh(self
)
381 self
.report({'WARNING'}, "Archimesh: Option only valid in Object mode")
385 # ------------------------------------------------------------------------------
387 # All custom values are passed using self container (self.myvariable)
388 # ------------------------------------------------------------------------------
389 def create_lamp_mesh(self
):
391 for o
in bpy
.data
.objects
:
394 bpy
.ops
.object.select_all(False)
400 # ------------------------------------------------------------------------------
402 # All custom values are passed using self container (self.myvariable)
403 # ------------------------------------------------------------------------------
404 def generate_lamp(self
):
405 location
= bpy
.context
.scene
.cursor_location
406 myloc
= copy(location
) # copy location to keep 3D cursor position
407 # ---------------------
409 # ---------------------
410 mydata
= create_lamp_base("Lamp_base", self
.base_height
,
411 myloc
.x
, myloc
.y
, myloc
.z
,
412 self
.base_segments
, self
.base_rings
,
413 [self
.br01
, self
.br02
, self
.br03
, self
.br04
, self
.br05
, self
.br06
,
414 self
.br07
, self
.br08
, self
.br09
, self
.br10
, self
.br11
, self
.br12
],
415 (self
.bz01
, self
.bz02
, self
.bz03
, self
.bz04
, self
.bz05
, self
.bz06
,
416 self
.bz07
, self
.bz08
, self
.bz09
, self
.bz10
, self
.bz11
, self
.bz12
),
418 self
.crt_mat
, self
.objcol
)
422 remove_doubles(mybase
)
428 set_modifier_subsurf(mybase
)
429 # ---------------------
431 # ---------------------
432 myholder
= create_lampholder("Lampholder", self
.holder
,
433 myloc
.x
, myloc
.y
, myloc
.z
,
436 remove_doubles(myholder
)
437 set_normals(myholder
)
440 myholder
.parent
= mybase
441 myholder
.location
.x
= 0
442 myholder
.location
.y
= 0
443 myholder
.location
.z
= posz
444 # ---------------------
446 # ---------------------
447 mystrings
= create_lampholder_strings("Lampstrings", self
.holder
,
448 myloc
.x
, myloc
.y
, myloc
.z
,
453 remove_doubles(mystrings
)
454 set_normals(mystrings
)
456 mystrings
.parent
= myholder
457 mystrings
.location
.x
= 0
458 mystrings
.location
.y
= 0
459 mystrings
.location
.z
= 0.03
460 # ---------------------
462 # ---------------------
463 mytop
= create_lampshade("Lampshade", self
.top_height
,
464 myloc
.x
, myloc
.y
, myloc
.z
,
466 self
.tr01
, self
.tr02
,
467 self
.pleats
, self
.tr03
,
471 remove_doubles(mytop
)
473 if self
.pleats
is False:
476 mytop
.parent
= mybase
479 mytop
.location
.z
= posz
+ self
.holder
480 # ---------------------
482 # ---------------------
484 bpy
.ops
.mesh
.primitive_uv_sphere_add(segments
=16, size
=radbulb
)
485 mybulb
= bpy
.data
.objects
[bpy
.context
.active_object
.name
]
486 mybulb
.name
= "Lamp_Bulb"
487 mybulb
.parent
= myholder
488 mybulb
.location
= (0, 0, radbulb
+ self
.holder
+ 0.04)
489 if self
.crt_mat
and bpy
.context
.scene
.render
.engine
== 'CYCLES':
490 mat
= create_emission_material(mybulb
.name
, True, 0.8, 0.8, 0.8, self
.energy
)
491 set_material(mybulb
, mat
)
494 for o
in bpy
.data
.objects
:
499 bpy
.context
.scene
.objects
.active
= mybase
504 # ------------------------------------------------------------------------------
507 # objName: Name for the new object
508 # height: Size in Z axis
509 # pX: position X axis
510 # pY: position Y axis
511 # pZ: position Z axis
512 # segments: number of segments
513 # rings: number of rings
514 # radios: ring radios
515 # ratios: Z shift ratios
516 # subdivide: Subdivision flag
517 # mat: Flag for creating materials
519 # ------------------------------------------------------------------------------
520 def create_lamp_base(objname
, height
, px
, py
, pz
, segments
, rings
, radios
, ratios
, subdivide
, mat
, objcol
):
522 h
= height
/ (rings
- 1)
525 for f
in range(0, rings
):
526 listheight
.extend([z
+ (z
* ratios
[f
])])
529 mydata
= create_cylinder_data(segments
, listheight
,
531 True, True, False, 0, subdivide
)
535 mymesh
= bpy
.data
.meshes
.new(objname
)
536 mycylinder
= bpy
.data
.objects
.new(objname
, mymesh
)
537 bpy
.context
.scene
.objects
.link(mycylinder
)
539 mymesh
.from_pydata(myvertex
, [], myfaces
)
540 mymesh
.update(calc_edges
=True)
542 mycylinder
.location
.x
= px
543 mycylinder
.location
.y
= py
544 mycylinder
.location
.z
= pz
546 if mat
and bpy
.context
.scene
.render
.engine
== 'CYCLES':
548 mymat
= create_diffuse_material(mycylinder
.name
+ "_material", True, rgb
[0], rgb
[1], rgb
[2], rgb
[0], rgb
[1],
550 set_material(mycylinder
, mymat
)
552 return mycylinder
, listheight
[len(listheight
) - 1]
555 # ------------------------------------------------------------------------------
558 # objName: Name for the new object
559 # height: Size in Z axis
560 # pX: position X axis
561 # pY: position Y axis
562 # pZ: position Z axis
563 # mat: Flag for creating materials
564 # ------------------------------------------------------------------------------
565 def create_lampholder(objname
, height
, px
, py
, pz
, mat
):
566 mydata
= create_cylinder_data(16, [0, height
, height
+ 0.005, height
+ 0.008, height
+ 0.05],
567 [0.005, 0.005, 0.010, 0.018, 0.018],
568 False, False, False, 0, False)
572 mymesh
= bpy
.data
.meshes
.new(objname
)
573 mycylinder
= bpy
.data
.objects
.new(objname
, mymesh
)
574 bpy
.context
.scene
.objects
.link(mycylinder
)
576 mymesh
.from_pydata(myvertex
, [], myfaces
)
577 mymesh
.update(calc_edges
=True)
579 mycylinder
.location
.x
= px
580 mycylinder
.location
.y
= py
581 mycylinder
.location
.z
= pz
584 if mat
and bpy
.context
.scene
.render
.engine
== 'CYCLES':
585 mat
= create_diffuse_material(mycylinder
.name
+ "_material", True, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.1)
586 set_material(mycylinder
, mat
)
591 # ------------------------------------------------------------------------------
592 # Create lampholder strings
594 # objName: Name for the new object
595 # height: Size in Z axis
596 # pX: position X axis
597 # pY: position Y axis
598 # pZ: position Z axis
599 # radio: radio of lampshade
600 # shadeh: height of lampshader
601 # mat: Flag for creating materials
602 # ------------------------------------------------------------------------------
603 def create_lampholder_strings(objname
, height
, px
, py
, pz
, radio
, shadeh
, mat
):
604 mydata
= create_cylinder_data(32, [height
+ 0.005, height
+ 0.005, height
+ 0.006, height
+ 0.006],
605 [0.018, 0.025, 0.025, 0.018],
606 False, False, False, 0, False)
610 mymesh
= bpy
.data
.meshes
.new(objname
)
611 mycylinder
= bpy
.data
.objects
.new(objname
, mymesh
)
612 bpy
.context
.scene
.objects
.link(mycylinder
)
614 mymesh
.from_pydata(myvertex
, [], myfaces
)
615 mymesh
.update(calc_edges
=True)
617 mycylinder
.location
.x
= px
618 mycylinder
.location
.y
= py
619 mycylinder
.location
.z
= pz
621 box1
= create_box_segments("Lamp_B1", shadeh
- 0.036, radio
- 0.023)
622 box1
.parent
= mycylinder
623 box1
.location
= (0.021, 0, height
+ 0.004)
625 box2
= create_box_segments("Lamp_B2", shadeh
- 0.036, -radio
+ 0.023)
626 box2
.parent
= mycylinder
627 box2
.location
= (-0.021, 0, height
+ 0.004)
630 if mat
and bpy
.context
.scene
.render
.engine
== 'CYCLES':
631 mat
= create_diffuse_material(mycylinder
.name
+ "_material", True, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.1)
632 set_material(mycylinder
, mat
)
633 set_material(box1
, mat
)
634 set_material(box2
, mat
)
639 # ------------------------------------------------------------------------------
642 # objName: Name for the new object
643 # height: Size in Z axis
644 # pX: position X axis
645 # pY: position Y axis
646 # pZ: position Z axis
647 # segments: number of segments
648 # radio1: ring radio 1
649 # radio2: ring radio 2
650 # pleats: flag for pleats
651 # pleatsize: difference in radios (less)
652 # opacity: opacity factor
653 # mat: Flag for creating materials
654 # ------------------------------------------------------------------------------
655 def create_lampshade(objname
, height
, px
, py
, pz
, segments
, radio1
, radio2
, pleats
, pleatsize
, opacity
, mat
):
657 radios
= [radio1
- gap
, radio1
- gap
, radio1
, radio2
, radio2
- gap
, radio2
- gap
]
658 heights
= [gap
* 2, 0, 0, height
, height
, height
- (gap
* 2)]
659 mydata
= create_cylinder_data(segments
, heights
,
661 False, False, pleats
, pleatsize
, False)
665 mymesh
= bpy
.data
.meshes
.new(objname
)
666 mycylinder
= bpy
.data
.objects
.new(objname
, mymesh
)
667 bpy
.context
.scene
.objects
.link(mycylinder
)
669 mymesh
.from_pydata(myvertex
, [], myfaces
)
670 mymesh
.update(calc_edges
=True)
672 mycylinder
.location
.x
= px
673 mycylinder
.location
.y
= py
674 mycylinder
.location
.z
= pz
676 if mat
and bpy
.context
.scene
.render
.engine
== 'CYCLES':
677 mymat
= create_translucent_material(mycylinder
.name
+ "_material", True, 0.8, 0.65, 0.45, 0.8, 0.65, 0.45,
679 set_material(mycylinder
, mymat
)
684 # ------------------------------------------------------------------------------
685 # Create box segments
687 # objName: Name for the new object
688 # height: Size in Z axis
689 # shift: Shift movement
690 # ------------------------------------------------------------------------------
691 def create_box_segments(objname
, height
, shift
):
693 myvertex
= [(0, 0, 0), (0, gap
, 0), (gap
, gap
, 0), (gap
, 0, 0),
695 (shift
, gap
, height
),
696 (shift
+ gap
, gap
, height
),
697 (shift
+ gap
, 0, height
)]
698 myfaces
= [(6, 5, 1, 2), (7, 6, 2, 3), (4, 7, 3, 0), (1, 5, 4, 0)]
700 mymesh
= bpy
.data
.meshes
.new(objname
)
701 mysegment
= bpy
.data
.objects
.new(objname
, mymesh
)
702 bpy
.context
.scene
.objects
.link(mysegment
)
704 mymesh
.from_pydata(myvertex
, [], myfaces
)
705 mymesh
.update(calc_edges
=True)
707 mysegment
.location
.x
= 0
708 mysegment
.location
.y
= 0
709 mysegment
.location
.z
= 0
714 # ------------------------------------------------------------------------------
715 # Create cylinders data
717 # segments: Number of pies
718 # listHeight: list of heights
719 # listRadio: list of radios
721 # bottom: bottom face flag
722 # pleats: flag for pleats
723 # pleatsize: difference in radios (less)
724 # subdiv: fix subdivision problem
725 # ------------------------------------------------------------------------------
726 def create_cylinder_data(segments
, listheight
, listradio
, bottom
, top
, pleats
, pleatsize
, subdiv
):
730 # Add at element 0 to fix subdivision problems
731 listheight
.insert(0, listheight
[0] + 0.001)
732 listradio
.insert(0, listradio
[0])
733 # Add at last element to fix subdivision problems
734 e
= len(listheight
) - 1
735 listheight
.insert(e
, listheight
[e
] + 0.001)
736 listradio
.insert(e
, listradio
[e
])
737 # -------------------------------------
739 # -------------------------------------
744 for i
in range(segments
):
745 x
= cos(radians(seg
)) * (listradio
[idx
] + rp
)
746 y
= sin(radians(seg
)) * (listradio
[idx
] + rp
)
747 mypoint
= [(x
, y
, z
)]
748 myvertex
.extend(mypoint
)
749 seg
+= 360 / segments
751 if pleats
is True and rp
== 0:
757 # -------------------------------------
759 # -------------------------------------
760 for r
in range(0, len(listheight
) - 1):
763 for n
in range(0, segments
):
767 myface
= [(n
+ s
, n
+ s
- segments
+ 1, n
+ s
+ 1, n
+ s
+ segments
)]
768 myfaces
.extend(myface
)
770 myface
= [(n
+ s
, n
+ s
+ 1, n
+ s
+ segments
+ 1, n
+ s
+ segments
)]
771 myfaces
.extend(myface
)
778 for f
in range(0, segments
):
786 for f
in range(len(myvertex
) - segments
, len(myvertex
)):
790 return myvertex
, myfaces