1 # SPDX-FileCopyrightText: 2015-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """ Get POV-Ray specific objects In and Out of Blender """
7 from math
import pi
, cos
, sin
10 from bpy_extras
.object_utils
import object_data_add
11 from bpy_extras
.io_utils
import ImportHelper
12 from bpy
.utils
import register_class
, unregister_class
13 from bpy
.types
import Operator
15 from bpy
.props
import (
24 from mathutils
import Vector
, Matrix
26 from . import model_primitives
28 class POV_OT_lathe_add(Operator
):
29 """Add the representation of POV lathe using a screw modifier."""
31 bl_idname
= "pov.addlathe"
33 bl_description
= "adds lathe"
34 bl_options
= {'REGISTER', 'UNDO'}
35 COMPAT_ENGINES
= {"POVRAY_RENDER"}
37 def execute(self
, context
):
40 bpy
.ops
.curve
.primitive_bezier_curve_add(
41 location
=context
.scene
.cursor
.location
,
45 ob
= context
.view_layer
.objects
.active
47 ob
.name
= ob_data
.name
= "PovLathe"
48 ob_data
.dimensions
= "2D"
49 ob_data
.transform(Matrix
.Rotation(-pi
/ 2.0, 4, "Z"))
50 ob
.pov
.object_as
= "LATHE"
52 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
54 ob
.pov
.curveshape
= "lathe"
55 bpy
.ops
.object.modifier_add(type="SCREW")
56 mod
= ob
.modifiers
[-1]
58 mod
.show_render
= False
59 ob
.update_tag() # as prop set via python not updated in depsgraph
63 def pov_superellipsoid_define(context
, op
, ob
):
64 """Create the proxy mesh of a POV superellipsoid using pov_superellipsoid_define()."""
74 se_param1
= n2
# op.se_param1
75 se_param2
= n1
# op.se_param2
86 se_param1
= ob
.pov
.se_param1
87 se_param2
= ob
.pov
.se_param2
92 stepSegment
= 360 / v
* pi
/ 180
98 for ring
in range(0, u
- 1):
100 for segment
in range(0, v
):
102 angSegment
+= stepSegment
103 x
= r
* (abs(cos(angRing
)) ** n1
) * (abs(cos(angSegment
)) ** n2
)
104 if (cos(angRing
) < 0 < cos(angSegment
)) or (cos(angRing
) > 0 > cos(angSegment
)):
106 y
= r
* (abs(cos(angRing
)) ** n1
) * (abs(sin(angSegment
)) ** n2
)
107 if (cos(angRing
) < 0 < sin(angSegment
)) or (cos(angRing
) > 0 > sin(angSegment
)):
109 z
= r
* (abs(sin(angRing
)) ** n1
)
115 verts
.append((x
, y
, z
))
116 if edit
== "TRIANGLES":
117 verts
.extend([(0, 0, 1),(0, 0, -1)])
121 for i
in range(0, u
- 2):
123 for p
in range(0, v
):
125 face
= (m
+ p
, 1 + m
+ p
, v
+ 1 + m
+ p
, v
+ m
+ p
)
127 face
= (m
+ p
, m
, v
+ m
, v
+ m
+ p
)
129 if edit
== "TRIANGLES":
130 indexUp
= len(verts
) - 2
131 indexDown
= len(verts
) - 1
132 indexStartDown
= len(verts
) - 2 - v
133 for i
in range(0, v
):
135 face
= (indexDown
, i
, i
+ 1)
138 face
= (indexDown
, i
, 0)
140 for i
in range(0, v
):
142 face
= (indexUp
, i
+ indexStartDown
, i
+ indexStartDown
+ 1)
145 face
= (indexUp
, i
+ indexStartDown
, indexStartDown
)
148 face
= list(range(v
))
151 indexUp
= len(verts
) - 1
152 for i
in range(0, v
):
153 face
.append(indexUp
- i
)
155 mesh
= model_primitives
.pov_define_mesh(mesh
, verts
, [], faces
, "SuperEllipsoid")
158 ob
= object_data_add(context
, mesh
, operator
=None)
159 # engine = context.scene.render.engine what for?
161 ob
.name
= ob
.data
.name
= "PovSuperellipsoid"
162 ob
.pov
.se_param1
= n2
163 ob
.pov
.se_param2
= n1
169 ob
.pov
.se_edit
= edit
171 bpy
.ops
.object.mode_set(mode
="EDIT")
172 bpy
.ops
.mesh
.hide(unselected
=False)
173 bpy
.ops
.object.mode_set(mode
="OBJECT")
174 ob
.data
.set_sharp_from_angle(angle
=1.3)
175 ob
.pov
.object_as
= "SUPERELLIPSOID"
176 ob
.update_tag() # as prop set via python not updated in depsgraph
178 class POV_OT_superellipsoid_add(Operator
):
179 """Add the representation of POV superellipsoid using the pov_superellipsoid_define()."""
181 bl_idname
= "pov.addsuperellipsoid"
182 bl_label
= "Add SuperEllipsoid"
183 bl_description
= "Create a SuperEllipsoid"
184 bl_options
= {'REGISTER', 'UNDO'}
185 COMPAT_ENGINES
= {"POVRAY_RENDER"}
187 # Keep in sync within model_properties.py section Superellipsoid
188 # as this allows interactive update
189 # If someone knows how to define operators' props from a func, I'd be delighted to learn it!
190 # XXX ARE the first two used for import ? could we hide or suppress them otherwise?
191 se_param1
: FloatProperty(name
="Parameter 1", description
="", min=0.00, max=10.0, default
=0.04)
193 se_param2
: FloatProperty(name
="Parameter 2", description
="", min=0.00, max=10.0, default
=0.04)
196 name
="U-segments", description
="radial segmentation", default
=20, min=4, max=265
199 name
="V-segments", description
="lateral segmentation", default
=20, min=4, max=265
201 se_n1
: FloatProperty(
202 name
="Ring manipulator",
203 description
="Manipulates the shape of the Ring",
208 se_n2
: FloatProperty(
209 name
="Cross manipulator",
210 description
="Manipulates the shape of the cross-section",
215 se_edit
: EnumProperty(
216 items
=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")],
217 name
="Fill up and down",
223 def poll(cls
, context
):
224 engine
= context
.scene
.render
.engine
225 return engine
in cls
.COMPAT_ENGINES
227 def execute(self
, context
):
228 pov_superellipsoid_define(context
, self
, None)
231 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
237 class POV_OT_superellipsoid_update(Operator
):
238 """Update the superellipsoid.
240 Delete its previous proxy geometry and rerun pov_superellipsoid_define() function
241 with the new parameters"""
243 bl_idname
= "pov.superellipsoid_update"
245 bl_description
= "Update Superellipsoid"
246 bl_options
= {'REGISTER', 'UNDO'}
247 COMPAT_ENGINES
= {"POVRAY_RENDER"}
250 def poll(cls
, context
):
251 engine
= context
.scene
.render
.engine
253 return ob
and ob
.data
and ob
.type == "MESH" and engine
in cls
.COMPAT_ENGINES
255 def execute(self
, context
):
256 bpy
.ops
.object.mode_set(mode
="EDIT")
257 bpy
.ops
.mesh
.reveal()
258 bpy
.ops
.mesh
.select_all(action
="SELECT")
259 bpy
.ops
.mesh
.delete(type="VERT")
260 bpy
.ops
.object.mode_set(mode
="OBJECT")
262 pov_superellipsoid_define(context
, None, context
.object)
267 def create_faces(vert_idx_1
, vert_idx_2
, closed
=False, flipped
=False):
268 """Generate viewport proxy mesh data for some pov primitives"""
270 if not vert_idx_1
or not vert_idx_2
:
272 if len(vert_idx_1
) < 2 and len(vert_idx_2
) < 2:
275 if len(vert_idx_1
) != len(vert_idx_2
):
276 if len(vert_idx_1
) == 1 and len(vert_idx_2
) > 1:
280 total
= len(vert_idx_2
)
283 face
= [vert_idx_1
[0], vert_idx_2
[0], vert_idx_2
[total
- 1]]
285 face
.append(vert_idx_1
[total
- 1])
287 face
= [vert_idx_2
[0], vert_idx_1
[0]]
289 face
.append(vert_idx_1
[total
- 1])
290 face
.append(vert_idx_2
[total
- 1])
293 for num
in range(total
- 1):
296 face
= [vert_idx_2
[num
], vert_idx_1
[0], vert_idx_2
[num
+ 1]]
298 face
= [vert_idx_2
[num
], vert_idx_1
[num
], vert_idx_1
[num
+ 1], vert_idx_2
[num
+ 1]]
300 face
= [vert_idx_1
[0], vert_idx_2
[num
], vert_idx_2
[num
+ 1]]
302 face
= [vert_idx_1
[num
], vert_idx_2
[num
], vert_idx_2
[num
+ 1], vert_idx_1
[num
+ 1]]
308 """Workaround to negative a, where the math.pow() method would return a ValueError."""
309 return -((-a
) ** b
) if a
< 0 else a
**b
312 def supertoroid(R
, r
, u
, v
, n1
, n2
):
318 s
= power(sin(i
* a
), n1
)
319 c
= power(cos(i
* a
), n1
)
321 c2
= R
+ r
* power(cos(j
* b
), n2
)
322 s2
= r
* power(sin(j
* b
), n2
)
323 verts
.append((c
* c2
, s
* c2
, s2
)) # type as a (mathutils.Vector(c*c2,s*c2,s2))?
325 f
= create_faces(range((i
- 1) * v
, i
* v
), range(i
* v
, (i
+ 1) * v
), closed
=True)
327 f
= create_faces(range((u
- 1) * v
, u
* v
), range(v
), closed
=True)
332 def pov_supertorus_define(context
, op
, ob
):
333 """Get POV supertorus properties from operator (object creation/import) or data update."""
348 st_R
= ob
.pov
.st_major_radius
349 st_r
= ob
.pov
.st_minor_radius
352 st_n1
= ob
.pov
.st_ring
353 st_n2
= ob
.pov
.st_cross
355 st_edit
= ob
.pov
.st_edit
358 rad1
= (st_R
+ st_r
) / 2
359 rad2
= (st_R
- st_r
) / 2
361 [rad1
, rad2
] = [rad2
, rad1
]
367 verts
, faces
= supertoroid(rad1
, rad2
, st_u
, st_v
, st_n1
, st_n2
)
368 mesh
= model_primitives
.pov_define_mesh(mesh
, verts
, [], faces
, "PovSuperTorus", True)
370 ob
= object_data_add(context
, mesh
, operator
=None)
371 ob
.pov
.object_as
= "SUPERTORUS"
372 ob
.pov
.st_major_radius
= st_R
373 ob
.pov
.st_minor_radius
= st_r
376 ob
.pov
.st_ring
= st_n1
377 ob
.pov
.st_cross
= st_n2
379 ob
.pov
.st_edit
= st_edit
380 ob
.update_tag() # as prop set via python not updated in depsgraph
383 class POV_OT_supertorus_add(Operator
):
384 """Add the representation of POV supertorus using the pov_supertorus_define() function."""
386 bl_idname
= "pov.addsupertorus"
387 bl_label
= "Add Supertorus"
388 bl_description
= "Create a SuperTorus"
389 bl_options
= {'REGISTER', 'UNDO'}
390 COMPAT_ENGINES
= {"POVRAY_RENDER"}
394 description
="The radius inside the tube",
400 name
="small radius", description
="The radius of the tube", default
=0.3, min=0.01, max=100.0
403 name
="U-segments", description
="radial segmentation", default
=16, min=3, max=265
406 name
="V-segments", description
="lateral segmentation", default
=8, min=3, max=265
408 st_n1
: FloatProperty(
409 name
="Ring manipulator",
410 description
="Manipulates the shape of the Ring",
415 st_n2
: FloatProperty(
416 name
="Cross manipulator",
417 description
="Manipulates the shape of the cross-section",
423 name
="Use Int.+Ext. radii", description
="Use internal and external radii", default
=False
425 st_edit
: BoolProperty(name
="", description
="", default
=False, options
={"HIDDEN"})
428 def poll(cls
, context
):
429 engine
= context
.scene
.render
.engine
430 return engine
in cls
.COMPAT_ENGINES
432 def execute(self
, context
):
433 pov_supertorus_define(context
, self
, None)
436 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
441 class POV_OT_supertorus_update(Operator
):
442 """Update the supertorus.
444 Delete its previous proxy geometry and rerun pov_supetorus_define() function
445 with the new parameters"""
447 bl_idname
= "pov.supertorus_update"
449 bl_description
= "Update SuperTorus"
450 bl_options
= {'REGISTER', 'UNDO'}
451 COMPAT_ENGINES
= {"POVRAY_RENDER"}
454 def poll(cls
, context
):
455 engine
= context
.scene
.render
.engine
457 return ob
and ob
.data
and ob
.type == "MESH" and engine
in cls
.COMPAT_ENGINES
459 def execute(self
, context
):
460 bpy
.ops
.object.mode_set(mode
="EDIT")
461 bpy
.ops
.mesh
.reveal()
462 bpy
.ops
.mesh
.select_all(action
="SELECT")
463 bpy
.ops
.mesh
.delete(type="VERT")
464 bpy
.ops
.object.mode_set(mode
="OBJECT")
466 pov_supertorus_define(context
, None, context
.object)
471 # -----------------------------------------------------------------------------
472 class POV_OT_loft_add(Operator
):
473 """Create the representation of POV loft using Blender curves."""
475 bl_idname
= "pov.addloft"
476 bl_label
= "Add Loft Data"
477 bl_description
= "Create a Curve data for Meshmaker"
478 bl_options
= {'REGISTER', 'UNDO'}
479 COMPAT_ENGINES
= {"POVRAY_RENDER"}
482 name
="Segments", description
="Vertical segments", default
=16, min=3, max=720
484 loft_rings_bottom
: IntProperty(
485 name
="Bottom", description
="Bottom rings", default
=5, min=2, max=100
487 loft_rings_side
: IntProperty(name
="Side", description
="Side rings", default
=10, min=2, max=100)
488 loft_thick
: FloatProperty(
490 description
="Manipulates the shape of the Ring",
495 loft_r
: FloatProperty(name
="Radius", description
="Radius", default
=1, min=0.01, max=10)
496 loft_height
: FloatProperty(
498 description
="Manipulates the shape of the Ring",
504 def execute(self
, context
):
506 props
= self
.properties
507 loft_data
= bpy
.data
.curves
.new("Loft", type="CURVE")
508 loft_data
.dimensions
= "3D"
509 loft_data
.resolution_u
= 2
510 # loft_data.show_normal_face = False # deprecated in 2.8
512 thick
= props
.loft_thick
513 side
= props
.loft_rings_side
514 bottom
= props
.loft_rings_bottom
515 h
= props
.loft_height
521 for i
in range(bottom
+ 1):
527 coords
.append((x
, y
, z
))
530 nurbs
= loft_data
.splines
.new("NURBS")
531 nurbs
.points
.add(len(coords
) - 1)
532 for c
, coord
in enumerate(coords
):
534 nurbs
.points
[c
].co
= (x
, y
, z
, 1)
535 nurbs
.use_cyclic_u
= True
536 for i
in range(side
):
543 coords
.append((x
, y
, z
))
545 nurbs
= loft_data
.splines
.new("NURBS")
546 nurbs
.points
.add(len(coords
) - 1)
547 for c
, coord
in enumerate(coords
):
549 nurbs
.points
[c
].co
= (x
, y
, z
, 1)
550 nurbs
.use_cyclic_u
= True
552 for i
in range(side
):
558 coords
.append((x
, y
, z
))
560 nurbs
= loft_data
.splines
.new("NURBS")
561 nurbs
.points
.add(len(coords
) - 1)
562 for c
, coord
in enumerate(coords
):
564 nurbs
.points
[c
].co
= (x
, y
, z
, 1)
565 nurbs
.use_cyclic_u
= True
568 distB
= (r
- 0.00001) / bottom
569 for i
in range(bottom
+ 1):
575 coords
.append((x
, y
, z
))
578 nurbs
= loft_data
.splines
.new("NURBS")
579 nurbs
.points
.add(len(coords
) - 1)
580 for c
, coord
in enumerate(coords
):
582 nurbs
.points
[c
].co
= (x
, y
, z
, 1)
583 nurbs
.use_cyclic_u
= True
584 ob
= bpy
.data
.objects
.new("Loft_shape", loft_data
)
585 scn
= bpy
.context
.scene
586 scn
.collection
.objects
.link(ob
)
587 context
.view_layer
.objects
.active
= ob
589 ob
.pov
.curveshape
= "loft"
593 # ----------------------------------- ISOSURFACES ----------------------------------- #
596 def pov_isosurface_view_define(context
, op
, ob
, loc
):
597 """create the representation of POV isosurface using a Blender empty."""
600 eq
= op
.isosurface_eq
602 loc
= bpy
.context
.scene
.cursor
.location
606 eq
= ob
.pov
.isosurface_eq
608 # keep object rotation and location for the add object operator
609 obrot
= ob
.rotation_euler
610 # obloc = ob.location
613 # bpy.ops.object.empty_add(type='CUBE', location=loc, rotation=obrot)
614 bpy
.ops
.mesh
.primitive_emptyvert_add()
616 # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
617 bpy
.ops
.transform
.resize(value
=obscale
)
618 # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
619 bpy
.ops
.object.mode_set(mode
="OBJECT")
621 # bpy.ops.object.empty_add(type='CUBE', location=loc)
622 bpy
.ops
.mesh
.primitive_emptyvert_add()
624 ob
.name
= ob
.data
.name
= "PovIsosurface"
625 ob
.pov
.object_as
= "ISOSURFACE_VIEW"
626 ob
.pov
.isosurface_eq
= eq
627 ob
.pov
.contained_by
= "box"
628 bpy
.ops
.object.mode_set(mode
="OBJECT")
629 ob
.update_tag() # as prop set via python not updated in depsgraph
632 class POV_OT_isosurface_add(Operator
):
633 """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
635 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
636 to keep proxy consistency by hiding edit geometry."""
638 bl_idname
= "pov.addisosurface"
639 bl_label
= "Generic Isosurface"
640 bl_description
= "Add Isosurface"
641 bl_options
= {'REGISTER', 'UNDO'}
642 COMPAT_ENGINES
= {"POVRAY_RENDER"}
644 # Keep in sync within model_properties.py section Sphere
645 # as this allows interactive update
646 isosurface_eq
: StringProperty(
648 description
="Type the POV Isosurface function syntax for equation, "
649 "pattern,etc. ruling an implicit surface to be rendered",
650 default
="sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 1.5",
652 imported_loc
: FloatVectorProperty(
653 name
="Imported Pov location", precision
=6, default
=(0.0, 0.0, 0.0)
656 def execute(self
, context
):
657 # layers = 20*[False]
659 props
= self
.properties
660 if ob
:= context
.object:
661 if ob
.pov
.imported_loc
:
662 LOC
= ob
.pov
.imported_loc
663 elif not props
.imported_loc
:
664 LOC
= bpy
.context
.scene
.cursor
.location
666 LOC
= props
.imported_loc
668 pov_isosurface_view_define(context
, self
, None, LOC
)
670 {"INFO"}, "This native POV-Ray primitive " "is only an abstract proxy in Blender"
672 except AttributeError:
673 self
.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
677 class POV_OT_isosurface_update(Operator
):
678 """Update the POV isosurface.
680 Rerun pov_isosurface_view_define() function
681 with the new parameters"""
683 bl_idname
= "pov.isosurface_update"
685 bl_description
= "Update Isosurface"
686 bl_options
= {'REGISTER', 'UNDO'}
687 COMPAT_ENGINES
= {"POVRAY_RENDER"}
690 def poll(cls
, context
):
691 engine
= context
.scene
.render
.engine
693 return ob
and ob
.data
and ob
.type == "ISOSURFACE_VIEW" and engine
in cls
.COMPAT_ENGINES
695 def execute(self
, context
):
697 pov_isosurface_view_define(context
, None, context
.object, context
.object.location
)
702 class POV_OT_isosurface_box_add(Operator
):
703 """Add the representation of POV isosurface box using also just a Blender mesh cube.
705 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
706 to keep proxy consistency by hiding edit geometry."""
708 bl_idname
= "pov.addisosurfacebox"
709 bl_label
= "Isosurface Box"
710 bl_description
= "Add Isosurface contained by Box"
711 bl_options
= {'REGISTER', 'UNDO'}
712 COMPAT_ENGINES
= {"POVRAY_RENDER"}
714 def execute(self
, context
):
715 # layers = 20*[False]
717 bpy
.ops
.mesh
.primitive_cube_add()
719 bpy
.ops
.object.mode_set(mode
="EDIT")
721 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
723 bpy
.ops
.mesh
.hide(unselected
=False)
724 bpy
.ops
.object.mode_set(mode
="OBJECT")
725 ob
.pov
.object_as
= "ISOSURFACE_NODE"
726 ob
.pov
.contained_by
= "box"
727 ob
.name
= "PovIsosurfaceBox"
728 ob
.update_tag() # as prop set via python not updated in depsgraph
732 class POV_OT_isosurface_sphere_add(Operator
):
733 """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
735 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
736 to keep proxy consistency by hiding edit geometry."""
738 bl_idname
= "pov.addisosurfacesphere"
739 bl_label
= "Isosurface Sphere"
740 bl_description
= "Add Isosurface contained by Sphere"
741 bl_options
= {'REGISTER', 'UNDO'}
742 COMPAT_ENGINES
= {"POVRAY_RENDER"}
744 def execute(self
, context
):
745 # layers = 20*[False]
747 bpy
.ops
.mesh
.primitive_ico_sphere_add(subdivisions
=4)
749 bpy
.ops
.object.mode_set(mode
="EDIT")
751 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
753 bpy
.ops
.mesh
.hide(unselected
=False)
754 bpy
.ops
.object.mode_set(mode
="OBJECT")
755 bpy
.ops
.object.shade_smooth()
756 ob
.pov
.object_as
= "ISOSURFACE_NODE"
757 ob
.pov
.contained_by
= "sphere"
758 ob
.name
= "PovIsosurfaceSphere"
759 ob
.update_tag() # as prop set via python not updated in depsgraph
763 class POV_OT_sphere_sweep_add(Operator
):
764 """Add the representation of POV sphere_sweep using a Blender NURBS curve.
766 Flag its primitive type with a specific ob.pov.curveshape attribute and
767 leave access to edit mode to keep user editable handles."""
769 bl_idname
= "pov.addspheresweep"
770 bl_label
= "Sphere Sweep"
771 bl_description
= "Create Sphere Sweep along curve"
772 bl_options
= {'REGISTER', 'UNDO'}
773 COMPAT_ENGINES
= {"POVRAY_RENDER"}
775 def execute(self
, context
):
776 # layers = 20*[False]
778 bpy
.ops
.curve
.primitive_nurbs_curve_add()
780 ob
.name
= ob
.data
.name
= "PovSphereSweep"
781 ob
.pov
.curveshape
= "sphere_sweep"
782 ob
.data
.bevel_depth
= 0.02
783 ob
.data
.bevel_resolution
= 4
784 ob
.data
.fill_mode
= "FULL"
785 # ob.data.splines[0].order_u = 4
790 class POV_OT_blobsphere_add(Operator
):
791 """Add the representation of POV blob using a Blender meta ball.
793 No need to flag its primitive type as meta are exported to blobs
794 and leave access to edit mode to keep user editable thresholds."""
796 bl_idname
= "pov.addblobsphere"
797 bl_label
= "Blob Sphere"
798 bl_description
= "Add Blob Sphere"
799 bl_options
= {'REGISTER', 'UNDO'}
800 COMPAT_ENGINES
= {"POVRAY_RENDER"}
802 def execute(self
, context
):
803 # layers = 20*[False]
805 bpy
.ops
.object.metaball_add(type="BALL")
811 class POV_OT_blobcapsule_add(Operator
):
812 """Add the representation of POV blob using a Blender meta ball.
814 No need to flag its primitive type as meta are exported to blobs
815 and leave access to edit mode to keep user editable thresholds."""
817 bl_idname
= "pov.addblobcapsule"
818 bl_label
= "Blob Capsule"
819 bl_description
= "Add Blob Capsule"
820 bl_options
= {'REGISTER', 'UNDO'}
821 COMPAT_ENGINES
= {"POVRAY_RENDER"}
823 def execute(self
, context
):
824 # layers = 20*[False]
826 bpy
.ops
.object.metaball_add(type="CAPSULE")
832 class POV_OT_blobplane_add(Operator
):
833 """Add the representation of POV blob using a Blender meta ball.
835 No need to flag its primitive type as meta are exported to blobs
836 and leave access to edit mode to keep user editable thresholds."""
838 bl_idname
= "pov.addblobplane"
839 bl_label
= "Blob Plane"
840 bl_description
= "Add Blob Plane"
841 bl_options
= {'REGISTER', 'UNDO'}
842 COMPAT_ENGINES
= {"POVRAY_RENDER"}
844 def execute(self
, context
):
845 # layers = 20*[False]
847 bpy
.ops
.object.metaball_add(type="PLANE")
853 class POV_OT_blobellipsoid_add(Operator
):
854 """Add the representation of POV blob using a Blender meta ball.
856 No need to flag its primitive type as meta are exported to blobs
857 and leave access to edit mode to keep user editable thresholds."""
859 bl_idname
= "pov.addblobellipsoid"
860 bl_label
= "Blob Ellipsoid"
861 bl_description
= "Add Blob Ellipsoid"
864 def execute(self
, context
):
865 # layers = 20*[False]
867 bpy
.ops
.object.metaball_add(type="ELLIPSOID")
873 class POV_OT_blobcube_add(Operator
):
874 """Add the representation of POV blob using a Blender meta ball.
876 No need to flag its primitive type as meta are exported to blobs
877 and leave access to edit mode to keep user editable thresholds."""
879 bl_idname
= "pov.addblobcube"
880 bl_label
= "Blob Cube"
881 bl_description
= "Add Blob Cube"
882 bl_options
= {'REGISTER', 'UNDO'}
883 COMPAT_ENGINES
= {"POVRAY_RENDER"}
885 def execute(self
, context
):
886 # layers = 20*[False]
888 bpy
.ops
.object.metaball_add(type="CUBE")
894 class POV_OT_height_field_add(bpy
.types
.Operator
, ImportHelper
):
895 """Add the representation of POV height_field using a displaced grid.
897 texture slot fix and displace modifier will be needed because noise
898 displace operator was deprecated in 2.8"""
900 bl_idname
= "pov.addheightfield"
901 bl_label
= "Height Field"
902 bl_description
= "Add Height Field"
903 bl_options
= {'REGISTER', 'UNDO'}
904 COMPAT_ENGINES
= {"POVRAY_RENDER"}
906 # Keep in sync within model_properties.py section HeightFields
907 # as this allows interactive update
909 # filename_ext = ".png"
911 # filter_glob = StringProperty(
912 # default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF",
913 # options={'HIDDEN'},
915 quality
: IntProperty(name
="Quality", description
="", default
=100, min=1, max=100)
916 hf_filename
: StringProperty(maxlen
=1024)
918 hf_gamma
: FloatProperty(name
="Gamma", description
="Gamma", min=0.0001, max=20.0, default
=1.0)
920 hf_premultiplied
: BoolProperty(name
="Premultiplied", description
="Premultiplied", default
=True)
922 hf_smooth
: BoolProperty(name
="Smooth", description
="Smooth", default
=False)
924 hf_water
: FloatProperty(
925 name
="Water Level", description
="Wather Level", min=0.00, max=1.00, default
=0.0
928 hf_hierarchy
: BoolProperty(name
="Hierarchy", description
="Height field hierarchy", default
=True)
930 def execute(self
, context
):
931 props
= self
.properties
932 impath
= bpy
.path
.abspath(self
.filepath
)
933 img
= bpy
.data
.images
.load(impath
)
935 im_name
, file_extension
= os
.path
.splitext(im_name
)
936 hf_tex
= bpy
.data
.textures
.new("%s_hf_image" % im_name
, type="IMAGE")
938 mat
= bpy
.data
.materials
.new("Tex_%s_hf" % im_name
)
939 hf_slot
= mat
.pov_texture_slots
.add()
940 hf_slot
.texture
= hf_tex
.name
941 # layers = 20*[False]
943 quality
= props
.quality
945 w
, h
= hf_tex
.image
.size
[:]
948 bpy
.ops
.mesh
.primitive_grid_add(x_subdivisions
=w
, y_subdivisions
=h
, size
=0.5)
950 ob
.name
= ob
.data
.name
= "%s" % im_name
951 ob
.data
.materials
.append(mat
)
952 bpy
.ops
.object.mode_set(mode
="EDIT")
953 # bpy.ops.mesh.noise(factor=1) # TODO replace by displace modifier, noise deprecated in 2.8
954 bpy
.ops
.object.mode_set(mode
="OBJECT")
956 # needs a loop to select by index?
957 # bpy.ops.object.material_slot_remove()
958 # material just left there for now
960 mat
.pov_texture_slots
.clear()
961 bpy
.ops
.object.mode_set(mode
="EDIT")
962 bpy
.ops
.mesh
.hide(unselected
=False)
963 bpy
.ops
.object.mode_set(mode
="OBJECT")
964 ob
.pov
.object_as
= "HEIGHT_FIELD"
965 # POV-Ray will soon use only forwards slashes on every OS and already can
966 forward_impath
= impath
.replace(os
.sep
, "/")
967 ob
.pov
.hf_filename
= forward_impath
968 ob
.update_tag() # as prop set via python not updated in depsgraph
972 # ----------------------------------- PARAMETRIC ----------------------------------- #
973 def pov_parametric_define(context
, op
, ob
):
974 """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon.
976 Picking properties either from creation operator, import, or data update.
977 But flag its primitive type with a specific pov.object_as attribute and lock edit mode
978 to keep proxy consistency by hiding edit geometry."""
998 # keep object rotation and location for the updated object
1000 obrot
= ob
.rotation_euler
# In radians
1001 # Parametric addon has no loc rot, some extra work is needed
1002 # in case cursor has moved
1003 curloc
= bpy
.context
.scene
.cursor
.location
1005 bpy
.ops
.object.mode_set(mode
="EDIT")
1006 bpy
.ops
.mesh
.reveal()
1007 bpy
.ops
.mesh
.select_all(action
="SELECT")
1008 bpy
.ops
.mesh
.delete(type="VERT")
1009 bpy
.ops
.mesh
.primitive_xyz_function_surface(
1018 bpy
.ops
.mesh
.select_all(action
="SELECT")
1020 bpy
.ops
.transform
.translate(value
=(obloc
- curloc
), proportional_size
=1)
1021 # XXX TODO : https://devtalk.blender.org/t/bpy-ops-transform-rotate-option-axis/6235/7
1022 # to complete necessary extra work rotation, after updating from blender version > 2.92
1023 # update and uncomment below, but simple axis deprecated since 2.8
1024 # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
1026 bpy
.ops
.mesh
.hide(unselected
=False)
1027 bpy
.ops
.object.mode_set(mode
="OBJECT")
1030 bpy
.ops
.mesh
.primitive_xyz_function_surface(
1040 ob
.name
= ob
.data
.name
= "PovParametric"
1042 ob
.pov
.u_min
= u_min
1043 ob
.pov
.u_max
= u_max
1044 ob
.pov
.v_min
= v_min
1045 ob
.pov
.v_max
= v_max
1050 bpy
.ops
.object.mode_set(mode
="EDIT")
1051 bpy
.ops
.mesh
.hide(unselected
=False)
1052 bpy
.ops
.object.mode_set(mode
="OBJECT")
1053 ob
.data
.set_sharp_from_angle(angle
=0.6)
1054 ob
.pov
.object_as
= "PARAMETRIC"
1055 ob
.update_tag() # as prop set via python not updated in depsgraph
1058 class POV_OT_parametric_add(Operator
):
1059 """Add the representation of POV parametric surfaces using pov_parametric_define() function."""
1061 bl_idname
= "pov.addparametric"
1062 bl_label
= "Parametric"
1063 bl_description
= "Add Paramertic"
1064 bl_options
= {'REGISTER', 'UNDO'}
1065 COMPAT_ENGINES
= {"POVRAY_RENDER"}
1067 # Keep in sync within model_properties.py section Parametric primitive
1068 # as this allows interactive update
1069 u_min
: FloatProperty(name
="U Min", description
="", default
=0.0)
1070 v_min
: FloatProperty(name
="V Min", description
="", default
=0.0)
1071 u_max
: FloatProperty(name
="U Max", description
="", default
=6.28)
1072 v_max
: FloatProperty(name
="V Max", description
="", default
=12.57)
1073 x_eq
: StringProperty(maxlen
=1024, default
="cos(v)*(1+cos(u))*sin(v/8)")
1074 y_eq
: StringProperty(maxlen
=1024, default
="sin(u)*sin(v/8)+cos(v/8)*1.5")
1075 z_eq
: StringProperty(maxlen
=1024, default
="sin(v)*(1+cos(u))*sin(v/8)")
1077 def execute(self
, context
):
1078 props
= self
.properties
1087 pov_parametric_define(context
, self
, None)
1090 "This native POV-Ray primitive " "won't have any vertex to show in edit mode",
1092 except AttributeError:
1093 self
.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
1097 class POV_OT_parametric_update(Operator
):
1098 """Update the representation of POV parametric surfaces.
1100 Delete its previous proxy geometry and rerun pov_parametric_define() function
1101 with the new parameters"""
1103 bl_idname
= "pov.parametric_update"
1105 bl_description
= "Update parametric object"
1106 bl_options
= {'REGISTER', 'UNDO'}
1107 COMPAT_ENGINES
= {"POVRAY_RENDER"}
1110 def poll(cls
, context
):
1111 engine
= context
.scene
.render
.engine
1113 return ob
and ob
.data
and ob
.type == "MESH" and engine
in cls
.COMPAT_ENGINES
1115 def execute(self
, context
):
1117 pov_parametric_define(context
, None, context
.object)
1122 # -----------------------------------------------------------------------------
1125 class POV_OT_polygon_to_circle_add(Operator
):
1126 """Add the proxy mesh for POV Polygon to circle lofting macro"""
1128 bl_idname
= "pov.addpolygontocircle"
1129 bl_label
= "Polygon To Circle Blending"
1130 bl_description
= "Add Polygon To Circle Blending Surface"
1131 bl_options
= {'REGISTER', 'UNDO'}
1132 COMPAT_ENGINES
= {"POVRAY_RENDER"}
1134 # Keep in sync within model_properties.py section PolygonToCircle properties
1135 # as this allows interactive update
1136 polytocircle_resolution
: IntProperty(
1137 name
="Resolution", description
="", default
=3, min=0, max=256
1139 polytocircle_ngon
: IntProperty(name
="NGon", description
="", min=3, max=64, default
=5)
1140 polytocircle_ngonR
: FloatProperty(name
="NGon Radius", description
="", default
=0.3)
1141 polytocircle_circleR
: FloatProperty(name
="Circle Radius", description
="", default
=1.0)
1143 def execute(self
, context
):
1144 props
= self
.properties
1145 ngon
= props
.polytocircle_ngon
1146 ngonR
= props
.polytocircle_ngonR
1147 circleR
= props
.polytocircle_circleR
1148 resolution
= props
.polytocircle_resolution
1149 # layers = 20*[False]
1151 bpy
.ops
.mesh
.primitive_circle_add(
1152 vertices
=ngon
, radius
=ngonR
, fill_type
="NGON", enter_editmode
=True
1154 bpy
.ops
.transform
.translate(value
=(0, 0, 1))
1155 bpy
.ops
.mesh
.subdivide(number_cuts
=resolution
)
1156 numCircleVerts
= ngon
+ (ngon
* resolution
)
1157 bpy
.ops
.mesh
.select_all(action
="DESELECT")
1158 bpy
.ops
.mesh
.primitive_circle_add(
1159 vertices
=numCircleVerts
, radius
=circleR
, fill_type
="NGON", enter_editmode
=True
1161 bpy
.ops
.transform
.translate(value
=(0, 0, -1))
1162 bpy
.ops
.mesh
.select_all(action
="SELECT")
1163 bpy
.ops
.mesh
.bridge_edge_loops()
1165 bpy
.ops
.mesh
.select_all(action
="DESELECT")
1166 bpy
.ops
.mesh
.primitive_circle_add(
1167 vertices
=ngon
, radius
=ngonR
, fill_type
="TRIFAN", enter_editmode
=True
1169 bpy
.ops
.transform
.translate(value
=(0, 0, 1))
1170 bpy
.ops
.mesh
.select_all(action
="SELECT")
1171 bpy
.ops
.mesh
.remove_doubles()
1172 bpy
.ops
.object.mode_set(mode
="OBJECT")
1174 ob
.name
= "Polygon_To_Circle"
1176 ob
.pov
.ngonR
= ngonR
1177 ob
.pov
.circleR
= circleR
1178 bpy
.ops
.object.mode_set(mode
="EDIT")
1179 bpy
.ops
.mesh
.hide(unselected
=False)
1180 bpy
.ops
.object.mode_set(mode
="OBJECT")
1181 ob
.pov
.object_as
= "POLYCIRCLE"
1182 ob
.update_tag() # as prop set via python not updated in depsgraph
1188 POV_OT_superellipsoid_add
,
1189 POV_OT_superellipsoid_update
,
1190 POV_OT_supertorus_add
,
1191 POV_OT_supertorus_update
,
1193 POV_OT_isosurface_add
,
1194 POV_OT_isosurface_update
,
1195 POV_OT_isosurface_box_add
,
1196 POV_OT_isosurface_sphere_add
,
1197 POV_OT_sphere_sweep_add
,
1198 POV_OT_blobsphere_add
,
1199 POV_OT_blobcapsule_add
,
1200 POV_OT_blobplane_add
,
1201 POV_OT_blobellipsoid_add
,
1202 POV_OT_blobcube_add
,
1203 POV_OT_height_field_add
,
1204 POV_OT_parametric_add
,
1205 POV_OT_parametric_update
,
1206 POV_OT_polygon_to_circle_add
,
1216 for cls
in reversed(classes
):
1217 unregister_class(cls
)